1# This module provides a LALR interactive parser, which is used for debugging and error handling 2 3from copy import copy 4 5from .. import Token 6from ..exceptions import UnexpectedToken 7 8 9class InteractiveParser(object): 10 """InteractiveParser gives you advanced control over parsing and error handling when parsing with LALR. 11 12 For a simpler interface, see the ``on_error`` argument to ``Lark.parse()``. 13 """ 14 def __init__(self, parser, parser_state, lexer_state): 15 self.parser = parser 16 self.parser_state = parser_state 17 self.lexer_state = lexer_state 18 19 def feed_token(self, token): 20 """Feed the parser with a token, and advance it to the next state, as if it received it from the lexer. 21 22 Note that ``token`` has to be an instance of ``Token``. 23 """ 24 return self.parser_state.feed_token(token, token.type == '$END') 25 26 def exhaust_lexer(self): 27 """Try to feed the rest of the lexer state into the interactive parser. 28 29 Note that this modifies the instance in place and does not feed an '$END' Token""" 30 for token in self.lexer_state.lex(self.parser_state): 31 self.parser_state.feed_token(token) 32 33 def feed_eof(self, last_token=None): 34 """Feed a '$END' Token. Borrows from 'last_token' if given.""" 35 eof = Token.new_borrow_pos('$END', '', last_token) if last_token is not None else Token('$END', '', 0, 1, 1) 36 return self.feed_token(eof) 37 38 39 def __copy__(self): 40 """Create a new interactive parser with a separate state. 41 42 Calls to feed_token() won't affect the old instance, and vice-versa. 43 """ 44 return type(self)( 45 self.parser, 46 copy(self.parser_state), 47 copy(self.lexer_state), 48 ) 49 50 def copy(self): 51 return copy(self) 52 53 def __eq__(self, other): 54 if not isinstance(other, InteractiveParser): 55 return False 56 57 return self.parser_state == other.parser_state and self.lexer_state == other.lexer_state 58 59 def as_immutable(self): 60 """Convert to an ``ImmutableInteractiveParser``.""" 61 p = copy(self) 62 return ImmutableInteractiveParser(p.parser, p.parser_state, p.lexer_state) 63 64 def pretty(self): 65 """Print the output of ``choices()`` in a way that's easier to read.""" 66 out = ["Parser choices:"] 67 for k, v in self.choices().items(): 68 out.append('\t- %s -> %r' % (k, v)) 69 out.append('stack size: %s' % len(self.parser_state.state_stack)) 70 return '\n'.join(out) 71 72 def choices(self): 73 """Returns a dictionary of token types, matched to their action in the parser. 74 75 Only returns token types that are accepted by the current state. 76 77 Updated by ``feed_token()``. 78 """ 79 return self.parser_state.parse_conf.parse_table.states[self.parser_state.position] 80 81 def accepts(self): 82 """Returns the set of possible tokens that will advance the parser into a new valid state.""" 83 accepts = set() 84 for t in self.choices(): 85 if t.isupper(): # is terminal? 86 new_cursor = copy(self) 87 try: 88 new_cursor.feed_token(Token(t, '')) 89 except UnexpectedToken: 90 pass 91 else: 92 accepts.add(t) 93 return accepts 94 95 def resume_parse(self): 96 """Resume automated parsing from the current state.""" 97 return self.parser.parse_from_state(self.parser_state) 98 99 100 101class ImmutableInteractiveParser(InteractiveParser): 102 """Same as ``InteractiveParser``, but operations create a new instance instead 103 of changing it in-place. 104 """ 105 106 result = None 107 108 def __hash__(self): 109 return hash((self.parser_state, self.lexer_state)) 110 111 def feed_token(self, token): 112 c = copy(self) 113 c.result = InteractiveParser.feed_token(c, token) 114 return c 115 116 def exhaust_lexer(self): 117 """Try to feed the rest of the lexer state into the parser. 118 119 Note that this returns a new ImmutableInteractiveParser and does not feed an '$END' Token""" 120 cursor = self.as_mutable() 121 cursor.exhaust_lexer() 122 return cursor.as_immutable() 123 124 def as_mutable(self): 125 """Convert to an ``InteractiveParser``.""" 126 p = copy(self) 127 return InteractiveParser(p.parser, p.parser_state, p.lexer_state) 128 129 130# Deprecated class names for the interactive parser 131ParserPuppet = InteractiveParser 132ImmutableParserPuppet = ImmutableInteractiveParser 133