from copy import deepcopy, copy from typing import Dict, Any, Generic, List from ..lexer import Token, LexerThread from ..common import ParserCallbacks from .lalr_analysis import Shift, ParseTableBase, StateT from lark.exceptions import UnexpectedToken ###{standalone class ParseConf(Generic[StateT]): __slots__ = 'parse_table', 'callbacks', 'start', 'start_state', 'end_state', 'states' parse_table: ParseTableBase[StateT] callbacks: ParserCallbacks start: str start_state: StateT end_state: StateT states: Dict[StateT, Dict[str, tuple]] def __init__(self, parse_table: ParseTableBase[StateT], callbacks: ParserCallbacks, start: str): self.parse_table = parse_table self.start_state = self.parse_table.start_states[start] self.end_state = self.parse_table.end_states[start] self.states = self.parse_table.states self.callbacks = callbacks self.start = start class ParserState(Generic[StateT]): __slots__ = 'parse_conf', 'lexer', 'state_stack', 'value_stack' parse_conf: ParseConf[StateT] lexer: LexerThread state_stack: List[StateT] value_stack: list def __init__(self, parse_conf: ParseConf[StateT], lexer: LexerThread, state_stack=None, value_stack=None): self.parse_conf = parse_conf self.lexer = lexer self.state_stack = state_stack or [self.parse_conf.start_state] self.value_stack = value_stack or [] @property def position(self) -> StateT: return self.state_stack[-1] # Necessary for match_examples() to work def __eq__(self, other) -> bool: if not isinstance(other, ParserState): return NotImplemented return len(self.state_stack) == len(other.state_stack) and self.position == other.position def __copy__(self): return self.copy() def copy(self, deepcopy_values=True) -> 'ParserState[StateT]': return type(self)( self.parse_conf, self.lexer, # XXX copy copy(self.state_stack), deepcopy(self.value_stack) if deepcopy_values else copy(self.value_stack), ) def feed_token(self, token: Token, is_end=False) -> Any: state_stack = self.state_stack value_stack = self.value_stack states = self.parse_conf.states end_state = self.parse_conf.end_state callbacks = self.parse_conf.callbacks while True: state = state_stack[-1] try: action, arg = states[state][token.type] except KeyError: expected = {s for s in states[state].keys() if s.isupper()} raise UnexpectedToken(token, expected, state=self, interactive_parser=None) assert arg != end_state if action is Shift: # shift once and return assert not is_end state_stack.append(arg) value_stack.append(token if token.type not in callbacks else callbacks[token.type](token)) return else: # reduce+shift as many times as necessary rule = arg size = len(rule.expansion) if size: s = value_stack[-size:] del state_stack[-size:] del value_stack[-size:] else: s = [] value = callbacks[rule](s) if callbacks else s _action, new_state = states[state_stack[-1]][rule.origin.name] assert _action is Shift state_stack.append(new_state) value_stack.append(value) if is_end and state_stack[-1] == end_state: return value_stack[-1] ###}