Exemplo n.º 1
0
 def __init__(self, opt):
     self.opt = opt
     self.sym = SymbolTable(opt)
     self.out_str = None
     self.lex = Lexer()
Exemplo n.º 2
0
 def __init__(self, opt):
     self.opt = opt
     self.sym = SymbolTable(opt)
     self.out_str = None
     self.lex = Lexer()
Exemplo n.º 3
0
class MacroParser(object):
    """
    The macro language parser and code generator class.
    """
    # pylint: disable=too-few-public-methods

    re_is_int = re.compile("^[-+]?[0-9]+$")
    re_is_hex = re.compile("^0[xX][0-9a-fA-F]+$")


    # Class constructor
    ###############################################################################
    def __init__(self, opt):
        self.opt = opt
        self.sym = SymbolTable(opt)
        self.out_str = None
        self.lex = Lexer()

    # function parse
    #
    # The used grammar is:
    # data:           /* empty */
    #               | data GIBBERISH
    #               | data IDENTIFIER
    #               | data '{:' data ':}'
    #               | data if_block
    #               ;
    #
    # if_block:     IF '(' exp_or ')' '{:' data ':}' elif_blocks else_block
    #               ;
    #
    # elif_blocks:    /* empty */
    #               | elif_blocks ELIF '(' exp_or ')' '{:' data ':}'
    #               ;
    #
    # else_block:     /* empty */
    #               | ELSE '{:' data ':}'
    #               ;
    #
    # exp_or:         exp_and
    #               | exp_or TOK_OR exp_and
    #               ;
    #
    # exp_and:        term
    #               | exp_and TOK_AND exp_comparison
    #               ;
    #
    # exp_comparison: term TOK_COMPARISON term
    #               ;
    #
    # term:           LITERAL
    #               | IDENTIFIER
    #               | '(' exp_or ')'
    #               ;
    ###############################################################################
    def parse(self, in_str):
        """
        Parse a macro string.
        """
        self.lex.set_str(in_str)
        self.out_str = ""
        self._parse_data(do_print=True)

        tok = self.lex.peek()
        if tok != self.lex.tok_eof:
            raise ParseError(
                "{0:s}: error: misaligned closing block '{1:s}'"
                .format(sys.argv[0], self.lex.text))


    # function _parse_data
    ###############################################################################
    def _parse_data(self, do_print):
        """
        Private top-level parsing function.
        """
        tok = self.lex.peek()
        while tok != self.lex.tok_eof:
            if tok == self.lex.tok_gibberish:
                self._parse_gibberish(do_print)
            elif tok == self.lex.tok_block_open:
                self._parse_data_block(do_print)
            elif tok == self.lex.tok_identifier and self.lex.text == "if":
                self._parse_if_block(do_print)
            elif tok == self.lex.tok_identifier:
                self._parse_identifier(do_print)
            elif tok == self.lex.tok_block_close:
                return
            else:
                raise ParseError(
                    "{0:s}: error: wrong token '{1:s}'".format(sys.argv[0], self.lex.text))
            tok = self.lex.peek()


    # function _parse_gibberish
    ###############################################################################
    def _parse_gibberish(self, do_print):
        """
        Parse gibberish.
        Actually, just print the characters in 'text' if do_print is True.
        """
        if do_print:
            self.out_str = self.out_str + self.lex.text
        self.lex.advance()


    # function _parse_identifier
    ###############################################################################
    def _parse_identifier(self, do_print):
        """
        Parse an identifier.
        """
        try:
            sym_value = self.sym.get_terminal(self.lex.text)
        except SymbolLookupError:
            raise ParseError(
                "{0:s}: error: unknown terminal '{1:s}'"
                .format(sys.argv[0], self.lex.text))
        self.lex.advance()
        if do_print:
            self.lex.prepend(sym_value)


    # function _parse_if_block
    ###############################################################################
    def _parse_if_block(self, do_print):
        """
        Parse an if block.
        """
        # parse the expression following the 'if' and the associated block.
        exp_res = self._parse_conditional_block(do_print)
        do_print = do_print and not exp_res

        # try $elif
        tok = self.lex.peek()
        while tok == self.lex.tok_identifier and self.lex.text == "elif":
            exp_res = self._parse_conditional_block(do_print)
            do_print = do_print and not exp_res
            tok = self.lex.peek()

        # try $else
        if tok == self.lex.tok_identifier and self.lex.text == "else":
            # get rid of the tok_identifier, 'else' and following spaces
            self.lex.advance()
            self.lex.delete_spaces()

            # expect a data block
            self._parse_data_block(do_print)



    # function _parse_conditional_block
    ###############################################################################
    def _parse_conditional_block(self, do_print):
        """
        Parse a conditional block (such as $if or $elif).
        Return the truth value of the expression.
        """
        # get rid of the tok_identifier, 'if' or 'elif'
        self.lex.advance()
        self.lex.set_state(self.lex.state_expr)

        # expect an open parenthesis
        tok = self.lex.peek()
        if tok != self.lex.tok_par_open:
            raise ParseError(
                "{0:s}: error: open parenthesis expected: '{1:s}'"
                .format(sys.argv[0], self.lex.text))
        self.lex.advance()

        # parse the boolean expression
        exp_res = self._parse_exp_or()

        # expect a closed parenthesis
        tok = self.lex.peek()
        if tok != self.lex.tok_par_close:
            raise ParseError(
                "{0:s}: error: closed parenthesis expected: '{1:s}'"
                .format(sys.argv[0], self.lex.text))
        self.lex.advance()

        # get rid of eventual spaces, and switch back to gibberish.
        self.lex.delete_spaces()
        self.lex.set_state(self.lex.state_gibberish)

        # expect a data block
        self._parse_data_block(do_print and exp_res)

        # get rid of eventual spaces
        # but only if followed by $if, $else or $elif
        self.lex.delete_spaces(skip_unconditional=False)

        return exp_res


    # function _parse_data_block
    ###############################################################################
    def _parse_data_block(self, do_print):
        """
        Parse a data block.
        """
        # expect an open block
        tok = self.lex.peek()
        if tok != self.lex.tok_block_open:
            raise ParseError(
                "{0:s}: error: open block expected: '{1:s}'"
                .format(sys.argv[0], self.lex.text))
        self.lex.advance(skip_nl=True)

        # more data follows...
        self._parse_data(do_print)

        # expect a closed block
        tok = self.lex.peek()
        if tok != self.lex.tok_block_close:
            raise ParseError(
                "{0:s}: error: closed block expected: '{1:s}'"
                .format(sys.argv[0], self.lex.text))
        self.lex.advance(skip_nl=True)


    # function _parse_exp_or
    ###############################################################################
    def _parse_exp_or(self):
        """
        Parse a boolean 'or' expression.
        """
        ret = False
        while True:
            ret = self._parse_exp_and() or ret

            # is the expression terminated?
            tok = self.lex.peek()
            if tok == self.lex.tok_par_close:
                return ret
            # expect an 'or' token.
            elif tok == self.lex.tok_or:
                self.lex.advance()
            # everything else is the end of the expression.
            # Let the caling function worry about error reporting.
            else:
                return ret
        return False


    # function _parse_exp_and
    ###############################################################################
    def _parse_exp_and(self):
        """
        Parse a boolean 'and' expression.
        """
        ret = True
        while True:
            ret = self._parse_exp_comparison() and ret

            # is the expression terminated?
            tok = self.lex.peek()
            if tok == self.lex.tok_par_close:
                return ret
            # expect an 'and' token.
            elif tok == self.lex.tok_and:
                self.lex.advance()
            # everything else is a parse error.
            else:
                return ret
        return False


    # function _parse_exp_comparison
    ###############################################################################
    def _parse_exp_comparison(self):
        """
        Parse a boolean comparison.
        """
        # left hand side of the comparison
        lhs = self._parse_exp_term()

        # expect a comparison
        tok = self.lex.peek()
        if tok != self.lex.tok_op:
            raise ParseError(
                "{0:s}: error: operator expected: '{1:s}'"
                .format(sys.argv[0], self.lex.text))
        operator = self.lex.text
        self.lex.advance()

        # right hand side of the comparison
        rhs = self._parse_exp_term()

        # if both operands ar numbers, convert them
        num_l = self._get_num(lhs)
        num_r = self._get_num(rhs)
        if num_l != None and num_r != None:
            lhs = num_l
            rhs = num_r

        # now calculate the result of the comparison, whatever that means
        if operator == "<=":
            ret = lhs <= rhs
        elif operator == "<":
            ret = lhs < rhs
        elif operator == "==":
            ret = lhs == rhs
        elif operator == "!=":
            ret = lhs != rhs
        elif operator == ">=":
            ret = lhs >= rhs
        elif operator == ">":
            ret = lhs > rhs
        else:
            raise ParseError(
                "{0:s}: error: unknow operator: '{1:s}'"
                .format(sys.argv[0], self.lex.text))
        return ret


    # function _parse_exp_term
    ###############################################################################
    def _parse_exp_term(self):
        """
        Parse a terminal.
        """
        tok = self.lex.peek()

        # identifier
        if tok == self.lex.tok_identifier:
            try:
                ret = self.sym.get_terminal(self.lex.text)
            except SymbolLookupError:
                raise ParseError(
                    "{0:s}: error: unknown terminal '{1:s}'"
                    .format(sys.argv[0], self.lex.text))
            if ret == None:
                ret = "Undefined"
        # string
        elif tok == self.lex.tok_str:
            ret = self.lex.text
        # number
        elif tok == self.lex.tok_num:
            ret = self.lex.text
        # parenthesised expression
        elif tok == self.lex.tok_par_open:
            self.lex.advance()
            ret = self._parse_exp_or()
            tok = self.lex.peek()
            if tok != self.lex.tok_par_close:
                raise ParseError(
                    "{0:s}: error: closed parenthesis expected: '{1:s}'"
                    .format(sys.argv[0], self.lex.text))
        self.lex.advance()
        return ret


    # function _get_num
    ###############################################################################
    def _get_num(self, in_str):
        """
        Check if in_str is a number and return the numeric value.
        """
        ret = None

        if in_str != None:
            match = self.re_is_int.match(in_str)
            if match != None:
                ret = int(in_str)

            match = self.re_is_hex.match(in_str)
            if match != None:
                ret = int(in_str, 16)

        return ret
