def extract_args_and_kwargs( self, context: InterpretingTask, options: TokenSeq, ) -> tuple[list, dict]: """ Returns a pair of positional argument list and keyword argument dict. """ section_flipped = False # kwargs found args = [] kwargs = {} for keyword_name, value_token in self.tokenize_args(context, options): if keyword_name is not None: section_flipped = True if keyword_name in kwargs: raise PaxterRenderError( f"duplicated keyword {keyword_name} at %(pos)s", pos=CharLoc(context.src_text, options.start_pos), ) kwargs[keyword_name] = context.transform_token(value_token) elif section_flipped: raise PaxterRenderError( "found positional argument after keyword argument at %(pos)s", pos=CharLoc(context.src_text, options.start_pos), ) else: args.append(context.transform_token(value_token)) return args, kwargs
def tokenize_args( context: InterpretingTask, options: TokenSeq, ) -> tuple[Optional[str], Token]: """ Generates a sequence of arguments, each of which is a tuple pair of (argument name, argument value token). The first component may be None which indicates positional arguments. """ remains: list[Token] = list(options.children) while remains: # Checks whether the second token is an '=' operator # indicating the existence of keyword argument keyword_name = None if len(remains) >= 2: first_token, second_token = remains[0], remains[1] if second_token == Operator.without_pos(symbols='='): # Then the first token must be an identifier if not isinstance(first_token, Identifier): raise PaxterRenderError( "expected an identifier before the '=' sign at %(pos)s", pos=CharLoc(context.src_text, first_token.start_pos), ) keyword_name = first_token.name remains = remains[2:] # Expects the next value token to exist if not remains: raise PaxterRenderError( "expected a value after the '=' sign at %(pos)s", pos=CharLoc(context.src_text, options.end_pos), ) value_token = remains[0] remains = remains[1:] # Yields the next argument yield keyword_name, value_token # If tokens are still remaining, the next one has to be a ',' operator if remains: end_token = remains[0] if end_token != Operator.without_pos(symbols=','): raise PaxterRenderError( "expected a comma token after the value token at %(pos)s", pos=CharLoc(context.src_text, end_token.start_pos), ) remains = remains[1:]
def transform_operator(self, token: Operator): """ Transforms a given parsed operator. """ raise PaxterRenderError( "operator not expected at %(pos)", pos=CharLoc(self.src_text, token.start_pos), )
def transform_identifier(self, token: Identifier): """ Transforms a given parsed identifier. """ raise PaxterRenderError( "identifier not expected at %(pos)", pos=CharLoc(self.src_text, token.start_pos), )
def transform_token_list(self, seq: TokenSeq): """ Transforms a given parsed token list. """ raise PaxterRenderError( "token list not expected at %(pos)s", pos=CharLoc(self.src_text, seq.start_pos), )
def transform_command(self, token: Command) -> Any: """ Transforms a given parsed command. """ # Try to interp the phrase section # using the interp function from _phrase_eval_ try: phrase_eval = self.env['_phrase_eval_'] except KeyError as exc: raise PaxterRenderError( "expected '_phrase_eval_' to be defined at %(pos)s", pos=CharLoc(self.src_text, token.start_pos), ) from exc try: phrase_value = phrase_eval(token.phrase, self.env) except PaxterRenderError: raise except Exception as exc: raise PaxterRenderError( "paxter command phrase evaluation error at %(pos)s: " f"{token.phrase!r}", pos=CharLoc(self.src_text, token.start_pos), ) from exc # Bail out if options section and main arg section are empty if token.options is None and token.main_arg is None: return phrase_value # Wrap the function if not yet wrapped if not isinstance(phrase_value, BaseApply): phrase_value = NormalApply(phrase_value) # Make the call to the wrapped function try: return phrase_value.call(self, token) except PaxterRenderError: raise except Exception as exc: raise PaxterRenderError( "paxter apply evaluation error at %(pos)s", pos=CharLoc(self.src_text, token.start_pos), ) from exc
def transform_fragment(self, fragment: Fragment) -> Any: """ Transforms a given parsed fragment. """ if isinstance(fragment, Text): return self.transform_text(fragment) if isinstance(fragment, Command): return self.transform_command(fragment) raise PaxterRenderError( "unrecognized fragment at %(pos)s", pos=CharLoc(self.src_text, fragment.start_pos), )
def transform_token(self, token: Token) -> Any: """ Transforms a given parsed token. """ if isinstance(token, Fragment): return self.transform_fragment(token) if isinstance(token, TokenSeq): return self.transform_token_list(token) if isinstance(token, Identifier): return self.transform_identifier(token) if isinstance(token, Operator): return self.transform_operator(token) if isinstance(token, Number): return self.transform_number(token) if isinstance(token, FragmentSeq): return self.transform_fragment_list(token) raise PaxterRenderError( "unrecognized token at %(pos)s", pos=CharLoc(self.src_text, token.start_pos), )
def raise_error(message): raise PaxterRenderError( f"{message} in if statement at %(pos)s", pos=CharLoc(context.src_text, node.options.start_pos), )