-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtree_reduction.py
More file actions
119 lines (83 loc) · 3.19 KB
/
tree_reduction.py
File metadata and controls
119 lines (83 loc) · 3.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
from typing import List, Callable, Optional
from dataclasses import dataclass, field
from functools import partial
@dataclass(frozen=True)
class Rule:
lhs: str
rhs: List[str]
action: Callable[[List["Node"]], "Node"]
@dataclass(frozen=True)
class Grammar:
rules: List[Rule]
@dataclass
class Node:
op: str
children: List["Node"] = field(default_factory=list)
value: Optional[int] = None
def action_addition(children: List["Node"]) -> "Node":
if len(children) < 3:
raise ValueError("Addition action requires exactly 3 children.")
left_value = children[0].value if children[0].value is not None else 0
right_value = children[2].value if children[2].value is not None else 0
node = Node("E", children)
node.value = left_value + right_value
return node
def action_multiplication(children: List["Node"]) -> "Node":
if len(children) < 3:
raise ValueError("Multiplication action requires exactly 3 children.")
left_value = children[0].value if children[0].value is not None else 1
right_value = children[2].value if children[2].value is not None else 1
node = Node("T", children)
node.value = left_value * right_value
return node
def action_parenthesis(children: List["Node"]) -> "Node":
if len(children) < 3:
raise ValueError("Parenthesis action requires exactly 3 children.")
return children[1] # Assuming it's ( E ) → return E
def action_id(children: List["Node"]) -> "Node":
node = Node("F", children)
node.value = 1 # Assuming ID evaluates to 1
return node
def identity_action(children: List["Node"]) -> "Node":
return children[0]
def shift_reduce_parser(tokens: List[str]) -> Node:
stack: List[Node] = []
index = 0
rules = Grammar(
[
Rule("E", ["E", "+", "T"], action_addition),
Rule("E", ["T"], partial(identity_action)),
Rule("T", ["T", "*", "F"], action_multiplication),
Rule("T", ["F"], partial(identity_action)),
Rule("F", ["(", "E", ")"], action_parenthesis),
Rule("F", ["id"], action_id),
]
)
rules = rules.rules
while index < len(tokens) or not stack:
reduced = False
# Try reducing from largest match
for rule in range(len(rules)):
rule = rules[rule]
if (
len(stack) >= len(rule.rhs)
and {n.op for n in stack[-len(rule.rhs) :]} == rule.rhs
):
children = stack[-len(rule.rhs) :]
stack = stack[: -len(rule.rhs)]
stack.append(rule.action(children))
reduced = True
break # Stop after a successful reduction
# If no reduction happened, shift next token
if not reduced:
if index >= len(tokens):
raise ValueError("Parsing error: unable to reduce or shift.")
stack.append(Node(tokens[index], [], 1 if tokens[index] == "id" else None))
index += 1
return stack[0]
def main():
expression = ["id", "+", "(", "id", "*", "id", ")"]
tree = shift_reduce_parser(expression)
print(f"Final result: {tree}")
if __name__ == "__main__":
main()