paco 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/.rbnext/2.3/paco/context.rb +51 -0
- data/lib/.rbnext/2.3/paco/index.rb +15 -0
- data/lib/.rbnext/2.3/paco/parse_error.rb +30 -0
- data/lib/.rbnext/2.5/paco/combinators.rb +174 -0
- data/lib/.rbnext/2.5/paco/parser.rb +180 -0
- data/lib/.rbnext/2.5/paco/rspec/parse_matcher.rb +47 -0
- data/lib/.rbnext/2.6/paco/context.rb +51 -0
- data/lib/paco/version.rb +1 -1
- metadata +10 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d72f440d9727dc302366b842d7a7bf3d3d2ac84f4e13b41ce8954dad4b971f0
|
4
|
+
data.tar.gz: 81b0e2b377c35060db0416472115754cd62d45efda5f302265003b1340e639b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3aaa2ce601f6da71e1d24a0a13bee121abc3c6d52b9120e6795b57e5880cefbb2fae9c24b69f7e6284a54913b293e25c6d3fcbe7e503105e782fd84817e13413
|
7
|
+
data.tar.gz: fed4af90111069f4c39f4ce6ed85b72480aaf6bf10bcf4f06e8701a60f037095638647b423282a7dfe00ae0f1e8c17e94b907368879394b0f664ae06a812a47e
|
data/CHANGELOG.md
CHANGED
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "paco/callstack"
|
4
|
+
require "paco/index"
|
5
|
+
|
6
|
+
module Paco
|
7
|
+
class Context
|
8
|
+
attr_reader :input, :last_pos, :callstack
|
9
|
+
attr_accessor :pos
|
10
|
+
|
11
|
+
def initialize(input, pos: 0, with_callstack: false)
|
12
|
+
@input = input
|
13
|
+
@pos = pos
|
14
|
+
@callstack = Callstack.new if with_callstack
|
15
|
+
end
|
16
|
+
|
17
|
+
def read(n)
|
18
|
+
input[pos, n]
|
19
|
+
end
|
20
|
+
|
21
|
+
def read_all
|
22
|
+
input[pos..-1]
|
23
|
+
end
|
24
|
+
|
25
|
+
def eof?
|
26
|
+
pos >= input.length
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param [Integer] from
|
30
|
+
# @return [Paco::Index]
|
31
|
+
def index(from = nil)
|
32
|
+
Index.calculate(input: input, pos: from || pos)
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param [Paco::Parser] parser
|
36
|
+
def failure_parse(parser)
|
37
|
+
((((__safe_lvar__ = @callstack) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.failure(pos: pos, parser: parser.desc))
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param [Paco::Parser] parser
|
41
|
+
def start_parse(parser)
|
42
|
+
((((__safe_lvar__ = @callstack) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.start(pos: pos, parser: parser.desc))
|
43
|
+
end
|
44
|
+
|
45
|
+
# @param [Object] result
|
46
|
+
# @param [Paco::Parser] parser
|
47
|
+
def success_parse(result, parser)
|
48
|
+
((((__safe_lvar__ = @callstack) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.success(pos: pos, result: result, parser: parser.desc))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Paco
|
2
|
+
Index = Struct.new(:pos, :line, :column) do
|
3
|
+
# @param [String] input
|
4
|
+
# @param [Integer] pos
|
5
|
+
def self.calculate(input:, pos:)
|
6
|
+
raise ArgumentError, "`pos` must be a non-negative integer" if pos < 0
|
7
|
+
raise ArgumentError, "`pos` is grater then input length" if pos > input.length
|
8
|
+
|
9
|
+
lines = input[0..pos].lines
|
10
|
+
line = lines.empty? ? 1 : lines.length
|
11
|
+
column = ((((__safe_lvar__ = lines.last) || true) && (!__safe_lvar__.nil? || nil)) && __safe_lvar__.length) || 1
|
12
|
+
new(pos, line, column)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Paco
|
4
|
+
class Error < StandardError; end
|
5
|
+
|
6
|
+
class ParseError < Error
|
7
|
+
attr_reader :ctx, :pos, :expected
|
8
|
+
|
9
|
+
# @param [Paco::Context] ctx
|
10
|
+
def initialize(ctx, expected)
|
11
|
+
@ctx = ctx
|
12
|
+
@pos = ctx.pos
|
13
|
+
@expected = expected
|
14
|
+
end
|
15
|
+
|
16
|
+
def callstack
|
17
|
+
ctx.callstack
|
18
|
+
end
|
19
|
+
|
20
|
+
def message
|
21
|
+
index = ctx.index(pos)
|
22
|
+
<<-MSG
|
23
|
+
\nParsing error
|
24
|
+
line #{index.line}, column #{index.column}:
|
25
|
+
unexpected #{ctx.eof? ? "end of file" : ctx.input[pos].inspect}
|
26
|
+
expecting #{expected}
|
27
|
+
MSG
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "paco/combinators/char"
|
4
|
+
require "paco/memoizer"
|
5
|
+
|
6
|
+
module Paco
|
7
|
+
module Combinators
|
8
|
+
include Char
|
9
|
+
extend Char
|
10
|
+
|
11
|
+
module_function
|
12
|
+
|
13
|
+
# Returns a parser that runs the passed `parser` without consuming the input, and
|
14
|
+
# returns `null` if the passed `parser` _does not match_ the input. Fails otherwise.
|
15
|
+
# @param [Paco::Parser] parser
|
16
|
+
# @return [Paco::Parser]
|
17
|
+
def not_followed_by(parser)
|
18
|
+
Parser.new("not #{parser.desc}") do |ctx, pars|
|
19
|
+
start_pos = ctx.pos
|
20
|
+
begin
|
21
|
+
parser._parse(ctx)
|
22
|
+
rescue ParseError
|
23
|
+
nil
|
24
|
+
else
|
25
|
+
pars.failure(ctx)
|
26
|
+
ensure
|
27
|
+
ctx.pos = start_pos
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns a parser that doesn't consume any input and always returns `result`.
|
33
|
+
# @return [Paco::Parser]
|
34
|
+
def succeed(result)
|
35
|
+
Parser.new("succeed(#{result})") { result }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns a parser that doesn't consume any input and always fails with passed `message`.
|
39
|
+
# @param [String] message
|
40
|
+
# @return [Paco::Parser]
|
41
|
+
def failed(message)
|
42
|
+
Parser.new(message) { |ctx, parser| parser.failure(ctx) }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns a parser that runs the passed `parser` without consuming the input,
|
46
|
+
# and returns empty string.
|
47
|
+
# @param [Paco::Parser] parser
|
48
|
+
# @return [Paco::Parser]
|
49
|
+
def lookahead(parser)
|
50
|
+
Parser.new("lookahead(#{parser.desc})") do |ctx|
|
51
|
+
start_pos = ctx.pos
|
52
|
+
parser._parse(ctx)
|
53
|
+
ctx.pos = start_pos
|
54
|
+
""
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Accepts any number of parsers, and returns a parser that returns the value of the first parser that succeeds, backtracking in between.
|
59
|
+
# @param [Array<Paco::Parser>] parsers
|
60
|
+
# @return [Paco::Parser]
|
61
|
+
def alt(*parsers)
|
62
|
+
raise ArgumentError, "no parsers specified" if parsers.empty?
|
63
|
+
|
64
|
+
Parser.new("alt(#{parsers.map(&:desc).join(", ")})") do |ctx|
|
65
|
+
result = nil
|
66
|
+
last_error = nil
|
67
|
+
start_pos = ctx.pos
|
68
|
+
parsers.each do |pars|
|
69
|
+
begin;break result = {value: pars._parse(ctx)}
|
70
|
+
rescue ParseError => e
|
71
|
+
last_error = e
|
72
|
+
ctx.pos = start_pos
|
73
|
+
next;end
|
74
|
+
end
|
75
|
+
raise last_error unless result
|
76
|
+
result[:value]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Accepts one or more parsers, and returns a parser that expects them
|
81
|
+
# to match in order, returns an array of all their results.
|
82
|
+
# If `block` specified, passes results of the `parses` as an arguments
|
83
|
+
# to a `block`, and at the end returns its result.
|
84
|
+
# @param [Array<Paco::Parser>] parsers
|
85
|
+
# @return [Paco::Parser]
|
86
|
+
def seq(*parsers)
|
87
|
+
raise ArgumentError, "no parsers specified" if parsers.empty?
|
88
|
+
|
89
|
+
result = Parser.new("seq(#{parsers.map(&:desc).join(", ")})") do |ctx|
|
90
|
+
start_pos = ctx.pos
|
91
|
+
begin
|
92
|
+
parsers.map { |parser| parser._parse(ctx) }
|
93
|
+
rescue ParseError => e
|
94
|
+
ctx.pos = start_pos
|
95
|
+
raise e
|
96
|
+
end
|
97
|
+
end
|
98
|
+
return result unless block_given?
|
99
|
+
|
100
|
+
result.fmap { |results| yield(*results) }
|
101
|
+
end
|
102
|
+
|
103
|
+
# Accepts a block that returns a parser, which is evaluated the first time the parser is used.
|
104
|
+
# This is useful for referencing parsers that haven't yet been defined, and for implementing recursive parsers.
|
105
|
+
# @return [Paco::Parser]
|
106
|
+
def lazy(desc = "", &block)
|
107
|
+
Parser.new(desc) { |ctx| block.call._parse(ctx) }
|
108
|
+
end
|
109
|
+
|
110
|
+
# Returns a parser that expects zero or more matches for `parser`,
|
111
|
+
# separated by the parser `separator`. Returns an array of `parser` results.
|
112
|
+
# @param [Paco::Parser] parser
|
113
|
+
# @param [Paco::Parser] separator
|
114
|
+
# @return [Paco::Parser]
|
115
|
+
def sep_by(parser, separator)
|
116
|
+
alt(sep_by!(parser, separator), succeed([]))
|
117
|
+
.with_desc("sep_by(#{parser.desc}, #{separator.desc})")
|
118
|
+
end
|
119
|
+
|
120
|
+
# Returns a parser that expects one or more matches for `parser`,
|
121
|
+
# separated by the parser `separator`. Returns an array of `parser` results.
|
122
|
+
# @param [Paco::Parser] parser
|
123
|
+
# @param [Paco::Parser] separator
|
124
|
+
# @return [Paco::Parser]
|
125
|
+
def sep_by!(parser, separator)
|
126
|
+
seq(parser, many(separator.next(parser))) { |first, arr| [first] + arr }
|
127
|
+
.with_desc("sep_by!(#{parser.desc}, #{separator.desc})")
|
128
|
+
end
|
129
|
+
alias_method :sep_by_1, :sep_by!
|
130
|
+
|
131
|
+
# Expects the parser `before` before `parser` and `after` after `parser. Returns the result of the parser.
|
132
|
+
# @param [Paco::Parser] before
|
133
|
+
# @param [Paco::Parser] after
|
134
|
+
# @param [Paco::Parser] parser
|
135
|
+
# @return [Paco::Parser]
|
136
|
+
def wrap(before, after, parser)
|
137
|
+
before.next(parser).skip(after)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Expects `parser` zero or more times, and returns an array of the results.
|
141
|
+
# @param [Paco::Parser] parser
|
142
|
+
# @return [Paco::Parser]
|
143
|
+
def many(parser)
|
144
|
+
Parser.new("many(#{parser.desc})") do |ctx|
|
145
|
+
results = []
|
146
|
+
loop do
|
147
|
+
begin;results << parser._parse(ctx)
|
148
|
+
rescue ParseError
|
149
|
+
break;end
|
150
|
+
end
|
151
|
+
results
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Returns parser that returns result of the passed `parser` or nil if `parser` fails.
|
156
|
+
# @param [Paco::Parser] parser
|
157
|
+
# @return [Paco::Parser]
|
158
|
+
def optional(parser)
|
159
|
+
alt(parser, succeed(nil))
|
160
|
+
end
|
161
|
+
|
162
|
+
# Returns parser that returns `Paco::Index` representing
|
163
|
+
# the current offset into the parse without consuming the input.
|
164
|
+
# @return [Paco::Parser]
|
165
|
+
def index
|
166
|
+
Parser.new { |ctx| ctx.index }
|
167
|
+
end
|
168
|
+
|
169
|
+
# Helper used for memoization
|
170
|
+
def memoize(&block)
|
171
|
+
Memoizer.memoize(block.source_location, &block)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "paco/combinators"
|
4
|
+
|
5
|
+
module Paco
|
6
|
+
class Parser
|
7
|
+
attr_reader :desc
|
8
|
+
|
9
|
+
# @param [String] desc
|
10
|
+
def initialize(desc = "", &block)
|
11
|
+
@desc = desc
|
12
|
+
@block = block
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [String] desc
|
16
|
+
# @return [Paco::Parser]
|
17
|
+
def with_desc(desc)
|
18
|
+
@desc = desc
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param [String, Paco::Context] input
|
23
|
+
# @param [true, false] with_callstack
|
24
|
+
def parse(input, with_callstack: false)
|
25
|
+
ctx = input.is_a?(Context) ? input : Context.new(input, with_callstack: with_callstack)
|
26
|
+
skip(Paco::Combinators.eof)._parse(ctx)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param [Paco::Context] ctx
|
30
|
+
def _parse(ctx)
|
31
|
+
ctx.start_parse(self)
|
32
|
+
res = @block.call(ctx, self)
|
33
|
+
ctx.success_parse(res, self)
|
34
|
+
res
|
35
|
+
end
|
36
|
+
|
37
|
+
# Raises ParseError
|
38
|
+
# @param [Paco::Context] ctx
|
39
|
+
# @raise [Paco::ParseError]
|
40
|
+
def failure(ctx)
|
41
|
+
ctx.failure_parse(self)
|
42
|
+
raise ParseError.new(ctx, desc), "", []
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns a new parser which tries `parser`, and if it fails uses `other`.
|
46
|
+
# @param [Paco::Parser] other
|
47
|
+
# @return [Paco::Parser]
|
48
|
+
def or(other)
|
49
|
+
Parser.new("or(#{desc}, #{other.desc})") do |ctx|
|
50
|
+
begin;_parse(ctx)
|
51
|
+
rescue ParseError
|
52
|
+
other._parse(ctx);end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
alias_method :|, :or
|
56
|
+
|
57
|
+
# Expects `other` parser to follow `parser`, but returns only the value of `parser`.
|
58
|
+
# @param [Poco::Parser] other
|
59
|
+
# @return [Paco::Parser]
|
60
|
+
def skip(other)
|
61
|
+
Paco::Combinators.seq(self, other).fmap { |results| results[0] }.with_desc("#{desc}.skip(#{other.desc})")
|
62
|
+
end
|
63
|
+
alias_method :<, :skip
|
64
|
+
|
65
|
+
# Expects `other` parser to follow `parser`, but returns only the value of `other` parser.
|
66
|
+
# @param [Poco::Parser] other
|
67
|
+
# @return [Paco::Parser]
|
68
|
+
def next(other)
|
69
|
+
Paco::Combinators.seq(self, other).fmap { |results| results[1] }
|
70
|
+
.with_desc("#{desc}.next(#{other.desc})")
|
71
|
+
end
|
72
|
+
alias_method :>, :next
|
73
|
+
|
74
|
+
# Transforms the output of `parser` with the given block.
|
75
|
+
# @return [Paco::Parser]
|
76
|
+
def fmap(&block)
|
77
|
+
Parser.new("#{desc}.fmap") do |ctx|
|
78
|
+
block.call(_parse(ctx))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns a new parser which tries `parser`, and on success
|
83
|
+
# calls the `block` with the result of the parse, which is expected
|
84
|
+
# to return another parser, which will be tried next. This allows you
|
85
|
+
# to dynamically decide how to continue the parse, which is impossible
|
86
|
+
# with the other Paco::Combinators.
|
87
|
+
# @return [Paco::Parser]
|
88
|
+
def bind(&block)
|
89
|
+
Parser.new("#{desc}.bind") do |ctx|
|
90
|
+
block.call(_parse(ctx))._parse(ctx)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
alias_method :chain, :bind
|
94
|
+
|
95
|
+
# Expects `parser` zero or more times, and returns an array of the results.
|
96
|
+
# @return [Paco::Parser]
|
97
|
+
def many
|
98
|
+
Paco::Combinators.many(self)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns a new parser with the same behavior, but which returns passed `value`.
|
102
|
+
# @return [Paco::Parser]
|
103
|
+
def result(value)
|
104
|
+
fmap { value }
|
105
|
+
end
|
106
|
+
|
107
|
+
# Returns a new parser which tries `parser` and, if it fails, returns `value` without consuming any input.
|
108
|
+
# @return [Paco::Parser]
|
109
|
+
def fallback(value)
|
110
|
+
self.or(Paco::Combinators.succeed(value))
|
111
|
+
end
|
112
|
+
|
113
|
+
# Expects `other` parser before and after `parser`, and returns the result of the parser.
|
114
|
+
# @param [Paco::Parser] other
|
115
|
+
# @return [Paco::Parser]
|
116
|
+
def trim(other)
|
117
|
+
other.next(self).skip(other)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Expects the parser `before` before `parser` and `after` after `parser. Returns the result of the parser.
|
121
|
+
# @param [Paco::Parser] before
|
122
|
+
# @param [Paco::Parser] after
|
123
|
+
# @return [Paco::Parser]
|
124
|
+
def wrap(before, after)
|
125
|
+
Paco::Combinators.wrap(before, after, self)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns a parser that runs passed `other` parser without consuming the input, and
|
129
|
+
# returns result of the `parser` if the passed one _does not match_ the input. Fails otherwise.
|
130
|
+
# @param [Paco::Parser] other
|
131
|
+
# @return [Paco::Parser]
|
132
|
+
def not_followed_by(other)
|
133
|
+
skip(Paco::Combinators.not_followed_by(other))
|
134
|
+
end
|
135
|
+
|
136
|
+
# Returns a parser that runs `parser` and concatenate it results with the `separator`.
|
137
|
+
# @param [String] separator
|
138
|
+
# @return [Paco::Parser]
|
139
|
+
def join(separator = "")
|
140
|
+
fmap { |result| result.join(separator) }
|
141
|
+
end
|
142
|
+
|
143
|
+
# Returns a parser that runs `parser` between `min` and `max` times,
|
144
|
+
# and returns an array of the results. When `max` is not specified, `max` = `min`.
|
145
|
+
# @param [Integer] min
|
146
|
+
# @param [Integer] max
|
147
|
+
# @return [Paco::Parser]
|
148
|
+
def times(min, max = nil)
|
149
|
+
max ||= min
|
150
|
+
if min < 0 || max < min
|
151
|
+
raise ArgumentError, "invalid attributes: min `#{min}`, max `#{max}`"
|
152
|
+
end
|
153
|
+
|
154
|
+
Parser.new("#{desc}.times(#{min}, #{max})") do |ctx|
|
155
|
+
results = min.times.map { _parse(ctx) }
|
156
|
+
(max - min).times.each do
|
157
|
+
begin;results << _parse(ctx)
|
158
|
+
rescue ParseError
|
159
|
+
break;end
|
160
|
+
end
|
161
|
+
|
162
|
+
results
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Returns a parser that runs `parser` at least `num` times,
|
167
|
+
# and returns an array of the results.
|
168
|
+
def at_least(num)
|
169
|
+
Paco::Combinators.seq(times(num), many) do |head, rest|
|
170
|
+
head + rest
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Returns a parser that runs `parser` at most `num` times,
|
175
|
+
# and returns an array of the results.
|
176
|
+
def at_most(num)
|
177
|
+
times(0, num)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec::Matchers.define(:parse) do |input|
|
4
|
+
chain :as do |expected_output = nil, &block|
|
5
|
+
@expected = expected_output
|
6
|
+
@block = block
|
7
|
+
end
|
8
|
+
|
9
|
+
chain :fully do
|
10
|
+
@expected = input
|
11
|
+
end
|
12
|
+
|
13
|
+
match do |parser|
|
14
|
+
begin;@result = parser.parse(input)
|
15
|
+
return @block.call(@result) if @block
|
16
|
+
return @expected == @result if defined?(@expected)
|
17
|
+
|
18
|
+
true
|
19
|
+
rescue Paco::ParseError => e
|
20
|
+
@error_message = e.message
|
21
|
+
false;end
|
22
|
+
end
|
23
|
+
|
24
|
+
failure_message do |subject|
|
25
|
+
msg = "expected output of parsing #{input.inspect} with #{subject.inspect} to"
|
26
|
+
was = (@result ? "was #{@result.inspect}" : "raised an error #{@error_message}")
|
27
|
+
return "#{msg} meet block conditions, but it didn't. It #{was}" if @block
|
28
|
+
return "#{msg} equal #{@expected.inspect}, but it #{was}" if defined?(@expected)
|
29
|
+
|
30
|
+
"expected #{subject.inspect} to successfully parse #{input.inspect}, but it #{was}"
|
31
|
+
end
|
32
|
+
|
33
|
+
failure_message_when_negated do |subject|
|
34
|
+
msg = "expected output of parsing #{input.inspect} with #{subject.inspect} not to"
|
35
|
+
return "#{msg} meet block conditions, but it did" if @block
|
36
|
+
return "#{msg} equal #{@expected.inspect}" if defined?(@expected)
|
37
|
+
|
38
|
+
"expected #{subject.inspect} to not parse #{input.inspect}, but it did"
|
39
|
+
end
|
40
|
+
|
41
|
+
description do
|
42
|
+
return "parse #{input.inspect} with block conditions" if @block
|
43
|
+
return "parse #{input.inspect} as #{@expected.inspect}" if defined?(@expected)
|
44
|
+
|
45
|
+
"parse #{input.inspect}"
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "paco/callstack"
|
4
|
+
require "paco/index"
|
5
|
+
|
6
|
+
module Paco
|
7
|
+
class Context
|
8
|
+
attr_reader :input, :last_pos, :callstack
|
9
|
+
attr_accessor :pos
|
10
|
+
|
11
|
+
def initialize(input, pos: 0, with_callstack: false)
|
12
|
+
@input = input
|
13
|
+
@pos = pos
|
14
|
+
@callstack = Callstack.new if with_callstack
|
15
|
+
end
|
16
|
+
|
17
|
+
def read(n)
|
18
|
+
input[pos, n]
|
19
|
+
end
|
20
|
+
|
21
|
+
def read_all
|
22
|
+
input[pos..-1]
|
23
|
+
end
|
24
|
+
|
25
|
+
def eof?
|
26
|
+
pos >= input.length
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param [Integer] from
|
30
|
+
# @return [Paco::Index]
|
31
|
+
def index(from = nil)
|
32
|
+
Index.calculate(input: input, pos: from || pos)
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param [Paco::Parser] parser
|
36
|
+
def failure_parse(parser)
|
37
|
+
@callstack&.failure(pos: pos, parser: parser.desc)
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param [Paco::Parser] parser
|
41
|
+
def start_parse(parser)
|
42
|
+
@callstack&.start(pos: pos, parser: parser.desc)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @param [Object] result
|
46
|
+
# @param [Paco::Parser] parser
|
47
|
+
def success_parse(result, parser)
|
48
|
+
@callstack&.success(pos: pos, result: result, parser: parser.desc)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/paco/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paco
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Svyatoslav Kryukov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-08-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-next-core
|
@@ -34,6 +34,13 @@ files:
|
|
34
34
|
- CHANGELOG.md
|
35
35
|
- LICENSE.txt
|
36
36
|
- README.md
|
37
|
+
- lib/.rbnext/2.3/paco/context.rb
|
38
|
+
- lib/.rbnext/2.3/paco/index.rb
|
39
|
+
- lib/.rbnext/2.3/paco/parse_error.rb
|
40
|
+
- lib/.rbnext/2.5/paco/combinators.rb
|
41
|
+
- lib/.rbnext/2.5/paco/parser.rb
|
42
|
+
- lib/.rbnext/2.5/paco/rspec/parse_matcher.rb
|
43
|
+
- lib/.rbnext/2.6/paco/context.rb
|
37
44
|
- lib/paco.rb
|
38
45
|
- lib/paco/callstack.rb
|
39
46
|
- lib/paco/combinators.rb
|
@@ -70,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
77
|
- !ruby/object:Gem::Version
|
71
78
|
version: '0'
|
72
79
|
requirements: []
|
73
|
-
rubygems_version: 3.
|
80
|
+
rubygems_version: 3.4.8
|
74
81
|
signing_key:
|
75
82
|
specification_version: 4
|
76
83
|
summary: Parser combinator library
|