parsanol 1.3.86 → 1.3.88

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 47aefbe702313d394398c0a3761f397d13f912521310b924bf84361bd336e920
4
- data.tar.gz: f575935f73c5244972ed7e3cd93c0c9b9ee4de775127587d85e3e36319202808
3
+ metadata.gz: c24632a252a0d882d2491b361a569d8e96132f5c25942660db5c531313ebe913
4
+ data.tar.gz: 6349b99005ed86363fc2a13943a2c4e5dd4ce3958a364d12cb82ceea4c660d64
5
5
  SHA512:
6
- metadata.gz: 1c1d2274eb415f449ee1f93d5ccd09a1ba087504ff3e842bcbf7212f40dfeac61d6cc988a3c285559b82f48f59ddc7be20a2640743fff9de1b643e5517b8ae93
7
- data.tar.gz: 3730499554f40a734a38385a20e5a81bb836306ef87e19469409e7ca76fde72f311b33571e941a0cc3539be54c14f717fa86c470e742d7120c8124b799eafe1a
6
+ metadata.gz: 81eca9be5955bc6e28413a88622392ad25c969307a9ee3d2d29255c4c59ad543f560c06c94cbcb34044b143365ad87622115a2786b4d8e7a5dd609cb91b5d4b6
7
+ data.tar.gz: 943d81f3c10eb2e2e662ca1312d41c25f5b0cff23e455f5aec1d605d0be868dd0e682104980f4b46333d5e262399814d9cf2a9fa8626a578297a045218fff52e
data/Cargo.lock CHANGED
@@ -307,9 +307,9 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
307
307
 
308
308
  [[package]]
309
309
  name = "parsanol"
310
- version = "0.18.0"
310
+ version = "0.18.2"
311
311
  source = "registry+https://github.com/rust-lang/crates.io-index"
