Skip to content

Commit 97528d7

Browse files
committed
Merge branch 'conflict-model'
2 parents ba9be4f + 7e9baec commit 97528d7

File tree

9 files changed

+139
-51
lines changed

9 files changed

+139
-51
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
clean:
2-
rm -rf src/syntactes/__pycache__ src/syntactes/tests/__pycache__ src/syntactes/parser/__pycache__
2+
rm -rf src/syntactes/__pycache__ src/syntactes/tests/__pycache__ src/syntactes/parser/__pycache__ src/syntactes/parsing_table/__pycache__
33
rm -rf dist src/syntactes.egg-info
44

55
test:

src/syntactes/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,3 @@
22
from .rule import Rule
33
from .grammar import Grammar
44
from .generator import LR0Generator, SLRGenerator
5-
from .table import LR0ParsingTable, SLRParsingTable

src/syntactes/generator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from syntactes import Grammar, Token
2-
from syntactes._action import Action, ActionType
2+
from syntactes._action import Action
33
from syntactes._item import LR0Item
44
from syntactes._state import LR0State
5-
from syntactes.table import Entry, LR0ParsingTable, SLRParsingTable
5+
from syntactes.parsing_table import Entry, LR0ParsingTable, SLRParsingTable
66

77

88
class LR0Generator:

src/syntactes/parser/parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
ParserError,
1111
UnexpectedTokenError,
1212
)
13-
from syntactes.table import LR0ParsingTable
13+
from syntactes.parsing_table import LR0ParsingTable
1414

1515

1616
class LR0Parser:
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .entry import Entry
2+
from .conflict import Conflict, ConflictType
3+
from .table import LR0ParsingTable, SLRParsingTable
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from enum import Enum
2+
3+
from syntactes import Token
4+
from syntactes._action import Action, ActionType
5+
from syntactes._state import LR0State
6+
7+
8+
class ConflictType(Enum):
9+
SHIFT_SHIFT = "shift/shift"
10+
SHIFT_REDUCE = "shift/reduce"
11+
REDUCE_REDUCE = "reduce/reduce"
12+
NO_CONFLICT = "NO_CONFLICT"
13+
14+
15+
class Conflict:
16+
def __init__(self, state: LR0State, token: Token, actions: list[Action]) -> None:
17+
self.state = state
18+
self.token = token
19+
self.actions = actions
20+
self._conflict_type = None
21+
22+
def pretty_str(self) -> str:
23+
"""
24+
Returns a pretty-formatted string with conflict details.
25+
"""
26+
string = f"{self.conflict_type.value} conflict in state {self.state}\n"
27+
string += f"Available actions on input token '{self.token}':\n"
28+
for action in self.actions:
29+
if action.action_type == ActionType.SHIFT:
30+
string += f"shift to {action.actionable}\n"
31+
elif action.action_type == ActionType.REDUCE:
32+
string += f"reduce by {action.actionable}\n"
33+
else:
34+
string += f"{action.action_type}, {action.actionable}\n"
35+
36+
return string
37+
38+
@property
39+
def conflict_type(self) -> ConflictType:
40+
if self._conflict_type is None:
41+
self._conflict_type = self._create_type()
42+
43+
return self._conflict_type
44+
45+
def _create_type(self) -> ConflictType:
46+
if not len(self.actions) > 1:
47+
return ConflictType.NO_CONFLICT
48+
49+
action_types = set(map(lambda a: a.action_type, self.actions))
50+
51+
if ActionType.SHIFT not in action_types:
52+
if ActionType.REDUCE not in action_types:
53+
return ConflictType.NO_CONFLICT
54+
55+
return ConflictType.REDUCE_REDUCE
56+
57+
if ActionType.REDUCE not in action_types:
58+
return ConflictType.SHIFT_SHIFT
59+
60+
return ConflictType.SHIFT_REDUCE
61+
62+
def __repr__(self) -> str:
63+
return f"<Conflict: {str(self)}>"
64+
65+
def __str__(self) -> str:
66+
return f"{self.state}, {self.token}, {self.actions}"
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from typing import TypeAlias
2+
3+
from syntactes import Token
4+
from syntactes._action import Action
5+
from syntactes._state import LR0State
6+
7+
Row: TypeAlias = dict[Token, list[Action]]
8+
9+
10+
class Entry:
11+
"""
12+
An entry of the parsing table. Holds the information of a transition from
13+
a state to another state via a symbol.
14+
"""
15+
16+
def __init__(self, from_state: LR0State, token: Token, action: Action) -> None:
17+
self.from_state = from_state
18+
self.token = token
19+
self.action = action
20+
21+
def __repr__(self) -> str:
22+
return f"<Entry: {str(self)}>"
23+
24+
def __str__(self) -> str:
25+
return f"{self.from_state.number}, {self.action}, {self.token}"
26+
27+
def __hash__(self) -> int:
28+
return hash((self.from_state, self.token, self.action))
29+
30+
def __eq__(self, other) -> bool:
31+
if not isinstance(other, Entry):
32+
return False
33+
34+
return (
35+
self.from_state == other.from_state
36+
and self.token == other.token
37+
and self.action == other.action
38+
)
Lines changed: 27 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,11 @@
33
from syntactes import Grammar, Token
44
from syntactes._action import Action
55
from syntactes._state import LR0State
6+
from syntactes.parsing_table import Conflict, Entry
67

