parsanol 1.3.4 → 1.3.7

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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +0 -2
  3. data/Rakefile +48 -48
  4. data/ext/parsanol_native/Cargo.toml +1 -2
  5. data/ext/parsanol_native/extconf.rb +4 -4
  6. data/lib/parsanol/ast_visitor.rb +1 -1
  7. data/lib/parsanol/atoms/alternative.rb +3 -2
  8. data/lib/parsanol/atoms/base.rb +12 -6
  9. data/lib/parsanol/atoms/can_flatten.rb +8 -8
  10. data/lib/parsanol/atoms/context.rb +23 -16
  11. data/lib/parsanol/atoms/custom.rb +2 -2
  12. data/lib/parsanol/atoms/dynamic.rb +1 -1
  13. data/lib/parsanol/atoms/infix.rb +10 -5
  14. data/lib/parsanol/atoms/lookahead.rb +7 -4
  15. data/lib/parsanol/atoms/re.rb +1 -1
  16. data/lib/parsanol/atoms/repetition.rb +29 -11
  17. data/lib/parsanol/atoms/sequence.rb +3 -2
  18. data/lib/parsanol/atoms/str.rb +9 -3
  19. data/lib/parsanol/atoms.rb +20 -20
  20. data/lib/parsanol/builder_callbacks.rb +2 -2
  21. data/lib/parsanol/cause.rb +2 -2
  22. data/lib/parsanol/context.rb +2 -2
  23. data/lib/parsanol/error_reporter.rb +5 -5
  24. data/lib/parsanol/expression/treetop.rb +17 -17
  25. data/lib/parsanol/expression.rb +1 -1
  26. data/lib/parsanol/fast_mode.rb +50 -12
  27. data/lib/parsanol/first_set.rb +1 -1
  28. data/lib/parsanol/grammar_builder.rb +10 -8
  29. data/lib/parsanol/incremental_parser.rb +13 -8
  30. data/lib/parsanol/interval_tree.rb +12 -3
  31. data/lib/parsanol/lazy_result.rb +2 -2
  32. data/lib/parsanol/mermaid.rb +12 -9
  33. data/lib/parsanol/native/batch_decoder.rb +13 -9
  34. data/lib/parsanol/native/dynamic.rb +7 -6
  35. data/lib/parsanol/native/parser.rb +12 -4
  36. data/lib/parsanol/native/serializer.rb +42 -42
  37. data/lib/parsanol/native/transformer.rb +55 -28
  38. data/lib/parsanol/native/types.rb +3 -3
  39. data/lib/parsanol/native.rb +60 -21
  40. data/lib/parsanol/optimizer.rb +6 -6
  41. data/lib/parsanol/optimizers/choice_optimizer.rb +1 -1
  42. data/lib/parsanol/optimizers/cut_inserter.rb +5 -2
  43. data/lib/parsanol/optimizers/lookahead_optimizer.rb +9 -3
  44. data/lib/parsanol/optimizers/quantifier_optimizer.rb +5 -5
  45. data/lib/parsanol/optimizers/sequence_optimizer.rb +1 -1
  46. data/lib/parsanol/options/zero_copy.rb +1 -1
  47. data/lib/parsanol/options.rb +1 -1
  48. data/lib/parsanol/parallel.rb +8 -13
  49. data/lib/parsanol/parser.rb +51 -13
  50. data/lib/parsanol/parslet.rb +7 -7
  51. data/lib/parsanol/pattern/binding.rb +1 -1
  52. data/lib/parsanol/pattern.rb +4 -1
  53. data/lib/parsanol/pool.rb +3 -3
  54. data/lib/parsanol/pools/buffer_pool.rb +2 -2
  55. data/lib/parsanol/pools/position_pool.rb +2 -2
  56. data/lib/parsanol/position.rb +1 -1
  57. data/lib/parsanol/result_builder.rb +4 -4
  58. data/lib/parsanol/result_stream.rb +10 -5
  59. data/lib/parsanol/slice.rb +11 -8
  60. data/lib/parsanol/source.rb +14 -9
  61. data/lib/parsanol/source_location.rb +1 -1
  62. data/lib/parsanol/streaming_parser.rb +3 -3
  63. data/lib/parsanol/string_view.rb +4 -1
  64. data/lib/parsanol/transform.rb +2 -2
  65. data/lib/parsanol/version.rb +1 -1
  66. data/lib/parsanol/wasm_parser.rb +1 -1
  67. data/lib/parsanol.rb +37 -39
  68. data/parsanol.gemspec +30 -30
  69. metadata +1 -1
