def p_expr_number(p: YaccProduction): """expr : INTEGER | FLOAT | INTEGER FLOAT""" if len(p) == 2: p[0] = Number([p[1]], p.lineno(1), p.lineno(1), p.lexpos(1), p.lexpos(1) + len(str(p[1])) - 1) elif len(p) == 3: p[0] = Number([p[1] + p[2]], p.lineno(1), p.lineno(2), p.lexpos(1), p.lexpos(2) + len(str(p[2])))
def p_expr_variable(p: YaccProduction): """expr : ID""" # Get the the table of variable from parser variables: dict = p.parser.variables # If the variable has not been defined, raise error if p[1] not in variables.keys(): raise UndefineError(p[1], p.lineno(1), p.lexpos(1)) # If the variable has been defined, get variable from table p[0] = Variable(variables.get(p[1]), p.lineno(1), p.lineno(1), p.lexpos(1), p.lexpos(1) + len(p[1]) - 1)
def p_expr_arithmetic(p: YaccProduction): """expr : LPAREN expr RPAREN | expr PLUS expr | expr MINUS expr | expr TIMES expr | expr DIVIDE expr""" if p[1] == "(" and p[3] == ")": p[0] = Arithmetic("()", [p[2]], p.lineno(1), p.lineno(3), p.lexpos(1), p.lexpos(3)) else: p[0] = Arithmetic(p[2], [p[1], p[3]], p[1].start_line_number, p[3].end_line_number, p[1].start_position, p[3].end_position)
def make_none(p: YaccProduction, token: int) -> Literal: assert namespace none = Literal(NoneValue()) none.location = Location(file, p.lineno(token)) none.namespace = namespace none.lexpos = p.lexpos(token) return none
def copy_p_tracking(self, p: P, from_: int = 1, to: int = 0) -> None: """Don't know why P's tracking info (lexpos and lineno) sometimes missing. Particular in recursion grammar situation. We have to copy it manually. Add this function in a p_xxx function when: 1. the p[0] is gona to be used in another parsing target. 2. and the tracking information is gona to be used there. """ p.set_lexpos(to, p.lexpos(from_)) p.set_lineno(to, p.lineno(from_))
def merge_lnr_to_string(p: YaccProduction, starttoken: int = 1, endtoken: int = 2) -> None: assert namespace v = p[0] et = p[endtoken] st = p[starttoken] expanded_range: Range = expand_range( st.location, et.location) if isinstance( st, LocatableString) else et.location p[0] = LocatableString(v, expanded_range, p.lexpos(endtoken), namespace)
def p_expr_function(p: YaccProduction): """expr : ID LPAREN RPAREN | ID LPAREN args RPAREN""" # Judge whether the function has been defined if p[1] not in dir(builtins): raise UndefineError(p[1], p.lineno(1), p.lexpos(1)) # Judge the count of arguments if len(p) == 4: input_args_count = 0 elif len(p) == 5: print(p[3]) input_args_count = len(p[3]) func = getattr(builtins, p[1]) args_count = func.__code__.co_argcount # why minus 1: because builtin function will be injected argument of 'env' at the first position if input_args_count != args_count - 1: raise ArgumentError(p[1], p.lineno(1), p.lexpos(1), args_count-1, input_args_count) if len(p) == 4: p[0] = Function(p[1], [], p.lineno(1), p.lineno(3), p.lexpos(1), p.lexpos(3)) elif len(p) == 5: p[0] = Function(p[1], p[3], p.lineno(1), p.lineno(4), p.lexpos(1), p.lexpos(4))
def p_statement_assign(p: YaccProduction): """statement : ID ASSIGN expr SEMI""" # register variable in parser, and it will help us detect undefine error early variables: dict = p.parser.variables p[0] = AssignStatement(p[1], p[2], p[3], p.lineno(1), p.lineno(4), p.lexpos(1), p.lexpos(4)) variables[p[1]] = p[0]
def loc(p: yacc.YaccProduction) -> Dict[str, int]: return { 'line': p.lineno(1), 'column': find_column(p.lexpos(1)), }
def p_comment(self, p: P) -> None: comment = p[1] self.push_comment(comment) self.set_last_newline_pos(p.lexpos(2))
def p_expr_unary(p: YaccProduction): """expr : MINUS expr %prec UMINUS | PLUS expr %prec UPLUS""" p[0] = Unary(p[1], [p[2]], p.lineno(1), p[2].end_line_number, p.lexpos(1), p[2].end_position)
def p_expr_bool(p: YaccProduction): """expr : BOOL""" p[0] = Bool([p[1]], p.lineno(1), p.lineno(1), p.lexpos(1), p.lexpos(1) + len(str(p[1])))
def p_expr_string(p: YaccProduction): """expr : STRING""" p[0] = String([p[1]], p.lineno(1), p.lineno(1), p.lexpos(1), p.lexpos(1) + len(p[1]) - 1)
def p_newline(self, p: P) -> None: self.clear_comment_block() self.set_last_newline_pos(p.lexpos(1))
def attach_lnr(p: YaccProduction, token: int = 1) -> None: v = p[0] v.location = Location(file, p.lineno(token)) v.namespace = namespace v.lexpos = p.lexpos(token)
def p_statement_conditional(p: YaccProduction): """statement : expr COMMA expr SEMI""" p[0] = ConditionalStatement(p[1], p[3], p[1].start_line_number, p.lineno(4), p[1].start_position, p.lexpos(4))
def p_statement_normal(p: YaccProduction): """statement : expr SEMI""" p[0] = NormalStatement(p[1], p[1].start_line_number, p.lineno(2), p[1].start_position, p.lexpos(2))
def p_statement_if(p: YaccProduction): """statement : IF expr THEN BEGIN statements END""" p[0] = IfStatement(p[2], p[5], p.lineno(1), p.lineno(6), p.lexpos(1), p.lexpos(6)+len(p[6])-1)
def current_indent(self, p: P, i: int = 1) -> int: lexpos = p.lexpos(i) indent = lexpos - self.last_newline_pos - 1 if indent < 0: return -1 return indent