Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/lark/parsers/lalr_interactive_parser.py: 60%
73 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:30 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:30 +0000
1# This module provides a LALR interactive parser, which is used for debugging and error handling
3from typing import Iterator, List
4from copy import copy
5import warnings
7from lark.exceptions import UnexpectedToken
8from lark.lexer import Token, LexerThread
11class InteractiveParser:
12 """InteractiveParser gives you advanced control over parsing and error handling when parsing with LALR.
14 For a simpler interface, see the ``on_error`` argument to ``Lark.parse()``.
15 """
16 def __init__(self, parser, parser_state, lexer_thread: LexerThread):
17 self.parser = parser
18 self.parser_state = parser_state
19 self.lexer_thread = lexer_thread
20 self.result = None
22 @property
23 def lexer_state(self) -> LexerThread:
24 warnings.warn("lexer_state will be removed in subsequent releases. Use lexer_thread instead.", DeprecationWarning)
25 return self.lexer_thread
27 def feed_token(self, token: Token):
28 """Feed the parser with a token, and advance it to the next state, as if it received it from the lexer.
30 Note that ``token`` has to be an instance of ``Token``.
31 """
32 return self.parser_state.feed_token(token, token.type == '$END')
34 def iter_parse(self) -> Iterator[Token]:
35 """Step through the different stages of the parse, by reading tokens from the lexer
36 and feeding them to the parser, one per iteration.
38 Returns an iterator of the tokens it encounters.
40 When the parse is over, the resulting tree can be found in ``InteractiveParser.result``.
41 """
42 for token in self.lexer_thread.lex(self.parser_state):
43 yield token
44 self.result = self.feed_token(token)
46 def exhaust_lexer(self) -> List[Token]:
47 """Try to feed the rest of the lexer state into the interactive parser.
49 Note that this modifies the instance in place and does not feed an '$END' Token
50 """
51 return list(self.iter_parse())
54 def feed_eof(self, last_token=None):
55 """Feed a '$END' Token. Borrows from 'last_token' if given."""
56 eof = Token.new_borrow_pos('$END', '', last_token) if last_token is not None else self.lexer_thread._Token('$END', '', 0, 1, 1)
57 return self.feed_token(eof)
60 def __copy__(self):
61 """Create a new interactive parser with a separate state.
63 Calls to feed_token() won't affect the old instance, and vice-versa.
64 """
65 return type(self)(
66 self.parser,
67 copy(self.parser_state),
68 copy(self.lexer_thread),
69 )
71 def copy(self):
72 return copy(self)
74 def __eq__(self, other):
75 if not isinstance(other, InteractiveParser):
76 return False
78 return self.parser_state == other.parser_state and self.lexer_thread == other.lexer_thread
80 def as_immutable(self):
81 """Convert to an ``ImmutableInteractiveParser``."""
82 p = copy(self)
83 return ImmutableInteractiveParser(p.parser, p.parser_state, p.lexer_thread)
85 def pretty(self):
86 """Print the output of ``choices()`` in a way that's easier to read."""
87 out = ["Parser choices:"]
88 for k, v in self.choices().items():
89 out.append('\t- %s -> %r' % (k, v))
90 out.append('stack size: %s' % len(self.parser_state.state_stack))
91 return '\n'.join(out)
93 def choices(self):
94 """Returns a dictionary of token types, matched to their action in the parser.
96 Only returns token types that are accepted by the current state.
98 Updated by ``feed_token()``.
99 """
100 return self.parser_state.parse_conf.parse_table.states[self.parser_state.position]
102 def accepts(self):
103 """Returns the set of possible tokens that will advance the parser into a new valid state."""
104 accepts = set()
105 for t in self.choices():
106 if t.isupper(): # is terminal?
107 new_cursor = copy(self)
108 try:
109 new_cursor.feed_token(self.lexer_thread._Token(t, ''))
110 except UnexpectedToken:
111 pass
112 else:
113 accepts.add(t)
114 return accepts
116 def resume_parse(self):
117 """Resume automated parsing from the current state.
118 """
119 return self.parser.parse_from_state(self.parser_state, last_token=self.lexer_thread.state.last_token)
123class ImmutableInteractiveParser(InteractiveParser):
124 """Same as ``InteractiveParser``, but operations create a new instance instead
125 of changing it in-place.
126 """
128 result = None
130 def __hash__(self):
131 return hash((self.parser_state, self.lexer_thread))
133 def feed_token(self, token):
134 c = copy(self)
135 c.result = InteractiveParser.feed_token(c, token)
136 return c
138 def exhaust_lexer(self):
139 """Try to feed the rest of the lexer state into the parser.
141 Note that this returns a new ImmutableInteractiveParser and does not feed an '$END' Token"""
142 cursor = self.as_mutable()
143 cursor.exhaust_lexer()
144 return cursor.as_immutable()
146 def as_mutable(self):
147 """Convert to an ``InteractiveParser``."""
148 p = copy(self)
149 return InteractiveParser(p.parser, p.parser_state, p.lexer_thread)