parsanol 1.3.81-arm-linux → 1.3.82-arm-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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 01fb11b456bec79f8f4bd6b6db018ef771260440414f75ef02f4e1520ec2caf2
4
- data.tar.gz: 75e011ecd46597e4c008cbb3fd2c7e3d2bda46ba4cd716c1613a351a28d99fb8
3
+ metadata.gz: 755c4aaa1b93bb1331ff3a845e21cc87b73451a63676776590fe76b17b398056
4
+ data.tar.gz: 46c3065db82aab1b10d5eae3ff1027772f3fcbad30d1851356fa1e7c8777db87
5
5
  SHA512:
6
- metadata.gz: 602203c6a4b11eef7c4af0613212091742b1c2f4d9a0250ee27dc7e6d67a891a77c318a70feb7f38b168d64dfd6843ace2123687cc8a17713b0dd9fcb8c8bc43
7
- data.tar.gz: 49afc1a994a7aeb1b3231446890bc729da4c6a76f520fa448c872437f45861fb4e912a1dfbf8476e13c37e4723dc21b3675e1e4dc8156851d645905006e0c1dd
6
+ metadata.gz: 044e61d3fc124a973e5a03bffb3814fac6fd5c695f860a713457cd592927146c9aa328db2460ca460010ee23cb514325825a831e721f537c00fbeb9f70956f24
7
+ data.tar.gz: d4d50039baa8eed43d7be5232bce8a0d66e8b1f72aeac9b6c124a6ccd0673125def471e49796c78969b324cd0463696c20c30081ef1ef33a0f55a0bf7330a315
@@ -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: 33956544 bytes
4
- sha256: 5f8d55c4f6ab0f5bba8ea501b6ab13b16df2bfa1b63b823152d70bb86b95daaf
3
+ size: 34008548 bytes
4
+ sha256: 3d9692b966fef81325ab09a430d755a70669b1bde6ec1e13ab8b15e6eb92dde9
@@ -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: 34141808 bytes
4
- sha256: c9ec101532d77f531c2b73fd93c48add52fcd2fc0a06e4047b9e1deb9dc5c4d6
3
+ size: 34193832 bytes
4
+ sha256: 316cbf3f2520113134750bc699742979580a901177be3ffcf74a736afc5f20d0
@@ -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: 34135964 bytes
4
- sha256: 42f14ef394d8a41ed832e9a9f61fbc0868056062d94df7ea62403290c5581c31
3
+ size: 34187972 bytes
4
+ sha256: 7f0d1e2437b36ffac4e98c7bc65b2b8b159758bc18101e7ef5cb340028287993
@@ -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: 34145720 bytes
4
- sha256: 953c7f621b8c343ab150a9dada110ad29b94d571cc621d0be94c8235419bd2b1
3
+ size: 34197704 bytes
4
+ sha256: 65f68a0f5e165622e51b676edefc6d404ed9cf1dce150e3e0d6fac152c7a9581
@@ -18,14 +18,25 @@ module Parsanol
18
18
  def initialize(*components)
19
19
  super()
20
20
  @parslets = components
21
-
22
- # Pre-built error message
23
- @fail_msg = "Failed to match sequence (#{inspect})"
21
+ # The failure message renders LAZILY (error_msgs): constructing
22
+ # sequences is hot-path work for the builder/injector, and
23
+ # stringifying the whole subtree eagerly is exponential on
24
+ # shared cross-referencing grammars (parsanol-ruby#180 side
25
+ # observation: a 25-rule Fibonacci-shaped grammar took 6s).
26
+ @fail_msg = nil
24
27
  end
25
28
 
26
29
  # Error messages hash (for compatibility)
27
30
  def error_msgs
28
- { failed: @fail_msg }
31
+ { failed: fail_msg }
32
+ end
33
+
34
+ # Rendered lazily (see initialize); construction is hot-path
35
+ # builder/injector work and stringifying the whole subtree
36
+ # eagerly is exponential on shared cross-referencing grammars
37
+ # (parsanol-ruby#180 side observation).
38
+ def fail_msg
39
+ @fail_msg ||= "Failed to match sequence (#{inspect})"
29
40
  end
30
41
 
31
42
  # Appends a parser to this sequence with flattening.
@@ -104,7 +115,7 @@ module Parsanol
104
115
  # Single element sequence
105
116
  def match_single(parser, source, context, consume_all)
106
117
  success, value = parser.apply(source, context, consume_all)
107
- return context.err(self, source, @fail_msg, [value]) unless success
118
+ return context.err(self, source, fail_msg, [value]) unless success
108
119
 
