parsanol 1.2.2-aarch64-linux → 1.3.2-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.
@@ -4,121 +4,83 @@
4
4
  # Preserves both the string value and its byte offset in the original input,
5
5
  # enabling precise error reporting and source mapping.
6
6
  #
7
- # Inspired by string slicing concepts in text editors and IDEs.
7
+ # Line/column is computed LAZILY on first access zero overhead
8
+ # for users who don't need position info.
8
9
  module Parsanol
9
10
  class Slice
10
11
  include Parsanol::Resettable
11
12
 
12
- attr_reader :content, :position_cache
13
+ attr_reader :content, :input
13
14
 
14
- # Creates a slice with position tracking.
15
- #
16
- # @param byte_offset [Integer] position in original input
17
- # @param string_content [String] the slice content
18
- # @param cache [Object] optional cache for line/column lookup
19
- def initialize(byte_offset = 0, string_content = '', cache = nil)
15
+ def initialize(byte_offset = 0, string_content = '', input = nil)
20
16
  @byte_position = byte_offset
21
17
  @content = string_content
22
- @position_cache = cache
18
+ @input = input
19
+ @line_and_column = nil
23
20
  end
24
21
 
25
- # Resets slice state for object pool reuse.
26
- #
27
- # @param new_offset [Integer] new byte position
28
- # @param new_content [String] new content
29
- # @param new_cache [Object] new line cache
30
- # @return [self] for method chaining
31
- def reset!(new_offset = 0, new_content = '', new_cache = nil)
22
+ def reset!(new_offset = 0, new_content = '', new_input = nil)
32
23
  @byte_position = new_offset
33
24
  @content = new_content
34
- @position_cache = new_cache
25
+ @input = new_input
26
+ @line_and_column = nil
35
27
  self
36
28
  end
37
29
 
38
- # Creates a Slice from a Rope concatenation.
39
- #
40
- # @param rope [Parsanol::Rope] rope to convert
41
- # @param offset [Integer] byte position
42
- # @param cache [Object] optional cache
43
- # @return [Parsanol::Slice] new slice
44
- def self.from_rope(rope, offset, cache = nil)
45
- new(offset, rope.to_s, cache)
30
+ def self.from_rope(rope, offset, input = nil)
31
+ new(offset, rope.to_s, input)
46
32
  end
47
33
 
48
- # @return [Integer] byte offset in original input
34
+ # Position
49
35
  def offset
50
36
  @byte_position
51
37
  end
52
38
 
53
39
  alias bytepos offset
54
40
  alias charpos offset
55
- alias str content # backward compatibility
56
- alias line_cache position_cache # backward compatibility
41
+ alias str content
57
42
 
58
- # Compares slices or strings for equality.
59
- #
60
- # @param other [Object] object to compare
61
- # @return [Boolean] true if equal
43
+ # Equality
62
44
  def ==(other)
63
45
  return content == other if other.is_a?(String)
64
46
  return content == other.content if other.is_a?(Parsanol::Slice)
65
-
66
47
  content == other
67
48
  end
68
49
 
69
- # Type-strict equality check.
70
- #
71
- # @param other [Object] object to compare
72
- # @return [Boolean] true if same type and content
73
50
  def eql?(other)
74
51
  other.is_a?(Parsanol::Slice) && content.eql?(other.content)
75
52
  end
76
53
 
77
- # Hash for use as hash keys.
78
- #
79
- # @return [Integer] hash combining content and position
80
54
  def hash
81
55
  [content, offset].hash
82
56
  end
83
57
 
84
- # Matches regular expression against content.
85
- #
86
- # @param pattern [Regexp] pattern to match
87
- # @return [MatchData, nil] match result
58
+ # Delegated methods
88
59
  def match(pattern)
89
60
  content.match(pattern)
90
61
  end
91
62
 
92
- # @return [Integer] length of slice content
93
63
  def size
94
64
  content.size
95
65
  end
96
66
 
97
67
  alias length size
98
68
 
99
- # Concatenates slices assuming second continues from first's end.
100
- #
101
- # @param other [Slice, String] slice to append
102
- # @return [Parsanol::Slice] combined slice
103
69
  def +(other)
104
- self.class.new(@byte_position, content + other.to_s, position_cache)
70
+ self.class.new(@byte_position, content + other.to_s, @input)
105
71
  end
106
72
 
107
- # Returns [line, column] tuple for this position.
108
- #
109
- # @return [Array<Integer, Integer>] line and column (1-indexed)
110
- # @raise [ArgumentError] if no line cache available
73
+ # Lazy line/column computed once and cached.
111
74
  def line_and_column