Exemplo n.º 4
0
class MacroParser(object):
    """
    The macro language parser and code generator class.
    """
    # pylint: disable=too-few-public-methods

    re_is_int = re.compile("^[-+]?[0-9]+$")
    re_is_hex = re.compile("^0[xX][0-9a-fA-F]+$")

    # Class constructor
    ###############################################################################
    def __init__(self, opt):
        self.opt = opt
        self.sym = SymbolTable(opt)
        self.out_str = None
        self.lex = Lexer()

    # function parse
    #
    # The used grammar is:
    # data:           /* empty */
    #               | data GIBBERISH
    #               | data IDENTIFIER
    #               | data '{:' data ':}'
    #               | data if_block
    #               ;
    #
    # if_block:     IF '(' exp_or ')' '{:' data ':}' elif_blocks else_block
    #               ;
    #
    # elif_blocks:    /* empty */
    #               | elif_blocks ELIF '(' exp_or ')' '{:' data ':}'
    #               ;
    #
    # else_block:     /* empty */
    #               | ELSE '{:' data ':}'
    #               ;
    #
    # exp_or:         exp_and
    #               | exp_or TOK_OR exp_and
    #               ;
    #
    # exp_and:        term
    #               | exp_and TOK_AND exp_comparison
    #               ;
    #
    # exp_comparison: term TOK_COMPARISON term
    #               ;
    #
    # term:           LITERAL
    #               | IDENTIFIER
    #               | '(' exp_or ')'
    #               ;
    ###############################################################################
    def parse(self, in_str):
        """
        Parse a macro string.
        """
        self.lex.set_str(in_str)
        self.out_str = ""
        self._parse_data(do_print=True)

        tok = self.lex.peek()
        if tok != self.lex.tok_eof:
            raise ParseError(
                "{0:s}: error: misaligned closing block '{1:s}'".format(
                    sys.argv[0], self.lex.text))

    # function _parse_data
    ###############################################################################
    def _parse_data(self, do_print):
        """
        Private top-level parsing function.
        """
        tok = self.lex.peek()
        while tok != self.lex.tok_eof:
            if tok == self.lex.tok_gibberish:
                self._parse_gibberish(do_print)
            elif tok == self.lex.tok_block_open:
                self._parse_data_block(do_print)
            elif tok == self.lex.tok_identifier and self.lex.text == "if":
                self._parse_if_block(do_print)
            elif tok == self.lex.tok_identifier:
                self._parse_identifier(do_print)
            elif tok == self.lex.tok_block_close:
                return
            else:
                raise ParseError("{0:s}: error: wrong token '{1:s}'".format(
                    sys.argv[0], self.lex.text))
            tok = self.lex.peek()

    # function _parse_gibberish
    ###############################################################################
    def _parse_gibberish(self, do_print):
        """
        Parse gibberish.
        Actually, just print the characters in 'text' if do_print is True.
        """
        if do_print:
            self.out_str = self.out_str + self.lex.text
        self.lex.advance()

    # function _parse_identifier
    ###############################################################################
    def _parse_identifier(self, do_print):
        """
        Parse an identifier.
        """
        try:
            sym_value = self.sym.get_terminal(self.lex.text)
        except SymbolLookupError:
            raise ParseError("{0:s}: error: unknown terminal '{1:s}'".format(
                sys.argv[0], self.lex.text))
        self.lex.advance()
        if do_print:
            self.lex.prepend(sym_value)

    # function _parse_if_block
    ###############################################################################
    def _parse_if_block(self, do_print):
        """
        Parse an if block.
        """
        # parse the expression following the 'if' and the associated block.
        exp_res = self._parse_conditional_block(do_print)
        do_print = do_print and not exp_res

        # try $elif
        tok = self.lex.peek()
        while tok == self.lex.tok_identifier and self.lex.text == "elif":
            exp_res = self._parse_conditional_block(do_print)
            do_print = do_print and not exp_res
            tok = self.lex.peek()

        # try $else
        if tok == self.lex.tok_identifier and self.lex.text == "else":
            # get rid of the tok_identifier, 'else' and following spaces
            self.lex.advance()
            self.lex.delete_spaces()

            # expect a data block
            self._parse_data_block(do_print)

    # function _parse_conditional_block
    ###############################################################################
    def _parse_conditional_block(self, do_print):
        """
        Parse a conditional block (such as $if or $elif).
        Return the truth value of the expression.
        """
        # get rid of the tok_identifier, 'if' or 'elif'
        self.lex.advance()
        self.lex.set_state(self.lex.state_expr)

        # expect an open parenthesis
        tok = self.lex.peek()
        if tok != self.lex.tok_par_open:
            raise ParseError(
                "{0:s}: error: open parenthesis expected: '{1:s}'".format(
                    sys.argv[0], self.lex.text))
        self.lex.advance()

        # parse the boolean expression
        exp_res = self._parse_exp_or()

        # expect a closed parenthesis
        tok = self.lex.peek()
        if tok != self.lex.tok_par_close:
            raise ParseError(
                "{0:s}: error: closed parenthesis expected: '{1:s}'".format(
                    sys.argv[0], self.lex.text))
        self.lex.advance()

        # get rid of eventual spaces, and switch back to gibberish.
        self.lex.delete_spaces()
        self.lex.set_state(self.lex.state_gibberish)

        # expect a data block
        self._parse_data_block(do_print and exp_res)

        # get rid of eventual spaces
        # but only if followed by $if, $else or $elif
        self.lex.delete_spaces(skip_unconditional=False)

        return exp_res

    # function _parse_data_block
    ###############################################################################
    def _parse_data_block(self, do_print):
        """
        Parse a data block.
        """
        # expect an open block
        tok = self.lex.peek()
        if tok != self.lex.tok_block_open:
            raise ParseError(
                "{0:s}: error: open block expected: '{1:s}'".format(
                    sys.argv[0], self.lex.text))
        self.lex.advance(skip_nl=True)

        # more data follows...
        self._parse_data(do_print)

        # expect a closed block
        tok = self.lex.peek()
        if tok != self.lex.tok_block_close:
            raise ParseError(
                "{0:s}: error: closed block expected: '{1:s}'".format(
                    sys.argv[0], self.lex.text))
        self.lex.advance(skip_nl=True)

    # function _parse_exp_or
    ###############################################################################
    def _parse_exp_or(self):
        """
        Parse a boolean 'or' expression.
        """
        ret = False
        while True:
            ret = self._parse_exp_and() or ret

            # is the expression terminated?
            tok = self.lex.peek()
            if tok == self.lex.tok_par_close:
                return ret
            # expect an 'or' token.
            elif tok == self.lex.tok_or:
                self.lex.advance()
            # everything else is the end of the expression.
            # Let the caling function worry about error reporting.
            else:
                return ret
        return False

    # function _parse_exp_and
    ###############################################################################
    def _parse_exp_and(self):
        """
        Parse a boolean 'and' expression.
        """
        ret = True
        while True:
            ret = self._parse_exp_comparison() and ret

            # is the expression terminated?
            tok = self.lex.peek()
            if tok == self.lex.tok_par_close:
                return ret
            # expect an 'and' token.
            elif tok == self.lex.tok_and:
                self.lex.advance()
            # everything else is a parse error.
            else:
                return ret
        return False

    # function _parse_exp_comparison
    ###############################################################################
    def _parse_exp_comparison(self):
        """
        Parse a boolean comparison.
        """
        # left hand side of the comparison
        lhs = self._parse_exp_term()

        # expect a comparison
        tok = self.lex.peek()
        if tok != self.lex.tok_op:
            raise ParseError("{0:s}: error: operator expected: '{1:s}'".format(
                sys.argv[0], self.lex.text))
        operator = self.lex.text
        self.lex.advance()

        # right hand side of the comparison
        rhs = self._parse_exp_term()

        # if both operands ar numbers, convert them
        num_l = self._get_num(lhs)
        num_r = self._get_num(rhs)
        if num_l != None and num_r != None:
            lhs = num_l
            rhs = num_r

        # now calculate the result of the comparison, whatever that means
        if operator == "<=":
            ret = lhs <= rhs
        elif operator == "<":
            ret = lhs < rhs
        elif operator == "==":
            ret = lhs == rhs
        elif operator == "!=":
            ret = lhs != rhs
        elif operator == ">=":
            ret = lhs >= rhs
        elif operator == ">":
            ret = lhs > rhs
        else:
            raise ParseError("{0:s}: error: unknow operator: '{1:s}'".format(
                sys.argv[0], self.lex.text))
        return ret

    # function _parse_exp_term
    ###############################################################################
    def _parse_exp_term(self):
        """
        Parse a terminal.
        """
        tok = self.lex.peek()

        # identifier
        if tok == self.lex.tok_identifier:
            try:
                ret = self.sym.get_terminal(self.lex.text)
            except SymbolLookupError:
                raise ParseError(
                    "{0:s}: error: unknown terminal '{1:s}'".format(
                        sys.argv[0], self.lex.text))
            if ret == None:
                ret = "Undefined"
        # string
        elif tok == self.lex.tok_str:
            ret = self.lex.text
        # number
        elif tok == self.lex.tok_num:
            ret = self.lex.text
        # parenthesised expression
        elif tok == self.lex.tok_par_open:
            self.lex.advance()
            ret = self._parse_exp_or()
            tok = self.lex.peek()
            if tok != self.lex.tok_par_close:
                raise ParseError(
                    "{0:s}: error: closed parenthesis expected: '{1:s}'".
                    format(sys.argv[0], self.lex.text))
        self.lex.advance()
        return ret

    # function _get_num
    ###############################################################################
    def _get_num(self, in_str):
        """
        Check if in_str is a number and return the numeric value.
        """
        ret = None

        if in_str != None:
            match = self.re_is_int.match(in_str)
            if match != None:
                ret = int(in_str)

            match = self.re_is_hex.match(in_str)
            if match != None:
                ret = int(in_str, 16)

        return ret