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 p_funccall_or_exp_plus(p: yacc.YaccProduction): """FUNCCALL_OR_EXPRESSION : PLUS FACTOR REC_UNARYEXPR REC_PLUS_MINUS_TERM OPT_REL_OP_NUM_EXPR | MINUS FACTOR REC_UNARYEXPR REC_PLUS_MINUS_TERM OPT_REL_OP_NUM_EXPR""" right_node = p[2]['node'] if p[1] == '-': right_node.value *= -1 if p[3]: result_type = check_type(p[3]['node'], right_node, p[3]['operation'], p.lineno(1)) right_node = Node(p[3]['node'], right_node, p[3]['operation'], result_type) if p[4]: result_type = check_type(p[4]['node'], right_node, p[4]['operation'], p.lineno(1)) right_node = Node(p[4]['node'], right_node, p[4]['operation'], result_type) num_expressions.append(right_node)
def p_relation_deprecated_comment(p: YaccProduction) -> None: "relation : class_ref ID multi REL multi class_ref ID MLS" if not (p[4] == "--"): LOGGER.warning( "DEPRECATION: use of %s in relation definition is deprecated, use -- (in %s)" % (p[4], Location(file, p.lineno(4)))) rel = DefineRelation((p[1], p[2], p[3]), (p[6], p[7], p[5])) rel.comment = str(p[8]) p[0] = rel attach_lnr(p, 2) deprecated_relation_warning(p)
def p_alias(self, p: P) -> None: if len(p) == 6: name, type, lineno, token = p[2], p[4], p.lineno(2), p[2] else: name, type, lineno, token = p[3], p[2], p.lineno(3), p[3] write_stderr( f"syntax warning: keyword typedef deprecated, suggestion: type {name} = ..." ) p[0] = alias = Alias( name=name, type=type, filepath=self.current_filepath(), lineno=lineno, token=token, indent=self.current_indent(p), scope_stack=self.current_scope_stack(), comment_block=self.collect_comment_block(), _bound=self.current_proto(), ) self.current_scope().push_member(alias)
def p_numexp(p: yacc.YaccProduction): """NUMEXPRESSION : TERM REC_PLUS_MINUS_TERM""" if p[2] is None: p[0] = p[1] else: result_type = check_type(p[1]['node'], p[2]['node'], p[2]['operation'], p.lineno(1)) p[0] = { 'node': Node(p[1]['node'], p[2]['node'], p[2]['operation'], result_type) }
def p_open_message_scope(self, p: P) -> None: message = Message( name=p[2], extensible=p[3], token=p[2], lineno=p.lineno(2), filepath=self.current_filepath(), indent=self.current_indent(p), comment_block=self.collect_comment_block(), scope_stack=self.current_scope_stack(), _bound=self.current_proto(), ) self.push_scope(message)
def p_open_enum_scope(self, p: P) -> None: enum = Enum( name=p[2], type=p[4], token=p[2], lineno=p.lineno(2), filepath=self.current_filepath(), indent=self.current_indent(p), comment_block=self.collect_comment_block(), scope_stack=self.current_scope_stack(), _bound=self.current_proto(), ) self.push_scope(enum)
def p_constant_reference_for_array_capacity(self, p: P) -> None: referenced = p[1] if isinstance(referenced, IntegerConstant): integer_constant = cast(IntegerConstant, referenced) p[0] = integer_constant.unwrap() # Using int format else: raise InvalidArrayCap( message= "Non integer constant referenced to use as array capacity.", filepath=self.current_filepath(), token=p[1].name, lineno=p.lineno(1), )
def p_constant_reference_for_calculation(self, p: P) -> None: referenced = p[1] if isinstance(referenced, IntegerConstant): integer_constant = cast(IntegerConstant, referenced) p[0] = integer_constant.unwrap() else: raise CalculationExpressionError( message= "Non integer constant referenced to use in calculation expression.", filepath=self.current_filepath(), token=p[1].name, lineno=p.lineno(1), )
def p_import(self, p: P) -> None: # Get filepath to import. importing_path = p[len(p) - 2] filepath = self._get_child_filepath(importing_path) # Check if this filepath already in parsing. if self._check_parsing_file(filepath): raise CyclicImport( message=f"cyclic importing {filepath}", filepath=self.current_filepath(), token=importing_path, lineno=p.lineno(2), ) # Check if this filepath already parsed by current proto. for _, proto in self.current_proto().protos(recursive=False): if os.path.samefile(proto.filepath, filepath): raise DuplicatedImport(filepath=self.current_filepath(), token=filepath, lineno=p.lineno(1)) # Parse. p[0] = child = self.parse_child(filepath) name = child.name if len(p) == 5: # Importing as `name` name = p[2] # Check if import (as) name already taken. if name in self.current_proto().members: raise DuplicatedDefinition( message="imported proto name already used", token=name, lineno=p.lineno(1), filepath=self.current_filepath(), ) # Push to current scope self.current_scope().push_member(child, name)
def p_statement_break(p: yacc.YaccProduction): """STATEMENT : BREAK SEMICOLON""" # If is not inside loop scope, consider semantic failure current_scope = scope_stack.seek() # Go into upper scopes trying to find a for loop while True: if current_scope.is_loop: break current_scope = current_scope.upper_scope if current_scope is None: raise BreakWithoutLoopError(p.lineno(2))
def p_error(self, p: P) -> None: filepath = self.current_filepath() if p is None: raise GrammarError(message="Grammar error at eof.", filepath=filepath) if isinstance(p, LexToken): raise GrammarError(filepath=filepath, token=str(p.value), lineno=p.lineno) if len(p) > 1: raise GrammarError(filepath=filepath, token=p.value(1), lineno=p.lineno(1)) raise GrammarError()
def p_option(self, p: P) -> None: name, value = p[2], p[4] p[0] = option = Option.from_value( value=value, name=name, scope_stack=self.current_scope_stack(), comment_block=self.collect_comment_block(), indent=self.current_indent(p), _bound=self.current_proto(), filepath=self.current_filepath(), lineno=p.lineno(2), token=p[2], ) self.current_scope().push_member(option)
def p_enum_field(self, p: P) -> None: name = p[1] value = p[3] field = EnumField( name=name, value=value, token=p[1], lineno=p.lineno(1), indent=self.current_indent(p), filepath=self.current_filepath(), scope_stack=self.current_scope_stack(), comment_block=self.collect_comment_block(), _bound=self.current_proto(), ) self.current_scope().push_member(field)
def p_term_unary_exp(p: yacc.YaccProduction): """TERM : UNARYEXPR REC_UNARYEXPR""" if p[2]: # If there's another operation being made result_type = check_type(p[1]['node'], p[2]['node'], p[2]['operation'], p.lineno(1)) p[0] = { 'node': Node(p[1]['node'], p[2]['node'], p[2]['operation'], result_type), 'operation': p[2]['operation'] } else: # Pass the UNARYEXPR node upwards p[0] = {'node': p[1]['node']}
def p_message_field(self, p: P) -> None: name = p[2] type = p[1] field_number = p[4] p[0] = message_field = MessageField( name=name, type=type, number=field_number, token=p[2], lineno=p.lineno(2), filepath=self.current_filepath(), comment_block=self.collect_comment_block(), indent=self.current_indent(p), scope_stack=self.current_scope_stack(), _bound=self.current_proto(), ) self.current_scope().push_member(message_field)
def p_enum_item_unsupported(self, p: P) -> None: if isinstance(p[1], Alias): raise AliasInEnumUnsupported.from_token(token=p[1]) if isinstance(p[1], Constant): raise ConstInEnumUnsupported.from_token(token=p[1]) if isinstance(p[1], Proto): raise ImportInEnumUnsupported.from_token(token=p[1]) if isinstance(p[1], Option): raise OptionInEnumUnsupported.from_token(token=p[1]) if isinstance(p[1], Enum): raise EnumInEnumUnsupported.from_token(token=p[1]) if isinstance(p[1], Message): raise MessageInEnumUnsupported.from_token(token=p[1]) if isinstance(p[1], MessageField): raise MessageFieldInEnumUnsupported.from_token(token=p[1]) raise StatementInMessageUnsupported(lineno=p.lineno(1), filepath=self.current_filepath())
def p_follow_ident_alloc(p: yacc.YaccProduction): """FOLLOW_IDENT : OPT_ALLOC_NUMEXP REC_UNARYEXPR REC_PLUS_MINUS_TERM OPT_REL_OP_NUM_EXPR""" node = None operation = '' if p[2]: node = p[2]['node'] operation = p[2]['operation'] if p[3]: if node is None: node = p[3]['node'] operation = p[3]['operation'] else: result_type = check_type(node, p[3]['node'], p[3]['operation'], p.lineno(0)) node = Node(node, p[3]['node'], p[3]['operation'], result_type) p[0] = {'vec_access': p[1], 'node': node, 'operation': operation}
def p_rec_plus_minus(p: yacc.YaccProduction): """REC_PLUS_MINUS_TERM : PLUS_OR_MINUS TERM REC_PLUS_MINUS_TERM | empty """ if len(p) < 3: # Case empty p[0] = None elif p[3]: # Case there's another recursive operation being made result_type = check_type(p[2]['node'], p[3]['node'], p[3]['operation'], p.lineno(1)) p[0] = { 'node': Node(p[2]['node'], p[3]['node'], p[3]['operation'], result_type), 'operation': p[1]['operation'] } else: # Case there's no more operattions to the right p[0] = {'node': p[2]['node'], 'operation': p[1]['operation']}
def p_const(self, p: P) -> None: name, value = p[2], p[4] constant_cls: Optional[T[Constant]] = None if isinstance(value, Constant): constant_ = cast(Constant, value) # Unwrap if value is a referenced constant. value = constant_.unwrap() p[0] = constant = Constant.from_value( value=value, name=name, scope_stack=self.current_scope_stack(), comment_block=self.collect_comment_block(), indent=self.current_indent(p), _bound=self.current_proto(), filepath=self.current_filepath(), token=p[2], lineno=p.lineno(2), ) self.current_scope().push_member(constant)
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 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_function_call(p: YaccProduction) -> None: "function_call : ns_ref '(' function_param_list ')'" assert namespace (args, kwargs, wrapped_kwargs) = p[3] p[0] = FunctionCall(p[1], args, kwargs, wrapped_kwargs, Location(file, p.lineno(2)), namespace)
def p_constructor(p: YaccProduction) -> None: "constructor : class_ref '(' param_list ')'" assert namespace p[0] = Constructor(p[1], p[3][0], p[3][1], Location(file, p.lineno(2)), namespace)
def __init__(self, prod: YaccProduction) -> None: self.line_number = prod.lineno(1) super(IllegalMultiwordError, self).__init__( f'Illegal multiword data at line {self.line_number}.')
def __init__(self, prod: YaccProduction) -> None: self.line_number = prod.lineno(1) super(IllegalEmptyNodeError, self).__init__( f'Illegal empty-node data at line {self.line_number}.')
def loc(p: yacc.YaccProduction) -> Dict[str, int]: return { 'line': p.lineno(1), 'column': find_column(p.lexpos(1)), }
def p_factor_expr(p: yacc.YaccProduction): """FACTOR : LPAREN NUMEXPRESSION RPAREN""" p[0] = p[2] num_expressions.append((p[2]['node'], p.lineno(1)))
def p_allocexp(p: yacc.YaccProduction): """ALLOCEXPRESSION : NEW DATATYPE LSQBRACKETS NUMEXPRESSION RSQBRACKETS OPT_ALLOC_NUMEXP""" num_expressions.append((p[4]['node'], p.lineno(1)))
def p_expression(p: yacc.YaccProduction): """EXPRESSION : NUMEXPRESSION OPT_REL_OP_NUM_EXPR""" num_expressions.append((p[1]['node'], p.lineno(1)))