-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.py
139 lines (117 loc) · 4.59 KB
/
server.py
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
"""
Back-end for the visualization of the parsing process
"""
from flask import render_template, Flask, request
from cfg_utils.cfg import ContextFreeGrammar
from lr1.action_goto_builder import ActionGotoBuilder
from lr1.lr1_io import LRPrinter
from lr1.lr1_itemset_automata import LRItemSetAutomata
from lang_def import LangDef
from lang_def_builder import LangDefBuilder
import os
from typing import List, Tuple
from server_utils.tree import Tree, TreeNode
app = Flask(__name__, template_folder="./server_utils/templates/")
app.secret_key = os.urandom(16)
@app.route("/")
def index():
return render_template("index.html")
@app.route("/generateLR", methods=["POST"])
def generate():
raw_cfg = request.form["CFG"]
cfg = ContextFreeGrammar.from_string(raw_cfg)
lp = LRPrinter(cfg)
app.config["cfg"] = cfg
app.config["ld"] = LangDefBuilder.new(raw_cfg)
app.config["lp"] = lp
lr_automata = LRItemSetAutomata.new(cfg)
item_set_to_id = lr_automata.item_set_to_id
action, goto = ActionGotoBuilder.new(cfg, lr_automata)
terminals, non_terminals = sorted(action.terminals), sorted(goto.non_terminals)
symbols = terminals + non_terminals
result = [["state"] + list(map(lp.to_string, symbols))]
for i in range(len(action)):
result.append(
[str(i)]
+ [str(action[i][str(k)]) if str(k) in action[i] else "" for k in terminals]
+ [str(goto[i][k]) if k in goto[i] else "" for k in non_terminals]
)
itemToID = {}
for k, v in item_set_to_id.items():
itemToID[lp.to_string(k)] = v
cfgForFirst = cfg.remove_left_recursion() if cfg.is_left_recursive() else cfg
firstDict = cfgForFirst.first()
firstSet = {
k: ", ".join([lp.to_string(sym) for sym in v]) for k, v in firstDict.items()
}
return render_template(
"parse_result.html", itemToID=itemToID, table=result, firstSet=firstSet
)
def parse_pt_n_log(
cfg: ContextFreeGrammar, ld: LangDef, lp: LRPrinter, tokens: List[Tuple[int, str]]
) -> Tuple[Tree, List[str]]:
log = []
state_stack = [0]
node_stack: List[TreeNode] = [
TreeNode("$")
] # str -> terminal, Any -> evaluated non_terminal, depends on PT action fn return type
for token_type, lex_str in tokens:
current_state = state_stack[-1]
while True:
if ld.action_json["table"][current_state][str(token_type)] is None:
raise ValueError("ERROR: %s, %s" % (current_state, str(token_type)))
action_type, next_state = ld.action_json["table"][current_state][
str(token_type)
]
if action_type == 0: # shift to another state
state_stack.append(next_state)
node_stack.append(TreeNode(lex_str))
log.append(
(
str(state_stack),
str(node_stack),
"Shift to state %d" % next_state,
)
)
break
elif action_type == 1:
prod_id: int = next_state
non_terminal, sequence = cfg.get_production(prod_id)
nargs = len(sequence)
log.append(
(
str(state_stack),
str(node_stack),
"Reduce using production %d: %s -> %s"
% (
prod_id,
non_terminal,
" ".join(lp.to_string(v) for v in sequence),
),
)
)
non_terminal_node = TreeNode(non_terminal)
for _ in range(nargs):
state_stack.pop()
non_terminal_node.childs.append(node_stack.pop())
non_terminal_node.childs.reverse()
current_state = state_stack[-1]
next_state = ld.goto_json["table"][current_state][non_terminal]
state_stack.append(next_state)
node_stack.append(non_terminal_node)
current_state = state_stack[-1]
continue
elif action_type == 2:
break
else:
assert False
return Tree(node_stack[-1]), log
@app.route("/parse", methods=["POST", "GET"])
def parse():
string = request.form["string"]
cfg = app.config["cfg"]
ld = app.config["ld"]
lp = app.config["lp"]
token_list = ld.scan(string)
pt, log = parse_pt_n_log(cfg, ld, lp, token_list)
return {"pt": str(pt), "log": log}