112
- raise ArgumentError, 'Line/column info requires a line cache. Pass one during parsing.' unless position_cache
113
-
114
- position_cache.line_and_column(@byte_position)
75
+ raise ArgumentError, 'Line/column requires input' unless @input
76
+ @line_and_column ||= compute_line_and_column
115
77
  end
116
78
 
117
- # String conversions ---------------------------------------------------------
118
-
79
+ # Conversions
119
80
  def to_str
120
- content.is_a?(String) ? content : content.to_s
81
+ content.to_s
121
82
  end
83
+
122
84
  alias to_s to_str
123
85
 
124
86
  def to_slice
@@ -137,71 +99,55 @@ module Parsanol
137
99
  content.to_f
138
100
  end
139
101
 
140
- # Inspection ---------------------------------------------------------
141
-
142
102
  def inspect
143
103
  "#{content.inspect}@#{offset}"
144
104
  end
145
105
 
146
- # JSON serialization --------------------------------------------------------
147
-
148
- # JSON serialization returns the full object with position info.
149
- # This is the default behavior - position info is ALWAYS included.
150
- #
151
- # @return [String] JSON representation with value and position
106
+ # JSON
152
107
  def to_json(*)
153
108
  as_json.to_json(*)
154
109
  end
155
110
 
156
- # Returns a hash with full position information for JSON serialization.
157
- # Line and column are always included when a position cache is available.
158
- #
159
- # @return [Hash] hash with value, offset, length, and line/column
160
111
  def as_json(_options = {})
161
- result = {
162
- 'value' => content,
163
- 'offset' => offset,
164
- 'length' => length
165
- }
166
-
167
- if position_cache
112
+ result = { 'value' => content, 'offset' => offset, 'length' => length }
113
+ if @input
168
114
  line, column = line_and_column
169
115
  result['line'] = line
170
116
  result['column'] = column
171
117
  end
172
-
173
118
  result
174
119
  end
175
120
 
176
- # Returns a SourceSpan representing this slice's position
177
- #
178
- # @param input [String, nil] the original input (needed for line/column)
179
- # @return [Parsanol::SourceSpan] span object
121
+ # Source span
180
122
  def to_span(_input = nil)
181
- start_pos = if position_cache
182
- line, column = line_and_column
183
- SourcePosition.new(offset: offset, line: line, column: column)
184
- else
185
- SourcePosition.new(offset: offset, line: 1, column: 1)
186
- end
187
-
188
- end_offset = offset + length
189
- end_pos = if position_cache
190
- line, column = position_cache.line_and_column(end_offset)
191
- SourcePosition.new(offset: end_offset, line: line, column: column)
192
- else
193
- SourcePosition.new(offset: end_offset, line: 1, column: 1)
194
- end
195
-
123
+ line, column = line_and_column
124
+ end_line, end_column = line_and_column_at(offset + length)
125
+ start_pos = SourcePosition.new(offset: offset, line: line, column: column)
126
+ end_pos = SourcePosition.new(offset: offset + length, line: end_line, column: end_column)
196
127
  SourceSpan.new(start_pos: start_pos, end_pos: end_pos)
197
128
  end
198
129
 
199
- # Extract this slice's content from the original input string
200
- #
201
- # @param input [String] the original input string
202
- # @return [String] the slice content extracted from input
203
- def extract_from(input)
204
- input.byteslice(offset, length)
130
+ private
131
+
132
+ def compute_line_and_column
133
+ line_and_column_at(@byte_position)
134
+ end
135
+
136
+ # Unified line/column computation:
137
+ # - String input: compute from input string
138
+ # - LineCache: delegate to cache
139
+ def line_and_column_at(pos)
140
+ if @input.respond_to?(:line_and_column)
141
+ # LineCache or duck-typed object
142
+ @input.line_and_column(pos)
143
+ else
144
+ # String input
145
+ prefix = @input.byteslice(0, pos) || ''
146
+ line = 1 + prefix.count("\n")
147
+ last_nl = prefix.rindex("\n")
148
+ column = last_nl ? pos - last_nl : pos + 1
149
+ [line, column]
150
+ end
205
151
  end
206
152
  end
207
153
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parsanol
4
- VERSION = '1.2.2'
4
+ VERSION = '1.3.2'
5
5
  end
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.2.2
4
+ version: 1.3.2
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-07 00:00:00.000000000 Z
11
+ date: 2026-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -198,6 +198,7 @@ files:
198
198
  - lib/parsanol/lazy_result.rb
199
199
  - lib/parsanol/mermaid.rb
200
200
  - lib/parsanol/native.rb
201
+ - lib/parsanol/native/batch_decoder.rb
201
202
  - lib/parsanol/native/dynamic.rb
202
203
  - lib/parsanol/native/parser.rb
203
204
  - lib/parsanol/native/serializer.rb