parsanol 1.3.71 → 1.3.72
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/Cargo.lock +4 -4
- data/ext/parsanol_cdylib/Cargo.toml +2 -2
- data/ext/parsanol_native/Cargo.toml +3 -3
- data/lib/parsanol/native/serializer.rb +16 -0
- data/lib/parsanol/parser.rb +32 -1
- data/lib/parsanol/skip.rb +110 -0
- data/lib/parsanol/version.rb +1 -1
- data/lib/parsanol.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bd9773776674bd865171d5b4b378628a063b0dee92e2a4442fcf3fe0182262da
|
|
4
|
+
data.tar.gz: 6e8554e163d07070a54b9255d6bed7ca348fd72fd6a8968a17308814323d0d4c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fa0c3526c5ab79f7d9b6ed08e7523738cf6b46e2b8a6c30e2a021845d6bdeeabd5bb3e7ddd4d31f36725c1682ad94e650975289889c1bc991364d48bd9e2c8d7
|
|
7
|
+
data.tar.gz: ea7fd569d5a432d56583d76ec896c5a0c03ab8b769d9af7863041f02e64343ce1280ee226f450efde730bcb98bb4a0391fb7fcd03d3f490c7e35b133f6f427e1
|
data/Cargo.lock
CHANGED
|
@@ -307,9 +307,9 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
|
307
307
|
|
|
308
308
|
[[package]]
|
|
309
309
|
name = "parsanol"
|
|
310
|
-
version = "0.
|
|
310
|
+
version = "0.13.0"
|
|
311
311
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
312
|
-
checksum = "
|
|
312
|
+
checksum = "126b715ffe53b3bc75ed8ff753333e350d51aaea64a0a51a6eb8588f0786d925"
|
|
313
313
|
dependencies = [
|
|
314
314
|
"ahash",
|
|
315
315
|
"getrandom 0.3.4",
|
|
@@ -326,9 +326,9 @@ dependencies = [
|
|
|
326
326
|
|
|
327
327
|
[[package]]
|
|
328
328
|
name = "parsanol-derive"
|
|
329
|
-
version = "0.
|
|
329
|
+
version = "0.13.0"
|
|
330
330
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
331
|
-
checksum = "
|
|
331
|
+
checksum = "6f1413076b79ef60c0af56973f8bbfddcc6034bd3cb47fbc5205fcd188bc8d79"
|
|
332
332
|
dependencies = [
|
|
333
333
|
"proc-macro2",
|
|
334
334
|
"quote",
|
|
@@ -29,9 +29,9 @@ magnus = { version = "0.9" }
|
|
|
29
29
|
|
|
30
30
|
# parsanol parser library (crates.io; 0.12 carries the rs#174 native
|
|
31
31
|
# backtracking fix: dynamic-dependent memoization + outcome cache)
|
|
32
|
-
# parsanol parser library (crates.io; 0.
|
|
33
|
-
#
|
|
34
|
-
parsanol = { version = "0.
|
|
32
|
+
# parsanol parser library (crates.io; 0.13 carries the rs#172 Trivia
|
|
33
|
+
# atom: native diagnostics skip trivia positions, matching ruby #145)
|
|
34
|
+
parsanol = { version = "0.13.0", features = ["ruby"] }
|
|
35
35
|
|
|
36
36
|
# Logging
|
|
37
37
|
log = "0.4"
|
|
@@ -78,6 +78,13 @@ module Parsanol
|
|
|
78
78
|
serialize_custom_ref(atom)
|
|
79
79
|
when Parsanol::Atoms::Dynamic
|
|
80
80
|
serialize_dynamic(atom)
|
|
81
|
+
when Parsanol::Atoms::Trivia
|
|
82
|
+
# Wire-distinct from Ignore (rs#172): the Rust
|
|
83
|
+
# engine suppresses trivia-position diagnostics by
|
|
84
|
+
# atom kind. Engines predating the Trivia tag
|
|
85
|
+
# reject skip-carrying artifacts loudly (unknown
|
|
86
|
+
# variant), which is the declared gate.
|
|
87
|
+
serialize_trivia(atom)
|
|
81
88
|
when Parsanol::Atoms::Ignored
|
|
82
89
|
serialize_ignored(atom)
|
|
83
90
|
else
|
|
@@ -276,6 +283,15 @@ module Parsanol
|
|
|
276
283
|
"parse this grammar with mode: :ruby"
|
|
277
284
|
end
|
|
278
285
|
|
|
286
|
+
def serialize_trivia(atom)
|
|
287
|
+
inner_id = serialize_atom(atom.wrapped_atom)
|
|
288
|
+
{
|
|
289
|
+
"Trivia" => {
|
|
290
|
+
"atom" => inner_id,
|
|
291
|
+
},
|
|
292
|
+
}
|
|
293
|
+
end
|
|
294
|
+
|
|
279
295
|
def serialize_ignored(atom)
|
|
280
296
|
inner_id = serialize_atom(atom.wrapped_atom)
|
|
281
297
|
{
|
data/lib/parsanol/parser.rb
CHANGED
|
@@ -49,7 +49,38 @@ module Parsanol
|
|
|
49
49
|
def root(rule_name)
|
|
50
50
|
# Remove any existing root method before redefining
|
|
51
51
|
undef_method :root if method_defined?(:root)
|
|
52
|
-
define_method(:root)
|
|
52
|
+
define_method(:root) do
|
|
53
|
+
atom = __send__(rule_name)
|
|
54
|
+
skip_name = self.class.skip_rule_name
|
|
55
|
+
skip_name ? Parsanol::Skip.inject(self, atom, skip_name)[0] : atom
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Declares the grammar's skip rule (parsanol-ruby#134 DSL
|
|
60
|
+
# surface): an optional match of the named rule is injected
|
|
61
|
+
# before every terminal of the root's rule tree, and the entry
|
|
62
|
+
# takes leading and trailing skips. The referenced rule (and
|
|
63
|
+
# everything it references) builds without injection; it must be
|
|
64
|
+
# non-nullable. Order-independent with +root+.
|
|
65
|
+
#
|
|
66
|
+
# @example
|
|
67
|
+
# class MyParser < Parsanol::Parser
|
|
68
|
+
# rule(:spaces) { match(/[ \t]/).repeat(1) }
|
|
69
|
+
# rule(:word) { match(/[a-z]/).repeat(1).as(:w) }
|
|
70
|
+
# rule(:doc) { word >> str(",") >> word }
|
|
71
|
+
# skip :spaces
|
|
72
|
+
# root :doc
|
|
73
|
+
# end
|
|
74
|
+
#
|
|
75
|
+
def skip(rule_name)
|
|
76
|
+
@skip_rule_name = rule_name
|
|
77
|
+
nil
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# The declared skip rule name, or nil (class-level; subclasses
|
|
81
|
+
# inherit unless they declare their own).
|
|
82
|
+
def skip_rule_name
|
|
83
|
+
@skip_rule_name
|
|
53
84
|
end
|
|
54
85
|
end
|
|
55
86
|
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Parsanol
|
|
4
|
+
# Skip-rule trivia injection for Ruby-DSL grammars (the PARG surface
|
|
5
|
+
# compiles the same semantics into artifacts; see
|
|
6
|
+
# Parsanol::PARG::Compiler#inject_skip_trivia). A parser declares
|
|
7
|
+
# `skip :trivia`; the resolved rule's atom becomes the injected
|
|
8
|
+
# optional trivia wrapper before every terminal of every rule body,
|
|
9
|
+
# with the declaration's transitive closure exempt (the skip atom
|
|
10
|
+
# must not contain itself).
|
|
11
|
+
module Skip
|
|
12
|
+
# +skip_rule_name+ resolves against the parser instance's rule
|
|
13
|
+
# methods. Returns [injected_root, skip_atom] with the wrapper
|
|
14
|
+
# built and the lint applied, or raises GrammarError for a
|
|
15
|
+
# nullable skip.
|
|
16
|
+
def self.inject(parser_instance, root_atom, skip_rule_name)
|
|
17
|
+
skip_atom = parser_instance.__send__(skip_rule_name)
|
|
18
|
+
skip_atom = skip_atom.parslet while skip_atom.is_a?(Atoms::Entity)
|
|
19
|
+
|
|
20
|
+
if VM::Compiler.new.nullable?(skip_atom)
|
|
21
|
+
raise GrammarError,
|
|
22
|
+
"skip rule must be non-nullable: a skip that can match " \
|
|
23
|
+
"empty loops forever at injection points"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
injector = Injector.new(skip_atom)
|
|
27
|
+
[injector.inject(root_atom, entry: true), skip_atom]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# The injection walk. Mirrors the PARG compiler's semantics:
|
|
31
|
+
# sequences interleave the wrapper before every child, alternatives
|
|
32
|
+
# and repetitions descend, named captures descend (spans stay
|
|
33
|
+
# clean — the wrapper is Trivia: Ignored + diagnostics
|
|
34
|
+
# transparency), bare terminals gain a leading wrapper, and the
|
|
35
|
+
# entry root takes leading and trailing wrappers.
|
|
36
|
+
class Injector
|
|
37
|
+
def initialize(skip_atom)
|
|
38
|
+
@skip_atom = skip_atom
|
|
39
|
+
@wrapper_ids = {}.compare_by_identity
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def inject(atom, entry: false)
|
|
43
|
+
injected = walk(atom, {})
|
|
44
|
+
return injected unless entry
|
|
45
|
+
|
|
46
|
+
Atoms::Sequence.new(maybe, injected, maybe)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def maybe
|
|
52
|
+
@maybe ||= begin
|
|
53
|
+
wrapper = Atoms::Trivia.new(Atoms::Repetition.new(@skip_atom, 0, 1))
|
|
54
|
+
@wrapper_ids[wrapper] = true
|
|
55
|
+
wrapper
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def wrapper?(atom)
|
|
60
|
+
@wrapper_ids.key?(atom)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def walk(atom, visited)
|
|
64
|
+
return atom if visited.key?(atom)
|
|
65
|
+
return atom if atom.equal?(@skip_atom)
|
|
66
|
+
|
|
67
|
+
visited = visited.merge(atom => true)
|
|
68
|
+
case atom
|
|
69
|
+
when Atoms::Sequence
|
|
70
|
+
out = []
|
|
71
|
+
atom.parslets.each do |child|
|
|
72
|
+
injected = walk(child, visited)
|
|
73
|
+
out << maybe unless wrapper?(injected) || injected.is_a?(Atoms::Trivia)
|
|
74
|
+
out << injected
|
|
75
|
+
end
|
|
76
|
+
out.length == 1 ? out.first : Atoms::Sequence.new(*out)
|
|
77
|
+
when Atoms::Alternative
|
|
78
|
+
Atoms::Alternative.new(*atom.alternatives.map { |a| walk(a, visited) })
|
|
79
|
+
when Atoms::Repetition
|
|
80
|
+
if wrapper?(atom)
|
|
81
|
+
atom
|
|
82
|
+
else
|
|
83
|
+
Atoms::Repetition.new(walk(atom.parslet, visited), atom.min, atom.max,
|
|
84
|
+
atom.result_tag)
|
|
85
|
+
end
|
|
86
|
+
when Atoms::Named
|
|
87
|
+
Atoms::Named.new(walk(atom.parslet, visited), atom.name)
|
|
88
|
+
when Atoms::Lookahead
|
|
89
|
+
Atoms::Lookahead.new(walk(atom.bound_parslet, visited), atom.positive)
|
|
90
|
+
when Atoms::Ignored
|
|
91
|
+
Atoms::Ignored.new(walk(atom.wrapped_atom, visited))
|
|
92
|
+
when Atoms::Entity
|
|
93
|
+
# Rule bodies own their injection when the grammar declares
|
|
94
|
+
# skip on every rule (PARG); in the DSL surface the root tree
|
|
95
|
+
# is walked once, so descend through resolved bodies with the
|
|
96
|
+
# identity visited-set guarding recursion.
|
|
97
|
+
begin
|
|
98
|
+
walk(atom.parslet, visited)
|
|
99
|
+
rescue StandardError
|
|
100
|
+
atom
|
|
101
|
+
end
|
|
102
|
+
when Atoms::Str, Atoms::Re
|
|
103
|
+
Atoms::Sequence.new(maybe, atom)
|
|
104
|
+
else
|
|
105
|
+
atom
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
data/lib/parsanol/version.rb
CHANGED
data/lib/parsanol.rb
CHANGED
|
@@ -290,6 +290,7 @@ module Parsanol
|
|
|
290
290
|
autoload :Pattern, "parsanol/pattern"
|
|
291
291
|
autoload :Transform, "parsanol/transform"
|
|
292
292
|
autoload :VM, "parsanol/vm"
|
|
293
|
+
autoload :Skip, "parsanol/skip"
|
|
293
294
|
autoload :Parser, "parsanol/parser"
|
|
294
295
|
autoload :ErrorReporter, "parsanol/error_reporter"
|
|
295
296
|
autoload :Scope, "parsanol/scope"
|
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.
|
|
4
|
+
version: 1.3.72
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -155,6 +155,7 @@ files:
|
|
|
155
155
|
- lib/parsanol/rig/rspec.rb
|
|
156
156
|
- lib/parsanol/rope.rb
|
|
157
157
|
- lib/parsanol/scope.rb
|
|
158
|
+
- lib/parsanol/skip.rb
|
|
158
159
|
- lib/parsanol/slice.rb
|
|
159
160
|
- lib/parsanol/source.rb
|
|
160
161
|
- lib/parsanol/source/line_cache.rb
|