109
120
  ok([:sequence, value])
110
121
  end
@@ -112,10 +123,10 @@ module Parsanol
112
123
  # Two-element sequence
113
124
  def match_pair(p1, p2, source, context, consume_all)
114
125
  success, v1 = p1.apply(source, context, false)
115
- return context.err(self, source, @fail_msg, [v1]) unless success
126
+ return context.err(self, source, fail_msg, [v1]) unless success
116
127
 
117
128
  success, v2 = p2.apply(source, context, consume_all)
118
- return context.err(self, source, @fail_msg, [v2]) unless success
129
+ return context.err(self, source, fail_msg, [v2]) unless success
119
130
 
120
131
  ok([:sequence, v1, v2])
121
132
  end
@@ -123,13 +134,13 @@ module Parsanol
123
134
  # Three-element sequence
124
135
  def match_triple(p1, p2, p3, source, context, consume_all)
125
136
  success, v1 = p1.apply(source, context, false)
126
- return context.err(self, source, @fail_msg, [v1]) unless success
137
+ return context.err(self, source, fail_msg, [v1]) unless success
127
138
 
128
139
  success, v2 = p2.apply(source, context, false)
129
- return context.err(self, source, @fail_msg, [v2]) unless success
140
+ return context.err(self, source, fail_msg, [v2]) unless success
130
141
 
131
142
  success, v3 = p3.apply(source, context, consume_all)
132
- return context.err(self, source, @fail_msg, [v3]) unless success
143
+ return context.err(self, source, fail_msg, [v3]) unless success
133
144
 
134
145
  ok([:sequence, v1, v2, v3])
135
146
  end
@@ -148,7 +159,7 @@ module Parsanol
148
159
  success, value = components[idx].apply(source, context, must_consume)
149
160
 
150
161
  unless success
151
- return context.err(self, source, @fail_msg, [value])
162
+ return context.err(self, source, fail_msg, [value])
152
163
  end
153
164
 
154
165
  result[idx + 1] = value
@@ -28,12 +28,16 @@ module Parsanol
28
28
  # +capturers+ maps leading-literal marker -> kind label (the
29
29
  # compiler derives markers from each capturer rule's first Str
30
30
  # atom, e.g. "//" => :line_comment); units not matching any
31
- # marker are whitespace-shaped and stay unrecorded.
32
- attr_reader :capturers
31
+ # marker are whitespace-shaped and stay unrecorded — unless
32
+ # +whitespace_kind+ is declared (parsanol-ruby#180): every
33
+ # unit then records under that label, giving source-preserving
34
+ # grammars the full ordered trivia stream.
35
+ attr_reader :capturers, :whitespace_kind
33
36
 
34
- def initialize(atom, capturers)
37
+ def initialize(atom, capturers, whitespace_kind: nil)
35
38
  super(atom)
36
39
  @capturers = capturers
40
+ @whitespace_kind = whitespace_kind
37
41
  end
38
42
 
39
43
  def apply(source, context, consume_all)
@@ -55,16 +59,16 @@ module Parsanol
55
59
  private
56
60
 
57
61
  def record(source, context, start_pos, end_pos)
58
- return unless @capturers
59
-
60
62
  text = source.input[start_pos...end_pos]
61
63
  # A trivia unit may lead with whitespace before the comment
62
64
  # shape; markers test the comment head.
63
65
  stripped = text.lstrip
64
- marker, label = @capturers.find { |m, _label| stripped.start_with?(m) }
65
- return unless marker
66
-
67
- context.push_captured_trivia(label, stripped.strip)
66
+ marker, label = @capturers&.find { |m, _label| stripped.start_with?(m) }
67
+ if marker
68
+ context.push_captured_trivia(label, stripped.strip)
69
+ elsif @whitespace_kind
70
+ context.push_captured_trivia(@whitespace_kind, text)
71
+ end
68
72
  end
69
73
  end
70
74
  end
Binary file
@@ -332,12 +332,16 @@ module Parsanol
332
332
 
333
333
  def serialize_trivia_capture(atom)
334
334
  inner_id = serialize_atom(atom.wrapped_atom)
