parsanol 1.3.13-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 +7 -0
- data/HISTORY.txt +98 -0
- data/LICENSE +23 -0
- data/README.adoc +905 -0
- data/Rakefile +141 -0
- data/lib/parsanol/3.2/parsanol_native.so +0 -0
- data/lib/parsanol/3.3/parsanol_native.so +0 -0
- data/lib/parsanol/3.4/parsanol_native.so +0 -0
- data/lib/parsanol/4.0/parsanol_native.so +0 -0
- data/lib/parsanol/ast_visitor.rb +122 -0
- data/lib/parsanol/atoms/alternative.rb +123 -0
- data/lib/parsanol/atoms/base.rb +208 -0
- data/lib/parsanol/atoms/can_flatten.rb +194 -0
- data/lib/parsanol/atoms/capture.rb +38 -0
- data/lib/parsanol/atoms/context.rb +357 -0
- data/lib/parsanol/atoms/context_optimized.rb +38 -0
- data/lib/parsanol/atoms/custom.rb +110 -0
- data/lib/parsanol/atoms/cut.rb +66 -0
- data/lib/parsanol/atoms/dsl.rb +96 -0
- data/lib/parsanol/atoms/dynamic.rb +39 -0
- data/lib/parsanol/atoms/entity.rb +75 -0
- data/lib/parsanol/atoms/ignored.rb +37 -0
- data/lib/parsanol/atoms/infix.rb +167 -0
- data/lib/parsanol/atoms/lookahead.rb +85 -0
- data/lib/parsanol/atoms/named.rb +74 -0
- data/lib/parsanol/atoms/re.rb +83 -0
- data/lib/parsanol/atoms/repetition.rb +277 -0
- data/lib/parsanol/atoms/scope.rb +35 -0
- data/lib/parsanol/atoms/sequence.rb +195 -0
- data/lib/parsanol/atoms/str.rb +109 -0
- data/lib/parsanol/atoms/visitor.rb +91 -0
- data/lib/parsanol/atoms.rb +46 -0
- data/lib/parsanol/buffer.rb +133 -0
- data/lib/parsanol/builder_callbacks.rb +353 -0
- data/lib/parsanol/cause.rb +122 -0
- data/lib/parsanol/context.rb +39 -0
- data/lib/parsanol/convenience.rb +36 -0
- data/lib/parsanol/edit_tracker.rb +111 -0
- data/lib/parsanol/error_reporter/contextual.rb +99 -0
- data/lib/parsanol/error_reporter/deepest.rb +120 -0
- data/lib/parsanol/error_reporter/tree.rb +63 -0
- data/lib/parsanol/error_reporter.rb +100 -0
- data/lib/parsanol/expression/treetop.rb +154 -0
- data/lib/parsanol/expression.rb +106 -0
- data/lib/parsanol/fast_mode.rb +187 -0
- data/lib/parsanol/first_set.rb +79 -0
- data/lib/parsanol/grammar_builder.rb +179 -0
- data/lib/parsanol/incremental_parser.rb +182 -0
- data/lib/parsanol/interval_tree.rb +226 -0
- data/lib/parsanol/lazy_result.rb +179 -0
- data/lib/parsanol/mermaid.rb +142 -0
- data/lib/parsanol/native/batch_decoder.rb +255 -0
- data/lib/parsanol/native/dynamic.rb +238 -0
- data/lib/parsanol/native/parser.rb +102 -0
- data/lib/parsanol/native/serializer.rb +252 -0
- data/lib/parsanol/native/transformer.rb +604 -0
- data/lib/parsanol/native/types.rb +29 -0
- data/lib/parsanol/native.rb +223 -0
- data/lib/parsanol/optimizer.rb +85 -0
- data/lib/parsanol/optimizers/choice_optimizer.rb +78 -0
- data/lib/parsanol/optimizers/cut_inserter.rb +182 -0
- data/lib/parsanol/optimizers/lookahead_optimizer.rb +56 -0
- data/lib/parsanol/optimizers/quantifier_optimizer.rb +60 -0
- data/lib/parsanol/optimizers/sequence_optimizer.rb +97 -0
- data/lib/parsanol/options/zero_copy.rb +127 -0
- data/lib/parsanol/options.rb +21 -0
- data/lib/parsanol/parallel.rb +128 -0
- data/lib/parsanol/parser.rb +242 -0
- data/lib/parsanol/parslet.rb +151 -0
- data/lib/parsanol/pattern/binding.rb +91 -0
- data/lib/parsanol/pattern.rb +162 -0
- data/lib/parsanol/pool.rb +219 -0
- data/lib/parsanol/pools/array_pool.rb +75 -0
- data/lib/parsanol/pools/buffer_pool.rb +182 -0
- data/lib/parsanol/pools/position_pool.rb +92 -0
- data/lib/parsanol/pools/slice_pool.rb +64 -0
- data/lib/parsanol/position.rb +94 -0
- data/lib/parsanol/resettable.rb +29 -0
- data/lib/parsanol/result.rb +46 -0
- data/lib/parsanol/result_builder.rb +208 -0
- data/lib/parsanol/result_stream.rb +266 -0
- data/lib/parsanol/rig/rspec.rb +71 -0
- data/lib/parsanol/rope.rb +81 -0
- data/lib/parsanol/scope.rb +104 -0
- data/lib/parsanol/slice.rb +160 -0
- data/lib/parsanol/source/line_cache.rb +102 -0
- data/lib/parsanol/source.rb +185 -0
- data/lib/parsanol/source_location.rb +167 -0
- data/lib/parsanol/streaming_parser.rb +124 -0
- data/lib/parsanol/string_view.rb +198 -0
- data/lib/parsanol/transform.rb +226 -0
- data/lib/parsanol/version.rb +5 -0
- data/lib/parsanol/wasm/README.md +80 -0
- data/lib/parsanol/wasm/package.json +51 -0
- data/lib/parsanol/wasm/parsanol.js +252 -0
- data/lib/parsanol/wasm/parslet.d.ts +129 -0
- data/lib/parsanol/wasm_parser.rb +240 -0
- data/lib/parsanol.rb +278 -0
- data/parsanol.gemspec +67 -0
- metadata +279 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Parsanol
|
|
4
|
+
# Grammar serializer for native parser
|
|
5
|
+
# Serializes Parslet atoms to JSON format expected by Rust parser
|
|
6
|
+
#
|
|
7
|
+
class GrammarSerializer
|
|
8
|
+
# Serialize a Parslet grammar (root atom) to JSON string
|
|
9
|
+
#
|
|
10
|
+
# @param root [Parsanol::Atoms::Base] The root atom of the grammar
|
|
11
|
+
# @return [String] JSON representation of the grammar
|
|
12
|
+
def self.serialize(root)
|
|
13
|
+
# Create fresh instance for each serialization
|
|
14
|
+
# (state is specific to each grammar)
|
|
15
|
+
new.serialize(root)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def initialize
|
|
19
|
+
@atoms = []
|
|
20
|
+
@atom_cache = {} # object_id => atom_id for deduplication
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Main serialization method
|
|
24
|
+
def serialize(root)
|
|
25
|
+
root_id = serialize_atom(root)
|
|
26
|
+
|
|
27
|
+
# Build JSON output directly to avoid intermediate Hash
|
|
28
|
+
# This is faster than creating a Hash and calling to_json
|
|
29
|
+
%({"atoms":#{@atoms.to_json},"root":#{root_id}})
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
# Serialize a single atom and return its atom_id
|
|
35
|
+
def serialize_atom(atom)
|
|
36
|
+
# Check cache for deduplication
|
|
37
|
+
cache_key = atom.object_id
|
|
38
|
+
return @atom_cache[cache_key] if @atom_cache.key?(cache_key)
|
|
39
|
+
|
|
40
|
+
# Entity atoms are special - they're just lazy references to other atoms
|
|
41
|
+
# Don't create a new atom, just resolve and return the referenced atom_id
|
|
42
|
+
return serialize_entity(atom) if atom.is_a?(Parsanol::Atoms::Entity)
|
|
43
|
+
|
|
44
|
+
# Serialize based on atom type first (recursively)
|
|
45
|
+
serialized = case atom
|
|
46
|
+
when Parsanol::Atoms::Str
|
|
47
|
+
serialize_str(atom)
|
|
48
|
+
when Parsanol::Atoms::Re
|
|
49
|
+
serialize_re(atom)
|
|
50
|
+
when Parsanol::Atoms::Sequence
|
|
51
|
+
serialize_sequence(atom)
|
|
52
|
+
when Parsanol::Atoms::Alternative
|
|
53
|
+
serialize_alternative(atom)
|
|
54
|
+
when Parsanol::Atoms::Repetition
|
|
55
|
+
serialize_repetition(atom)
|
|
56
|
+
when Parsanol::Atoms::Named
|
|
57
|
+
serialize_named(atom)
|
|
58
|
+
when Parsanol::Atoms::Lookahead
|
|
59
|
+
serialize_lookahead(atom)
|
|
60
|
+
when Parsanol::Atoms::Capture
|
|
61
|
+
serialize_capture(atom)
|
|
62
|
+
when Parsanol::Atoms::Scope
|
|
63
|
+
serialize_scope(atom)
|
|
64
|
+
when Parsanol::Atoms::Dynamic
|
|
65
|
+
serialize_dynamic(atom)
|
|
66
|
+
else
|
|
67
|
+
# Fallback for unknown atom types
|
|
68
|
+
serialize_unknown(atom)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Now reserve an atom_id and cache
|
|
72
|
+
atom_id = @atoms.size
|
|
73
|
+
@atom_cache[cache_key] = atom_id
|
|
74
|
+
@atoms << serialized
|
|
75
|
+
|
|
76
|
+
atom_id
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def serialize_str(atom)
|
|
80
|
+
{
|
|
81
|
+
"Str" => {
|
|
82
|
+
"pattern" => atom.str,
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def serialize_re(atom)
|
|
88
|
+
# Ruby's Regexp#to_s produces "(?-mix:pattern)" format
|
|
89
|
+
# We need to extract just the pattern for the Rust parser
|
|
90
|
+
pattern = atom.match
|
|
91
|
+
pattern = ::Regexp.last_match(1) if pattern =~ /^\(\?[-mix]*:(.+)\)$/
|
|
92
|
+
{
|
|
93
|
+
"Re" => {
|
|
94
|
+
"pattern" => pattern,
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def serialize_sequence(atom)
|
|
100
|
+
atom_ids = atom.parslets.map { |p| serialize_atom(p) }
|
|
101
|
+
{
|
|
102
|
+
"Sequence" => {
|
|
103
|
+
"atoms" => atom_ids,
|
|
104
|
+
},
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def serialize_alternative(atom)
|
|
109
|
+
atom_ids = atom.alternatives.map { |p| serialize_atom(p) }
|
|
110
|
+
{
|
|
111
|
+
"Alternative" => {
|
|
112
|
+
"atoms" => atom_ids,
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def serialize_repetition(atom)
|
|
118
|
+
{
|
|
119
|
+
"Repetition" => {
|
|
120
|
+
"atom" => serialize_atom(atom.parslet),
|
|
121
|
+
"min" => atom.min,
|
|
122
|
+
"max" => atom.max,
|
|
123
|
+
"tag" => atom.result_tag == :maybe ? "Maybe" : "Repetition",
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def serialize_named(atom)
|
|
129
|
+
{
|
|
130
|
+
"Named" => {
|
|
131
|
+
"name" => atom.name.to_s,
|
|
132
|
+
"atom" => serialize_atom(atom.parslet),
|
|
133
|
+
},
|
|
134
|
+
}
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def serialize_entity(atom)
|
|
138
|
+
# Entity is a lazy reference - resolve it to the actual parslet
|
|
139
|
+
# Cache FIRST before resolving to handle circular references
|
|
140
|
+
cache_key = atom.object_id
|
|
141
|
+
|
|
142
|
+
# Reserve an atom_id and cache it before resolving
|
|
143
|
+
# This prevents infinite recursion when a rule references itself
|
|
144
|
+
atom_id = @atoms.size
|
|
145
|
+
@atom_cache[cache_key] = atom_id
|
|
146
|
+
|
|
147
|
+
# Add a placeholder that will be replaced
|
|
148
|
+
@atoms << nil
|
|
149
|
+
|
|
150
|
+
parslet = begin
|
|
151
|
+
atom.parslet
|
|
152
|
+
rescue StandardError
|
|
153
|
+
nil
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
if parslet
|
|
157
|
+
# Serialize the resolved parslet inline (don't call serialize_atom to avoid double-caching)
|
|
158
|
+
serialized = case parslet
|
|
159
|
+
when Parsanol::Atoms::Str
|
|
160
|
+
serialize_str(parslet)
|
|
161
|
+
when Parsanol::Atoms::Re
|
|
162
|
+
serialize_re(parslet)
|
|
163
|
+
when Parsanol::Atoms::Sequence
|
|
164
|
+
serialize_sequence(parslet)
|
|
165
|
+
when Parsanol::Atoms::Alternative
|
|
166
|
+
serialize_alternative(parslet)
|
|
167
|
+
when Parsanol::Atoms::Repetition
|
|
168
|
+
serialize_repetition(parslet)
|
|
169
|
+
when Parsanol::Atoms::Named
|
|
170
|
+
serialize_named(parslet)
|
|
171
|
+
when Parsanol::Atoms::Entity
|
|
172
|
+
# Nested entity - just reference it via serialize_atom
|
|
173
|
+
{ "Entity" => { "atom" => serialize_atom(parslet) } }
|
|
174
|
+
when Parsanol::Atoms::Lookahead
|
|
175
|
+
serialize_lookahead(parslet)
|
|
176
|
+
else
|
|
177
|
+
serialize_unknown(parslet)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Replace the placeholder with the serialized atom
|
|
181
|
+
@atoms[atom_id] = serialized
|
|
182
|
+
else
|
|
183
|
+
# If the entity's block returns nil, create a placeholder that will fail
|
|
184
|
+
@atoms[atom_id] = {
|
|
185
|
+
"Str" => {
|
|
186
|
+
"pattern" => "\x00__UNIMPLEMENTED_ENTITY_#{atom.name}__",
|
|
187
|
+
},
|
|
188
|
+
}
|
|
189
|
+
end
|
|
190
|
+
atom_id
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def serialize_lookahead(atom)
|
|
194
|
+
{
|
|
195
|
+
"Lookahead" => {
|
|
196
|
+
"atom" => serialize_atom(atom.bound_parslet),
|
|
197
|
+
"positive" => atom.positive,
|
|
198
|
+
},
|
|
199
|
+
}
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def serialize_capture(atom)
|
|
203
|
+
# Capture stores matched text for later reference by Dynamic atoms.
|
|
204
|
+
# Now properly serialized for native parser support (parsanol-rs 0.3.0+).
|
|
205
|
+
{
|
|
206
|
+
"Capture" => {
|
|
207
|
+
"name" => atom.capture_key.to_s,
|
|
208
|
+
"atom" => serialize_atom(atom.inner_atom),
|
|
209
|
+
},
|
|
210
|
+
}
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def serialize_scope(atom)
|
|
214
|
+
# Scope creates an isolated capture context.
|
|
215
|
+
# Captures made within scope are discarded when scope exits.
|
|
216
|
+
inner = begin
|
|
217
|
+
atom.block.call
|
|
218
|
+
rescue StandardError
|
|
219
|
+
nil
|
|
220
|
+
end
|
|
221
|
+
return serialize_unknown(atom) unless inner
|
|
222
|
+
|
|
223
|
+
{
|
|
224
|
+
"Scope" => {
|
|
225
|
+
"atom" => serialize_atom(inner),
|
|
226
|
+
},
|
|
227
|
+
}
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def serialize_dynamic(atom)
|
|
231
|
+
# Dynamic evaluates a Ruby block at parse time.
|
|
232
|
+
# Register the block and get a callback ID for FFI.
|
|
233
|
+
callback_id = Parsanol::Native::Dynamic.register(atom.block)
|
|
234
|
+
|
|
235
|
+
{
|
|
236
|
+
"Dynamic" => {
|
|
237
|
+
"callback_id" => callback_id,
|
|
238
|
+
},
|
|
239
|
+
}
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def serialize_unknown(_atom)
|
|
243
|
+
# For unsupported atom types, create a placeholder
|
|
244
|
+
# This will cause a parse error at runtime
|
|
245
|
+
{
|
|
246
|
+
"Str" => {
|
|
247
|
+
"pattern" => "", # Empty pattern that will never match
|
|
248
|
+
},
|
|
249
|
+
}
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|