最後活躍 1747970795

nova-parser.lua 原始檔案 Playground
1local pprint = require "pprint"
2local None = {}
3local whitespace = {
4 [' '] = true,
5 ['\n'] = true,
6 ['\r'] = true,
7 ['\t'] = true,
8}
9local yield, wrap = coroutine.yield, coroutine.wrap
10
11local function gen(t)
12 return wrap(function()
13 for i, v in ipairs(t) do
14 yield(i, v)
15 end
16 end)
17end
18local function iter(f)
19 return function(arg)
20 return wrap(function () f(arg) end)
21 end
22end
23
24local function chars(string)
25 local cs = {}
26 for char in string:gmatch(".") do
27 table.insert(cs, char)
28 end
29 return cs
30end
31
32local function copy(t)
33 local t_copy = {}
34 for k, v in pairs(t) do
35 t_copy[k] = v
36 end
37 return t_copy
38end
39
40local function append(a, b)
41 local appended = {}
42 for _, v in ipairs(a) do
43 table.insert(appended, v)
44 end
45 for _, v in ipairs(b) do
46 table.insert(appended, v)
47 end
48 return appended
49end
50
51local function clear(t)
52 for k, v in pairs(t) do
53 t[k] = nil
54 end
55end
56
57local function insert(t, what)
58 table.insert(t, what)
59 return t
60end
61
62local parser_container = iter(function(string)
63 local left = {}
64 local right = {}
65 local target = left
66 local characters = gen(insert(chars(string), None))
67 local delimiter = None
68
69 for _, character in characters do
70 if not whitespace[character] then
71 delimiter = character
72 break
73 end
74
75 end
76 if delimiter == None then
77 return None
78 end
79
80 for _, character in characters do
81 if character == delimiter or character == None then
82 if target == left then
83 target = right
84 else
85 target = left
86 yield(copy(left), copy(right))
87 clear(left) clear(right)
88 end
89 else
90 table.insert(target, character)
91 end
92 end
93end)
94
95local parse_labels = iter(function(rules)
96 local left = {}
97 local right = {}
98 local pair = {{}, {}}
99 local target = left
100 local delimiter = None
101 for condition, result in rules do
102 for _, side in ipairs({condition, result}) do
103 local characters = gen(insert(side, None))
104 local state = "find delimiter"
105 for _, character in characters do
106 if character == None then
107 local label = table.concat(pair[1]):match "^%s*(.-)%s*$"
108 local pattern = table.concat(pair[2]):match "^%s*(.-)%s*$"
109 if not (#label == 0 and #pattern == 0) then
110 table.insert(target, {label, pattern})
111 end
112 clear(pair[1]) clear(pair[2])
113 break
114 elseif state == "find delimiter" then
115 if not whitespace[character] then
116 delimiter = character
117 state = "parse label"
118 end
119 elseif state == "parse label" then
120 if character == delimiter then
121 state = "parse pattern"
122 else
123 table.insert(pair[1], character)
124 end
125 elseif state == "parse pattern" then
126 if character == delimiter or character == None then
127 state = "parse label"
128 local label = table.concat(pair[1]):match "^%s*(.-)%s*$"
129 local pattern = table.concat(pair[2]):match "^%s*(.-)%s*$"
130 if not (#label == 0 and #pattern == 0) then
131 table.insert(target, {label, pattern})
132 end
133 clear(pair[1]) clear(pair[2])
134 else
135 table.insert(pair[2], character)
136 end
137 end
138 end
139 if target == left then
140 target = right
141 else
142 target = left
143 end
144 end
145 yield(copy(left), copy(right))
146 clear(left) clear(right)
147 end
148end)
149
150local parse_patterns = iter(function(rules)
151 for conditions, results in rules do
152 local left = {}
153 local right = {}
154 local preserves = {}
155 for _, condition in ipairs(conditions) do
156 local label, pattern = condition[1], condition[2]
157 local preserve = false
158 if pattern:match("%?$") then
159 pattern = pattern:sub(1, -2)
160 preserve = true
161 end
162 if pattern:match("^[\"'](.*)[\"']$") then
163 for i, character in ipairs(chars(pattern:match("^[\"'](.*)[\"\']$"))) do
164 table.insert(left, {label, {tostring(string.byte(character))}})
165 if preserves then
166 table.insert(right, {label, {tostring(string.byte(character))}})
167 end
168 end
169 elseif pattern:match("^%((.*)%)$") then
170 for token in pattern:match("^%((.*)%)$"):gmatch("%S+") do
171 table.insert(left, {label, {token}})
172 if preserve then
173 table.insert(right, {label, {token}})
174 end
175 end
176 elseif pattern:find("^%.") then
177 -- TODO
178 else
179 local words = {}
180 for word in pattern:gmatch("%S+") do
181 table.insert(words, word)
182 end
183 table.insert(left, {label, words})
184 if preserve then
185 table.insert(preserves, {label, words})
186 end
187 end
188 end
189
190 for _, result in ipairs(results) do
191 local label, pattern = result[1], result[2]
192
193 if pattern:match("^[\"'](.*)[\"']$") then
194 for i, character in ipairs(chars(pattern:match("^[\"'](.*)[\"\']$"))) do
195 table.insert(right, {label, {tostring(string.byte(character))}})
196 end
197 elseif pattern:match("^%((.*)%)$") then
198 for token in pattern:match("^%((.*)%)$"):gmatch("%S+") do
199 table.insert(right, {label, {token}})
200 end
201 elseif pattern:find("^%.") then
202 -- TODO
203 else
204 local words = {}
205 for word in pattern:gmatch("%S+") do
206 table.insert(words, word)
207 end
208 table.insert(right, {label, words})
209 end
210 end
211
212 yield(copy(left), append(right, preserves))
213 clear(left) clear(right) clear(preserves)
214 end
215end)
216
217local file = io.open("main.nv", "r")
218for left, right in parse_patterns(parse_labels(parser_container(file:read("*a")))) do
219 io.write("|")
220 local left_pattern = {}
221 for _, pattern in ipairs(left) do
222 table.insert(left_pattern, ":" .. pattern[1] .. ": " .. table.concat(pattern[2], " "))
223 end
224 io.write(table.concat(left_pattern, " "))
225 io.write("|\n")
226 for _, pattern in ipairs(right) do
227 print("", ":" .. pattern[1] .. ": " .. table.concat(pattern[2], " "))
228 end
229 io.write("\n")
230end