-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparser.py
More file actions
107 lines (96 loc) · 3.95 KB
/
Copy pathparser.py
File metadata and controls
107 lines (96 loc) · 3.95 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
# To the extent possible under law, the person who associated CC0 with
# this project has waived all copyright and related or neighboring rights
# to this project.
# You should have received a copy of the CC0 legalcode along with this
# work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
from re import finditer, search
class Parse:
def __init__(self, nonterminal, children):
self.nonterminal = nonterminal
self.children = children
class Parser:
def __init__(self):
self.tokens = []
self.rules = {}
self.initialized = False
def add_rule(self, nonterminal, pattern):
assert pattern
node = {"table": self.rules}
for symbol in pattern[:: -1]:
if symbol not in node["table"]:
node["table"][symbol] = {"table": {}}
node = node["table"][symbol]
assert "nonterminal" not in node
node["nonterminal"] = nonterminal
self.initialized = False
def add_token(self, token):
self.tokens.append(token)
self.initialized = False
@staticmethod
def find_rules(table, partial_parse, i):
if -i > len(partial_parse) or partial_parse[i].nonterminal not in table:
return []
rules = [
(nonterminal, length + 1)
for nonterminal, length in Parser.find_rules(table[partial_parse[i].nonterminal]["table"], partial_parse, i - 1)]
if "nonterminal" in table[partial_parse[i].nonterminal]:
return rules + [(table[partial_parse[i].nonterminal]["nonterminal"], 1)]
else:
return rules
def initialize(self):
rules = Parser.list_rules(self.rules)
self.precede = {}
for token in self.tokens:
self.precede[token] = set()
for nonterminal, pattern in rules:
self.precede[nonterminal] = set()
for symbol in pattern:
self.precede[symbol] = set()
for nonterminal, pattern in rules:
for i in range(1, len(pattern)):
self.precede[pattern[i]].add(pattern[i - 1])
size = 0
new_size = sum(len(self.precede[symbol]) for symbol in self.precede)
while new_size > size:
for nonterminal, pattern in rules:
self.precede[pattern[0]].update(self.precede[nonterminal])
size = new_size
new_size = sum(len(self.precede[nonterminal]) for nonterminal in self.precede)
self.initialized = True
@staticmethod
def list_rules(table):
return (
[(nonterminal, pattern + [symbol]) for symbol in table for nonterminal, pattern in Parser.list_rules(table[symbol]["table"])] +
[(table[symbol]["nonterminal"], [symbol]) for symbol in table if "nonterminal" in table[symbol]])
def parse(self, string):
if not self.initialized:
self.initialize()
partial_parses = [[]]
for token in self.tokenize(string):
current_partial_parses = [partial_parse + [token] for partial_parse in partial_parses]
partial_parses = []
while current_partial_parses:
new_partial_parses = [
partial_parse[: -length] + [Parse(nonterminal, partial_parse[-length :])]
for partial_parse in current_partial_parses
for nonterminal, length in Parser.find_rules(self.rules, partial_parse, -1)]
partial_parses = partial_parses + current_partial_parses
current_partial_parses = new_partial_parses
partial_parses = [
partial_parse
for partial_parse in partial_parses
if len(partial_parse) < 2 or partial_parse[-2].nonterminal in self.precede[partial_parse[-1].nonterminal]]
return [partial_parse[0] for partial_parse in partial_parses if len(partial_parse) == 1]
def tokenize(self, string):
yield Parse(r"^", "")
for match in finditer(r"|".join(self.tokens) + r"|[ \n]+|.", string):
if search(r"^(?:" + r"|".join(self.tokens) + r")$", match.group()):
for regex in self.tokens:
if search(r"^" + regex + r"$", match.group()):
yield Parse(regex, match.group())
break
elif search(r"^[ \n]+$", match.group()):
pass
else:
raise Exception()
yield Parse(r"$", "")