@@ -36,7 +36,7 @@ module Parsanol
36
36
  super()
37
37
 
38
38
  # Handle nil max_count (unbounded repetition)
39
- if max_count && max_count.zero?
39
+ if max_count&.zero?
40
40
  raise ArgumentError, "Cannot repeat zero times: #{parser.inspect}"
41
41
  end
42
42
 
@@ -50,7 +50,7 @@ module Parsanol
50
50
 
51
51
  # Pre-built error messages
52
52
  @min_error = "Expected at least #{min_count} of #{parser.inspect}"
53
- @extra_error = 'Extra input after last repetition'
53
+ @extra_error = "Extra input after last repetition"
54
54
  end
55
55
 
56
56
  # Error messages hash (for compatibility)
@@ -71,10 +71,16 @@ module Parsanol
71
71
  end
72
72
 
73
73
  # Maybe (0 or 1) - very common, optimize
74
- return try_maybe(source, context, consume_all) if @min.zero? && @max == 1
74
+ if @min.zero? && @max == 1
75
+ return try_maybe(source, context,
76
+ consume_all)
77
+ end
75
78
 
76
79
  # Exact count optimization
77
- return try_exact(source, context, consume_all) if @min == @max && @max && @max <= 3
80
+ if @min == @max && @max && @max <= 3
81
+ return try_exact(source, context,
82
+ consume_all)
83
+ end
78
84
 
79
85
  # General case
80
86
  try_general(source, context, consume_all)
@@ -88,7 +94,7 @@ module Parsanol
88
94
  # @return [String]
89
95
  def to_s_inner(prec)
90
96
  suffix = if @min.zero? && @max == 1
91
- '?'
97
+ "?"
92
98
  else
93
99
  "{#{@min}, #{@max}}"
94
100
  end
@@ -135,7 +141,10 @@ module Parsanol
135
141
 
136
142
  def double_match(source, context, consume_all)
137
143
  success, v1 = @parslet.apply(source, context, false)
138
- return context.err_at(self, source, @min_error, source.bytepos, [v1]) unless success
144
+ unless success
145
+ return context.err_at(self, source, @min_error, source.bytepos,
146
+ [v1])
147
+ end
139
148
 
140
149
  success, v2 = @parslet.apply(source, context, consume_all)
141
150
  return ok([@result_tag, v1, v2]) if success
@@ -145,10 +154,16 @@ module Parsanol
145
154
 
146
155
  def triple_match(source, context, consume_all)
147
156
  success, v1 = @parslet.apply(source, context, false)
148
- return context.err_at(self, source, @min_error, source.bytepos, [v1]) unless success
157
+ unless success
158
+ return context.err_at(self, source, @min_error, source.bytepos,
159
+ [v1])
160
+ end
149
161
 
150
162
  success, v2 = @parslet.apply(source, context, false)
151
- return context.err_at(self, source, @min_error, source.bytepos, [v2]) unless success
163
+ unless success
164
+ return context.err_at(self, source, @min_error, source.bytepos,
165
+ [v2])
166
+ end
152
167
 
153
168
  success, v3 = @parslet.apply(source, context, consume_all)
154
169
  return ok([@result_tag, v1, v2, v3]) if success
@@ -184,7 +199,8 @@ module Parsanol
184
199
  if occurrence < @min
185
200
  context.release_buffer(buffer)
186
201
  source.bytepos = start_pos