312
- checksum = "dc899494d6b94ea8df14279b7dcbac89d59ba4d1be52f7b8ffe840674c6c8e3c"
312
+ checksum = "b059d18a92c61ba7c7c6241bb446196729dadf618e6e34c0b79935f0292d7a27"
313
313
  dependencies = [
314
314
  "ahash",
315
315
  "getrandom 0.3.4",
@@ -326,9 +326,9 @@ dependencies = [
326
326
 
327
327
  [[package]]
328
328
  name = "parsanol-derive"
329
- version = "0.18.0"
329
+ version = "0.18.2"
330
330
  source = "registry+https://github.com/rust-lang/crates.io-index"
331
- checksum = "b437046956f356eee0eb9b407ff1f1f6286af1f70f1af1d305c54a64c65b64e2"
331
+ checksum = "4b9a6de83c376137ccce01cb8890427a8879b34b6ac4832cc0413c5624923001"
332
332
  dependencies = [
333
333
  "proc-macro2",
334
334
  "quote",
@@ -157,7 +157,11 @@ module Parsanol
157
157
  # @param outer [Integer] caller's precedence
158
158
  # @return [String]
159
159
  def to_s(outer = TOP)
160
- text = label || to_s_inner(precedence)
160
+ # The inner text is cached per instance: renders walk the atom
161
+ # tree through shared references, and an uncached render is
162
+ # exponential on cross-referencing grammars (parsanol-ruby#190).
163
+ # The paren wrapper stays dynamic; the label is read live.
164
+ text = label || (@to_s_inner_cache ||= to_s_inner(precedence))
161
165
  outer < precedence ? "(#{text})" : text
162
166
  end
163
167
 
@@ -263,10 +263,10 @@ module Parsanol
263
263
  [false, @reporter.err(*)]
264
264
  end
265
265
 
266
- # Records one captured trivia unit (kind, text) for attachment to
267
- # the next successful Named capture.
268
- def push_captured_trivia(kind, text)
269
- @pending_trivia << { kind: kind, text: text }
266
+ # Records one captured trivia unit (kind, text, input offset)
267
+ # for attachment to the next successful Named capture.
268
+ def push_captured_trivia(kind, text, pos)
269
+ @pending_trivia << { kind: kind, text: text, pos: pos }
270
270
  nil
271
271
  end
272
272
 
@@ -41,7 +41,7 @@ module Parsanol
41
41
 
42
42
  comments = context.take_pending_trivia
43
43
  result = wrap_result(value)
44
- result = attach_comments(result, comments) unless comments.empty?
44
+ result = attach_comments(result, comments, source) unless comments.empty?
45
45
  ok(result)
46
46
  end
47
47
 
@@ -50,9 +50,13 @@ module Parsanol
50
50
  # parsanol-ruby#152: trivia captured ahead of this capture rides
51
51
  # with it under `comments:`. The attachment only fires when the
52
52
  # grammar declared capture AND trivia actually preceded — key
53
- # absence everywhere else keeps v1 trees byte-identical.
54
- def attach_comments(result, comments)
55
- list = comments.map { |unit| { unit[:kind] => unit[:text] } }
53
+ # absence everywhere else keeps v1 trees byte-identical. Units
54
+ # carry their input offset so the value is a positioned Slice,
55
+ # like every other capture value (native parity, ruby#180).
56
+ def attach_comments(result, comments, source)
57
+ list = comments.map do |unit|
58
+ { unit[:kind] => Slice.new(unit[:pos], unit[:text], source.input) }
59
+ end
56
60
  if result.is_a?(Hash)
57
61
  result.merge(comments: list)
58
62
  else
@@ -61,13 +61,17 @@ module Parsanol
61
61
  def record(source, context, start_pos, end_pos)
62
62
  text = source.input[start_pos...end_pos]
63
63
  # A trivia unit may lead with whitespace before the comment
64
- # shape; markers test the comment head.
64
+ # shape; markers test the comment head. The recorded offset
65
+ # must agree with the recorded content as a slice of the
66
+ # input: trimmed marker text starts after the unit's leading
67
+ # whitespace, not at the wrapper (native parity, ruby#180).
65
68
  stripped = text.lstrip
66
69
  marker, label = @capturers&.find { |m, _label| stripped.start_with?(m) }
67
70
  if marker
68
- context.push_captured_trivia(label, stripped.strip)
71
+ context.push_captured_trivia(label, stripped.strip,
72
+ start_pos + (text.length - stripped.length))
69
73
  elsif @whitespace_kind
70
- context.push_captured_trivia(@whitespace_kind, text)
74
+ context.push_captured_trivia(@whitespace_kind, text, start_pos)
71
75
  end
72
76
  end
73
77
  end
@@ -310,9 +310,16 @@ module Parsanol
310
310
  transformed
311
311
  elsif is_repetition
312
312
  if transformed.is_a?(Array)
313
- if transformed.all? do |item|
314
- item.is_a?(Hash) && item.key?(sym_key)
315
- end
313
+ if transformed.all?(Hash)
314
+ # Hash items carry their own capture
315
+ # names (parslet semantics) — the
316
+ # single-key path
317
+ # (transform_repetition_value) already
318
+ # keeps them; this multi-key path (a
319
+ # Named with a trivia attachment, or
320
+ # merged siblings) must agree — only
321
+ # unnamed items take the parent name
322
+ # per item.
316
323
  transformed
317
324
  else
318
325
  transformed.map { |item| { sym_key => item } }
@@ -296,29 +296,49 @@ module Parsanol
296
296
  if peek&.type == :punct && peek.value == "*"
297
297
  advance
298
298
  max = peek&.type == :num ? advance.value.to_i : nil
299
- node = Node.new(:rep, parse_postfixed, min, max)
299
+ node = Node.new(:rep, parse_rep_body, min, max)
300
300
  else
301
- node = Node.new(:rep, parse_postfixed, min, min)
301
+ node = Node.new(:rep, parse_rep_body, min, min)
302
302
  end
303
- return node
303
+ return capture_postfix(node)
304
304
  elsif token.type == :punct && token.value == "*"
305
305
  advance
306
306
  max = peek&.type == :num ? advance.value.to_i : nil
307
- return Node.new(:rep, parse_postfixed, 0, max)
307
+ return capture_postfix(Node.new(:rep, parse_rep_body, 0, max))
308
308
  end
309
309
  parse_postfixed
310
310
  end
311
311
 
312
- def parse_postfixed
312
+ # A repetition's body must not claim the `as` that follows the
313
+ # repetition's own closing paren (parsanol-ruby#197): the body
314
+ # parser is postfix-blind, and the postfix wraps the REPETITION —
315
+ # `*( X ) as name` captures the whole repeated run, matching the
316
+ # Ruby DSL's `.repeat.as(:name)`. An `as` inside the body's own
317
+ # parens still binds the body — write `*( ( X ) as name )` for
318
+ # that (naming each iteration's X).
319
+ def parse_rep_body
313
320
  sign = nil
314
321
  if peek&.type == :punct && %w[! &].include?(peek.value)
315
322
  sign = advance.value == "&"
316
323
  end
317
324
  node = parse_primary
325
+ sign.nil? ? node : Node.new(:pred, sign, node)
326
+ end
327
+
328
+ def capture_postfix(node)
318
329
  if peek&.type == :ident && peek.value == "as"
319
330
  advance
320
331
  node = Node.new(:cap, ident.value, node)
321
332
  end
333
+ node
334
+ end
335
+
336
+ def parse_postfixed
337
+ sign = nil
338
+ if peek&.type == :punct && %w[! &].include?(peek.value)
339
+ sign = advance.value == "&"
340
+ end
341
+ node = capture_postfix(parse_primary)
322
342
  sign.nil? ? node : Node.new(:pred, sign, node)
323
343
  end
324
344
 
data/lib/parsanol/skip.rb CHANGED
@@ -201,12 +201,26 @@ module Parsanol
201
201
  when Atoms::Entity
202
202
  # Rule bodies own their injection when the grammar declares
203
203
  # skip on every rule (PARG); in the DSL surface the root tree
204
- # is walked once, so descend through resolved bodies with
205
- # the in-progress set guarding recursion. A rule body is a
206
- # fresh injection scope even inside a repetition interior —
207
- # the d54a29e exception is for rule-reference children.
204
+ # is walked once, so inject the resolved body as a fresh
205
+ # injection scope (even inside a repetition interior — the
206
+ # d54a29e exception is for rule-reference children). The
207
+ # reference itself stays an Entity pointing at the injected
208
+ # body: inlining the body into every referencing site makes
209
+ # every rendered string (to_s, and failure messages built
210
+ # from inspect) expand the grammar exponentially on
211
+ # cross-referencing grammars — one failing Sequence rendered
212
+ # a multi-gigabyte message (parsanol-ruby#190).
208
213
  begin
209
- walk(atom.parslet, mode == :exempt ? :exempt : false)
214
+ body = walk(atom.parslet, mode == :exempt ? :exempt : false)
215
+ if body.is_a?(Atoms::Lookahead)
216
+ # A whole-body lookahead keeps its #192 boundary visible
217
+ # to enclosing sequences (no wrapper before it).
218
+ body
219
+ else
220
+ ref = Atoms::Entity.new(atom.rule_name) { body }
221
+ ref.label = atom.label if atom.label
222
+ ref
223
+ end
210
224
  rescue StandardError
211
225
  atom
212
226
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parsanol
4
- VERSION = "1.3.86"
4
+ VERSION = "1.3.88"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parsanol
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.86
4
+ version: 1.3.88
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.