Skip to content

Commit 29b3847

Browse files
committed
Defines lr1 parser
1 parent b4fa2db commit 29b3847

File tree

3 files changed

+53
-26
lines changed

3 files changed

+53
-26
lines changed

src/syntactes/parser/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from .exception import NotAcceptedError, ParserError, UnexpectedTokenError
22
from .execute import ExecutablesRegistry, execute_on
3-
from .parser import LR0Parser, SLRParser
3+
from .parser import LR0Parser, SLRParser, LR1Parser

src/syntactes/parser/parser.py

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
from abc import ABC
12
from collections import deque
2-
from typing import Iterable
3+
from typing import Iterable, Type
34

4-
from syntactes import Grammar, LR0Generator, SLRGenerator, Token
5+
from syntactes import Grammar, LR0Generator, LR1Generator, SLRGenerator, Token
56
from syntactes._action import Action, ActionType
67
from syntactes._state import LR0State
78
from syntactes.parser import (
@@ -10,28 +11,26 @@
1011
ParserError,
1112
UnexpectedTokenError,
1213
)
13-
from syntactes.parsing_table import LR0ParsingTable
14+
from syntactes.parsing_table import LR0ParsingTable, ParsingTable
1415

1516

16-
class LR0Parser:
17-
"""
18-
Parses streams of tokens based on the configured parsing table.
19-
"""
17+
class Parser(ABC):
18+
generator_cls: Type
2019

21-
def __init__(self, table: LR0ParsingTable) -> None:
20+
def __init__(self, table: ParsingTable) -> None:
2221
self._table = table
2322
self._token_stack: deque[Token] = deque()
2423
self._state_stack: deque[LR0State] = deque()
2524
self._token_stream: deque[Token] = deque()
2625

27-
@staticmethod
28-
def from_grammar(grammar: Grammar) -> "LR0Parser":
26+
@classmethod
27+
def from_grammar(cls, grammar: Grammar) -> "Parser":
2928
"""
3029
Create a parser for the given grammar.
3130
"""
32-
generator = LR0Generator(grammar)
31+
generator = cls.generator_cls(grammar)
3332
parsing_table = generator.generate()
34-
parser = LR0Parser(parsing_table)
33+
parser = cls(parsing_table)
3534
return parser
3635

3736
def parse(self, stream: Iterable[Token]) -> None:
@@ -107,17 +106,13 @@ def _raise(self, error: ParserError) -> None:
107106
raise error from None
108107

109108

110-
class SLRParser(LR0Parser):
111-
"""
112-
Parses streams of tokens based on the configured parsing table.
113-
"""
109+
class LR0Parser(Parser):
110+
generator_cls = LR0Generator
114111

115-
@staticmethod
116-
def from_grammar(grammar: Grammar) -> "SLRParser":
117-
"""
118-
Create a parser for the given grammar.
119-
"""
120-
generator = SLRGenerator(grammar)
121-
parsing_table = generator.generate()
122-
parser = SLRParser(parsing_table)
123-
return parser
112+
113+
class SLRParser(Parser):
114+
generator_cls = SLRGenerator
115+
116+
117+
class LR1Parser(Parser):
118+
generator_cls = LR1Generator

src/syntactes/tests/test_parser.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@
44
from syntactes.parser import (
55
ExecutablesRegistry,
66
LR0Parser,
7+
LR1Parser,
78
ParserError,
89
SLRParser,
910
execute_on,
1011
)
1112
from syntactes.tests.data import (
1213
EOF,
14+
LPAREN,
1315
PLUS,
16+
RPAREN,
1417
lr0_parsing_table,
18+
lr1_parsing_table,
1519
rule_2_1,
1620
rule_4_1,
1721
slr_parsing_table,
@@ -144,3 +148,31 @@ def test_x_x_eof_raises(self):
144148
@args(x, PLUS, x, EOF)
145149
def test_x_plus_x(self):
146150
self.result()
151+
152+
153+
class TestLR1Parser(TestCase):
154+
def parser(self):
155+
return self._parser
156+
157+
def setUp(self):
158+
self._parser = LR1Parser(lr1_parsing_table())
159+
160+
def assert_parser_error(self):
161+
self.assertResultRaises(ParserError)
162+
163+
164+
class TestLR1ParserParse(TestLR1Parser):
165+
def subject(self, *stream):
166+
return self.parser().parse(stream)
167+
168+
@args(LPAREN, RPAREN)
169+
def test_no_eof_raises(self):
170+
self.assert_parser_error()
171+
172+
@args(LPAREN, RPAREN, EOF)
173+
def test_valid_syntax_does_not_raise(self):
174+
self.result()
175+
176+
@args(LPAREN, RPAREN, RPAREN, EOF)
177+
def test_invalid_syntax_raises(self):
178+
self.assert_parser_error()

0 commit comments

Comments
 (0)