187
- return context.err_at(self, source, @min_error, start_pos, [last_error])
202
+ return context.err_at(self, source, @min_error, start_pos,
203
+ [last_error])
188
204
  end
189
205
 
190
206
  # Check complete consumption
@@ -236,14 +252,16 @@ module Parsanol
236
252
  # Cache successful prefix
237
253
  if occurrence.positive?
238
254
  end_pos = positions[occurrence]
239
- context.store_tree_memo(cache_key, start_pos, buffer.to_a[1..], end_pos)
255
+ context.store_tree_memo(cache_key, start_pos, buffer.to_a[1..],
256
+ end_pos)
240
257
  end
241
258
 
242
259
  # Check minimum
243
260
  if occurrence < @min
244
261
  context.release_buffer(buffer)
245
262
  source.bytepos = start_pos
246
- return context.err_at(self, source, @min_error, start_pos, [last_error])
263
+ return context.err_at(self, source, @min_error, start_pos,
264
+ [last_error])
247
265
  end
248
266
 
249
267
  # Check consumption
@@ -63,7 +63,8 @@ module Parsanol
63
63
  when 2
64
64
  match_pair(components[0], components[1], source, context, consume_all)
65
65
  when 3
66
- match_triple(components[0], components[1], components[2], source, context, consume_all)
66
+ match_triple(components[0], components[1], components[2], source,
67
+ context, consume_all)
67
68
  else
68
69
  match_general(components, source, context, consume_all)
69
70
  end
@@ -76,7 +77,7 @@ module Parsanol
76
77
  # @param prec [Integer] precedence
77
78
  # @return [String]
78
79
  def to_s_inner(prec)
79
- @parslets.map { |p| p.to_s(prec) }.join(' ')
80
+ @parslets.map { |p| p.to_s(prec) }.join(" ")
80
81
  end
81
82
 
82
83
  # FIRST set is first element's FIRST set (with epsilon propagation).
@@ -21,7 +21,7 @@ module Parsanol
21
21
  @char_count = @str.length
22
22
 
23
23
  # Pre-built error messages (frozen)
24
- @early_eof_msg = 'Unexpected end of input'
24
+ @early_eof_msg = "Unexpected end of input"
25
25
  @mismatch_msg = "Expected #{@str.inspect}, but got "
26
26
 
27
27
  # Optimization: single-char fast path
@@ -75,7 +75,10 @@ module Parsanol
75
75
 
76
76
  # Fast path for single-character strings.
77
77
  def single_char_match(source, context)
78
- return context.err(self, source, @early_eof_msg) if source.chars_left < 1
78
+ if source.chars_left < 1
79
+ return context.err(self, source,
80
+ @early_eof_msg)
81
+ end
79
82
 
80
83
  pos = source.pos
81
84
  slice = source.consume(1)
@@ -88,7 +91,10 @@ module Parsanol
88
91
 
89
92
  # Standard path for multi-character strings.
90
93
  def multi_char_match(source, context)
91
- return context.err(self, source, @early_eof_msg) if source.chars_left < @char_count
94
+ if source.chars_left < @char_count
95
+ return context.err(self, source,
96
+ @early_eof_msg)
97
+ end
92
98
 
93
99
  pos = source.pos
94
100
  slice = source.consume(@char_count)
@@ -21,26 +21,26 @@ module Parsanol
21
21
  end
22
22
 
23
23
  # Load atom implementations
