parsanol 1.3.112-aarch64-linux → 1.3.114-aarch64-linux
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/lib/parsanol/3.2/parsanol_native.so +2 -2
- data/lib/parsanol/3.3/parsanol_native.so +2 -2
- data/lib/parsanol/3.4/parsanol_native.so +2 -2
- data/lib/parsanol/4.0/parsanol_native.so +2 -2
- data/lib/parsanol/native/libparsanol.so +2 -2
- data/lib/parsanol/parg/artifact.rb +21 -9
- data/lib/parsanol/parg/compiler.rb +117 -15
- data/lib/parsanol/parg/diff.rb +15 -8
- data/lib/parsanol/parg/document.rb +5 -1
- data/lib/parsanol/parg/parser.rb +47 -9
- data/lib/parsanol/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8fc3b8894eb91a11d91f9992914d6051c711ddc66b695b83f513088323c10d99
|
|
4
|
+
data.tar.gz: e3492ffc96d70fc028c17bb347ac315029caf706b3c49ea70e65deaa46b8f52f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4527d1fb5b73608aec071c81e972034bd73d98c254be9536d52cce6d4e381d37aea76834a30982912335bab11a7076d42a0a9fc3499d4f4a93172fd590ddb98d
|
|
7
|
+
data.tar.gz: a9a9e610e50b4ff6bd156a9889c8899ec92ee97b7c2d6c7ff815abdb15cb9868c0d9783123dc8df35c3ebbdf36f604e7b97c2d20258f121f60315c9056399729
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: data/lib/parsanol/3.2/parsanol_native.so
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 46122264 bytes
|
|
4
|
+
sha256: 4c782021e134c6025ae13057d9f1bfc7dcfa845d0227606474b767d23e7c0afe
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: data/lib/parsanol/3.3/parsanol_native.so
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 46357288 bytes
|
|
4
|
+
sha256: 8ab53f41d835b00b78c69abd9a8abcf40bab2255f3d48af7465c8e201f0dcd5a
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: data/lib/parsanol/3.4/parsanol_native.so
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 46356976 bytes
|
|
4
|
+
sha256: 99972eec6d81c218e3f39f63db827914d58d9f5214aa605d31dfc0c8c5cb05a5
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: data/lib/parsanol/4.0/parsanol_native.so
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 46366976 bytes
|
|
4
|
+
sha256: 827c2e86e427e028b8022ef0cc7530794cd64c954b88022e0db1faa2fa02cbc6
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: data/lib/parsanol/native/libparsanol.so
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 41881688 bytes
|
|
4
|
+
sha256: 0cbe6973c43062f450a0cdabbdfb09d9401991038df1bd62dc67d78e030092c7
|
|
@@ -27,8 +27,16 @@ module Parsanol
|
|
|
27
27
|
SUPPORTED_SHAPE = "parsanol-tree/v2"
|
|
28
28
|
|
|
29
29
|
# The editions this runtime parses (ruby#357's consolidation):
|
|
30
|
-
#
|
|
31
|
-
SUPPORTED_EDITIONS =
|
|
30
|
+
# 1 = the legacy shapes; 2 = named trees + explicit spans.
|
|
31
|
+
SUPPORTED_EDITIONS = [1, 2].freeze
|
|
32
|
+
|
|
33
|
+
# The header and envelope spellings that have shipped: the quoted
|
|
34
|
+
# v-forms stay loadable so artifacts baked before the numeric
|
|
35
|
+
# surface remain readable.
|
|
36
|
+
EDITION_ALIASES = {
|
|
37
|
+
1 => 1, "1" => 1, "v1" => 1, "classic" => 1,
|
|
38
|
+
2 => 2, "2" => 2, "v2" => 2
|
|
39
|
+
}.freeze
|
|
32
40
|
|
|
33
41
|
def initialize(envelope, path, tables_dir, imports_dir: nil)
|
|
34
42
|
unless envelope["shape"] == SUPPORTED_SHAPE
|
|
@@ -42,16 +50,13 @@ module Parsanol
|
|
|
42
50
|
bindings = envelope["bindings"]
|
|
43
51
|
validate_bindings!(bindings) unless bindings.nil?
|
|
44
52
|
@envelope = envelope
|
|
45
|
-
# ruby#353: absent field = the pre-1.3.109 semantics. A level
|
|
46
|
-
# above this runtime's is a hard error (the trees would carry
|
|
47
|
-
# nodes this engine never emits); a lower one warns — parsing
|
|
48
|
-
# works, but frozen tree comparisons will differ.
|
|
49
53
|
# The declared edition must be one this runtime understands
|
|
50
|
-
# (ruby#357's consolidation: one axis, `edition
|
|
51
|
-
|
|
54
|
+
# (ruby#357's consolidation: one axis, `edition`). Absent field
|
|
55
|
+
# = the pre-1.3.109 artifacts, whose trees are edition 1.
|
|
56
|
+
edition = EDITION_ALIASES[envelope.fetch("edition", 1)]
|
|
52
57
|
unless SUPPORTED_EDITIONS.include?(edition)
|
|
53
58
|
raise ArtifactError,
|
|
54
|
-
"artifact declares edition #{edition.inspect}; this " \
|
|
59
|
+
"artifact declares edition #{envelope['edition'].inspect}; this " \
|
|
55
60
|
"runtime supports #{SUPPORTED_EDITIONS.map(&:inspect).join(', ')}"
|
|
56
61
|
end
|
|
57
62
|
@path = path
|
|
@@ -66,6 +71,13 @@ module Parsanol
|
|
|
66
71
|
|
|
67
72
|
def version = envelope["version"]
|
|
68
73
|
|
|
74
|
+
# The edition the artifact's trees carry: 1 = the legacy shapes,
|
|
75
|
+
# 2 = fully named trees. Legacy artifacts that spell the field
|
|
76
|
+
# "v2" (or omit it) normalize here.
|
|
77
|
+
def edition
|
|
78
|
+
EDITION_ALIASES.fetch(envelope.fetch("edition", 1))
|
|
79
|
+
end
|
|
80
|
+
|
|
69
81
|
# Freshness (parsanol-ruby#209): what source revision and compiler
|
|
70
82
|
# produced this artifact. `source_hash` is the SHA-256 of the .parg
|
|
71
83
|
# text at compile time; nil on artifacts baked before the field
|
|
@@ -233,12 +233,24 @@ module Parsanol
|
|
|
233
233
|
# compile time — the tree shape consumers read, opt-in via
|
|
234
234
|
# `mode` in the grammar header. Engines see ordinary
|
|
235
235
|
# Named atoms; identity rides the differential corpus.
|
|
236
|
-
def apply_modern_captures(atom)
|
|
236
|
+
def apply_modern_captures(atom, rule_name)
|
|
237
237
|
return atom if @document.edition == "v1"
|
|
238
|
+
# A glue rule's own body compiles unwrapped (parsanol-ruby#364):
|
|
239
|
+
# the declaration means the tree never sees this subtree named,
|
|
240
|
+
# inside or out.
|
|
241
|
+
return atom if glue_rule?(rule_name)
|
|
238
242
|
|
|
239
243
|
wrap_uncaptured(atom)
|
|
240
244
|
end
|
|
241
245
|
|
|
246
|
+
# The `glue` declaration's verdict (parsanol-ruby#364): references
|
|
247
|
+
# to declared glue rules never wrap — the trivia exemption's
|
|
248
|
+
# "glue never becomes a capture", author-declared for rules that
|
|
249
|
+
# eat more than whitespace.
|
|
250
|
+
def glue_rule?(name)
|
|
251
|
+
@document.glue_rules.include?(name.to_s)
|
|
252
|
+
end
|
|
253
|
+
|
|
242
254
|
def wrap_uncaptured(atom)
|
|
243
255
|
case atom
|
|
244
256
|
when Atoms::Named
|
|
@@ -254,10 +266,11 @@ module Parsanol
|
|
|
254
266
|
# capture-producing refs both stay unwrapped; the bodies
|
|
255
267
|
# agree by design.
|
|
256
268
|
if trivia_only?(atom.rule_name.to_s, { atom.rule_name.to_s => true }) ||
|
|
269
|
+
glue_rule?(atom.rule_name.to_s) ||
|
|
257
270
|
produces_capture?(atom.parslet)
|
|
258
271
|
atom
|
|
259
272
|
else
|
|
260
|
-
Atoms::Named.new(atom, atom.rule_name.
|
|
273
|
+
Atoms::Named.new(atom, atom.rule_name.to_sym)
|
|
261
274
|
end
|
|
262
275
|
# rubocop:enable Lint/DuplicateBranch
|
|
263
276
|
when Atoms::Sequence, Atoms::Alternative
|
|
@@ -274,10 +287,10 @@ module Parsanol
|
|
|
274
287
|
end
|
|
275
288
|
|
|
276
289
|
# Whether the named rule's body is provably whitespace-only:
|
|
277
|
-
# literals strip to empty;
|
|
278
|
-
# compose the verdict; references
|
|
279
|
-
# with a cycle set; anything else
|
|
280
|
-
# captures) disqualifies.
|
|
290
|
+
# literals strip to empty, character classes are whitespace-only;
|
|
291
|
+
# sequence/alternation/repetition compose the verdict; references
|
|
292
|
+
# resolve through the atom cache with a cycle set; anything else
|
|
293
|
+
# (customs, authored captures) disqualifies.
|
|
281
294
|
def trivia_only?(name, seen)
|
|
282
295
|
return false unless @document.rules.key?(name)
|
|
283
296
|
|
|
@@ -288,6 +301,8 @@ module Parsanol
|
|
|
288
301
|
case atom
|
|
289
302
|
when Atoms::Str
|
|
290
303
|
atom.str.to_s.strip.empty?
|
|
304
|
+
when Atoms::Re
|
|
305
|
+
whitespace_class?(atom.match)
|
|
291
306
|
when Atoms::Sequence, Atoms::Alternative
|
|
292
307
|
kids = atom.respond_to?(:alternatives) ? atom.alternatives : atom.parslets
|
|
293
308
|
kids.all? { |child| trivia_walk(child, seen) }
|
|
@@ -304,6 +319,91 @@ module Parsanol
|
|
|
304
319
|
end
|
|
305
320
|
end
|
|
306
321
|
|
|
322
|
+
# A class matches only whitespace when every codepoint it admits
|
|
323
|
+
# strips to empty — the same verdict Atoms::Str gets. Compiled
|
|
324
|
+
# classes are the dialect class_pattern emits: bracket bodies of
|
|
325
|
+
# literal chars, \t\n\v\f\r, \xHH, \uXXXX and \u{...}, plus ranges
|
|
326
|
+
# between them. Anything else disqualifies.
|
|
327
|
+
def whitespace_class?(pattern)
|
|
328
|
+
body = pattern.to_s[/\A\[(.*)\]\z/m, 1]
|
|
329
|
+
return false if body.nil? || body.empty? || body.start_with?("^")
|
|
330
|
+
|
|
331
|
+
ranges = class_ranges(body)
|
|
332
|
+
ranges&.all? { |lo, hi| whitespace_range?(lo, hi) }
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
WHITESPACE_CODEPOINTS = [0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x20].freeze
|
|
336
|
+
|
|
337
|
+
def whitespace_range?(low, high)
|
|
338
|
+
return false if low > high
|
|
339
|
+
|
|
340
|
+
union = WHITESPACE_CODEPOINTS.count { |code| code.between?(low, high) }
|
|
341
|
+
union == high - low + 1
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def class_ranges(body)
|
|
345
|
+
slots = []
|
|
346
|
+
pending_range = false
|
|
347
|
+
index = 0
|
|
348
|
+
while index < body.length
|
|
349
|
+
if body[index] == "-" && !pending_range && !slots.empty?
|
|
350
|
+
pending_range = true
|
|
351
|
+
index += 1
|
|
352
|
+
next
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
code, index = class_token(body, index)
|
|
356
|
+
return nil unless code
|
|
357
|
+
|
|
358
|
+
if pending_range
|
|
359
|
+
previous = slots.pop
|
|
360
|
+
slots << [previous[0], code].minmax
|
|
361
|
+
pending_range = false
|
|
362
|
+
else
|
|
363
|
+
slots << [code, code]
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
return nil if pending_range
|
|
367
|
+
|
|
368
|
+
slots
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def class_token(body, index)
|
|
372
|
+
char = body[index]
|
|
373
|
+
unless char == "\\"
|
|
374
|
+
return [char.ord, index + 1]
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
escaped = body[index + 1]
|
|
378
|
+
return nil if escaped.nil?
|
|
379
|
+
|
|
380
|
+
case escaped
|
|
381
|
+
when "x" then hex_token(body, index + 2, 2)
|
|
382
|
+
when "u"
|
|
383
|
+
if body[index + 2] == "{"
|
|
384
|
+
close = body.index("}", index + 3)
|
|
385
|
+
return nil unless close
|
|
386
|
+
|
|
387
|
+
[body[(index + 3)...close].to_i(16), close + 1]
|
|
388
|
+
else
|
|
389
|
+
hex_token(body, index + 2, 4)
|
|
390
|
+
end
|
|
391
|
+
when "t" then [0x09, index + 2]
|
|
392
|
+
when "n" then [0x0A, index + 2]
|
|
393
|
+
when "v" then [0x0B, index + 2]
|
|
394
|
+
when "f" then [0x0C, index + 2]
|
|
395
|
+
when "r" then [0x0D, index + 2]
|
|
396
|
+
else [escaped.ord, index + 2]
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def hex_token(body, start, length)
|
|
401
|
+
hex = body[start, length]
|
|
402
|
+
return nil unless hex&.match?(/\A\h{#{length}}\z/)
|
|
403
|
+
|
|
404
|
+
[hex.to_i(16), start + length]
|
|
405
|
+
end
|
|
406
|
+
|
|
307
407
|
# Whether the atom's flattening surfaces any Named capture:
|
|
308
408
|
# rule references resolve through (their own wrap pass already
|
|
309
409
|
# ran — a wrapped ref produces a capture), trivia never
|
|
@@ -353,7 +453,11 @@ module Parsanol
|
|
|
353
453
|
case atom
|
|
354
454
|
when Atoms::Named then nil
|
|
355
455
|
when Atoms::Entity
|
|
356
|
-
|
|
456
|
+
# Mirror wrap_uncaptured's verdicts: trivia glue, declared
|
|
457
|
+
# glue, and capture-producing refs all stay bare.
|
|
458
|
+
unless trivia_only?(atom.rule_name.to_s, { atom.rule_name.to_s => true }) ||
|
|
459
|
+
glue_rule?(atom.rule_name.to_s) ||
|
|
460
|
+
produces_capture?(atom.parslet)
|
|
357
461
|
lines << "rule #{site}: reference to #{atom.rule_name} produces no capture -- " \
|
|
358
462
|
"modern mode would name it #{atom.rule_name}"
|
|
359
463
|
end
|
|
@@ -392,7 +496,7 @@ module Parsanol
|
|
|
392
496
|
# atom_for; a mutual-reference grammar must hit the cache,
|
|
393
497
|
# not rebuild), then the cache takes the wrapped tree.
|
|
394
498
|
@atom_cache[name] = atom
|
|
395
|
-
atom = apply_modern_captures(atom)
|
|
499
|
+
atom = apply_modern_captures(atom, name)
|
|
396
500
|
@atom_cache[name] = atom
|
|
397
501
|
atom
|
|
398
502
|
end
|
|
@@ -1233,13 +1337,11 @@ module Parsanol
|
|
|
1233
1337
|
"unicode_tables" => "onigmo (Ruby #{RUBY_VERSION})",
|
|
1234
1338
|
"compiler_version" => Parsanol::VERSION,
|
|
1235
1339
|
}
|
|
1236
|
-
# ruby#304: the envelope always names the capture
|
|
1237
|
-
# consumers read the tree-shape contract before parsing.
|
|
1238
|
-
|
|
1239
|
-
#
|
|
1240
|
-
|
|
1241
|
-
# explicit end_guard/marker/hyphen nodes = 2). Consumers freeze
|
|
1242
|
-
# tree shapes and mappers; the loader asserts the level at
|
|
1340
|
+
# ruby#304: the envelope always names the capture edition —
|
|
1341
|
+
# consumers read the tree-shape contract before parsing. The
|
|
1342
|
+
# numeric form is canonical: 1 = the legacy shapes, 2 = fully
|
|
1343
|
+
# named trees + explicit spans.
|
|
1344
|
+
envelope["edition"] = @document.edition == "v2" ? 2 : 1
|
|
1243
1345
|
if @document.skip_declaration
|
|
1244
1346
|
# parsanol-ruby#134: declared trivia rule. Injection is baked
|
|
1245
1347
|
# into the compiled trees (plain atom kinds), so consumers
|
data/lib/parsanol/parg/diff.rb
CHANGED
|
@@ -21,7 +21,7 @@ module Parsanol
|
|
|
21
21
|
|
|
22
22
|
def diff(old_envelope, new_envelope)
|
|
23
23
|
findings = []
|
|
24
|
-
findings.concat(
|
|
24
|
+
findings.concat(diff_edition(old_envelope, new_envelope))
|
|
25
25
|
findings.concat(diff_entries(old_envelope, new_envelope))
|
|
26
26
|
findings.concat(diff_bindings(old_envelope, new_envelope))
|
|
27
27
|
findings.concat(diff_tests(old_envelope, new_envelope))
|
|
@@ -168,18 +168,25 @@ module Parsanol
|
|
|
168
168
|
branches.uniq.sort
|
|
169
169
|
end
|
|
170
170
|
|
|
171
|
-
# --
|
|
171
|
+
# -- edition ------------------------------------------------------
|
|
172
172
|
|
|
173
|
-
def
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
return [] if
|
|
173
|
+
def diff_edition(old_env, new_env)
|
|
174
|
+
old_edition = edition_level(old_env)
|
|
175
|
+
new_edition = edition_level(new_env)
|
|
176
|
+
return [] if old_edition == new_edition
|
|
177
177
|
|
|
178
|
-
[Finding.new(:major, :
|
|
179
|
-
"
|
|
178
|
+
[Finding.new(:major, :edition_changed,
|
|
179
|
+
"edition changed #{old_edition.inspect} -> #{new_edition.inspect} " \
|
|
180
180
|
"(every uncaptured node's tree shape changes)")]
|
|
181
181
|
end
|
|
182
182
|
|
|
183
|
+
# Artifacts baked before the numeric surface spell the edition
|
|
184
|
+
# "v1"/"v2"; they mean the same levels, not a change.
|
|
185
|
+
def edition_level(envelope)
|
|
186
|
+
declared = envelope.fetch("edition", 1)
|
|
187
|
+
Artifact::EDITION_ALIASES.fetch(declared, declared)
|
|
188
|
+
end
|
|
189
|
+
|
|
183
190
|
# -- bindings -----------------------------------------------------
|
|
184
191
|
|
|
185
192
|
def diff_bindings(old_env, new_env)
|
|
@@ -18,7 +18,7 @@ module Parsanol
|
|
|
18
18
|
Test = Struct.new(:entry, :kind, :input, :expect)
|
|
19
19
|
|
|
20
20
|
attr_accessor :grammar_name, :version, :edition, :source, :uses, :skip_declaration, :skip_source,
|
|
21
|
-
:atomic_rules, :skip_capture_key, :brace_rules
|
|
21
|
+
:atomic_rules, :skip_capture_key, :brace_rules, :glue_rules
|
|
22
22
|
# R2 declared resync: rule name -> skip pattern (the span matched
|
|
23
23
|
# at a recovery point is consumed as annotated error text).
|
|
24
24
|
attr_reader :resyncs
|
|
@@ -60,6 +60,10 @@ module Parsanol
|
|
|
60
60
|
# contiguously).
|
|
61
61
|
@atomic_rules = []
|
|
62
62
|
@brace_rules = []
|
|
63
|
+
# Rules declared `glue <name> ...` (parsanol-ruby#364): the
|
|
64
|
+
# edition-2 wrap never names them — references stay bare and
|
|
65
|
+
# the rule's own body compiles unwrapped.
|
|
66
|
+
@glue_rules = []
|
|
63
67
|
@authored_rules = nil
|
|
64
68
|
# R2 declared resync: rule -> skip pattern. Recovery consumes
|
|
65
69
|
# each pattern match at a failure point as annotated error text
|
data/lib/parsanol/parg/parser.rb
CHANGED
|
@@ -18,6 +18,14 @@ module Parsanol
|
|
|
18
18
|
|
|
19
19
|
IN_BLOCK_SECTIONS = %w[entry bindings preprocess test].freeze
|
|
20
20
|
|
|
21
|
+
# The header's edition spellings: the bare numbers are canonical
|
|
22
|
+
# (Rust's Cargo.toml model); the quoted forms stay accepted as
|
|
23
|
+
# aliases for grammars written before the numeric surface.
|
|
24
|
+
EDITIONS = {
|
|
25
|
+
"1" => "v1", "v1" => "v1", "classic" => "v1",
|
|
26
|
+
"2" => "v2", "v2" => "v2"
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
21
29
|
STATE_KEYWORDS = %w[custom state].freeze
|
|
22
30
|
KEYWORDS = %w[grammar as alt from_table column bindings render derive
|
|
23
31
|
preprocess entry table_lookup custom state set switch
|
|
@@ -227,6 +235,23 @@ module Parsanol
|
|
|
227
235
|
|
|
228
236
|
def punct(char) = expect(:punct, char)
|
|
229
237
|
|
|
238
|
+
def edition_value
|
|
239
|
+
token = advance
|
|
240
|
+
raw = case token&.type
|
|
241
|
+
when :num then token.value
|
|
242
|
+
when :str then unquote(token.value)
|
|
243
|
+
else
|
|
244
|
+
raise ParseError,
|
|
245
|
+
"edition must be a number (1 or 2) or a quoted alias " \
|
|
246
|
+
"(\"v1\", \"v2\", \"classic\")"
|
|
247
|
+
end
|
|
248
|
+
EDITIONS.fetch(raw) do
|
|
249
|
+
raise ParseError,
|
|
250
|
+
"unknown edition #{raw.inspect} -- supported editions are " \
|
|
251
|
+
"1 (default) and 2 (named trees)"
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
230
255
|
def parse_grammar(document)
|
|
231
256
|
document.grammar_name = ident.value
|
|
232
257
|
ident("version")
|
|
@@ -234,19 +259,14 @@ module Parsanol
|
|
|
234
259
|
# `edition` — the language edition (ruby#304, ruby#357's
|
|
235
260
|
# consolidation; Rust's Cargo.toml `edition` is the model):
|
|
236
261
|
# the author declares, per grammar, which tree semantics the
|
|
237
|
-
# grammar means. THE DEFAULT IS
|
|
238
|
-
# owner's 2026-10-28 reversal).
|
|
262
|
+
# grammar means. THE DEFAULT IS 1 (the legacy shapes — the
|
|
263
|
+
# owner's 2026-10-28 reversal). 2 = fully named trees +
|
|
239
264
|
# explicit spans. `mode` remains accepted as a deprecated
|
|
240
265
|
# alias for the keyword. The edition is canonicalized here —
|
|
241
266
|
# everything downstream only ever sees "v1"/"v2".
|
|
242
267
|
if peek&.type == :ident && %w[edition mode].include?(peek.value)
|
|
243
|
-
|
|
244
|
-
edition =
|
|
245
|
-
unless %w[v2 v1 classic].include?(edition)
|
|
246
|
-
raise ParseError,
|
|
247
|
-
"unknown edition #{edition.inspect} -- supported editions are \"v1\" (default) and \"v2\" (named trees)"
|
|
248
|
-
end
|
|
249
|
-
document.edition = edition == "v2" ? "v2" : "v1"
|
|
268
|
+
advance
|
|
269
|
+
document.edition = edition_value
|
|
250
270
|
end
|
|
251
271
|
punct("{")
|
|
252
272
|
skip_newlines
|
|
@@ -318,6 +338,24 @@ module Parsanol
|
|
|
318
338
|
skip_newlines
|
|
319
339
|
next
|
|
320
340
|
end
|
|
341
|
+
# `glue <rule> [<rule>...]` (parsanol-ruby#364): names rules
|
|
342
|
+
# whose references edition 2 never wraps — glue the tree must
|
|
343
|
+
# not see. Same-line names accumulate; the declaration may
|
|
344
|
+
# appear any number of times.
|
|
345
|
+
if peek&.type == :ident && peek.value == "glue"
|
|
346
|
+
advance
|
|
347
|
+
names = []
|
|
348
|
+
while peek&.type == :ident
|
|
349
|
+
names << ident.value
|
|
350
|
+
end
|
|
351
|
+
if names.empty?
|
|
352
|
+
raise ParseError, "glue must name at least one rule"
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
document.glue_rules.concat(names)
|
|
356
|
+
skip_newlines
|
|
357
|
+
next
|
|
358
|
+
end
|
|
321
359
|
# `atomic <rule> = ...` suppresses skip-trivia injection inside
|
|
322
360
|
# the rule body: token-shaped rules match their input
|
|
323
361
|
# contiguously instead of spanning trivia between iterations.
|
data/lib/parsanol/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: parsanol
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.3.
|
|
4
|
+
version: 1.3.114
|
|
5
5
|
platform: aarch64-linux
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A small Ruby library for constructing parsers in the PEG (Parsing Expression
|
|
14
14
|
Grammar) fashion. Parsanol provides Parslet-compatible API with additional features
|