78
Row: TypeAlias = dict[Token, list[Action]]
89

910

10-
class Entry:
11-
"""
12-
An entry of the parsing table. Holds the information of a transition from
13-
a state to another state via a symbol.
14-
"""
15-
16-
def __init__(self, from_state: LR0State, token: Token, action: Action) -> None:
17-
self.from_state = from_state
18-
self.token = token
19-
self.action = action
20-
21-
def __repr__(self) -> str:
22-
return f"<Entry: {str(self)}>"
23-
24-
def __str__(self) -> str:
25-
return f"{self.from_state.number}, {self.action}, {self.token}"
26-
27-
def __hash__(self) -> int:
28-
return hash((self.from_state, self.token, self.action))
29-
30-
def __eq__(self, other) -> bool:
31-
if not isinstance(other, Entry):
32-
return False
33-
34-
return (
35-
self.from_state == other.from_state
36-
and self.token == other.token
37-
and self.action == other.action
38-
)
39-
40-
4111
class LR0ParsingTable:
4212
"""
4313
Table that contains all the transitions from state to state with a symbol.
@@ -48,6 +18,21 @@ def __init__(self, grammar: Grammar) -> None:
4818
self._grammar = grammar
4919
self._initial_state = None
5020

21+
@staticmethod
22+
def from_entries(
23+
entries: Iterable[Entry], tokens: Iterable[Token]
24+
) -> "LR0ParsingTable":
25+
"""
26+
Create a parsing table from the given entries.
27+
"""
28+
table = LR0ParsingTable(tokens)
29+
{table.add_entry(entry) for entry in entries}
30+
return table
31+
32+
@property
33+
def initial_state(self) -> LR0State:
34+
return self._initial_state
35+
5136
def get_actions(self, state: LR0State, token: Token) -> Optional[list[Action]]:
5237
"""
5338
Get the actions from state with given number with `token`.
@@ -73,26 +58,23 @@ def add_entry(self, entry: Entry) -> None:
7358
actions = row.setdefault(entry.token, list())
7459
actions.append(entry.action)
7560

76-
@staticmethod
77-
def from_entries(
78-
entries: Iterable[Entry], tokens: Iterable[Token]
79-
) -> "LR0ParsingTable":
80-
"""
81-
Create a parsing table from the given entries.
82-
"""
83-
table = LR0ParsingTable(tokens)
84-
{table.add_entry(entry) for entry in entries}
85-
return table
86-
8761
def pretty_str(self) -> str:
8862
"""
8963
Returns a pretty-formatted string representation of the table.
9064
"""
9165
return self._rules_pretty_str() + "\n\n" + self._table_pretty_str()
9266

93-
@property
94-
def initial_state(self) -> LR0State:
95-
return self._initial_state
67+
def conflicts(self) -> list[Conflict]:
68+
"""
69+
Retuns a list with all the conflicts in the parsing table.
70+
"""
71+
conflicts = []
72+
for state, row in self.rows.items():
73+
for token, actions in row.items():
74+
if len(actions) > 1:
75+
conflicts.append(Conflict(state, token, actions))
76+
77+
return conflicts
9678

9779
def _rules_pretty_str(self) -> str:
9880
rules = [str(i) + ". " + str(r) for i, r in enumerate(self._grammar.rules)]

src/syntactes/tests/data.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from syntactes._action import Action
33
from syntactes._item import LR0Item
44
from syntactes._state import LR0State
5-
from syntactes.table import Entry, LR0ParsingTable, SLRParsingTable
5+
from syntactes.parsing_table import Entry, LR0ParsingTable, SLRParsingTable
66

77
EOF = Token.eof()
88
S = Token("S", False)

0 commit comments

Comments
 (0)