24
- require 'parsanol/atoms/can_flatten'
25
- require 'parsanol/atoms/context'
26
- require 'parsanol/atoms/dsl'
27
- require 'parsanol/atoms/base'
28
- require 'parsanol/atoms/custom'
29
- require 'parsanol/atoms/ignored'
30
- require 'parsanol/atoms/named'
31
- require 'parsanol/atoms/lookahead'
32
- require 'parsanol/atoms/cut'
33
- require 'parsanol/atoms/alternative'
34
- require 'parsanol/atoms/sequence'
35
- require 'parsanol/atoms/repetition'
36
- require 'parsanol/atoms/re'
37
- require 'parsanol/atoms/str'
38
- require 'parsanol/atoms/entity'
39
- require 'parsanol/atoms/capture'
40
- require 'parsanol/atoms/dynamic'
41
- require 'parsanol/atoms/scope'
42
- require 'parsanol/atoms/infix'
24
+ require "parsanol/atoms/can_flatten"
25
+ require "parsanol/atoms/context"
26
+ require "parsanol/atoms/dsl"
27
+ require "parsanol/atoms/base"
28
+ require "parsanol/atoms/custom"
29
+ require "parsanol/atoms/ignored"
30
+ require "parsanol/atoms/named"
31
+ require "parsanol/atoms/lookahead"
32
+ require "parsanol/atoms/cut"
33
+ require "parsanol/atoms/alternative"
34
+ require "parsanol/atoms/sequence"
35
+ require "parsanol/atoms/repetition"
36
+ require "parsanol/atoms/re"
37
+ require "parsanol/atoms/str"
38
+ require "parsanol/atoms/entity"
39
+ require "parsanol/atoms/capture"
40
+ require "parsanol/atoms/dynamic"
41
+ require "parsanol/atoms/scope"
42
+ require "parsanol/atoms/infix"
43
43
  # Load visitor pattern (must be after all atom classes)
44
- require 'parsanol/atoms/visitor'
44
+ require "parsanol/atoms/visitor"
45
45
  end
46
46
  end
@@ -204,7 +204,7 @@ module Parsanol
204
204
  end
205
205
 
206
206
  def on_success
207
- @events << 'success'
207
+ @events << "success"
208
208
  end
209
209
 
210
210
  def on_error(message)
@@ -228,7 +228,7 @@ module Parsanol
228
228
  end
229
229
 
230
230
  def on_nil
231
- @events << 'nil'
231
+ @events << "nil"
232
232
  end
233
233
 
234
234
  def on_hash_start(size = nil)
@@ -113,10 +113,10 @@ module Parsanol
113
113
  return if prefix_flags.size < 2
114
114
 
115
115
  prefix_flags[1..-2].each do |is_last|
116
- stream.print is_last ? ' ' : '| '
116
+ stream.print is_last ? " " : "| "
117
117
  end
118
118
 
119
- stream.print prefix_flags.last ? '`- ' : '|- '
119
+ stream.print prefix_flags.last ? "`- " : "|- "
120
120
  end
121
121
  end
122
122
  end
@@ -32,8 +32,8 @@ module Parsanol
32
32
  #
33
33
  # @param name [Symbol, String] method name
34
34
  # @yield block to execute when method is called
35
- def define_singleton_method(name, &body)
36
- singleton_class.define_method(name, &body)
35
+ def define_singleton_method(name, &)
36
+ singleton_class.define_method(name, &)
37
37
  end
38
38
  end
39
39
  end
@@ -50,7 +50,7 @@ module Parsanol
50
50
  #
51
51
  def err(atom, source, message, children = nil)
52
52
  raise NotImplementedError,
53
- 'Error reporters must implement #err(atom, source, message, children)'
53
+ "Error reporters must implement #err(atom, source, message, children)"
54
54
  end
55
55
 
56
56
  # Report an error at a specific position.
@@ -66,7 +66,7 @@ module Parsanol
66
66
  #
67
67
  def err_at(atom, source, message, pos, children = nil)
68
68
  raise NotImplementedError,
69
- 'Error reporters must implement #err_at(atom, source, message, pos, children)'
69
+ "Error reporters must implement #err_at(atom, source, message, pos, children)"
70
70
  end
71
71
 
72
72
  # Called when an expression successfully parses.
@@ -95,6 +95,6 @@ module Parsanol
95
95
  end
96
96
  end
97
97
 
98
- require 'parsanol/error_reporter/tree'
99
- require 'parsanol/error_reporter/deepest'
100
- require 'parsanol/error_reporter/contextual'
98
+ require "parsanol/error_reporter/tree"
99
+ require "parsanol/error_reporter/deepest"
100
+ require "parsanol/error_reporter/contextual"
@@ -44,7 +44,7 @@ module Parsanol
44
44
 
