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,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Literal string matcher. Matches an exact sequence of characters.
|
|
4
|
+
#
|
|
5
|
+
# @example Match literal text
|
|
6
|
+
# str('hello') # matches exactly 'hello'
|
|
7
|
+
#
|
|
8
|
+
module Parsanol
|
|
9
|
+
module Atoms
|
|
10
|
+
class Str < Parsanol::Atoms::Base
|
|
11
|
+
# @return [String] the literal to match
|
|
12
|
+
attr_reader :str
|
|
13
|
+
|
|
14
|
+
# Creates a new string matcher.
|
|
15
|
+
#
|
|
16
|
+
# @param text [String, Object] the literal string to match
|
|
17
|
+
def initialize(text)
|
|
18
|
+
super()
|
|
19
|
+
@str = text.to_s
|
|
20
|
+
@byte_size = @str.bytesize
|
|
21
|
+
@char_count = @str.length
|
|
22
|
+
|
|
23
|
+
# Pre-built error messages (frozen)
|
|
24
|
+
@early_eof_msg = "Unexpected end of input"
|
|
25
|
+
@mismatch_msg = "Expected #{@str.inspect}, but got "
|
|
26
|
+
|
|
27
|
+
# Optimization: single-char fast path
|
|
28
|
+
@single_char = (@str if @char_count == 1)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Attempts to match the literal at current position.
|
|
32
|
+
#
|
|
33
|
+
# @param source [Parsanol::Source] input
|
|
34
|
+
# @param context [Parsanol::Atoms::Context] context
|
|
35
|
+
# @param _consume_all [Boolean] ignored
|
|
36
|
+
# @return [Array(Boolean, Object)] result
|
|
37
|
+
def try(source, context, _consume_all)
|
|
38
|
+
# Single-character optimization
|
|
39
|
+
return single_char_match(source, context) if @single_char
|
|
40
|
+
|
|
41
|
+
# Multi-character matching
|
|
42
|
+
multi_char_match(source, context)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# String representation.
|
|
46
|
+
#
|
|
47
|
+
# @param _prec [Integer] unused
|
|
48
|
+
# @return [String]
|
|
49
|
+
def to_s_inner(_prec)
|
|
50
|
+
"'#{@str}'"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Simple atoms don't benefit from caching.
|
|
54
|
+
#
|
|
55
|
+
# @return [Boolean]
|
|
56
|
+
def cached?
|
|
57
|
+
false
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Produces flat results (Slice).
|
|
61
|
+
#
|
|
62
|
+
# @return [Boolean]
|
|
63
|
+
def flat?
|
|
64
|
+
true
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# FIRST set is this atom itself.
|
|
68
|
+
#
|
|
69
|
+
# @return [Set]
|
|
70
|
+
def compute_first_set
|
|
71
|
+
Set.new([self])
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
# Fast path for single-character strings.
|
|
77
|
+
def single_char_match(source, context)
|
|
78
|
+
if source.chars_left < 1
|
|
79
|
+
return context.err(self, source,
|
|
80
|
+
@early_eof_msg)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
pos = source.pos
|
|
84
|
+
slice = source.consume(1)
|
|
85
|
+
|
|
86
|
+
return ok(slice) if slice.content == @single_char
|
|
87
|
+
|
|
88
|
+
source.bytepos = pos
|
|
89
|
+
context.err_at(self, source, [@mismatch_msg, slice], pos)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Standard path for multi-character strings.
|
|
93
|
+
def multi_char_match(source, context)
|
|
94
|
+
if source.chars_left < @char_count
|
|
95
|
+
return context.err(self, source,
|
|
96
|
+
@early_eof_msg)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
pos = source.pos
|
|
100
|
+
slice = source.consume(@char_count)
|
|
101
|
+
|
|
102
|
+
return ok(slice) if slice.content == @str
|
|
103
|
+
|
|
104
|
+
source.bytepos = pos
|
|
105
|
+
context.err_at(self, source, [@mismatch_msg, slice], pos)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Visitor pattern for traversing parser atom trees.
|
|
4
|
+
# Each atom type dispatches to a corresponding visitor method.
|
|
5
|
+
module Parsanol
|
|
6
|
+
module Atoms
|
|
7
|
+
class Base
|
|
8
|
+
# Accepts visitor and dispatches to type-specific method.
|
|
9
|
+
# Override in subclasses.
|
|
10
|
+
#
|
|
11
|
+
# @param visitor [Object] implements visit_* methods
|
|
12
|
+
# @raise [NotImplementedError] if not overridden
|
|
13
|
+
def accept(visitor)
|
|
14
|
+
raise NotImplementedError,
|
|
15
|
+
"Missing #accept in #{self.class.name}"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class Str
|
|
20
|
+
# Dispatches to visitor's visit_str.
|
|
21
|
+
#
|
|
22
|
+
# @param visitor [Object] visitor object
|
|
23
|
+
def accept(visitor)
|
|
24
|
+
visitor.visit_str(str)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class Entity
|
|
29
|
+
# Dispatches to visitor's visit_entity.
|
|
30
|
+
#
|
|
31
|
+
# @param visitor [Object] visitor object
|
|
32
|
+
def accept(visitor)
|
|
33
|
+
visitor.visit_entity(rule_name, @body)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class Named
|
|
38
|
+
# Dispatches to visitor's visit_named.
|
|
39
|
+
#
|
|
40
|
+
# @param visitor [Object] visitor object
|
|
41
|
+
def accept(visitor)
|
|
42
|
+
visitor.visit_named(name, parslet)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class Sequence
|
|
47
|
+
# Dispatches to visitor's visit_sequence.
|
|
48
|
+
#
|
|
49
|
+
# @param visitor [Object] visitor object
|
|
50
|
+
def accept(visitor)
|
|
51
|
+
visitor.visit_sequence(parslets)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class Repetition
|
|
56
|
+
# Dispatches to visitor's visit_repetition.
|
|
57
|
+
#
|
|
58
|
+
# @param visitor [Object] visitor object
|
|
59
|
+
def accept(visitor)
|
|
60
|
+
visitor.visit_repetition(result_tag, min, max, parslet)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class Alternative
|
|
65
|
+
# Dispatches to visitor's visit_alternative.
|
|
66
|
+
#
|
|
67
|
+
# @param visitor [Object] visitor object
|
|
68
|
+
def accept(visitor)
|
|
69
|
+
visitor.visit_alternative(alternatives)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
class Lookahead
|
|
74
|
+
# Dispatches to visitor's visit_lookahead.
|
|
75
|
+
#
|
|
76
|
+
# @param visitor [Object] visitor object
|
|
77
|
+
def accept(visitor)
|
|
78
|
+
visitor.visit_lookahead(positive, bound_parslet)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class Re
|
|
83
|
+
# Dispatches to visitor's visit_re.
|
|
84
|
+
#
|
|
85
|
+
# @param visitor [Object] visitor object
|
|
86
|
+
def accept(visitor)
|
|
87
|
+
visitor.visit_re(match)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Parser atoms - the building blocks for grammars.
|
|
4
|
+
# Each atom type handles a specific parsing primitive or combinator.
|
|
5
|
+
module Parsanol
|
|
6
|
+
module Atoms
|
|
7
|
+
# Precedence levels for pretty-printing.
|
|
8
|
+
# Higher values bind more loosely.
|
|
9
|
+
module Precedence
|
|
10
|
+
ATOM = 1 # literals, entities
|
|
11
|
+
LOOKAHEAD = 2 # &expr, !expr
|
|
12
|
+
REPETITION = 3 # expr*, expr+, expr?
|
|
13
|
+
SEQUENCE = 4 # expr expr
|
|
14
|
+
CHOICE = 5 # expr | expr
|
|
15
|
+
TOP = 6 # outer level
|
|
16
|
+
|
|
17
|
+
# Backward-compatible aliases
|
|
18
|
+
BASE = ATOM
|
|
19
|
+
ALTERNATE = CHOICE
|
|
20
|
+
OUTER = TOP
|
|
21
|
+
end
|
|
22
|
+
|
|
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"
|
|
43
|
+
# Load visitor pattern (must be after all atom classes)
|
|
44
|
+
require "parsanol/atoms/visitor"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Parsanol
|
|
4
|
+
# A fixed-size buffer for efficient array operations.
|
|
5
|
+
#
|
|
6
|
+
# Buffer wraps an array with a logical size separate from capacity.
|
|
7
|
+
# This allows reusing buffers without reallocating arrays, reducing GC pressure.
|
|
8
|
+
#
|
|
9
|
+
# == Usage
|
|
10
|
+
#
|
|
11
|
+
# buffer = Buffer.new(capacity: 10)
|
|
12
|
+
# buffer.push("a")
|
|
13
|
+
# buffer.push("b")
|
|
14
|
+
# buffer.size # => 2
|
|
15
|
+
# buffer.to_a # => ["a", "b"]
|
|
16
|
+
# buffer.clear!
|
|
17
|
+
# buffer.size # => 0 (but capacity still 10)
|
|
18
|
+
#
|
|
19
|
+
# == Size vs Capacity
|
|
20
|
+
#
|
|
21
|
+
# - Size: Number of elements logically in the buffer
|
|
22
|
+
# - Capacity: Maximum elements before reallocation
|
|
23
|
+
#
|
|
24
|
+
# Reusing buffers maintains capacity while resetting size.
|
|
25
|
+
#
|
|
26
|
+
class Buffer
|
|
27
|
+
include Resettable
|
|
28
|
+
|
|
29
|
+
# @return [Integer] Logical size (number of elements)
|
|
30
|
+
attr_reader :size
|
|
31
|
+
|
|
32
|
+
# @return [Integer] Maximum capacity before growth
|
|
33
|
+
attr_reader :capacity
|
|
34
|
+
|
|
35
|
+
# @return [Array] Underlying array storage
|
|
36
|
+
attr_reader :storage
|
|
37
|
+
|
|
38
|
+
# Initialize a new buffer with specified capacity.
|
|
39
|
+
#
|
|
40
|
+
# @param capacity [Integer] Initial capacity (default: 10)
|
|
41
|
+
#
|
|
42
|
+
def initialize(capacity: 10)
|
|
43
|
+
@capacity = capacity
|
|
44
|
+
@storage = Array.new(capacity)
|
|
45
|
+
@size = 0
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Add an element to the buffer.
|
|
49
|
+
#
|
|
50
|
+
# Grows buffer if needed, but this should be rare with proper size classes.
|
|
51
|
+
#
|
|
52
|
+
# @param element [Object] Element to add
|
|
53
|
+
# @return [self] For method chaining
|
|
54
|
+
#
|
|
55
|
+
def push(element)
|
|
56
|
+
grow! if @size >= @capacity
|
|
57
|
+
@storage[@size] = element
|
|
58
|
+
@size += 1
|
|
59
|
+
self
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
alias << push
|
|
63
|
+
|
|
64
|
+
# Get element at index.
|
|
65
|
+
#
|
|
66
|
+
# @param index [Integer] Zero-based index
|
|
67
|
+
# @return [Object] Element at index, or nil if out of bounds
|
|
68
|
+
#
|
|
69
|
+
def [](index)
|
|
70
|
+
return nil if index >= @size
|
|
71
|
+
|
|
72
|
+
@storage[index]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Set element at index.
|
|
76
|
+
#
|
|
77
|
+
# @param index [Integer] Zero-based index
|
|
78
|
+
# @param value [Object] Value to set
|
|
79
|
+
#
|
|
80
|
+
def []=(index, value)
|
|
81
|
+
@storage[index] = value if index < @size
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Convert buffer to array (creates new array slice of logical size).
|
|
85
|
+
#
|
|
86
|
+
# @return [Array] Array containing elements [0...size]
|
|
87
|
+
#
|
|
88
|
+
def to_a
|
|
89
|
+
@storage[0...@size]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Clear the buffer (reset logical size, keep capacity).
|
|
93
|
+
#
|
|
94
|
+
# @return [self] For method chaining
|
|
95
|
+
#
|
|
96
|
+
def clear!
|
|
97
|
+
# Clear references for GC (keep capacity)
|
|
98
|
+
@size.upto(@capacity - 1) { |i| @storage[i] = nil }
|
|
99
|
+
@size = 0
|
|
100
|
+
self
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Check if buffer is empty.
|
|
104
|
+
#
|
|
105
|
+
# @return [Boolean] true if size is zero
|
|
106
|
+
#
|
|
107
|
+
def empty?
|
|
108
|
+
@size.zero?
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Reset protocol for ObjectPool compatibility.
|
|
112
|
+
# Delegates to clear! for buffer cleanup.
|
|
113
|
+
#
|
|
114
|
+
# @return [self] For method chaining
|
|
115
|
+
#
|
|
116
|
+
def reset!
|
|
117
|
+
clear!
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
# Grow buffer capacity (double it).
|
|
123
|
+
# Should be rare with proper size class selection.
|
|
124
|
+
#
|
|
125
|
+
def grow!
|
|
126
|
+
new_capacity = @capacity * 2
|
|
127
|
+
new_storage = Array.new(new_capacity)
|
|
128
|
+
@storage.each_with_index { |elem, i| new_storage[i] = elem }
|
|
129
|
+
@storage = new_storage
|
|
130
|
+
@capacity = new_capacity
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|