335
- {
336
- "TriviaCapture" => {
337
- "atom" => inner_id,
338
- "rules" => atom.capturers.map { |marker, label| [marker, label.to_s] },
339
- },
335
+ payload = {
336
+ "atom" => inner_id,
337
+ "rules" => atom.capturers.map { |marker, label| [marker, label.to_s] },
340
338
  }
339
+ # parsanol-ruby#180: the optional whitespace kind — units that
340
+ # match no marker record under it (source-preserving grammars).
341
+ # Optional on the wire: engines predating it deserialize and
342
+ # simply don't record whitespace units (documented caveat).
343
+ payload["whitespace"] = atom.whitespace_kind.to_s if atom.whitespace_kind
344
+ { "TriviaCapture" => payload }
341
345
  end
342
346
 
343
347
  def serialize_trivia(atom)
@@ -54,7 +54,8 @@ module Parsanol
54
54
  skip_name = self.class.skip_rule_name
55
55
  if skip_name
56
56
  Parsanol::Skip.inject(self, atom, skip_name,
57
- captures: self.class.skip_captures)[0]
57
+ captures: self.class.skip_captures,
58
+ whitespace: self.class.skip_whitespace)[0]
58
59
  else
59
60
  atom
60
61
  end
@@ -89,9 +90,10 @@ module Parsanol
89
90
  # rule(:line_comment) { str("//") >> match(/[^\n]/).repeat }
90
91
  # skip :trivia, capture: { line_comment: :line_comment }
91
92
  #
92
- def skip(rule_name, capture: nil)
93
+ def skip(rule_name, capture: nil, whitespace: nil)
93
94
  @skip_rule_name = rule_name
94
95
  @skip_captures = capture
96
+ @skip_whitespace = whitespace
95
97
  nil
96
98
  end
97
99
 
@@ -106,6 +108,12 @@ module Parsanol
106
108
  def skip_captures
107
109
  @skip_captures
108
110
  end
111
+
112
+ # The declared whitespace kind (units matching no capturer
113
+ # record under it), or nil — see {.skip}.
114
+ def skip_whitespace
115
+ @skip_whitespace
116
+ end
109
117
  end
110
118
 
111
119
  # Delegates matching to the root rule.
data/lib/parsanol/skip.rb CHANGED
@@ -20,7 +20,8 @@ module Parsanol
20
20
  # capturer's leading literal attach to the next Named capture
21
21
  # under `comments:` — the PARG `skip = trivia capture: comments`
22
22
  # shape. Whitespace-shaped units never record.
23
- def self.inject(parser_instance, root_atom, skip_rule_name, captures: nil)
23
+ def self.inject(parser_instance, root_atom, skip_rule_name, captures: nil,
24
+ whitespace: nil)
24
25
  skip_atom = parser_instance.__send__(skip_rule_name)
25
26
  skip_atom = skip_atom.parslet while skip_atom.is_a?(Atoms::Entity)
26
27
 
@@ -31,7 +32,8 @@ module Parsanol
31
32
  end
32
33
 
33
34
  markers = capturer_markers(parser_instance, captures)
34
- injector = Injector.new(skip_atom, markers)
35
+ whitespace_kind = whitespace&.to_sym
36
+ injector = Injector.new(skip_atom, markers, whitespace_kind)
35
37
  [injector.inject(root_atom, entry: true), skip_atom]
36
38
  end
37
39
 
@@ -92,15 +94,25 @@ module Parsanol
92
94
  # clean — the wrapper is Trivia: Ignored + diagnostics
93
95
  # transparency), bare terminals gain a leading wrapper, and the
94
96
  # entry root takes leading and trailing wrappers.
97
+ # The walk is a pure function of the atom subtree, so results are
98
+ # memoized: dense cross-referencing grammars (expressir's ~250
99
+ # rules) revisit shared subtrees once per referencing path, which
100
+ # made the path-scoped visited-set exponential — a 25-rule
101
+ # Fibonacci-shaped grammar took 6.2s to build (parsanol-ruby#180
102
+ # side observation). In-progress atoms mark a cycle re-entry and
103
+ # return raw, exactly as the path-scoped set did.
95
104
  class Injector
96
- def initialize(skip_atom, markers = nil)
105
+ def initialize(skip_atom, markers = nil, whitespace_kind = nil)
97
106
  @skip_atom = skip_atom
98
107
  @markers = markers
108
+ @whitespace_kind = whitespace_kind
99
109
  @wrapper_ids = {}.compare_by_identity
110
+ @memo = {}.compare_by_identity
111
+ @in_progress = {}.compare_by_identity
100
112
  end
101
113
 
102
114
  def inject(atom, entry: false)
103
- injected = walk(atom, {})
115
+ injected = walk(atom)
104
116
  return injected unless entry
105
117
 
106
118
  Atoms::Sequence.new(maybe, injected, maybe)
@@ -112,8 +124,9 @@ module Parsanol
112
124
  @maybe ||= begin
113
125
  inner = Atoms::Repetition.new(@skip_atom, 0, 1)