45
45
  # Alternative: 'a' / 'b'
46
46
  rule(:alternatives) do
47
- (simple >> (spaced('/') >> simple).repeat).as(:alt)
47
+ (simple >> (spaced("/") >> simple).repeat).as(:alt)
48
48
  end
49
49
 
50
50
  # Sequence by concatenation: 'a' 'b'
@@ -52,15 +52,15 @@ module Parsanol
52
52
 
53
53
  # Occurrence modifiers: ?, *, +, {min,max}
54
54
  rule(:occurrence) do
55
- (atom.as(:repetition) >> spaced('*').as(:sign)) |
56
- (atom.as(:repetition) >> spaced('+').as(:sign)) |
55
+ (atom.as(:repetition) >> spaced("*").as(:sign)) |
56
+ (atom.as(:repetition) >> spaced("+").as(:sign)) |
57
57
  (atom.as(:repetition) >> repetition_spec) |
58
- (atom.as(:maybe) >> spaced('?')) |
58
+ (atom.as(:maybe) >> spaced("?")) |
59
59
  atom
60
60
  end
61
61
 
62
62
  rule(:atom) do
63
- (spaced('(') >> expression.as(:unwrap) >> spaced(')')) |
63
+ (spaced("(") >> expression.as(:unwrap) >> spaced(")")) |
64
64
  dot |
65
65
  string |
66
66
  char_class
@@ -68,30 +68,30 @@ module Parsanol
68
68
 
69
69
  # Character class: [a-z], [0-9], etc.
70
70
  rule(:char_class) do
71
- (str('[') >>
72
- ((str('\\') >> any) | (str(']').absent? >> any)).repeat(1) >>
73
- str(']')).as(:match) >> space?
71
+ (str("[") >>
72
+ ((str("\\") >> any) | (str("]").absent? >> any)).repeat(1) >>
73
+ str("]")).as(:match) >> space?
74
74
  end
75
75
 
76
76
  # Any character: .
77
- rule(:dot) { spaced('.').as(:any) }
77
+ rule(:dot) { spaced(".").as(:any) }
78
78
 
79
79
  # String literal: 'hello'
80
80
  rule(:string) do
81
81
  str("'") >>
82
- ((str('\\') >> any) | (str("'").absent? >> any)).repeat.as(:string) >>
82
+ ((str("\\") >> any) | (str("'").absent? >> any)).repeat.as(:string) >>
83
83
  str("'") >> space?
84
84
  end
85
85
 
86
86
  # Repetition specification: {1,3}, {2,}, {,5}
87
87
  rule(:repetition_spec) do
88
- spaced('{') >>
89
- integer.maybe.as(:min) >> spaced(',') >>
90
- integer.maybe.as(:max) >> spaced('}')
88
+ spaced("{") >>
89
+ integer.maybe.as(:min) >> spaced(",") >>
90
+ integer.maybe.as(:max) >> spaced("}")
91
91
  end
92
92
 
93
93
  rule(:integer) do
94
- match['0-9'].repeat(1)
94
+ match["0-9"].repeat(1)
95
95
  end
96
96
 
97
97
  # Whitespace handling
@@ -115,7 +115,7 @@ module Parsanol
115
115
  class Transform < Parsanol::Transform
116
116
  # Repetition with sign: * (zero+) or + (one+)
117
117
  rule(repetition: simple(:rep), sign: simple(:sign)) do
118
- min = sign == '+' ? 1 : 0
118
+ min = sign == "+" ? 1 : 0
119
119
  Parsanol::Atoms::Repetition.new(rep, min, nil)
120
120
  end
121
121
 
@@ -124,7 +124,7 @@ module Parsanol
124
124
  Parsanol::Atoms::Repetition.new(
125
125
  rep,
126
126
  Integer(min || 0),
127
- (max && Integer(max)) || nil
127
+ (max && Integer(max)) || nil,
128
128
  )
129
129
  end
130
130
 
