parsanol 1.3.112-aarch64-linux → 1.3.113-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 +100 -12
- data/lib/parsanol/parg/diff.rb +15 -8
- data/lib/parsanol/parg/parser.rb +29 -9
- data/lib/parsanol/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9d346b7aab39294839b92c4c362b605854384254d58b8687b57f426bf9341701
|
|
4
|
+
data.tar.gz: 7b119bc3421018d3d3c3c5341f9833f1324d4a11b5890e5972dfbfa42e0c6c31
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1d546f40925347f2f7a9c80ec01594907c073fa35398548b21b7b7cebe4050a991d946bda46ed2810c52b3a2e390d406c99aad8b5060553f5dc4684fc5c32628
|
|
7
|
+
data.tar.gz: 160406b0ae9a185c809825e4590bfd10645e7f1ec4bcbefb498b447984f98cb1df72d0c6193b90fa583bd08ba1f61f167d5d9b366cd8af8648266aed3ad8d3c7
|
|
@@ -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: 45899640 bytes
|
|
4
|
+
sha256: 2152fb3fabceb57fe7c9c040b62c8c05ce5041c6cd998bd2d1e26c8a02698626
|
|
@@ -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: 46134680 bytes
|
|
4
|
+
sha256: c2f6e3916142c8dfec0cc1707b071cbf38491cfcec3382452b21f766dc7c42a0
|
|
@@ -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: 46130320 bytes
|
|
4
|
+
sha256: 817de189148e9924aec84d5ea725fa1cfbc303e3043366db18c140e18efa8100
|
|
@@ -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: 46140360 bytes
|
|
4
|
+
sha256: c4eb75d0ff822c077450b4fb765a113eddc2138f22fb0164e607034fa9a09999
|
|
@@ -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: 41659008 bytes
|
|
4
|
+
sha256: f9f0f4e0aee0a6fb9d3ec8fd026b3753719de2a01a016c3eff507288705704f0
|
|
@@ -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
|
|
@@ -274,10 +274,10 @@ module Parsanol
|
|
|
274
274
|
end
|
|
275
275
|
|
|
276
276
|
# 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.
|
|
277
|
+
# literals strip to empty, character classes are whitespace-only;
|
|
278
|
+
# sequence/alternation/repetition compose the verdict; references
|
|
279
|
+
# resolve through the atom cache with a cycle set; anything else
|
|
280
|
+
# (customs, authored captures) disqualifies.
|
|
281
281
|
def trivia_only?(name, seen)
|
|
282
282
|
return false unless @document.rules.key?(name)
|
|
283
283
|
|
|
@@ -288,6 +288,8 @@ module Parsanol
|
|
|
288
288
|
case atom
|
|
289
289
|
when Atoms::Str
|
|
290
290
|
atom.str.to_s.strip.empty?
|
|
291
|
+
when Atoms::Re
|
|
292
|
+
whitespace_class?(atom.match)
|
|
291
293
|
when Atoms::Sequence, Atoms::Alternative
|
|
292
294
|
kids = atom.respond_to?(:alternatives) ? atom.alternatives : atom.parslets
|
|
293
295
|
kids.all? { |child| trivia_walk(child, seen) }
|
|
@@ -304,6 +306,91 @@ module Parsanol
|
|
|
304
306
|
end
|
|
305
307
|
end
|
|
306
308
|
|
|
309
|
+
# A class matches only whitespace when every codepoint it admits
|
|
310
|
+
# strips to empty — the same verdict Atoms::Str gets. Compiled
|
|
311
|
+
# classes are the dialect class_pattern emits: bracket bodies of
|
|
312
|
+
# literal chars, \t\n\v\f\r, \xHH, \uXXXX and \u{...}, plus ranges
|
|
313
|
+
# between them. Anything else disqualifies.
|
|
314
|
+
def whitespace_class?(pattern)
|
|
315
|
+
body = pattern.to_s[/\A\[(.*)\]\z/m, 1]
|
|
316
|
+
return false if body.nil? || body.empty? || body.start_with?("^")
|
|
317
|
+
|
|
318
|
+
ranges = class_ranges(body)
|
|
319
|
+
ranges&.all? { |lo, hi| whitespace_range?(lo, hi) }
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
WHITESPACE_CODEPOINTS = [0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x20].freeze
|
|
323
|
+
|
|
324
|
+
def whitespace_range?(low, high)
|
|
325
|
+
return false if low > high
|
|
326
|
+
|
|
327
|
+
union = WHITESPACE_CODEPOINTS.count { |code| code.between?(low, high) }
|
|
328
|
+
union == high - low + 1
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def class_ranges(body)
|
|
332
|
+
slots = []
|
|
333
|
+
pending_range = false
|
|
334
|
+
index = 0
|
|
335
|
+
while index < body.length
|
|
336
|
+
if body[index] == "-" && !pending_range && !slots.empty?
|
|
337
|
+
pending_range = true
|
|
338
|
+
index += 1
|
|
339
|
+
next
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
code, index = class_token(body, index)
|
|
343
|
+
return nil unless code
|
|
344
|
+
|
|
345
|
+
if pending_range
|
|
346
|
+
previous = slots.pop
|
|
347
|
+
slots << [previous[0], code].minmax
|
|
348
|
+
pending_range = false
|
|
349
|
+
else
|
|
350
|
+
slots << [code, code]
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
return nil if pending_range
|
|
354
|
+
|
|
355
|
+
slots
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def class_token(body, index)
|
|
359
|
+
char = body[index]
|
|
360
|
+
unless char == "\\"
|
|
361
|
+
return [char.ord, index + 1]
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
escaped = body[index + 1]
|
|
365
|
+
return nil if escaped.nil?
|
|
366
|
+
|
|
367
|
+
case escaped
|
|
368
|
+
when "x" then hex_token(body, index + 2, 2)
|
|
369
|
+
when "u"
|
|
370
|
+
if body[index + 2] == "{"
|
|
371
|
+
close = body.index("}", index + 3)
|
|
372
|
+
return nil unless close
|
|
373
|
+
|
|
374
|
+
[body[(index + 3)...close].to_i(16), close + 1]
|
|
375
|
+
else
|
|
376
|
+
hex_token(body, index + 2, 4)
|
|
377
|
+
end
|
|
378
|
+
when "t" then [0x09, index + 2]
|
|
379
|
+
when "n" then [0x0A, index + 2]
|
|
380
|
+
when "v" then [0x0B, index + 2]
|
|
381
|
+
when "f" then [0x0C, index + 2]
|
|
382
|
+
when "r" then [0x0D, index + 2]
|
|
383
|
+
else [escaped.ord, index + 2]
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def hex_token(body, start, length)
|
|
388
|
+
hex = body[start, length]
|
|
389
|
+
return nil unless hex&.match?(/\A\h{#{length}}\z/)
|
|
390
|
+
|
|
391
|
+
[hex.to_i(16), start + length]
|
|
392
|
+
end
|
|
393
|
+
|
|
307
394
|
# Whether the atom's flattening surfaces any Named capture:
|
|
308
395
|
# rule references resolve through (their own wrap pass already
|
|
309
396
|
# ran — a wrapped ref produces a capture), trivia never
|
|
@@ -353,7 +440,10 @@ module Parsanol
|
|
|
353
440
|
case atom
|
|
354
441
|
when Atoms::Named then nil
|
|
355
442
|
when Atoms::Entity
|
|
356
|
-
|
|
443
|
+
# Mirror wrap_uncaptured's verdicts: trivia glue and
|
|
444
|
+
# capture-producing refs both stay bare.
|
|
445
|
+
unless trivia_only?(atom.rule_name.to_s, { atom.rule_name.to_s => true }) ||
|
|
446
|
+
produces_capture?(atom.parslet)
|
|
357
447
|
lines << "rule #{site}: reference to #{atom.rule_name} produces no capture -- " \
|
|
358
448
|
"modern mode would name it #{atom.rule_name}"
|
|
359
449
|
end
|
|
@@ -1233,13 +1323,11 @@ module Parsanol
|
|
|
1233
1323
|
"unicode_tables" => "onigmo (Ruby #{RUBY_VERSION})",
|
|
1234
1324
|
"compiler_version" => Parsanol::VERSION,
|
|
1235
1325
|
}
|
|
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
|
|
1326
|
+
# ruby#304: the envelope always names the capture edition —
|
|
1327
|
+
# consumers read the tree-shape contract before parsing. The
|
|
1328
|
+
# numeric form is canonical: 1 = the legacy shapes, 2 = fully
|
|
1329
|
+
# named trees + explicit spans.
|
|
1330
|
+
envelope["edition"] = @document.edition == "v2" ? 2 : 1
|
|
1243
1331
|
if @document.skip_declaration
|
|
1244
1332
|
# parsanol-ruby#134: declared trivia rule. Injection is baked
|
|
1245
1333
|
# 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)
|
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
|
data/lib/parsanol/version.rb
CHANGED