def __init__(self, opt): self.opt = opt self.sym = SymbolTable(opt) self.out_str = None self.lex = Lexer()
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
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