@@ -147,7 +147,7 @@ module Parsanol
147
147
  rule(match: simple(:m)) { Parsanol::Atoms::Re.new("[#{m}]") }
148
148
 
149
149
  # Any character: .
150
- rule(any: simple(:_a)) { Parsanol::Atoms::Re.new('.') }
150
+ rule(any: simple(:_a)) { Parsanol::Atoms::Re.new(".") }
151
151
  end
152
152
  end
153
153
  end
@@ -56,7 +56,7 @@ module Parsanol
56
56
  class Expression
57
57
  include Parsanol
58
58
 
59
- autoload :Treetop, 'parsanol/expression/treetop'
59
+ autoload :Treetop, "parsanol/expression/treetop"
60
60
 
61
61
  # Creates a parser atom from a treetop-style expression string.
62
62
  #
@@ -25,7 +25,10 @@ module Parsanol
25
25
  unless (entry = @cache[beg]&.[](obj.object_id))
26
26
  result = obj.try(source, self, consume_all)
27
27
 
28
- (@cache[beg] ||= {})[obj.object_id] = [result, source.bytepos - beg] if obj.cached?
28
+ if obj.cached?
29
+ (@cache[beg] ||= {})[obj.object_id] =
30
+ [result, source.bytepos - beg]
31
+ end
29
32
 
30
33
  return result
31
34
  end
@@ -45,13 +48,24 @@ module Parsanol
45
48
  case parslets.size
46
49
  when 1
47
50
  success, value = parslets[0].apply(source, context, consume_all)
48
- success ? succ([:sequence, value]) : context.err(self, source, @error_msg, [value])
51
+ if success
52
+ succ([:sequence,
53
+ value])
54
+ else
55
+ context.err(self, source, @error_msg,
56
+ [value])
57
+ end
49
58
  when 2
50
59
  success, v1 = parslets[0].apply(source, context, false)
51
60
  return context.err(self, source, @error_msg, [v1]) unless success
52
61
 
53
62
  success, v2 = parslets[1].apply(source, context, consume_all)
54
- success ? succ([:sequence, v1, v2]) : context.err(self, source, @error_msg, [v2])
63
+ if success
64
+ succ([:sequence, v1,
65
+ v2])
66
+ else
67
+ context.err(self, source, @error_msg, [v2])
68
+ end
55
69
  when 3
56
70
  success, v1 = parslets[0].apply(source, context, false)
57
71
  return context.err(self, source, @error_msg, [v1]) unless success
@@ -60,13 +74,19 @@ module Parsanol
60
74
  return context.err(self, source, @error_msg, [v2]) unless success
61
75
 
62
76
  success, v3 = parslets[2].apply(source, context, consume_all)
63
- success ? succ([:sequence, v1, v2, v3]) : context.err(self, source, @error_msg, [v3])
77
+ if success
78
+ succ([:sequence, v1, v2,
79
+ v3])
80
+ else
81
+ context.err(self, source, @error_msg, [v3])
82
+ end
64
83
  else
65
84
  result = [:sequence]
66
85
  last_idx = parslets.size - 1
67
86
  i = 0
68
87
  while i <= last_idx
69
- success, value = parslets[i].apply(source, context, consume_all && i == last_idx)
88
+ success, value = parslets[i].apply(source, context,
89
+ consume_all && i == last_idx)
70
90
  return context.err(self, source, @error_msg, [value]) unless success
71
91
 
72
92
  result << value
@@ -100,22 +120,37 @@ module Parsanol
100
120
  case max
101
121
  when 1
102
122
  success, value = parslet.apply(source, context, consume_all)
103
- return success ? succ([tag, value]) : context.err_at(self, source, @error_msg, source.bytepos, [value])
123
+ return success ? succ([tag,
124
+ value]) : context.err_at(self, source,
125
+ @error_msg, source.bytepos, [value])
104
126
  when 2
105
127
  success, v1 = parslet.apply(source, context, false)
