dendroid 0.2.00 → 0.2.01
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +3 -0
- data/lib/dendroid/formatters/ascii_tree.rb +142 -142
- data/lib/dendroid/formatters/base_formatter.rb +24 -25
- data/lib/dendroid/formatters/bracket_notation.rb +50 -50
- data/lib/dendroid/grm_analysis/dotted_item.rb +0 -1
- data/lib/dendroid/grm_analysis/grm_analyzer.rb +9 -1
- data/lib/dendroid/grm_analysis/rule_items.rb +1 -1
- data/lib/dendroid/parsing/and_node.rb +54 -56
- data/lib/dendroid/parsing/chart_walker.rb +301 -293
- data/lib/dendroid/parsing/composite_parse_node.rb +21 -21
- data/lib/dendroid/parsing/empty_rule_node.rb +28 -28
- data/lib/dendroid/parsing/or_node.rb +46 -51
- data/lib/dendroid/parsing/parse_node.rb +26 -26
- data/lib/dendroid/parsing/parse_tree_visitor.rb +127 -127
- data/lib/dendroid/parsing/parser.rb +5 -5
- data/lib/dendroid/parsing/terminal_node.rb +32 -32
- data/lib/dendroid/parsing/walk_progress.rb +121 -117
- data/lib/dendroid/recognizer/chart.rb +1 -6
- data/lib/dendroid/recognizer/recognizer.rb +12 -6
- data/lib/dendroid/syntax/grammar.rb +4 -0
- data/lib/dendroid/syntax/rule.rb +1 -1
- data/spec/dendroid/grm_analysis/dotted_item_spec.rb +1 -1
- data/spec/dendroid/parsing/chart_walker_spec.rb +250 -223
- data/spec/dendroid/parsing/terminal_node_spec.rb +36 -36
- data/spec/dendroid/recognizer/recognizer_spec.rb +0 -1
- data/spec/dendroid/support/sample_grammars.rb +0 -2
- data/version.txt +1 -1
- metadata +3 -3
@@ -1,293 +1,301 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'walk_progress'
|
4
|
-
|
5
|
-
module Dendroid
|
6
|
-
module Parsing
|
7
|
-
class ChartWalker
|
8
|
-
attr_reader :chart
|
9
|
-
attr_reader :last_item
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end
|
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
|
-
when :
|
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
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
walk_progress.curr_item =
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
end
|
207
|
-
|
208
|
-
def
|
209
|
-
predecessors
|
210
|
-
end
|
211
|
-
|
212
|
-
def
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
walk_progress.
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
if
|
258
|
-
|
259
|
-
|
260
|
-
walk_progress.curr_item =
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
new_node = walk_progress.
|
265
|
-
walk_progress.curr_item = pred
|
266
|
-
entry2node[pred] = new_node
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'walk_progress'
|
4
|
+
|
5
|
+
module Dendroid
|
6
|
+
module Parsing
|
7
|
+
class ChartWalker
|
8
|
+
attr_reader :chart
|
9
|
+
attr_reader :last_item
|
10
|
+
|
11
|
+
# rubocop: disable Metrics/AbcSize
|
12
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
13
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
14
|
+
|
15
|
+
def initialize(theChart)
|
16
|
+
@chart = theChart
|
17
|
+
end
|
18
|
+
|
19
|
+
def walk(start_item)
|
20
|
+
curr_rank = chart.size - 1
|
21
|
+
|
22
|
+
parents = []
|
23
|
+
progress = WalkProgress.new(curr_rank, start_item, parents)
|
24
|
+
paths = [progress]
|
25
|
+
|
26
|
+
if start_item.predecessors.size > 1
|
27
|
+
# Create n times start_item as predecessors, then for each path initialize to its unique own predecessor
|
28
|
+
forerunners = disambiguate(progress, start_item.predecessors)
|
29
|
+
if forerunners.size == 1
|
30
|
+
parents << ANDNode.new(start_item, curr_rank)
|
31
|
+
else
|
32
|
+
preds = sort_predecessors(forerunners)
|
33
|
+
if start_item.rule.rhs.size == 1
|
34
|
+
parents << ANDNode.new(start_item, curr_rank)
|
35
|
+
progress.push_or_node(start_item.origin, preds.size)
|
36
|
+
else
|
37
|
+
parents << OrNode.new(start_item.lhs, start_item.origin, curr_rank, preds.size)
|
38
|
+
end
|
39
|
+
progress.curr_item = start_item
|
40
|
+
fork(progress, paths, preds)
|
41
|
+
end
|
42
|
+
else
|
43
|
+
parents << ANDNode.new(start_item, curr_rank)
|
44
|
+
end
|
45
|
+
token2node = {}
|
46
|
+
entry2node = {}
|
47
|
+
sharing = {}
|
48
|
+
or_nodes_crossed = {}
|
49
|
+
|
50
|
+
loop do # Iterate over rank values
|
51
|
+
pass = :primary
|
52
|
+
loop do # Iterate over paths until all are ready for previous rank
|
53
|
+
paths.each do |prg|
|
54
|
+
next if prg.state == :Complete || prg.state == :Delegating
|
55
|
+
next if pass == :secondary && prg.state == :Waiting
|
56
|
+
|
57
|
+
step_back(prg, paths, token2node, entry2node, sharing, or_nodes_crossed)
|
58
|
+
end
|
59
|
+
# TODO: handle path removal
|
60
|
+
break if paths.none? { |pg| pg.state == :Running || pg.state == :Forking }
|
61
|
+
|
62
|
+
pass = :secondary
|
63
|
+
end
|
64
|
+
break if paths.all? { |prg| prg.state == :Complete }
|
65
|
+
|
66
|
+
entry2node.clear
|
67
|
+
end
|
68
|
+
|
69
|
+
parents[0]
|
70
|
+
end
|
71
|
+
|
72
|
+
def step_back(walk_progress, paths, token2node, entry2node, sharing, or_nodes_crossed)
|
73
|
+
loop do
|
74
|
+
case walk_progress.state
|
75
|
+
when :Waiting, :New
|
76
|
+
predecessors = predecessors_of(walk_progress.curr_item, walk_progress.parents)
|
77
|
+
last_parent = walk_progress.parents.last
|
78
|
+
if sharing.include? last_parent
|
79
|
+
delegating = sharing[last_parent]
|
80
|
+
unless delegating.include? walk_progress
|
81
|
+
delegating.each do |dlg|
|
82
|
+
dlg.curr_rank = walk_progress.curr_rank
|
83
|
+
dlg.curr_item = walk_progress.curr_item
|
84
|
+
dlg.state = :Waiting
|
85
|
+
end
|
86
|
+
sharing.delete(last_parent)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
walk_progress.state = :Running
|
90
|
+
|
91
|
+
when :Running
|
92
|
+
predecessors = predecessors_of(walk_progress.curr_item, walk_progress.parents)
|
93
|
+
|
94
|
+
when :Forking
|
95
|
+
# predecessors = [walk_progress.curr_item]
|
96
|
+
predecessors = [walk_progress.predecessor]
|
97
|
+
walk_progress.predecessor = nil
|
98
|
+
walk_progress.state = :Running
|
99
|
+
end
|
100
|
+
|
101
|
+
if predecessors.empty?
|
102
|
+
walk_progress.state = :Complete
|
103
|
+
break
|
104
|
+
end
|
105
|
+
|
106
|
+
case walk_progress.curr_item.algo
|
107
|
+
when :completer
|
108
|
+
completer_backwards(walk_progress, paths, entry2node, sharing, predecessors)
|
109
|
+
break if walk_progress.state == :Delegating
|
110
|
+
|
111
|
+
when :scanner
|
112
|
+
curr_token = chart.tokens[walk_progress.curr_rank - 1]
|
113
|
+
if token2node.include? curr_token
|
114
|
+
walk_progress.add_child_node(token2node[curr_token])
|
115
|
+
walk_progress.curr_rank -= 1
|
116
|
+
else
|
117
|
+
new_node = walk_progress.add_terminal_node(chart.tokens[walk_progress.curr_rank - 1])
|
118
|
+
token2node[curr_token] = new_node
|
119
|
+
end
|
120
|
+
if predecessors.size == 1
|
121
|
+
walk_progress.curr_item = predecessors[0]
|
122
|
+
walk_progress.state = :Waiting
|
123
|
+
break
|
124
|
+
else
|
125
|
+
# TODO: challenge assumption single predecessor
|
126
|
+
raise StandardError
|
127
|
+
end
|
128
|
+
|
129
|
+
when :predictor
|
130
|
+
unless walk_progress.parents.last.partial?
|
131
|
+
last_parent = walk_progress.parents.pop
|
132
|
+
if sharing.include? last_parent
|
133
|
+
delegating = sharing[last_parent]
|
134
|
+
unless delegating.include? walk_progress
|
135
|
+
delegating.each do |dlg|
|
136
|
+
dlg.curr_rank = walk_progress.curr_rank
|
137
|
+
dlg.curr_item = walk_progress.curr_item
|
138
|
+
dlg.state = :Running
|
139
|
+
end
|
140
|
+
sharing.delete(last_parent)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
if last_parent.is_a?(OrNode)
|
144
|
+
if or_nodes_crossed.include?(last_parent)
|
145
|
+
walk_progress.state = :Complete
|
146
|
+
break
|
147
|
+
else
|
148
|
+
or_nodes_crossed[last_parent] = true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
index_empty = predecessors.find_index { |entry| entry.dotted_item.empty? }
|
153
|
+
if index_empty
|
154
|
+
entry_empty = predecessors.delete_at(index_empty)
|
155
|
+
walk_progress.add_node_empty(entry_empty)
|
156
|
+
raise StandardError unless predecessors.empty? # Uncovered case
|
157
|
+
|
158
|
+
walk_progress.curr_item = entry_empty
|
159
|
+
next
|
160
|
+
end
|
161
|
+
if predecessors.size == 1
|
162
|
+
walk_progress.curr_item = predecessors[0]
|
163
|
+
else
|
164
|
+
# curr_item has multiple predecessors from distinct rules
|
165
|
+
# look in lineage the latest entry that matches one of the ancestors AND
|
166
|
+
# has a free slot for the current symbol
|
167
|
+
matches = walk_progress.match_parent?(predecessors, true)
|
168
|
+
if matches.empty?
|
169
|
+
walk_progress.state = :Complete
|
170
|
+
break
|
171
|
+
end
|
172
|
+
(matching_pred, stack_offset) = matches.first
|
173
|
+
walk_progress.curr_item = matching_pred
|
174
|
+
unless stack_offset.zero?
|
175
|
+
removed = walk_progress.parents.pop(stack_offset)
|
176
|
+
if removed.is_a?(Array)
|
177
|
+
or_nodes = removed.select { |entry| entry.is_a?(OrNode) }
|
178
|
+
unless or_nodes.empty?
|
179
|
+
or_nodes.reverse_each do |or_nd|
|
180
|
+
if or_nodes_crossed.include?(or_nd)
|
181
|
+
walk_progress.state = :Complete
|
182
|
+
break
|
183
|
+
else
|
184
|
+
or_nodes_crossed[or_nd] = true
|
185
|
+
end
|
186
|
+
end
|
187
|
+
break if walk_progress.state == :Complete
|
188
|
+
|
189
|
+
end
|
190
|
+
elsif removed.is_a?(OrNode)
|
191
|
+
if or_nodes_crossed.include?(removed)
|
192
|
+
walk_progress.state = :Complete
|
193
|
+
break
|
194
|
+
else
|
195
|
+
or_nodes_crossed[removed] = true
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
else
|
201
|
+
raise StandardError
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
walk_progress
|
206
|
+
end
|
207
|
+
|
208
|
+
def disambiguate(_progress, predecessors)
|
209
|
+
predecessors
|
210
|
+
end
|
211
|
+
|
212
|
+
def sort_predecessors(predecessors)
|
213
|
+
predecessors
|
214
|
+
end
|
215
|
+
|
216
|
+
def predecessors_of(anEItem, parents)
|
217
|
+
# Rule: if anEItem has itself as predecessor AND parents contains
|
218
|
+
# only a start item, then remove anEItem from its own predecessor(s).
|
219
|
+
if (parents.size == 1) && anEItem.predecessors.include?(anEItem)
|
220
|
+
# raise StandardError unless parents[0].match(anEItem)
|
221
|
+
unless parents[0].match(anEItem)
|
222
|
+
raise StandardError
|
223
|
+
|
224
|
+
end
|
225
|
+
preds = anEItem.predecessors.dup
|
226
|
+
preds.delete(anEItem)
|
227
|
+
preds
|
228
|
+
else
|
229
|
+
anEItem.predecessors
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def completer_backwards(walk_progress, paths, entry2node, sharing, predecessors)
|
234
|
+
# Trying to remove some predecessors with some disambiguation technique
|
235
|
+
forerunners = disambiguate(walk_progress, predecessors)
|
236
|
+
|
237
|
+
if forerunners.size == 1
|
238
|
+
pred = forerunners[0]
|
239
|
+
if entry2node.include? pred
|
240
|
+
shared_node = entry2node[pred]
|
241
|
+
if sharing.include? shared_node
|
242
|
+
sharing[shared_node] << walk_progress
|
243
|
+
else
|
244
|
+
sharing[shared_node] = [walk_progress]
|
245
|
+
end
|
246
|
+
walk_progress.add_child_node(shared_node)
|
247
|
+
walk_progress.parents.push(shared_node)
|
248
|
+
walk_progress.state = :Delegating
|
249
|
+
|
250
|
+
elsif pred.predecessors.size == 1
|
251
|
+
new_node = walk_progress.push_and_node(pred)
|
252
|
+
walk_progress.curr_item = pred
|
253
|
+
entry2node[pred] = new_node
|
254
|
+
else
|
255
|
+
pre_forerunners = disambiguate(walk_progress, pred.predecessors)
|
256
|
+
index_empty = pre_forerunners.find_index { |entry| entry.dotted_item.empty? }
|
257
|
+
if index_empty
|
258
|
+
entry_empty = pre_forerunners.delete_at(index_empty)
|
259
|
+
walk_progress.add_node_empty(entry_empty)
|
260
|
+
walk_progress.curr_item = entry_empty.predecessors[0] # Assuming only one predecessor
|
261
|
+
end
|
262
|
+
if pre_forerunners.size == 1
|
263
|
+
pred = forerunners[0]
|
264
|
+
new_node = walk_progress.push_and_node(pre_forerunners[0])
|
265
|
+
walk_progress.curr_item = pred
|
266
|
+
entry2node[pred] = new_node
|
267
|
+
else
|
268
|
+
prepreds = sort_predecessors(pre_forerunners)
|
269
|
+
new_node = walk_progress.push_or_node(pred.origin, prepreds.size)
|
270
|
+
walk_progress.curr_item = pred
|
271
|
+
entry2node[pred] = new_node
|
272
|
+
fork(walk_progress, paths, prepreds)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
else
|
276
|
+
# AMBIGUITY: multiple valid predecessors
|
277
|
+
preds = sort_predecessors(forerunners)
|
278
|
+
walk_progress.push_or_node(preds)
|
279
|
+
fork(walk_progress, paths, preds)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def fork(walk_progress, paths, sorted_predecessors)
|
284
|
+
progs = [walk_progress]
|
285
|
+
walk_progress.fork(sorted_predecessors[0])
|
286
|
+
sorted_predecessors[1..].each do |prd|
|
287
|
+
alternate = walk_progress.dup
|
288
|
+
alternate.fork(prd)
|
289
|
+
paths << alternate
|
290
|
+
progs << alternate
|
291
|
+
end
|
292
|
+
|
293
|
+
progs.each { |pg| pg.push_and_node(pg.curr_item) }
|
294
|
+
end
|
295
|
+
end # class
|
296
|
+
|
297
|
+
# rubocop: enable Metrics/AbcSize
|
298
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
299
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
300
|
+
end # module
|
301
|
+
end # module
|
@@ -1,21 +1,21 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'parse_node'
|
4
|
-
|
5
|
-
module Dendroid
|
6
|
-
module Parsing
|
7
|
-
class CompositeParseNode < ParseNode
|
8
|
-
attr_reader :range
|
9
|
-
attr_reader :children
|
10
|
-
|
11
|
-
def initialize(lowerBound, upperBound, child_count)
|
12
|
-
super(lowerBound, upperBound)
|
13
|
-
@children = Array.new(child_count, nil)
|
14
|
-
end
|
15
|
-
|
16
|
-
def add_child(child_node, index)
|
17
|
-
children[index] = child_node
|
18
|
-
end
|
19
|
-
end # class
|
20
|
-
end # module
|
21
|
-
end # module
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'parse_node'
|
4
|
+
|
5
|
+
module Dendroid
|
6
|
+
module Parsing
|
7
|
+
class CompositeParseNode < ParseNode
|
8
|
+
attr_reader :range
|
9
|
+
attr_reader :children
|
10
|
+
|
11
|
+
def initialize(lowerBound, upperBound, child_count)
|
12
|
+
super(lowerBound, upperBound)
|
13
|
+
@children = Array.new(child_count, nil)
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_child(child_node, index)
|
17
|
+
children[index] = child_node
|
18
|
+
end
|
19
|
+
end # class
|
20
|
+
end # module
|
21
|
+
end # module
|