birman 0.2.2 → 0.3.0
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/CHANGELOG.md +204 -0
- data/README.md +381 -14
- data/lib/birman/ast.rb +10 -1
- data/lib/birman/combinators.rb +25 -14
- data/lib/birman/compiler/actions.rb +309 -0
- data/lib/birman/compiler/emitter.rb +940 -0
- data/lib/birman/compiler/literals.rb +29 -0
- data/lib/birman/compiler/nodes.rb +158 -0
- data/lib/birman/compiler/regexps.rb +182 -0
- data/lib/birman/compiler/runtime.rb +758 -0
- data/lib/birman/compiler/types.rb +128 -0
- data/lib/birman/compiler.rb +155 -0
- data/lib/birman/context.rb +124 -15
- data/lib/birman/grammar.rb +93 -16
- data/lib/birman/left_recursion.rb +409 -0
- data/lib/birman/matchers.rb +224 -38
- data/lib/birman/result.rb +10 -1
- data/lib/birman/rule_set.rb +19 -6
- data/lib/birman/rules.rb +10 -5
- data/lib/birman/standalone.rb +111 -0
- data/lib/birman/version.rb +1 -1
- data/lib/birman.rb +22 -0
- metadata +11 -1
|
@@ -0,0 +1,940 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Birman
|
|
4
|
+
class Compiler
|
|
5
|
+
# Writes the Parser's methods: one per matcher (shared by equal
|
|
6
|
+
# matchers), plus a packrat memo wrapper (ref_<rule>) per rule.
|
|
7
|
+
#
|
|
8
|
+
# Each matcher becomes a fixed template, following Birman's matchers
|
|
9
|
+
# line by line. A method takes a byte position p and returns where
|
|
10
|
+
# its match ended, or FAIL. A matcher whose value something reads
|
|
11
|
+
# leaves it in its own instance variable, which the caller copies
|
|
12
|
+
# straight away: the next match may overwrite it.
|
|
13
|
+
# [class.Compiler.Emitter]
|
|
14
|
+
# purpose = "Turns each matcher into a method of the generated Parser"
|
|
15
|
+
class Emitter
|
|
16
|
+
# How to call a compiled matcher: the name of the method to call with a
|
|
17
|
+
# position, and the expression for its value just after a
|
|
18
|
+
# successful call (nil when nothing reads it).
|
|
19
|
+
Call = Struct.new(:name, :value)
|
|
20
|
+
|
|
21
|
+
PUNCTUATION = {
|
|
22
|
+
"=" => "eq", "+" => "plus", "-" => "minus", "*" => "star", "/" => "slash", "(" => "lparen",
|
|
23
|
+
")" => "rparen", "{" => "lbrace", "}" => "rbrace", "[" => "lbracket", "]" => "rbracket",
|
|
24
|
+
"," => "comma", ":" => "colon", ";" => "semicolon", "." => "dot", "<" => "lt", ">" => "gt",
|
|
25
|
+
"!" => "bang", "?" => "question", "&" => "amp", "|" => "bar", "%" => "percent", "^" => "caret"
|
|
26
|
+
}.freeze
|
|
27
|
+
|
|
28
|
+
# An inline RBS signature for every position-taking method: one
|
|
29
|
+
# position Spinel can't type would widen them all to boxed values.
|
|
30
|
+
POSITION = "#: (Integer) -> Integer\n"
|
|
31
|
+
|
|
32
|
+
SAVE = <<~RUBY
|
|
33
|
+
of = @furthest
|
|
34
|
+
oe = @expected
|
|
35
|
+
@furthest = p
|
|
36
|
+
@expected = NONE
|
|
37
|
+
RUBY
|
|
38
|
+
|
|
39
|
+
# Rules whose values something reads (Refs met wanting a value).
|
|
40
|
+
attr_reader :read_rules
|
|
41
|
+
|
|
42
|
+
# valued: the rules to keep values for. If read_rules turns out
|
|
43
|
+
# bigger, compile again with those (see Compiler#to_ruby).
|
|
44
|
+
def initialize(grammar, valued)
|
|
45
|
+
@grammar = grammar
|
|
46
|
+
@valued = valued
|
|
47
|
+
@read_rules = [grammar.class.root]
|
|
48
|
+
@types = Types.new(grammar)
|
|
49
|
+
@nodes = Nodes.new
|
|
50
|
+
@actions = Actions.new(grammar)
|
|
51
|
+
@methods = []
|
|
52
|
+
@compiled = {}
|
|
53
|
+
@taken = {}
|
|
54
|
+
@values = {}
|
|
55
|
+
@rules = {}
|
|
56
|
+
@ignores = []
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def compile
|
|
60
|
+
root = @grammar.class.root or raise Error, "#{@grammar.class} has no root rule"
|
|
61
|
+
queue = [root, ignore_rule].compact
|
|
62
|
+
while (name = queue.shift)
|
|
63
|
+
next if @rules.key?(name)
|
|
64
|
+
|
|
65
|
+
@rules[name] = compile_rule(name)
|
|
66
|
+
queue.concat(@pending)
|
|
67
|
+
end
|
|
68
|
+
refuse_clashes
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def node_classes = @nodes.sources
|
|
72
|
+
|
|
73
|
+
def node_names = @nodes.names
|
|
74
|
+
|
|
75
|
+
def parser_body
|
|
76
|
+
[*@actions.constants.map { "#{_1}\n" }, initializer, parse_method, "private\n",
|
|
77
|
+
*@actions.methods, *@methods, *@ignores.map { skip_method(_1) }, init_values, init_memos,
|
|
78
|
+
*@rules.map { |name, call| ref_method(name, call) }].join("\n")
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def ignore_rule = @grammar.class.ignore
|
|
84
|
+
|
|
85
|
+
def refuse_clashes
|
|
86
|
+
met = @rules.keys.group_by { ident(_1) }.values.find { _1.size > 1 }
|
|
87
|
+
raise Error, "rules #{met.map(&:inspect).join(" and ")} would have one name in the generated parser" if met
|
|
88
|
+
|
|
89
|
+
clash = @actions.names.find { @taken.key?(_1.to_s) || @rules.key?(_1) }
|
|
90
|
+
raise Error, "the grammar method #{clash} would clash with a generated method" if clash
|
|
91
|
+
|
|
92
|
+
shared = (@actions.constant_names & @nodes.names).first
|
|
93
|
+
raise Error, "#{shared} is both a node class and a constant copied actions read" if shared
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# A rule's name as part of a Ruby identifier: rule(:valid?) is ref_valid_3f.
|
|
97
|
+
def ident(name) = name.to_s.gsub(/[^A-Za-z0-9_]/) { format("_%02x", _1.ord) }
|
|
98
|
+
|
|
99
|
+
def compile_rule(name)
|
|
100
|
+
@pending = []
|
|
101
|
+
@rule = name
|
|
102
|
+
@prefix = ident(name)
|
|
103
|
+
matcher = begin
|
|
104
|
+
@grammar.matcher_for(name)
|
|
105
|
+
rescue ArgumentError => e
|
|
106
|
+
raise Error, e.message
|
|
107
|
+
end
|
|
108
|
+
want = @valued.include?(name)
|
|
109
|
+
call = compile_matcher(matcher, want, "rule_#{ident(name)}")
|
|
110
|
+
@values["@v_ref_#{ident(name)}"] = @types.initial(Ref.new(name)) if want
|
|
111
|
+
call
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# --- Dispatch ---
|
|
115
|
+
|
|
116
|
+
def compile_matcher(matcher, want, name = nil)
|
|
117
|
+
key = [matcher, want]
|
|
118
|
+
return @compiled[key] if @compiled.key?(key)
|
|
119
|
+
|
|
120
|
+
@compiled[key] = translate(matcher, want, name)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def translate(matcher, want, name) # rubocop:disable Metrics
|
|
124
|
+
case matcher
|
|
125
|
+
when Str then str(matcher, want)
|
|
126
|
+
when Pat then pat(matcher, want, name)
|
|
127
|
+
when Captures then captures(matcher, want, name)
|
|
128
|
+
when Lit then lit(matcher, want)
|
|
129
|
+
when Seq then seq(matcher, want, name)
|
|
130
|
+
when Pick then pick(matcher, want, name)
|
|
131
|
+
when SepBy then sep_by(matcher, want, name)
|
|
132
|
+
when Choice then choice(matcher, want, name)
|
|
133
|
+
when Rep then rep(matcher, want, name)
|
|
134
|
+
when Maybe then maybe(matcher, want, name)
|
|
135
|
+
when Not then negative(matcher, want, name)
|
|
136
|
+
when And then positive(matcher, want, name)
|
|
137
|
+
when Both then both(matcher, want, name)
|
|
138
|
+
when Const then const(matcher, want)
|
|
139
|
+
when Send, Action then transform(matcher, want, name)
|
|
140
|
+
when Label then label(matcher, want, name)
|
|
141
|
+
when Check then check(matcher, want, name)
|
|
142
|
+
when Named then compile_matcher(matcher.inner, want, name)
|
|
143
|
+
when To then to(matcher, want, name)
|
|
144
|
+
when Infix then infix(matcher, want, name)
|
|
145
|
+
when Lexeme then lexeme(matcher, want, name)
|
|
146
|
+
when Quiet then quiet(matcher, want, name)
|
|
147
|
+
when Ref then ref(matcher, want)
|
|
148
|
+
when Map
|
|
149
|
+
raise Error, "rule :#{@rule} uses .map with a block, which can't be compiled; " \
|
|
150
|
+
"use .map(:method_name) or .action(:method_name)"
|
|
151
|
+
else raise Error, "rule :#{@rule} uses #{matcher.class}, which the compiler doesn't know"
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# --- Methods ---
|
|
156
|
+
|
|
157
|
+
# Defines a method for matcher, its body from the block (given the
|
|
158
|
+
# value variable, or nil if no value is kept), and returns its Call.
|
|
159
|
+
# The slot is taken first, so a method comes before those it calls.
|
|
160
|
+
def define(kind, matcher, keep, name = nil, value: nil)
|
|
161
|
+
name = claim(name || "#{@prefix}_#{kind}")
|
|
162
|
+
ivar = "@v_#{name}" if keep
|
|
163
|
+
@values[ivar] = @types.initial(matcher) if ivar
|
|
164
|
+
slot = @methods.size
|
|
165
|
+
@methods << nil
|
|
166
|
+
body = yield(ivar).gsub(/^[ \t]+$/, "").gsub(/\n{3,}/, "\n\n")
|
|
167
|
+
@methods[slot] = "# #{describe(matcher)}\n#{POSITION}def #{name}(p)\n#{indent(body.chomp)}\nend\n"
|
|
168
|
+
Call.new(name, value || ivar)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# name, or name with a number added if it's taken. ref_ and skip_
|
|
172
|
+
# names (and @v_ref_ values) are the rules' own (see ref_method and
|
|
173
|
+
# skip_method), so a part of rule :ref is rule_ref_seq, not ref_seq.
|
|
174
|
+
def claim(name)
|
|
175
|
+
name = "rule_#{name}" if name.start_with?("ref_", "skip_")
|
|
176
|
+
candidate = name
|
|
177
|
+
n = 1
|
|
178
|
+
candidate = "#{name}#{n += 1}" while @taken.key?(candidate)
|
|
179
|
+
@taken[candidate] = true
|
|
180
|
+
candidate
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def describe(matcher)
|
|
184
|
+
kind = matcher.class.name.split("::").last
|
|
185
|
+
kind = "#{kind} #{matcher.name.inspect}" if matcher.is_a?(Label) || matcher.is_a?(Check)
|
|
186
|
+
"#{kind}: #{EBNF.expr(matcher.to_ast, EBNF::CHOICE)}"
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# How an EBNF expression appears in a generated string literal.
|
|
190
|
+
def expected(matcher) = EBNF.expr(matcher.to_ast, EBNF::CHOICE).inspect
|
|
191
|
+
|
|
192
|
+
def indent(text) = text.lines.map { _1.strip.empty? ? "\n" : " #{_1}" }.join
|
|
193
|
+
|
|
194
|
+
# --- Terminals ---
|
|
195
|
+
|
|
196
|
+
def str(matcher, want)
|
|
197
|
+
text = matcher.str
|
|
198
|
+
call = @compiled[[matcher, false]] ||= define(:str, matcher, false, "str_#{slug(text)}") do
|
|
199
|
+
next "p" if text.empty?
|
|
200
|
+
|
|
201
|
+
test = if text.bytesize == 1
|
|
202
|
+
"p < @len && @input.getbyte(p) == #{text.getbyte(0)}"
|
|
203
|
+
else
|
|
204
|
+
"bytes_at?(p, #{text.inspect})"
|
|
205
|
+
end
|
|
206
|
+
"return fail_at(p, #{text.inspect.inspect}) unless #{test}\n\np + #{text.bytesize}"
|
|
207
|
+
end
|
|
208
|
+
Call.new(call.name, want ? text.inspect : nil)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def pat(matcher, want, name)
|
|
212
|
+
regexp = anchored(matcher.re)
|
|
213
|
+
define(:pat, matcher, want, name) do |v|
|
|
214
|
+
found = if v
|
|
215
|
+
"unless len.nil?\n #{v} = @input.byteslice(p, len)\n return p + len\nend\n"
|
|
216
|
+
else
|
|
217
|
+
"return p + len unless len.nil?\n\n"
|
|
218
|
+
end
|
|
219
|
+
"@sc.pos = p\nlen = @sc.skip(#{regexp})\n#{found}fail_at(p, #{matcher.source.inspect})"
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Groups by number: Spinel's scanner can't look them up by name.
|
|
224
|
+
# Named groups are numbered in order, and once a group is named the
|
|
225
|
+
# unnamed ones don't capture (see Captures).
|
|
226
|
+
def captures(matcher, want, name)
|
|
227
|
+
regexp = anchored(matcher.pat.re)
|
|
228
|
+
define(:captures, matcher, want, name) do |v|
|
|
229
|
+
pairs = matcher.pat.re.names.each_with_index.map { |group, i| "#{hash_key(group.to_sym)} @sc[#{i + 1}]" }
|
|
230
|
+
found = if v
|
|
231
|
+
"unless len.nil?\n #{v} = { #{pairs.join(", ")} }\n return p + len\nend\n"
|
|
232
|
+
else
|
|
233
|
+
"return p + len unless len.nil?\n\n"
|
|
234
|
+
end
|
|
235
|
+
"@sc.pos = p\nlen = @sc.skip(#{regexp})\n#{found}fail_at(p, #{matcher.pat.source.inspect})"
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def anchored(re)
|
|
240
|
+
Regexps.anchored(re)
|
|
241
|
+
rescue Error => e
|
|
242
|
+
raise Error, "rule :#{@rule}: #{e.message}"
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# A literal is whatever the grammar says it stands for (keyword or
|
|
246
|
+
# exact string, then the ignore rule), resolved now.
|
|
247
|
+
def lit(matcher, want)
|
|
248
|
+
outer = @prefix
|
|
249
|
+
@prefix = "lit_#{slug(matcher.text)}"
|
|
250
|
+
compile_matcher(@grammar.literal_matcher(matcher.text), want, @prefix)
|
|
251
|
+
ensure
|
|
252
|
+
@prefix = outer
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def slug(text)
|
|
256
|
+
return text if text.match?(/\A\w+\z/)
|
|
257
|
+
|
|
258
|
+
text.each_char.map { |c| PUNCTUATION[c] || c.bytes.map { format("x%02x", _1) }.join }.join("_")
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# --- Sequences ---
|
|
262
|
+
|
|
263
|
+
def seq(matcher, want, name, build: nil)
|
|
264
|
+
wanted = seq_wanted(matcher, want || build)
|
|
265
|
+
define(:seq, matcher, want, name) do |v|
|
|
266
|
+
lines, locals = steps(matcher.parts, matcher.commit_at, wanted)
|
|
267
|
+
lines << "#{v} = #{build ? build.call(locals) : seq_value(matcher, locals)}" if v
|
|
268
|
+
[*lines, "q"].join("\n")
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def seq_wanted(matcher, want)
|
|
273
|
+
return [] unless want
|
|
274
|
+
return [matcher.only_at] if matcher.only_at
|
|
275
|
+
|
|
276
|
+
named = matcher.parts.each_index.select { matcher.parts[_1].capture_key }
|
|
277
|
+
named.empty? ? matcher.parts.each_index.to_a : named
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def seq_value(matcher, locals)
|
|
281
|
+
return locals.fetch(matcher.only_at) if matcher.only_at
|
|
282
|
+
|
|
283
|
+
keys = matcher.parts.map(&:capture_key)
|
|
284
|
+
return "[#{locals.values.join(", ")}]" if keys.none?
|
|
285
|
+
|
|
286
|
+
pairs = {}
|
|
287
|
+
keys.each_with_index { |key, i| pairs[key] = locals[i] if key }
|
|
288
|
+
"{ #{pairs.map { |key, local| "#{hash_key(key)} #{local}" }.join(", ")} }"
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def hash_key(key) = key.is_a?(Symbol) && key.match?(/\A[a-z_]\w*\z/) ? "#{key}:" : "#{key.inspect} =>"
|
|
292
|
+
|
|
293
|
+
# The parts in order from p, ending at q. A part that fails before
|
|
294
|
+
# commit_at fails the sequence; one at or after it ends the parse.
|
|
295
|
+
# Returns the lines and {index => local} for the wanted values.
|
|
296
|
+
def steps(parts, commit_at, wanted)
|
|
297
|
+
locals = {}
|
|
298
|
+
lines = parts.each_with_index.flat_map do |part, i|
|
|
299
|
+
keep = wanted.include?(i)
|
|
300
|
+
locals[i] = "v#{i}" if keep
|
|
301
|
+
step(compile_matcher(part, keep), part, i.zero? ? "p" : "q", commit_at && i >= commit_at, keep && "v#{i}")
|
|
302
|
+
end
|
|
303
|
+
lines.unshift("q = p") if parts.empty?
|
|
304
|
+
[lines, locals]
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def step(call, part, at, committed, local)
|
|
308
|
+
keep = local ? ["#{local} = #{call.value}"] : []
|
|
309
|
+
return ["q = #{call.name}(#{at})", "return FAIL if q == FAIL\n", *keep] unless committed
|
|
310
|
+
|
|
311
|
+
["r = #{call.name}(#{at})", "commit_failure(#{at}, #{expected(part)}) if r == FAIL", *keep, "q = r"]
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def pick(matcher, want, name)
|
|
315
|
+
wanted = want ? [matcher.index] : []
|
|
316
|
+
define(:pick, matcher, want, name) do |v|
|
|
317
|
+
lines, locals = steps(matcher.parts, nil, wanted)
|
|
318
|
+
lines << "#{v} = #{locals.fetch(matcher.index)}" if v
|
|
319
|
+
[*lines, "q"].join("\n")
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def sep_by(matcher, want, name)
|
|
324
|
+
define(:sep_by, matcher, want, name) do |v|
|
|
325
|
+
item = compile_matcher(matcher.item, want)
|
|
326
|
+
sep = compile_matcher(matcher.sep, false)
|
|
327
|
+
<<~RUBY
|
|
328
|
+
q = #{item.name}(p)
|
|
329
|
+
#{sep_by_none(matcher, v)}
|
|
330
|
+
#{"values = [#{item.value}]" if v}
|
|
331
|
+
while true
|
|
332
|
+
s = #{sep.name}(q)
|
|
333
|
+
break if s == FAIL
|
|
334
|
+
|
|
335
|
+
r = #{item.name}(s)
|
|
336
|
+
#{"q = s if r == FAIL # a trailing separator\n " if matcher.trailing}break if r == FAIL || r == q
|
|
337
|
+
|
|
338
|
+
#{"values << #{item.value}" if v}
|
|
339
|
+
q = r
|
|
340
|
+
end
|
|
341
|
+
#{"#{v} = values" if v}
|
|
342
|
+
q
|
|
343
|
+
RUBY
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def sep_by_none(matcher, value)
|
|
348
|
+
return "return FAIL if q == FAIL\n" unless matcher.min.zero?
|
|
349
|
+
return "return p if q == FAIL\n" unless value
|
|
350
|
+
|
|
351
|
+
"if q == FAIL\n #{value} = []\n return p\nend\n"
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# --- Choice and repetition ---
|
|
355
|
+
|
|
356
|
+
def choice(matcher, want, name)
|
|
357
|
+
define(:choice, matcher, want, name) do |v|
|
|
358
|
+
*alts, last = matcher.alts.map { compile_matcher(_1, want) }
|
|
359
|
+
tries = alts.map do |alt|
|
|
360
|
+
taken = v ? "if q != FAIL\n #{v} = #{alt.value}\n return q\nend\n" : "return q if q != FAIL\n"
|
|
361
|
+
"q = #{alt.name}(p)\n#{taken}"
|
|
362
|
+
end
|
|
363
|
+
[*tries, "q = #{last.name}(p)", ("#{v} = #{last.value} if q != FAIL" if v), "q"].compact.join("\n")
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def rep(matcher, want, name)
|
|
368
|
+
define(:rep, matcher, want, name) do |v|
|
|
369
|
+
inner = compile_matcher(matcher.inner, want)
|
|
370
|
+
<<~RUBY
|
|
371
|
+
n = 0
|
|
372
|
+
#{"values = []" if v}
|
|
373
|
+
while #{matcher.max ? "n < #{matcher.max}" : "true"}
|
|
374
|
+
r = #{inner.name}(p)
|
|
375
|
+
break if r == FAIL
|
|
376
|
+
|
|
377
|
+
# Matching nothing ends the loop, but counts as the first match.
|
|
378
|
+
if r == p
|
|
379
|
+
if n.zero?
|
|
380
|
+
#{"values << #{inner.value}" if v}
|
|
381
|
+
n = 1
|
|
382
|
+
end
|
|
383
|
+
break
|
|
384
|
+
end
|
|
385
|
+
#{"values << #{inner.value}" if v}
|
|
386
|
+
n += 1
|
|
387
|
+
p = r
|
|
388
|
+
end
|
|
389
|
+
#{"return FAIL if n < #{matcher.min}" if matcher.min.positive?}
|
|
390
|
+
#{"#{v} = values" if v}
|
|
391
|
+
p
|
|
392
|
+
RUBY
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def maybe(matcher, want, name)
|
|
397
|
+
define(:maybe, matcher, want, name) do |v|
|
|
398
|
+
inner = compile_matcher(matcher.inner, want)
|
|
399
|
+
next "q = #{inner.name}(p)\nq == FAIL ? p : q" unless v
|
|
400
|
+
|
|
401
|
+
"q = #{inner.name}(p)\nif q == FAIL\n #{v} = nil\n return p\nend\n#{v} = #{inner.value}\nq"
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# --- Lookahead ---
|
|
406
|
+
|
|
407
|
+
# Not: isolated, a commit failure inside is just a failure, and its
|
|
408
|
+
# diagnostics are dropped either way.
|
|
409
|
+
def negative(matcher, want, name)
|
|
410
|
+
define(:not, matcher, false, name, value: want ? "nil" : nil) do
|
|
411
|
+
inner = compile_matcher(matcher.inner, false)
|
|
412
|
+
"#{SAVE}#{lookahead(inner)}@furthest = of\n@expected = oe\nq == FAIL ? p : FAIL"
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# A commit failure inside a lookahead is just a failure. The rules it
|
|
417
|
+
# unwound through are left in progress, so they're abandoned. Each
|
|
418
|
+
# active lookahead holds one of Spinel's 64 begin blocks, so they're
|
|
419
|
+
# counted (see Compiler::LOOKAHEAD_DEPTH).
|
|
420
|
+
def lookahead(inner)
|
|
421
|
+
<<~RUBY
|
|
422
|
+
raise TooDeep, "lookaheads nested too deeply" if @lookaheads >= LOOKAHEAD_DEPTH
|
|
423
|
+
|
|
424
|
+
@lookaheads += 1
|
|
425
|
+
depth = @in_progress_at.size
|
|
426
|
+
begin
|
|
427
|
+
q = #{inner.name}(p)
|
|
428
|
+
rescue Committed
|
|
429
|
+
abandon(depth)
|
|
430
|
+
q = FAIL
|
|
431
|
+
end
|
|
432
|
+
@lookaheads -= 1
|
|
433
|
+
RUBY
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
def positive(matcher, want, name)
|
|
437
|
+
define(:and, matcher, false, name, value: want ? "nil" : nil) do
|
|
438
|
+
inner = compile_matcher(matcher.inner, false)
|
|
439
|
+
"#{SAVE}#{lookahead(inner)}peek_end(p, q, of, oe, #{expected(matcher.inner)}) ? p : FAIL"
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
def both(matcher, want, name)
|
|
444
|
+
define(:both, matcher, want, name) do |v|
|
|
445
|
+
left = compile_matcher(matcher.left, false)
|
|
446
|
+
right = compile_matcher(matcher.right, want)
|
|
447
|
+
"#{SAVE}#{lookahead(left)}return FAIL unless peek_end(p, q, of, oe, #{expected(matcher.left)})\n\n" \
|
|
448
|
+
"q = #{right.name}(p)\n#{"#{v} = #{right.value} if q != FAIL\n" if v}q"
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
# --- Values ---
|
|
453
|
+
|
|
454
|
+
def const(matcher, want)
|
|
455
|
+
inner = compile_matcher(matcher.inner, false)
|
|
456
|
+
return inner unless want
|
|
457
|
+
|
|
458
|
+
unless Literals.literal?(matcher.constant)
|
|
459
|
+
raise Error, "rule :#{@rule} uses .means(#{matcher.constant.inspect}), which isn't a literal"
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
Call.new(inner.name, matcher.constant.inspect)
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
# Send and Action. Both run even when nothing reads their value, as
|
|
466
|
+
# the interpreter's do: either may raise (an action checking its
|
|
467
|
+
# value, nil.upcase).
|
|
468
|
+
def transform(matcher, want, name)
|
|
469
|
+
check_send(matcher) if matcher.is_a?(Send)
|
|
470
|
+
|
|
471
|
+
kind = matcher.is_a?(Send) ? :send : :action
|
|
472
|
+
define(kind, matcher, want, name) do |v|
|
|
473
|
+
inner = compile_matcher(matcher.inner, true)
|
|
474
|
+
call = apply(matcher, inner.value)
|
|
475
|
+
"q = #{inner.name}(p)\n#{"#{v} = " if v}#{call} if q != FAIL\nq"
|
|
476
|
+
end
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def apply(matcher, value)
|
|
480
|
+
name = plain_name(matcher)
|
|
481
|
+
# String#to_i, Bignums included (Spinel's overflows past 63 bits).
|
|
482
|
+
if name == :to_i && matcher.is_a?(Send)
|
|
483
|
+
return "BirmanRT.to_i(#{value})" if @types.of(matcher.inner) == :str
|
|
484
|
+
return "(#{value}.nil? ? 0 : BirmanRT.to_i(#{value}))" if @types.str_or_nil?(matcher.inner)
|
|
485
|
+
end
|
|
486
|
+
return "#{value}.#{name}" if matcher.is_a?(Send)
|
|
487
|
+
|
|
488
|
+
@actions.copy(name, @rule, "action")
|
|
489
|
+
"#{name}(#{value})"
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
# The method name of a .map, .action or .check, which the generated
|
|
493
|
+
# code calls as written.
|
|
494
|
+
def plain_name(matcher)
|
|
495
|
+
name = matcher.method_name
|
|
496
|
+
return name if name.match?(/\A[a-z_]\w*[?!]?\z/)
|
|
497
|
+
|
|
498
|
+
form = { Send => "map", Action => "action", Check => "check" }.fetch(matcher.class)
|
|
499
|
+
raise Error, "rule :#{@rule} uses .#{form}(#{name.inspect}), which isn't a plain method name"
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
# .map(:hex) and .map(:oct) raise past 63 bits in a Spinel binary, as
|
|
503
|
+
# does .map(:to_i) unless the value is a String (BirmanRT.to_i reads
|
|
504
|
+
# those, and nil, as from str.maybe, is 0) or an Integer.
|
|
505
|
+
def check_send(matcher)
|
|
506
|
+
name = matcher.method_name
|
|
507
|
+
return unless %i[hex oct].include?(name) || (name == :to_i && !to_i_safe?(matcher.inner))
|
|
508
|
+
|
|
509
|
+
unknown = " (its value isn't always a String or an Integer)" if name == :to_i
|
|
510
|
+
raise Error, "rule :#{@rule} uses .map(:#{name})#{unknown}: #{Overflows::MESSAGES.fetch(name)}"
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
def to_i_safe?(inner) = %i[str int].include?(@types.of(inner)) || @types.str_or_nil?(inner)
|
|
514
|
+
|
|
515
|
+
# --- Nodes ---
|
|
516
|
+
|
|
517
|
+
def to(matcher, want, name)
|
|
518
|
+
return compile_matcher(matcher.inner, false) unless want
|
|
519
|
+
|
|
520
|
+
node = @nodes.name_for(matcher.node, @rule)
|
|
521
|
+
keys = hash_keys(matcher.inner)
|
|
522
|
+
return seq_node(matcher, node, name) if keys.is_a?(Array) && direct_seq?(matcher.inner)
|
|
523
|
+
|
|
524
|
+
define(:to, matcher, true, name) do |v|
|
|
525
|
+
inner = compile_matcher(matcher.inner, true)
|
|
526
|
+
args = node_args(matcher.node, keys, inner.value)
|
|
527
|
+
value = "x = #{inner.value}\n" if keys == :unknown && !matcher.node.members.empty?
|
|
528
|
+
"q = #{inner.name}(p)\nreturn FAIL if q == FAIL\n\n#{value}#{v} = #{node_call(node, args, "span(p, q)")}\nq"
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
# A seq of named parts builds the node from its locals, no Hash.
|
|
533
|
+
def seq_node(matcher, node, name)
|
|
534
|
+
fields = hash_keys(matcher.inner).to_h { [_1, nil] }
|
|
535
|
+
node_fields(matcher.node, fields) # checked before compiling the seq
|
|
536
|
+
build = ->(locals) { node_new(matcher.inner, node, matcher.node, locals) }
|
|
537
|
+
seq(matcher.inner, true, name, build:).tap { @compiled[[matcher, true]] = _1 }
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
def node_new(seq, node, node_class, locals)
|
|
541
|
+
by_key = {}
|
|
542
|
+
seq.parts.each_with_index { |part, i| by_key[part.capture_key] = locals[i] if part.capture_key }
|
|
543
|
+
node_call(node, node_fields(node_class, by_key), "span(p, q)")
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
# node.new(fields..., span), with no stray comma when there are no fields.
|
|
547
|
+
def node_call(node, args, span) = "#{node}.new(#{[*args, span].join(", ")})"
|
|
548
|
+
|
|
549
|
+
def direct_seq?(matcher) = matcher.is_a?(Seq) && matcher.only_at.nil?
|
|
550
|
+
|
|
551
|
+
# The keys of the Hash that matcher's value is: nil if it's never a
|
|
552
|
+
# Hash, :unknown if that's only known at parse time.
|
|
553
|
+
def hash_keys(matcher, seen = []) # rubocop:disable Metrics
|
|
554
|
+
case matcher
|
|
555
|
+
when Seq
|
|
556
|
+
return hash_keys(matcher.parts[matcher.only_at], seen) if matcher.only_at
|
|
557
|
+
|
|
558
|
+
keys = matcher.parts.filter_map(&:capture_key)
|
|
559
|
+
keys.empty? ? nil : keys.uniq
|
|
560
|
+
when Pick then hash_keys(matcher.parts[matcher.index], seen)
|
|
561
|
+
when Label, Check, Quiet, Named, Lexeme then hash_keys(matcher.inner, seen)
|
|
562
|
+
when Both then hash_keys(matcher.right, seen)
|
|
563
|
+
when Captures then matcher.pat.re.names.map(&:to_sym)
|
|
564
|
+
when Lit, Str, Pat, Rep, SepBy, Not, And, To then nil
|
|
565
|
+
when Infix then hash_keys(matcher.operand, seen) && :unknown # with no operator, the operand's value
|
|
566
|
+
when Send then Types::SENDS.key?(matcher.method_name) && matcher.method_name != :to_h ? nil : :unknown
|
|
567
|
+
when Const then matcher.constant.is_a?(Hash) ? matcher.constant.keys : nil
|
|
568
|
+
when Choice
|
|
569
|
+
keys = matcher.alts.map { hash_keys(_1, seen) }.uniq
|
|
570
|
+
keys.size == 1 ? keys.first : :unknown
|
|
571
|
+
when Ref
|
|
572
|
+
seen.include?(matcher.name) ? :unknown : hash_keys(@grammar.matcher_for(matcher.name), [*seen, matcher.name])
|
|
573
|
+
else :unknown
|
|
574
|
+
end
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
# Like Birman.build: a Hash value fills the fields by name, and any
|
|
578
|
+
# other value is the first field. Fields left out get their defaults.
|
|
579
|
+
def node_args(node, keys, value)
|
|
580
|
+
return node_fields(node, keys.to_h { [_1, "#{value}[#{_1.inspect}]"] }) if keys.is_a?(Array)
|
|
581
|
+
return [] if node.members.empty? # As in To#match: a node with no fields drops the value.
|
|
582
|
+
|
|
583
|
+
first, *rest = node.members
|
|
584
|
+
defaults = @nodes.defaults(node)
|
|
585
|
+
unless (rest - defaults.keys).empty?
|
|
586
|
+
raise Error, "rule :#{@rule} builds a node with #{node.members.size} fields from one value"
|
|
587
|
+
end
|
|
588
|
+
return node_fields(node, first => value) if keys.nil?
|
|
589
|
+
|
|
590
|
+
node.members.map { unknown_field(_1, _1 == first, defaults[_1]) }
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
# A field from x, which may or may not be a Hash, and may be one
|
|
594
|
+
# that leaves the field out.
|
|
595
|
+
def unknown_field(member, first, default)
|
|
596
|
+
key = member.inspect
|
|
597
|
+
from_hash = default ? "(x.key?(#{key}) ? x[#{key}] : #{default})" : "x.fetch(#{key})"
|
|
598
|
+
"x.is_a?(Hash) ? #{from_hash} : #{first ? "x" : default}"
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
# node.new's field arguments: each field from given (sources by field),
|
|
602
|
+
# or its default, written out so that each node gets its own.
|
|
603
|
+
def node_fields(node, given)
|
|
604
|
+
defaults = @nodes.defaults(node)
|
|
605
|
+
unless (given.keys - node.members).empty? && (node.members - given.keys - defaults.keys).empty?
|
|
606
|
+
raise Error, "rule :#{@rule} builds a node with fields #{node.members.inspect} " \
|
|
607
|
+
"from named parts #{given.keys.inspect}"
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
node.members.map { |member| given.fetch(member) { defaults.fetch(member) } }
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
def infix(matcher, want, name)
|
|
614
|
+
node = @nodes.name_for(matcher.node, @rule)
|
|
615
|
+
members = matcher.node.members
|
|
616
|
+
unless members.sort == %i[left op right]
|
|
617
|
+
raise Error, "rule :#{@rule}: infix needs a node with fields op, left and right"
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
define(:infix, matcher, want, name) do |v|
|
|
621
|
+
infix_body(matcher, node, members, v)
|
|
622
|
+
end
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
def infix_body(matcher, node, members, value)
|
|
626
|
+
operand = compile_matcher(matcher.operand, true)
|
|
627
|
+
op = compile_matcher(matcher.op, true)
|
|
628
|
+
fields = { op: "op", left: "left", right: operand.value }
|
|
629
|
+
<<~RUBY
|
|
630
|
+
r = #{operand.name}(p)
|
|
631
|
+
return FAIL if r == FAIL
|
|
632
|
+
|
|
633
|
+
left = #{operand.value}
|
|
634
|
+
q = r
|
|
635
|
+
while true
|
|
636
|
+
o = #{op.name}(q)
|
|
637
|
+
break if o == FAIL
|
|
638
|
+
|
|
639
|
+
op = #{op.value}
|
|
640
|
+
r = #{operand.name}(o)
|
|
641
|
+
break if r == FAIL
|
|
642
|
+
|
|
643
|
+
left = #{node}.new(#{members.map { fields.fetch(_1) }.join(", ")}, span(p, r))
|
|
644
|
+
q = r
|
|
645
|
+
end
|
|
646
|
+
#{"#{value} = left" if value}
|
|
647
|
+
q
|
|
648
|
+
RUBY
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
# --- Diagnostics, tokens, rules ---
|
|
652
|
+
|
|
653
|
+
def label(matcher, want, name)
|
|
654
|
+
inner = nil
|
|
655
|
+
call = define(:label, matcher, false, name) do
|
|
656
|
+
inner = compile_matcher(matcher.inner, want)
|
|
657
|
+
"#{SAVE}q = #{inner.name}(p)\nlabel_end(p, q, of, oe, #{matcher.name.inspect})"
|
|
658
|
+
end
|
|
659
|
+
Call.new(call.name, inner.value) # label_end leaves values alone
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
# The predicate runs whether or not anything reads the value (so the
|
|
663
|
+
# inner value is always kept), as an action does, but only after a
|
|
664
|
+
# match: after a failure the value variable is stale.
|
|
665
|
+
# `? true : false` keeps ok a bool for Spinel.
|
|
666
|
+
def check(matcher, want, name)
|
|
667
|
+
inner = nil
|
|
668
|
+
call = define(:check, matcher, false, name) do
|
|
669
|
+
inner = compile_matcher(matcher.inner, true)
|
|
670
|
+
predicate = plain_name(matcher)
|
|
671
|
+
@actions.copy(predicate, @rule, "check")
|
|
672
|
+
"#{SAVE}q = #{inner.name}(p)\nok = q == FAIL || (#{predicate}(#{inner.value}) ? true : false)\n" \
|
|
673
|
+
"check_end(p, q, ok, of, oe, #{matcher.name.inspect})"
|
|
674
|
+
end
|
|
675
|
+
Call.new(call.name, want ? inner.value : nil) # check_end leaves values alone
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
def quiet(matcher, want, name)
|
|
679
|
+
inner = nil
|
|
680
|
+
call = define(:quiet, matcher, false, name) do
|
|
681
|
+
inner = compile_matcher(matcher.inner, want)
|
|
682
|
+
"#{SAVE}q = #{inner.name}(p)\n@furthest = of\n@expected = oe\nq"
|
|
683
|
+
end
|
|
684
|
+
Call.new(call.name, inner.value)
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
def lexeme(matcher, want, name)
|
|
688
|
+
return compile_matcher(matcher.inner, want, name) unless ignore_rule
|
|
689
|
+
|
|
690
|
+
@ignores |= [ignore_rule]
|
|
691
|
+
define(:lexeme, matcher, want, name) do |v|
|
|
692
|
+
inner = compile_matcher(matcher.inner, want)
|
|
693
|
+
keep = "#{v} = #{inner.value}\n" if v
|
|
694
|
+
"q = #{inner.name}(p)\nreturn FAIL if q == FAIL\n\n#{keep}skip_#{ident(ignore_rule)}(q)"
|
|
695
|
+
end
|
|
696
|
+
end
|
|
697
|
+
|
|
698
|
+
def ref(matcher, want)
|
|
699
|
+
@pending << matcher.name
|
|
700
|
+
@read_rules |= [matcher.name] if want
|
|
701
|
+
Call.new("ref_#{ident(matcher.name)}", want ? "@v_ref_#{ident(matcher.name)}" : nil)
|
|
702
|
+
end
|
|
703
|
+
|
|
704
|
+
# --- The rest of the Parser ---
|
|
705
|
+
|
|
706
|
+
def initializer
|
|
707
|
+
<<~RUBY
|
|
708
|
+
def initialize(input)
|
|
709
|
+
super
|
|
710
|
+
@sc = StringScanner.new(input)
|
|
711
|
+
init_values
|
|
712
|
+
end
|
|
713
|
+
RUBY
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
def parse_method
|
|
717
|
+
start = ignore_rule ? "start = ref_#{ident(ignore_rule)}(0)\nstart = 0 if start == FAIL" : "start = 0"
|
|
718
|
+
root = @grammar.class.root
|
|
719
|
+
<<~RUBY
|
|
720
|
+
# Grammar#parse: Success(value) if all of the input parses, else Failure.
|
|
721
|
+
def parse
|
|
722
|
+
bad = BirmanRT.invalid_byte(@input)
|
|
723
|
+
return invalid_input(bad) if bad != FAIL
|
|
724
|
+
|
|
725
|
+
reset
|
|
726
|
+
init_memos
|
|
727
|
+
#{indent(start)}
|
|
728
|
+
q = ref_#{ident(root)}(start)
|
|
729
|
+
return Success.new(@v_ref_#{ident(root)}) if q == @len
|
|
730
|
+
|
|
731
|
+
fail_at(q, "end of input") if q != FAIL
|
|
732
|
+
failure(@furthest, @expected, false)
|
|
733
|
+
rescue Committed => e
|
|
734
|
+
failure(e.pos, e.expected, true)
|
|
735
|
+
rescue SystemStackError, TooDeep
|
|
736
|
+
failure_because(@position, "input is nested too deeply")
|
|
737
|
+
end
|
|
738
|
+
RUBY
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
# Lexeme's tail: the ignore rule, recorded so spans leave it out.
|
|
742
|
+
def skip_method(rule)
|
|
743
|
+
<<~RUBY
|
|
744
|
+
#{POSITION.chomp}
|
|
745
|
+
def skip_#{ident(rule)}(q)
|
|
746
|
+
s = ref_#{ident(rule)}(q)
|
|
747
|
+
return q if s == FAIL
|
|
748
|
+
|
|
749
|
+
ignored_after(q, s)
|
|
750
|
+
s
|
|
751
|
+
end
|
|
752
|
+
RUBY
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
def init_values
|
|
756
|
+
"def init_values\n#{indent(@values.map { |ivar, initial| "#{ivar} = #{initial}\n" }.join)}end\n"
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
def init_memos
|
|
760
|
+
lines = @rules.keys.flat_map do |name|
|
|
761
|
+
(@valued.include?(name) ? %w[mp mv mf me] : %w[mp mf me]).map { "@#{_1}_#{ident(name)} = {}\n" }
|
|
762
|
+
end
|
|
763
|
+
lines += @rules.keys.flat_map { round_memos(_1) }
|
|
764
|
+
"def init_memos\n#{indent(lines.join)}end\n"
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
# A cycle's leader numbers its rounds in @gp_; the cycle's other
|
|
768
|
+
# rules keep a memo for the round (see in_round).
|
|
769
|
+
def round_memos(name)
|
|
770
|
+
leader = @grammar.left_recursion.leader(name)
|
|
771
|
+
tables = if leader.nil? then []
|
|
772
|
+
elsif leader == name then leads_others?(name) ? %w[gp] : []
|
|
773
|
+
else @valued.include?(name) ? %w[rr rp rv rf re] : %w[rr rp rf re]
|
|
774
|
+
end
|
|
775
|
+
tables.map { "@#{_1}_#{ident(name)} = {}\n" }
|
|
776
|
+
end
|
|
777
|
+
|
|
778
|
+
# Ref: the rule's memo entry at p, computed the first time with
|
|
779
|
+
# fresh failure tracking, which is then merged into the caller's.
|
|
780
|
+
# No begin/ensure: Spinel allows only 64 of them active at once, and
|
|
781
|
+
# each level of nesting in the input would use one. Instead, entries
|
|
782
|
+
# being computed are kept on a stack, and a lookahead that catches a
|
|
783
|
+
# commit failure marks the ones it unwound through ABSENT (never
|
|
784
|
+
# deleted: Hash#delete is O(size) in Spinel), so the rule can be
|
|
785
|
+
# tried again there.
|
|
786
|
+
def ref_method(name, call)
|
|
787
|
+
leader = @grammar.left_recursion.leader(name)
|
|
788
|
+
return growing_ref_method(name, call) if leader == name
|
|
789
|
+
|
|
790
|
+
valued = @valued.include?(name)
|
|
791
|
+
id = ident(name)
|
|
792
|
+
hit = leader ? "unless q.nil? || q == ABSENT || q == IN_PROGRESS" : "unless q.nil? || q == ABSENT"
|
|
793
|
+
<<~RUBY
|
|
794
|
+
#{POSITION.chomp}
|
|
795
|
+
def ref_#{id}(p)
|
|
796
|
+
q = @mp_#{id}[p]
|
|
797
|
+
#{hit}
|
|
798
|
+
left_recursion(#{name.to_s.inspect}) if q == IN_PROGRESS
|
|
799
|
+
#{" @v_ref_#{id} = @mv_#{id}[p] if q != FAIL" if valued}
|
|
800
|
+
merge(@mf_#{id}[p], @me_#{id}[p])
|
|
801
|
+
return q
|
|
802
|
+
end#{"\n#{indent(in_round(name, leader, call)).chomp}" if leader}
|
|
803
|
+
@mp_#{id}[p] = IN_PROGRESS
|
|
804
|
+
@position = p
|
|
805
|
+
of = @furthest
|
|
806
|
+
oe = @expected
|
|
807
|
+
@furthest = p
|
|
808
|
+
@expected = NONE
|
|
809
|
+
@in_progress << @mp_#{id}
|
|
810
|
+
@in_progress_at << p
|
|
811
|
+
q = #{call.name}(p)
|
|
812
|
+
@in_progress.pop
|
|
813
|
+
@in_progress_at.pop
|
|
814
|
+
f = @furthest
|
|
815
|
+
ex = @expected
|
|
816
|
+
@furthest = of
|
|
817
|
+
@expected = oe
|
|
818
|
+
@mp_#{id}[p] = q
|
|
819
|
+
#{indent("if q != FAIL\n @v_ref_#{id} = #{call.value || "nil"}\n @mv_#{id}[p] = @v_ref_#{id}\nend").chomp if valued}
|
|
820
|
+
@mf_#{id}[p] = f
|
|
821
|
+
@me_#{id}[p] = ex
|
|
822
|
+
merge(f, ex)
|
|
823
|
+
q
|
|
824
|
+
end
|
|
825
|
+
RUBY
|
|
826
|
+
end
|
|
827
|
+
|
|
828
|
+
# The middle of Ref for a rule in a cycle led by another rule, as
|
|
829
|
+
# Birman::Context#memoize, after a final memo hit: while the leader
|
|
830
|
+
# grows at p (its @gp_ entry is the round's number, above 0), the
|
|
831
|
+
# rule is memoized there for the round only, in its @rr_ (the round
|
|
832
|
+
# each entry is for), @rp_, @rv_, @rf_ and @re_ tables, with fresh
|
|
833
|
+
# failure tracking merged into the caller's.
|
|
834
|
+
def in_round(name, leader, call)
|
|
835
|
+
valued = @valued.include?(name)
|
|
836
|
+
id = ident(name)
|
|
837
|
+
<<~RUBY
|
|
838
|
+
g = @gp_#{ident(leader)}[p]
|
|
839
|
+
unless g.nil? || g <= 0
|
|
840
|
+
r = @rr_#{id}[p]
|
|
841
|
+
unless r.nil? || r != g
|
|
842
|
+
q = @rp_#{id}[p]
|
|
843
|
+
#{" @v_ref_#{id} = @rv_#{id}[p] if q != FAIL" if valued}
|
|
844
|
+
merge(@rf_#{id}[p], @re_#{id}[p])
|
|
845
|
+
return q
|
|
846
|
+
end
|
|
847
|
+
@position = p
|
|
848
|
+
of = @furthest
|
|
849
|
+
oe = @expected
|
|
850
|
+
@furthest = p
|
|
851
|
+
@expected = NONE
|
|
852
|
+
q = #{call.name}(p)
|
|
853
|
+
#{indent("if q != FAIL\n @v_ref_#{id} = #{call.value || "nil"}\n @rv_#{id}[p] = @v_ref_#{id}\nend").chomp if valued}
|
|
854
|
+
f = @furthest
|
|
855
|
+
ex = @expected
|
|
856
|
+
@furthest = of
|
|
857
|
+
@expected = oe
|
|
858
|
+
@rr_#{id}[p] = g
|
|
859
|
+
@rp_#{id}[p] = q
|
|
860
|
+
@rf_#{id}[p] = f
|
|
861
|
+
@re_#{id}[p] = ex
|
|
862
|
+
merge(f, ex)
|
|
863
|
+
return q
|
|
864
|
+
end
|
|
865
|
+
RUBY
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
# Does name lead a cycle through other rules too?
|
|
869
|
+
def leads_others?(name)
|
|
870
|
+
analysis = @grammar.left_recursion
|
|
871
|
+
analysis.leader(name) == name && !analysis.cycle(name).one?
|
|
872
|
+
end
|
|
873
|
+
|
|
874
|
+
# Ref for a cycle's leader: seed-growing, as Birman::Context#grow.
|
|
875
|
+
# The memo entry starts as a failing seed, so the rule's own call is
|
|
876
|
+
# an ordinary memo hit, and each match that gets further replaces
|
|
877
|
+
# it. Each attempt starts with fresh failure tracking, so a commit
|
|
878
|
+
# failure reports only its own attempt's, and is then merged into
|
|
879
|
+
# the rule's, so every attempt's expectations are kept. A leader of
|
|
880
|
+
# other rules numbers each round (@round counts them across the
|
|
881
|
+
# parse) in its @gp_ table at p, and sets it to 0 when done. A
|
|
882
|
+
# lookahead that catches a commit failure mid-growth abandons the
|
|
883
|
+
# entry (ABSENT), seed, round number and all.
|
|
884
|
+
def growing_ref_method(name, call)
|
|
885
|
+
valued = @valued.include?(name)
|
|
886
|
+
id = ident(name)
|
|
887
|
+
marks = leads_others?(name)
|
|
888
|
+
<<~RUBY
|
|
889
|
+
#{POSITION.chomp}
|
|
890
|
+
def ref_#{id}(p)
|
|
891
|
+
q = @mp_#{id}[p]
|
|
892
|
+
unless q.nil? || q == ABSENT
|
|
893
|
+
#{" @v_ref_#{id} = @mv_#{id}[p] if q != FAIL" if valued}
|
|
894
|
+
merge(@mf_#{id}[p], @me_#{id}[p])
|
|
895
|
+
return q
|
|
896
|
+
end
|
|
897
|
+
@mp_#{id}[p] = FAIL
|
|
898
|
+
@mf_#{id}[p] = p
|
|
899
|
+
@me_#{id}[p] = NONE
|
|
900
|
+
@position = p
|
|
901
|
+
of = @furthest
|
|
902
|
+
oe = @expected
|
|
903
|
+
@furthest = p
|
|
904
|
+
@expected = NONE
|
|
905
|
+
@in_progress << @mp_#{id}
|
|
906
|
+
@in_progress_at << p#{"\n @in_progress << @gp_#{id}\n @in_progress_at << p" if marks}
|
|
907
|
+
while true#{"\n @round += 1\n @gp_#{id}[p] = @round" if marks}
|
|
908
|
+
af = @furthest
|
|
909
|
+
ae = @expected
|
|
910
|
+
@furthest = p
|
|
911
|
+
@expected = NONE
|
|
912
|
+
q = #{call.name}(p)
|
|
913
|
+
f = @furthest
|
|
914
|
+
ex = @expected
|
|
915
|
+
@furthest = af
|
|
916
|
+
@expected = ae
|
|
917
|
+
merge(f, ex)
|
|
918
|
+
break if q <= @mp_#{id}[p]
|
|
919
|
+
|
|
920
|
+
@mp_#{id}[p] = q
|
|
921
|
+
#{" @mv_#{id}[p] = #{call.value || "nil"}" if valued}
|
|
922
|
+
end
|
|
923
|
+
@in_progress.pop
|
|
924
|
+
@in_progress_at.pop#{"\n @gp_#{id}[p] = 0\n @in_progress.pop\n @in_progress_at.pop" if marks}
|
|
925
|
+
q = @mp_#{id}[p]
|
|
926
|
+
#{" @v_ref_#{id} = @mv_#{id}[p] if q != FAIL" if valued}
|
|
927
|
+
f = @furthest
|
|
928
|
+
ex = @expected
|
|
929
|
+
@furthest = of
|
|
930
|
+
@expected = oe
|
|
931
|
+
@mf_#{id}[p] = f
|
|
932
|
+
@me_#{id}[p] = ex
|
|
933
|
+
merge(f, ex)
|
|
934
|
+
q
|
|
935
|
+
end
|
|
936
|
+
RUBY
|
|
937
|
+
end
|
|
938
|
+
end
|
|
939
|
+
end
|
|
940
|
+
end
|