106
- return context.err_at(self, source, @error_msg, source.bytepos, [v1]) unless success
128
+ unless success
129
+ return context.err_at(self, source, @error_msg, source.bytepos,
130
+ [v1])
131
+ end
107
132
 
108
133
  success, v2 = parslet.apply(source, context, consume_all)
109
- return success ? succ([tag, v1, v2]) : context.err_at(self, source, @error_msg, source.bytepos, [v2])
134
+ return success ? succ([tag, v1,
135
+ v2]) : context.err_at(self, source,
136
+ @error_msg, source.bytepos, [v2])
110
137
  when 3
111
138
  success, v1 = parslet.apply(source, context, false)
112
- return context.err_at(self, source, @error_msg, source.bytepos, [v1]) unless success
139
+ unless success
140
+ return context.err_at(self, source, @error_msg, source.bytepos,
141
+ [v1])
142
+ end
113
143
 
114
144
  success, v2 = parslet.apply(source, context, false)
115
- return context.err_at(self, source, @error_msg, source.bytepos, [v2]) unless success
145
+ unless success
146
+ return context.err_at(self, source, @error_msg, source.bytepos,
147
+ [v2])
148
+ end
116
149
 
117
150
  success, v3 = parslet.apply(source, context, consume_all)
118
- return success ? succ([tag, v1, v2, v3]) : context.err_at(self, source, @error_msg, source.bytepos, [v3])
151
+ return success ? succ([tag, v1, v2,
152
+ v3]) : context.err_at(self, source,
153
+ @error_msg, source.bytepos, [v3])
119
154
  end
120
155
  end
121
156
 
@@ -140,7 +175,10 @@ module Parsanol
140
175
  return context.err_at(self, source, @error_msg, start_pos, [break_on])
141
176
  end
142
177
 
143
- return context.err(self, source, @unconsumed_msg, [break_on]) if consume_all && source.chars_left.positive?
178
+ if consume_all && source.chars_left.positive?
179
+ return context.err(self, source, @unconsumed_msg,
180
+ [break_on])
181
+ end
144
182
 
145
183
  succ(result)
146
184
  end
@@ -58,7 +58,7 @@ module Parsanol
58
58
  return true if real_set1.empty? || real_set2.empty?
59
59
 
60
60
  # Check if intersection is empty (using to_a for Opal compatibility)
61
- (real_set1.to_a & real_set2.to_a).empty?
61
+ !real_set1.to_a.intersect?(real_set2.to_a)
62
62
  end
63
63
 
64
64
  # Check if all FIRST sets in a collection are mutually disjoint
@@ -72,7 +72,8 @@ module Parsanol
72
72
  when Hash
73
73
  grammar
74
74
  else
75
- raise ArgumentError, "Expected GrammarBuilder or Hash, got #{grammar.class}"
75
+ raise ArgumentError,
76
+ "Expected GrammarBuilder or Hash, got #{grammar.class}"
76
77
  end
77
78
 
78
79
  @imports << { grammar: grammar_data, prefix: prefix }
@@ -92,7 +93,8 @@ module Parsanol
92
93
  when Hash
93
94
  grammar
94
95
  else
95
- raise ArgumentError, "Expected GrammarBuilder or Hash, got #{grammar.class}"
96
+ raise ArgumentError,
97
+ "Expected GrammarBuilder or Hash, got #{grammar.class}"
96
98
  end
97
99
 
98
100
  @imports << { grammar: grammar_data, prefix: prefix, rules: rules }
@@ -106,7 +108,7 @@ module Parsanol
106
108
  {
107
109
  rules: @rules,
108
110
  root: @root,
109
- imports: @imports
111
+ imports: @imports,
110
112
  }
111
113
  end
112
114
 
@@ -140,7 +142,7 @@ module Parsanol
140
142
  if grammar_name
141
143
  ref("#{grammar_name}:root")
142
144
  else
143
- ref('root')
145
+ ref("root")
144
146
  end
145
147
  end
146
148
 
@@ -149,9 +151,9 @@ module Parsanol
149
151
  #
150
152
  # @yield [GrammarBuilder] Builder to configure