114
126
  wrapper =
115
- if @markers
116
- Atoms::TriviaCapture.new(inner, @markers)
127
+ if @markers || @whitespace_kind
128
+ Atoms::TriviaCapture.new(inner, @markers,
129
+ whitespace_kind: @whitespace_kind)
117
130
  else
118
131
  Atoms::Trivia.new(inner)
119
132
  end
@@ -126,50 +139,56 @@ module Parsanol
126
139
  @wrapper_ids.key?(atom)
127
140
  end
128
141
 
129
- def walk(atom, visited)
130
- return atom if visited.key?(atom)
142
+ def walk(atom)
143
+ cached = @memo[atom]
144
+ return cached if cached
145
+ return atom if @in_progress.key?(atom)
131
146
  return atom if atom.equal?(@skip_atom)
132
147
 
133
- visited = visited.merge(atom => true)
134
- case atom
135
- when Atoms::Sequence
136
- out = []
137
- atom.parslets.each do |child|
138
- injected = walk(child, visited)
139
- out << maybe unless wrapper?(injected) || injected.is_a?(Atoms::Trivia)
140
- out << injected
141
- end
142
- out.length == 1 ? out.first : Atoms::Sequence.new(*out)
143
- when Atoms::Alternative
144
- Atoms::Alternative.new(*atom.alternatives.map { |a| walk(a, visited) })
145
- when Atoms::Repetition
146
- if wrapper?(atom)
147
- atom
148
+ @in_progress[atom] = true
149
+ injected =
150
+ case atom
151
+ when Atoms::Sequence
152
+ out = []
153
+ atom.parslets.each do |child|
154
+ injected = walk(child)
155
+ out << maybe unless wrapper?(injected) || injected.is_a?(Atoms::Trivia)
156
+ out << injected
157
+ end
158
+ out.length == 1 ? out.first : Atoms::Sequence.new(*out)
159
+ when Atoms::Alternative
160
+ Atoms::Alternative.new(*atom.alternatives.map { |a| walk(a) })
161
+ when Atoms::Repetition
162
+ if wrapper?(atom)
163
+ atom
164
+ else
165
+ Atoms::Repetition.new(walk(atom.parslet), atom.min, atom.max,
166
+ atom.result_tag)
167
+ end
168
+ when Atoms::Named
169
+ Atoms::Named.new(walk(atom.parslet), atom.name)
170
+ when Atoms::Lookahead
171
+ Atoms::Lookahead.new(walk(atom.bound_parslet), atom.positive)
172
+ when Atoms::Ignored
173
+ Atoms::Ignored.new(walk(atom.wrapped_atom))
174
+ when Atoms::Entity
175
+ # Rule bodies own their injection when the grammar declares
176
+ # skip on every rule (PARG); in the DSL surface the root tree
177
+ # is walked once, so descend through resolved bodies with the
178
+ # identity visited-set guarding recursion.
179
+ begin
180
+ walk(atom.parslet)
181
+ rescue StandardError
182
+ atom
183
+ end
184
+ when Atoms::Str, Atoms::Re
185
+ Atoms::Sequence.new(maybe, atom)
148
186
  else
149
- Atoms::Repetition.new(walk(atom.parslet, visited), atom.min, atom.max,
150
- atom.result_tag)
151
- end
152
- when Atoms::Named
153
- Atoms::Named.new(walk(atom.parslet, visited), atom.name)
154
- when Atoms::Lookahead
155
- Atoms::Lookahead.new(walk(atom.bound_parslet, visited), atom.positive)
156
- when Atoms::Ignored
157
- Atoms::Ignored.new(walk(atom.wrapped_atom, visited))
158
- when Atoms::Entity
159
- # Rule bodies own their injection when the grammar declares
160
- # skip on every rule (PARG); in the DSL surface the root tree
161
- # is walked once, so descend through resolved bodies with the
162
- # identity visited-set guarding recursion.
163
- begin
164
- walk(atom.parslet, visited)
165
- rescue StandardError
166
187
  atom
167
188
  end
168
- when Atoms::Str, Atoms::Re
169
- Atoms::Sequence.new(maybe, atom)
170
- else
171
- atom
172
- end
189
+ @in_progress.delete(atom)
190
+ @memo[atom] = injected
191
+ injected
173
192
  end
174
193
  end
175
194
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parsanol
4
- VERSION = "1.3.81"
4
+ VERSION = "1.3.82"
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.81
4
+ version: 1.3.82
5
5
  platform: arm-linux
6
6
  authors:
7
7
  - Ribose Inc.