parsanol 1.3.54-arm-linux → 1.3.57-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 +4 -4
- data/exe/parsanol +7 -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/atoms/base.rb +1 -1
- data/lib/parsanol/atoms/cut.rb +1 -1
- data/lib/parsanol/atoms/repetition.rb +13 -2
- data/lib/parsanol/cause.rb +1 -1
- data/lib/parsanol/error_reporter/contextual.rb +1 -1
- data/lib/parsanol/lazy_result.rb +12 -3
- data/lib/parsanol/native/libparsanol.so +0 -0
- data/lib/parsanol/native/serializer.rb +5 -2
- data/lib/parsanol/native/transformer.rb +12 -8
- data/lib/parsanol/native.rb +4 -2
- data/lib/parsanol/parg/artifact.rb +213 -0
- data/lib/parsanol/parg/authoring.rb +110 -0
- data/lib/parsanol/parg/bindings.rb +129 -0
- data/lib/parsanol/parg/cli.rb +251 -0
- data/lib/parsanol/parg/compiler.rb +465 -0
- data/lib/parsanol/parg/derive.rb +28 -0
- data/lib/parsanol/parg/document.rb +69 -0
- data/lib/parsanol/parg/error.rb +18 -0
- data/lib/parsanol/parg/frontend.rb +173 -0
- data/lib/parsanol/parg/import.rb +34 -0
- data/lib/parsanol/parg/importers/abnf.rb +299 -0
- data/lib/parsanol/parg/importers/ebnf.rb +201 -0
- data/lib/parsanol/parg/importers/pest.rb +314 -0
- data/lib/parsanol/parg/imports.rb +87 -0
- data/lib/parsanol/parg/lexer.rb +72 -0
- data/lib/parsanol/parg/lints.rb +171 -0
- data/lib/parsanol/parg/lsp.rb +208 -0
- data/lib/parsanol/parg/lutaml.rb +67 -0
- data/lib/parsanol/parg/node.rb +20 -0
- data/lib/parsanol/parg/parser.rb +458 -0
- data/lib/parsanol/parg/preprocess.rb +36 -0
- data/lib/parsanol/parg/render.rb +44 -0
- data/lib/parsanol/parg/selfhost.rb +49 -0
- data/lib/parsanol/parg/visitor.rb +64 -0
- data/lib/parsanol/parg.rb +38 -0
- data/lib/parsanol/parser.rb +1 -1
- data/lib/parsanol/pattern.rb +1 -1
- data/lib/parsanol/pool.rb +1 -1
- data/lib/parsanol/rope.rb +1 -1
- data/lib/parsanol/slice.rb +4 -6
- data/lib/parsanol/source/line_cache.rb +1 -1
- data/lib/parsanol/source.rb +2 -2
- data/lib/parsanol/version.rb +1 -1
- data/lib/parsanol/vm.rb +95 -5
- data/lib/parsanol.rb +9 -0
- data/parsanol.gemspec +3 -1
- metadata +31 -4
data/lib/parsanol/pattern.rb
CHANGED
|
@@ -90,7 +90,7 @@ module Parsanol
|
|
|
90
90
|
return true if pattern_val === target
|
|
91
91
|
|
|
92
92
|
# Check if pattern is a binding expression (like simple(:x))
|
|
93
|
-
if pattern_val.
|
|
93
|
+
if pattern_val.is_a?(Parsanol::Pattern::SubtreeBind) && pattern_val.can_bind?(target)
|
|
94
94
|
return capture_binding(target, pattern_val, captured)
|
|
95
95
|
end
|
|
96
96
|
|
data/lib/parsanol/pool.rb
CHANGED
data/lib/parsanol/rope.rb
CHANGED
data/lib/parsanol/slice.rb
CHANGED
|
@@ -142,18 +142,16 @@ module Parsanol
|
|
|
142
142
|
|
|
143
143
|
# Unified line/column computation:
|
|
144
144
|
# - String input: compute from input string
|
|
145
|
-
# - LineCache: delegate
|
|
145
|
+
# - Source / LineCache: delegate
|
|
146
146
|
def line_and_column_at(pos)
|
|
147
|
-
if @input.
|
|
148
|
-
# LineCache or duck-typed object
|
|
149
|
-
@input.line_and_column(pos)
|
|
150
|
-
else
|
|
151
|
-
# String input
|
|
147
|
+
if @input.is_a?(String)
|
|
152
148
|
prefix = @input.byteslice(0, pos) || ""
|
|
153
149
|
line = 1 + prefix.count("\n")
|
|
154
150
|
last_nl = prefix.rindex("\n")
|
|
155
151
|
column = last_nl ? pos - last_nl : pos + 1
|
|
156
152
|
[line, column]
|
|
153
|
+
else
|
|
154
|
+
@input.line_and_column(pos)
|
|
157
155
|
end
|
|
158
156
|
end
|
|
159
157
|
end
|
data/lib/parsanol/source.rb
CHANGED
|
@@ -33,9 +33,9 @@ module Parsanol
|
|
|
33
33
|
# @raise [ArgumentError] if input doesn't respond to to_str
|
|
34
34
|
#
|
|
35
35
|
def initialize(input)
|
|
36
|
-
unless input.
|
|
36
|
+
unless input.is_a?(String)
|
|
37
37
|
raise ArgumentError,
|
|
38
|
-
"Source requires a
|
|
38
|
+
"Source requires a String (got #{input.class})"
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
@raw_string = input.to_str
|
data/lib/parsanol/version.rb
CHANGED
data/lib/parsanol/vm.rb
CHANGED
|
@@ -149,6 +149,7 @@ module Parsanol
|
|
|
149
149
|
def compile_with_inline(atom, inline)
|
|
150
150
|
root = atom.is_a?(Parsanol::Parser) ? atom.root : atom
|
|
151
151
|
compiler = Compiler.new(inline: inline)
|
|
152
|
+
validate_alternatives(root)
|
|
152
153
|
return nil unless compiler.compile_atom(root, true)
|
|
153
154
|
|
|
154
155
|
# HALT terminates the MAIN program; subroutines follow it so the
|
|
@@ -167,13 +168,88 @@ module Parsanol
|
|
|
167
168
|
# included — the seed is recomputed at every compile), so the
|
|
168
169
|
# cold-start never pays the doomed unmemoized exploration.
|
|
169
170
|
if compiler.backtracking_prone
|
|
170
|
-
# rubocop:disable-next Lint/HashCompareByIdentity -- object_id keys
|
|
171
|
+
# rubocop:disable-next Lint/HashCompareByIdentity -- object_id keys mirror run_for's heavy flag
|
|
171
172
|
(@heavy ||= {})[root.object_id] = true
|
|
172
173
|
end
|
|
173
174
|
|
|
174
175
|
compiler.to_program
|
|
175
176
|
end
|
|
176
177
|
|
|
178
|
+
def validate_choice_branches(atom)
|
|
179
|
+
atom.alternatives.each_with_index do |branch, idx|
|
|
180
|
+
walk_branch = branch
|
|
181
|
+
next if idx == atom.alternatives.size - 1
|
|
182
|
+
|
|
183
|
+
# Entities resolve lazily and may be recursive; resolve
|
|
184
|
+
# through the wrapper for the nullability check only.
|
|
185
|
+
while walk_branch.is_a?(Parsanol::Atoms::Entity) ||
|
|
186
|
+
walk_branch.is_a?(Parsanol::Atoms::Named)
|
|
187
|
+
walk_branch = begin
|
|
188
|
+
walk_branch.parslet
|
|
189
|
+
rescue StandardError
|
|
190
|
+
nil
|
|
191
|
+
end
|
|
192
|
+
break if walk_branch.nil?
|
|
193
|
+
end
|
|
194
|
+
next if walk_branch.nil?
|
|
195
|
+
next unless Compiler.new.nullable?(walk_branch)
|
|
196
|
+
|
|
197
|
+
raise Parsanol::GrammarError,
|
|
198
|
+
"wrong grammar: alternative branch #{idx} " \
|
|
199
|
+
"(#{branch.inspect}) can match empty input and is not " \
|
|
200
|
+
"the last branch — ordered choice commits to its empty " \
|
|
201
|
+
"match, so #{atom.alternatives.size - idx - 1} later " \
|
|
202
|
+
"branch(es) can never match at positions where it " \
|
|
203
|
+
"matches empty. Reorder branches so empty-matchable " \
|
|
204
|
+
"ones come last, or make the branch non-empty."
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Grammar validation, run once per compile: an ordered-choice
|
|
209
|
+
# branch that can match empty input while not being last commits
|
|
210
|
+
# to its empty match and permanently shadows every later branch —
|
|
211
|
+
# the grammar is wrong, and inputs it would mis-parse fail with a
|
|
212
|
+
# baffling cause. Raise before any input is seen.
|
|
213
|
+
def validate_alternatives(root)
|
|
214
|
+
visited = {}.compare_by_identity
|
|
215
|
+
walk = lambda do |atom, depth|
|
|
216
|
+
next if atom.nil? || depth > 20
|
|
217
|
+
next if visited[atom]
|
|
218
|
+
|
|
219
|
+
visited[atom] = true
|
|
220
|
+
case atom
|
|
221
|
+
when Parsanol::Atoms::Alternative
|
|
222
|
+
validate_choice_branches(atom)
|
|
223
|
+
atom.alternatives.each { |c| walk.call(c, depth + 1) }
|
|
224
|
+
when Parsanol::Atoms::Sequence
|
|
225
|
+
atom.parslets.each { |c| walk.call(c, depth + 1) }
|
|
226
|
+
when Parsanol::Atoms::Repetition, Parsanol::Atoms::Named
|
|
227
|
+
walk.call(atom.parslet, depth + 1)
|
|
228
|
+
when Parsanol::Atoms::Lookahead
|
|
229
|
+
walk.call(atom.bound_parslet, depth + 1)
|
|
230
|
+
when Parsanol::Atoms::Ignored
|
|
231
|
+
walk.call(atom.wrapped_atom, depth + 1)
|
|
232
|
+
when Parsanol::Atoms::Entity
|
|
233
|
+
begin
|
|
234
|
+
walk.call(atom.parslet, depth + 1)
|
|
235
|
+
rescue Parsanol::GrammarError
|
|
236
|
+
raise
|
|
237
|
+
rescue StandardError
|
|
238
|
+
# Lazily resolved / recursive entity that cannot resolve
|
|
239
|
+
# outside a parse: skip this branch of the walk.
|
|
240
|
+
nil
|
|
241
|
+
end
|
|
242
|
+
when Parsanol::Atoms::Scope
|
|
243
|
+
begin
|
|
244
|
+
walk.call(atom.block.call, depth + 1)
|
|
245
|
+
rescue StandardError
|
|
246
|
+
nil
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
walk.call(root, 0)
|
|
251
|
+
end
|
|
252
|
+
|
|
177
253
|
# Executes a compiled program. Returns [true, value] on success;
|
|
178
254
|
# [false, nil] on failure or budget exhaustion — callers fall back
|
|
179
255
|
# to the interpreter, which reproduces exact diagnostics.
|
|
@@ -380,8 +456,7 @@ module Parsanol
|
|
|
380
456
|
end
|
|
381
457
|
end
|
|
382
458
|
|
|
383
|
-
# Wrapper branches (Entity/Scope) share body shapes by design
|
|
384
|
-
# rubocop:disable-next Lint/DuplicateBranch
|
|
459
|
+
# rubocop:disable Lint/DuplicateBranch -- Wrapper branches (Entity/Scope) share body shapes by design
|
|
385
460
|
def nullable?(atom, depth = 0)
|
|
386
461
|
return false if depth > 10
|
|
387
462
|
|
|
@@ -397,12 +472,14 @@ module Parsanol
|
|
|
397
472
|
when Parsanol::Atoms::Ignored then nullable?(atom.wrapped_atom, depth + 1)
|
|
398
473
|
when Parsanol::Atoms::Lookahead then true
|
|
399
474
|
when Parsanol::Atoms::Entity, Parsanol::Atoms::Scope
|
|
400
|
-
|
|
475
|
+
# rubocop:disable Lint/DuplicateBranch -- Entity/Scope differ only in accessor
|
|
476
|
+
nullable_wrapped?(
|
|
401
477
|
-> { atom.is_a?(Parsanol::Atoms::Entity) ? atom.parslet : atom.block.call }, depth
|
|
402
478
|
)
|
|
403
479
|
else false
|
|
404
480
|
end
|
|
405
481
|
end
|
|
482
|
+
# rubocop:enable Lint/DuplicateBranch
|
|
406
483
|
|
|
407
484
|
# Entity and Scope wrap lazily-resolvable inners through different
|
|
408
485
|
# accessors; the nullability logic is shared.
|
|
@@ -790,7 +867,7 @@ module Parsanol
|
|
|
790
867
|
memo_stack = []
|
|
791
868
|
trace = ENV["VM_TRACE"] ? [] : nil
|
|
792
869
|
|
|
793
|
-
# rubocop:disable
|
|
870
|
+
# rubocop:disable Metrics/BlockLength -- the dispatch loop IS execute
|
|
794
871
|
loop do
|
|
795
872
|
steps += 1
|
|
796
873
|
if steps > budget
|
|
@@ -977,12 +1054,24 @@ module Parsanol
|
|
|
977
1054
|
count = frames[cbase]
|
|
978
1055
|
return BAIL if count.nil?
|
|
979
1056
|
|
|
1057
|
+
old_end = frames[cbase + 1]
|
|
980
1058
|
count += 1
|
|
981
1059
|
frames[cbase] = count
|
|
982
1060
|
frames[cbase + 1] = pos
|
|
983
1061
|
max = ops[pc + 3]
|
|
984
1062
|
pc = if max && count >= max
|
|
985
1063
|
ops[pc + 2] # exit
|
|
1064
|
+
elsif pos == old_end
|
|
1065
|
+
# Zero-width body match: count it, stop iterating.
|
|
1066
|
+
# Mirrors Repetition#try_general: success with the
|
|
1067
|
+
# prefix when min is met, failure of the repetition
|
|
1068
|
+
# otherwise.
|
|
1069
|
+
if count >= frames[cbase + 3]
|
|
1070
|
+
ops[pc + 2] # exit
|
|
1071
|
+
else
|
|
1072
|
+
frames.slice!(cbase..)
|
|
1073
|
+
fail_pc
|
|
1074
|
+
end
|
|
986
1075
|
else
|
|
987
1076
|
ops[pc + 1] # test
|
|
988
1077
|
end
|
|
@@ -1252,6 +1341,7 @@ module Parsanol
|
|
|
1252
1341
|
else
|
|
1253
1342
|
return BAIL
|
|
1254
1343
|
end
|
|
1344
|
+
# rubocop:enable Metrics/BlockLength
|
|
1255
1345
|
end
|
|
1256
1346
|
end
|
|
1257
1347
|
|
data/lib/parsanol.rb
CHANGED
|
@@ -39,11 +39,20 @@
|
|
|
39
39
|
# Inspired by Parslet (MIT License).
|
|
40
40
|
|
|
41
41
|
module Parsanol
|
|
42
|
+
# The PARG grammar language (text source -> checksummed artifact).
|
|
43
|
+
autoload :PARG, "parsanol/parg"
|
|
44
|
+
|
|
42
45
|
# Hook to extend including classes with ClassMethods.
|
|
43
46
|
def self.included(base)
|
|
44
47
|
base.extend(ClassMethods)
|
|
45
48
|
end
|
|
46
49
|
|
|
50
|
+
# Raised when the GRAMMAR itself is malformed — detected before any
|
|
51
|
+
# input is parsed. The canonical case: an ordered-choice branch that
|
|
52
|
+
# can match empty input while not being last, which commits to its
|
|
53
|
+
# empty match and permanently shadows every later branch.
|
|
54
|
+
class GrammarError < StandardError; end
|
|
55
|
+
|
|
47
56
|
# Exception raised when parsing fails. Contains detailed error information
|
|
48
57
|
# in the #parse_failure_cause attribute.
|
|
49
58
|
class ParseFailed < StandardError
|
data/parsanol.gemspec
CHANGED
|
@@ -29,7 +29,9 @@ Gem::Specification.new do |spec|
|
|
|
29
29
|
# Rust extension
|
|
30
30
|
spec.extensions = ["ext/parsanol_native/extconf.rb"]
|
|
31
31
|
|
|
32
|
-
spec.
|
|
32
|
+
spec.executables = %w[parsanol]
|
|
33
|
+
spec.bindir = "exe"
|
|
34
|
+
spec.files = Dir.glob("{lib,ext,exe}/**/*") + %w[
|
|
33
35
|
HISTORY.txt
|
|
34
36
|
LICENSE
|
|
35
37
|
Rakefile
|
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.3.
|
|
4
|
+
version: 1.3.57
|
|
5
5
|
platform: arm-linux
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
|
-
bindir:
|
|
9
|
+
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A small Ruby library for constructing parsers in the PEG (Parsing Expression
|
|
14
14
|
Grammar) fashion. Parsanol provides Parslet-compatible API with additional features
|
|
@@ -16,7 +16,8 @@ description: A small Ruby library for constructing parsers in the PEG (Parsing E
|
|
|
16
16
|
for improved performance.
|
|
17
17
|
email:
|
|
18
18
|
- open.source@ribose.com
|
|
19
|
-
executables:
|
|
19
|
+
executables:
|
|
20
|
+
- parsanol
|
|
20
21
|
extensions: []
|
|
21
22
|
extra_rdoc_files: []
|
|
22
23
|
files:
|
|
@@ -24,6 +25,7 @@ files:
|
|
|
24
25
|
- LICENSE
|
|
25
26
|
- README.adoc
|
|
26
27
|
- Rakefile
|
|
28
|
+
- exe/parsanol
|
|
27
29
|
- lib/parsanol.rb
|
|
28
30
|
- lib/parsanol/3.2/parsanol_native.so
|
|
29
31
|
- lib/parsanol/3.3/parsanol_native.so
|
|
@@ -91,6 +93,31 @@ files:
|
|
|
91
93
|
- lib/parsanol/options.rb
|
|
92
94
|
- lib/parsanol/options/zero_copy.rb
|
|
93
95
|
- lib/parsanol/parallel.rb
|
|
96
|
+
- lib/parsanol/parg.rb
|
|
97
|
+
- lib/parsanol/parg/artifact.rb
|
|
98
|
+
- lib/parsanol/parg/authoring.rb
|
|
99
|
+
- lib/parsanol/parg/bindings.rb
|
|
100
|
+
- lib/parsanol/parg/cli.rb
|
|
101
|
+
- lib/parsanol/parg/compiler.rb
|
|
102
|
+
- lib/parsanol/parg/derive.rb
|
|
103
|
+
- lib/parsanol/parg/document.rb
|
|
104
|
+
- lib/parsanol/parg/error.rb
|
|
105
|
+
- lib/parsanol/parg/frontend.rb
|
|
106
|
+
- lib/parsanol/parg/import.rb
|
|
107
|
+
- lib/parsanol/parg/importers/abnf.rb
|
|
108
|
+
- lib/parsanol/parg/importers/ebnf.rb
|
|
109
|
+
- lib/parsanol/parg/importers/pest.rb
|
|
110
|
+
- lib/parsanol/parg/imports.rb
|
|
111
|
+
- lib/parsanol/parg/lexer.rb
|
|
112
|
+
- lib/parsanol/parg/lints.rb
|
|
113
|
+
- lib/parsanol/parg/lsp.rb
|
|
114
|
+
- lib/parsanol/parg/lutaml.rb
|
|
115
|
+
- lib/parsanol/parg/node.rb
|
|
116
|
+
- lib/parsanol/parg/parser.rb
|
|
117
|
+
- lib/parsanol/parg/preprocess.rb
|
|
118
|
+
- lib/parsanol/parg/render.rb
|
|
119
|
+
- lib/parsanol/parg/selfhost.rb
|
|
120
|
+
- lib/parsanol/parg/visitor.rb
|
|
94
121
|
- lib/parsanol/parser.rb
|
|
95
122
|
- lib/parsanol/parslet.rb
|
|
96
123
|
- lib/parsanol/pattern.rb
|