151
153
  # @return [Hash] Built grammar
152
- def build(&block)
154
+ def build(&)
153
155
  builder = new
154
- builder.instance_eval(&block)
156
+ builder.instance_eval(&)
155
157
  builder.build
156
158
  end
157
159
 
@@ -170,8 +172,8 @@ module Parsanol
170
172
  # Create a new grammar builder
171
173
  #
172
174
  # @return [GrammarBuilder] New builder
173
- def grammar(&block)
174
- GrammarBuilder.build(&block)
175
+ def grammar(&)
176
+ GrammarBuilder.build(&)
175
177
  end
176
178
  end
177
179
  end
@@ -19,7 +19,7 @@ module Parsanol
19
19
  class Edit
20
20
  attr_reader :start, :deleted, :inserted
21
21
 
22
- def initialize(start:, deleted:, inserted: '')
22
+ def initialize(start:, deleted:, inserted: "")
23
23
  @start = start
24
24
  @deleted = deleted
25
25
  @inserted = inserted
@@ -61,13 +61,14 @@ module Parsanol
61
61
  #
62
62
  # @param grammar [Parsanol::Parser, Parsanol::Atoms::Base] Grammar to use
63
63
  # @param initial_input [String] Initial input string
64
- def initialize(grammar, initial_input = '')
64
+ def initialize(grammar, initial_input = "")
65
65
  @grammar = grammar
66
66
  @input = initial_input
67
67
 
68
68
  if Parsanol::Native.available?
69
69
  grammar_json = Parsanol::Native.serialize_grammar(grammar.root)
70
- @native_parser = Parsanol::Native.incremental_parser_new(grammar_json, initial_input)
70
+ @native_parser = Parsanol::Native.incremental_parser_new(grammar_json,
71
+ initial_input)
71
72
  else
72
73
  @native_parser = nil
73
74
  end
@@ -81,7 +82,7 @@ module Parsanol
81
82
  # @param start [Integer] Start position of edit
82
83
  # @param deleted [Integer] Number of characters deleted
83
84
  # @param inserted [String] Text to insert
84
- def apply_edit(start:, deleted:, inserted: '')
85
+ def apply_edit(start:, deleted:, inserted: "")
85
86
  edit = Edit.new(start: start, deleted: deleted, inserted: inserted)
86
87
  @edits << edit
87
88
 
@@ -93,7 +94,8 @@ module Parsanol
93
94
 
94
95
  return unless @native_parser
95
96
 
96
- Parsanol::Native.incremental_parser_apply_edit(@native_parser, start, deleted, inserted)
97
+ Parsanol::Native.incremental_parser_apply_edit(@native_parser, start,
98
+ deleted, inserted)
97
99
  end
98
100
 
99
101
  # Convenience method to apply multiple edits
@@ -119,7 +121,9 @@ module Parsanol
119
121
  return @cached_result if @cached_result
120
122
 
121
123
  if @native_parser
122
- @cached_result = Parsanol::Native.incremental_parser_reparse(@native_parser, @input)
124
+ @cached_result = Parsanol::Native.incremental_parser_reparse(
125
+ @native_parser, @input
126
+ )
123
127
  else
124
128
  # Pure Ruby fallback - reparse from scratch
125
129
  root = @grammar.root
@@ -164,14 +168,15 @@ module Parsanol
164
168
  #
165
169
  # @param new_input [String, nil] Optional new initial input
166
170
  def reset(new_input = nil)
167
- @input = new_input || ''
171
+ @input = new_input || ""
168
172
  @edits.clear
169
173
  @cached_result = nil
170
174
 
171
175
  return unless @native_parser && new_input
172
176
 
173
177
  grammar_json = Parsanol::Native.serialize_grammar(@grammar.root)
174
- @native_parser = Parsanol::Native.incremental_parser_new(grammar_json, @input)
178
+ @native_parser = Parsanol::Native.incremental_parser_new(grammar_json,
179
+ @input)
175
180
  end
176
181
  end
177
182
  end