packrat_parser 0.1.0 → 0.2.0
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/README.md +44 -9
- data/lib/packrat_parser/base.rb +47 -15
- data/lib/packrat_parser/parser.rb +95 -28
- data/lib/packrat_parser/version.rb +1 -1
- metadata +92 -26
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5600ad17276add802741ba1c0bda893d7757dcdab4a22dc8ae10e2b18a928fd9
|
|
4
|
+
data.tar.gz: ac53b87a45954f9d319f0b3298222217c2636fb478281c7596c4d440d81742b2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8ca4a20c8f1f3e4c3063db70c39441b88401bd144c1ac1cc50b29a03ec58b845029d93c3a5879176b454e7b79b0fd47b35193943a327b928a50552c055d46d29
|
|
7
|
+
data.tar.gz: 882cb9d1845f4c06f360f2fd933ff6f84d80c03e749dff3ba5aa6e687fed0b98cdd75a247043913ffbb5f8da89349a4fa6b4dfe0c6d3f5fae4b84a2b669f357c
|
data/README.md
CHANGED
|
@@ -49,9 +49,14 @@ The default Prism parser rejects `for ... then`. The library itself
|
|
|
49
49
|
|
|
50
50
|
## API
|
|
51
51
|
|
|
52
|
-
Subclass `PackratParser`. **Every method you define in the subclass is a
|
|
53
|
-
rule** and must return a parser; rule methods are automatically made lazy
|
|
54
|
-
memoized so they can reference one another (and themselves) recursively.
|
|
52
|
+
Subclass `PackratParser`. **Every public method you define in the subclass is a
|
|
53
|
+
grammar rule** and must return a parser; rule methods are automatically made lazy
|
|
54
|
+
and memoized so they can reference one another (and themselves) recursively.
|
|
55
|
+
|
|
56
|
+
**Private methods are left as ordinary helpers** (not rules), so you can factor
|
|
57
|
+
out plain Ruby logic. Use the `private` section form (`private` on its own line,
|
|
58
|
+
then the defs); `private def foo` is not detected, because the method is still
|
|
59
|
+
public when it is defined.
|
|
55
60
|
|
|
56
61
|
### Building blocks (available inside rule methods)
|
|
57
62
|
|
|
@@ -66,18 +71,26 @@ memoized so they can reference one another (and themselves) recursively.
|
|
|
66
71
|
- `map { |v| new_value }` — transform the result.
|
|
67
72
|
- `filter { |v| bool }` — succeed only when the predicate holds.
|
|
68
73
|
- `a | b` — ordered choice: try `a`, and if it fails try `b`.
|
|
69
|
-
- `a
|
|
70
|
-
yield the pair `[a, b]`.
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
- `a * b` — sequence, keep **both** results (Scala's `~`): run `a` then `b`,
|
|
75
|
+
yield the pair `[a, b]`. `*` reads as a product: the result is the product of
|
|
76
|
+
the two values, just as `|` (choice) is the algebraic sum. Left-associative and
|
|
77
|
+
nesting, so `a * b * c` yields `[[a, b], c]`; Ruby's block-parameter
|
|
78
|
+
destructuring takes them apart the way Scala's `case a ~ b ~ c` does:
|
|
79
|
+
`(a * b * c).map { |(x, y), z| ... }`.
|
|
73
80
|
- `a << b` — sequence, keep the **left** result (Scala's `<~`): run `a` then
|
|
74
81
|
`b`, yield `a`'s value and discard `b`'s.
|
|
75
82
|
- `a >> b` — sequence, keep the **right** result (Scala's `~>`): run `a` then
|
|
76
83
|
`b`, yield `b`'s value and discard `a`'s.
|
|
84
|
+
- `p.rep` — zero or more (Scala's `rep`): yields an array of results (empty when
|
|
85
|
+
there are no matches). Always succeeds.
|
|
86
|
+
- `p.rep1` — one or more (Scala's `rep1`): like `rep` but fails unless `p`
|
|
87
|
+
matches at least once; yields a non-empty array.
|
|
88
|
+
- `p.opt` — optional (Scala's `opt`): yields `p`'s value, or `nil` (consuming
|
|
89
|
+
nothing) when `p` does not match.
|
|
77
90
|
|
|
78
91
|
The arrow direction is a useful mnemonic: `<<`/`>>` keeps whichever side it
|
|
79
92
|
points to. They are handy for discarding punctuation, e.g. `( expr )` is
|
|
80
|
-
`term("(") >> expr << term(")")`. Ruby's precedence (
|
|
93
|
+
`term("(") >> expr << term(")")`. Ruby's precedence (`*` over `<<`/`>>` over
|
|
81
94
|
`|`) means sequencing binds tighter than ordered choice, as you'd want.
|
|
82
95
|
|
|
83
96
|
`flat_map`, `map`, and `filter` are exactly what the `for ... then`
|
|
@@ -114,6 +127,10 @@ The setting is inherited by subclasses, so a base parser can enable it once.
|
|
|
114
127
|
If omitted, the first defined method is used as the start symbol.
|
|
115
128
|
- `Klass.parse(input)` / `Klass.new.parse(input)` — parse, returning the value.
|
|
116
129
|
Raises `PackratParser::ParseError` on failure or leftover input.
|
|
130
|
+
- Parse from any rule, not just the start symbol:
|
|
131
|
+
`Klass.new.number.parse("123")` (call `parse` on the rule) or
|
|
132
|
+
`Klass.new.parse("123", :number)` (pass the start rule). Both apply the same
|
|
133
|
+
full-consumption check and whitespace handling.
|
|
117
134
|
|
|
118
135
|
## Notes / limitations
|
|
119
136
|
|
|
@@ -123,11 +140,29 @@ The setting is inherited by subclasses, so a base parser can enable it once.
|
|
|
123
140
|
- **No implicit whitespace by default.** `term` matches exactly. Enable implicit
|
|
124
141
|
whitespace skipping with `skip_whitespace` (see above) when your grammar needs
|
|
125
142
|
it.
|
|
143
|
+
- **Byte-oriented positions.** The parser tracks byte offsets and matches with
|
|
144
|
+
`byteslice` / `byteindex`, so it stays efficient on UTF-8 input (indexing a
|
|
145
|
+
multibyte string by character is O(n)). Positions are byte offsets throughout,
|
|
146
|
+
including the `pos` reported on a raised `ParseError`.
|
|
126
147
|
|
|
127
148
|
## Running the tests
|
|
128
149
|
|
|
129
150
|
```sh
|
|
151
|
+
rake # or: rake test
|
|
152
|
+
# or directly:
|
|
130
153
|
bin/test
|
|
131
154
|
# or:
|
|
132
|
-
ruby --parser=parse.y -Ilib
|
|
155
|
+
ruby --parser=parse.y -Ilib test/test_packrat_parser.rb
|
|
133
156
|
```
|
|
157
|
+
|
|
158
|
+
The `rake test` task runs the suite under `--parser=parse.y` for you (the test
|
|
159
|
+
grammars need the fork's legacy parser).
|
|
160
|
+
|
|
161
|
+
## Benchmark
|
|
162
|
+
|
|
163
|
+
`benchmark/` compares this library against [racc](https://github.com/ruby/racc)
|
|
164
|
+
(LALR) and [parslet](https://kschiess.github.io/parslet/) (PEG combinators) on
|
|
165
|
+
the same calculator grammar. Run it with `rake bench` (needs `parslet`). Roughly,
|
|
166
|
+
all three are linear on this grammar, with **racc ≫ packrat_parser > parslet** on
|
|
167
|
+
constant factors (packrat_parser ~3× racc, parslet ~8× packrat_parser). See
|
|
168
|
+
[benchmark/README.md](benchmark/README.md) for numbers and analysis.
|
data/lib/packrat_parser/base.rb
CHANGED
|
@@ -53,10 +53,17 @@ class PackratParser
|
|
|
53
53
|
# Rule. Guards against rewriting the base class's own infrastructure and
|
|
54
54
|
# against re-entering while we install the replacement (define_method itself
|
|
55
55
|
# fires method_added).
|
|
56
|
+
#
|
|
57
|
+
# Private methods are left alone, so they can be used as ordinary helpers
|
|
58
|
+
# rather than grammar rules. Note this only sees the visibility in effect when
|
|
59
|
+
# the method is defined, so it recognises the `private` section form but not
|
|
60
|
+
# `private def foo ... end` (which is still public at this point and only made
|
|
61
|
+
# private afterwards).
|
|
56
62
|
def self.method_added(name)
|
|
57
63
|
return if self == PackratParser
|
|
58
64
|
return if name == :initialize
|
|
59
65
|
return if @__defining_rule
|
|
66
|
+
return if private_method_defined?(name)
|
|
60
67
|
|
|
61
68
|
@__defining_rule = true
|
|
62
69
|
@start_symbol ||= name
|
|
@@ -70,15 +77,27 @@ class PackratParser
|
|
|
70
77
|
end
|
|
71
78
|
end
|
|
72
79
|
|
|
73
|
-
# Per-input packrat memo table
|
|
80
|
+
# Per-input packrat memo table: a two-level hash, rule_name => (pos => result).
|
|
74
81
|
def __memo
|
|
75
82
|
@__memo ||= {}
|
|
76
83
|
end
|
|
77
84
|
|
|
85
|
+
# Cache of built combinators, keyed by rule name. The combinator graph for a
|
|
86
|
+
# rule is stable, so it is built once and reused (loop variables are
|
|
87
|
+
# block-local, so reusing a closure across recursive activations is safe).
|
|
88
|
+
def __built
|
|
89
|
+
@__built ||= {}
|
|
90
|
+
end
|
|
91
|
+
|
|
78
92
|
# A terminal parser. A String matches that exact literal at the current
|
|
79
93
|
# position; a Regexp is matched anchored at the current position. The matched
|
|
80
94
|
# substring is the parser's value.
|
|
81
95
|
#
|
|
96
|
+
# Positions are *byte* offsets, not character offsets: indexing a UTF-8 string
|
|
97
|
+
# by character is O(n), so matching by byte (+byteslice+ for literals,
|
|
98
|
+
# +byteindex+ with a `\G` anchor for regexps) keeps each step O(match length)
|
|
99
|
+
# regardless of how far into the input we are.
|
|
100
|
+
#
|
|
82
101
|
# When the class enables +skip_whitespace+, leading whitespace is consumed
|
|
83
102
|
# before the match is attempted, mirroring Scala's RegexParsers.
|
|
84
103
|
def term(pattern)
|
|
@@ -86,22 +105,30 @@ class PackratParser
|
|
|
86
105
|
ws = /\G(?:#{ws})/ if ws
|
|
87
106
|
case pattern
|
|
88
107
|
when String
|
|
108
|
+
bytes = pattern.bytesize
|
|
109
|
+
# The failure message is constant for this terminal, and failures are
|
|
110
|
+
# common (ordered choice discards them) while the message is only read if
|
|
111
|
+
# the whole parse fails. Build it once and share the frozen string rather
|
|
112
|
+
# than interpolating on every failed match.
|
|
113
|
+
msg = "expected #{pattern.inspect}".freeze
|
|
89
114
|
Parser.new do |input, pos|
|
|
90
115
|
pos = __skip_ws(ws, input, pos)
|
|
91
|
-
if input
|
|
92
|
-
Success.new(pattern, pos +
|
|
116
|
+
if input.byteslice(pos, bytes) == pattern
|
|
117
|
+
Success.new(pattern, pos + bytes)
|
|
93
118
|
else
|
|
94
|
-
Failure.new(pos,
|
|
119
|
+
Failure.new(pos, msg)
|
|
95
120
|
end
|
|
96
121
|
end
|
|
97
122
|
when Regexp
|
|
98
123
|
anchored = /\G(?:#{pattern})/
|
|
124
|
+
msg = "expected #{pattern.inspect}".freeze
|
|
99
125
|
Parser.new do |input, pos|
|
|
100
126
|
pos = __skip_ws(ws, input, pos)
|
|
101
|
-
if (
|
|
102
|
-
|
|
127
|
+
if input.byteindex(anchored, pos)
|
|
128
|
+
s = Regexp.last_match[0]
|
|
129
|
+
Success.new(s, pos + s.bytesize)
|
|
103
130
|
else
|
|
104
|
-
Failure.new(pos,
|
|
131
|
+
Failure.new(pos, msg)
|
|
105
132
|
end
|
|
106
133
|
end
|
|
107
134
|
else
|
|
@@ -109,11 +136,11 @@ class PackratParser
|
|
|
109
136
|
end
|
|
110
137
|
end
|
|
111
138
|
|
|
112
|
-
# Advance +pos+ past whitespace matched by the anchored regexp
|
|
113
|
-
# skipping is disabled). Returns the new
|
|
139
|
+
# Advance the byte offset +pos+ past whitespace matched by the anchored regexp
|
|
140
|
+
# +ws+ (nil when skipping is disabled). Returns the new byte offset.
|
|
114
141
|
def __skip_ws(ws, input, pos)
|
|
115
142
|
return pos unless ws
|
|
116
|
-
(
|
|
143
|
+
input.byteindex(ws, pos) ? pos + Regexp.last_match[0].bytesize : pos
|
|
117
144
|
end
|
|
118
145
|
|
|
119
146
|
# A parser that succeeds with +value+ without consuming any input (monadic
|
|
@@ -122,11 +149,16 @@ class PackratParser
|
|
|
122
149
|
Parser.new { |_input, pos| Success.new(value, pos) }
|
|
123
150
|
end
|
|
124
151
|
|
|
125
|
-
# Parse +input
|
|
126
|
-
# value on success; raises ParseError on failure or
|
|
127
|
-
|
|
152
|
+
# Parse +input+, starting from rule +start+ (defaults to the configured start
|
|
153
|
+
# symbol). Returns the parsed value on success; raises ParseError on failure or
|
|
154
|
+
# on leftover input. Pass +start+ to parse from any rule, e.g.
|
|
155
|
+
# +parser.parse("123", :number)+ or, equivalently, +parser.number.parse("123")+.
|
|
156
|
+
#
|
|
157
|
+
# Positions are byte offsets throughout, including the +pos+ reported on a
|
|
158
|
+
# ParseError (see +term+ for why matching is byte-oriented).
|
|
159
|
+
def parse(input, start = nil)
|
|
128
160
|
@__memo = {}
|
|
129
|
-
name = self.class.start_symbol
|
|
161
|
+
name = start || self.class.start_symbol
|
|
130
162
|
raise ParseError.new("no start symbol defined", 0) unless name
|
|
131
163
|
|
|
132
164
|
result = send(name).call(input, 0)
|
|
@@ -138,7 +170,7 @@ class PackratParser
|
|
|
138
170
|
# all input was used.
|
|
139
171
|
ws = self.class.whitespace
|
|
140
172
|
end_pos = __skip_ws(ws && /\G(?:#{ws})/, input, result.pos)
|
|
141
|
-
if end_pos < input.
|
|
173
|
+
if end_pos < input.bytesize
|
|
142
174
|
raise ParseError.new("unexpected trailing input", end_pos)
|
|
143
175
|
end
|
|
144
176
|
result.value
|
|
@@ -76,7 +76,18 @@ class PackratParser
|
|
|
76
76
|
#
|
|
77
77
|
# number << term(";") # parse a number followed by ";", yield the number
|
|
78
78
|
def <<(other)
|
|
79
|
-
flat_map { |x| other.map { |_| x } }
|
|
79
|
+
# Equivalent to flat_map { |x| other.map { |_| x } }, but written directly
|
|
80
|
+
# so the combinator graph is built once (at bind time) instead of
|
|
81
|
+
# allocating a fresh `map` parser on every successful match.
|
|
82
|
+
Parser.new do |input, pos|
|
|
83
|
+
a = call(input, pos)
|
|
84
|
+
if a.success?
|
|
85
|
+
b = other.call(input, a.pos)
|
|
86
|
+
b.success? ? Success.new(a.value, b.pos) : b
|
|
87
|
+
else
|
|
88
|
+
a
|
|
89
|
+
end
|
|
90
|
+
end
|
|
80
91
|
end
|
|
81
92
|
|
|
82
93
|
# Sequence, keeping the *right* result (Scala's `~>`). Run this parser, then
|
|
@@ -84,18 +95,71 @@ class PackratParser
|
|
|
84
95
|
#
|
|
85
96
|
# term("(") >> additive # skip "(", yield whatever additive produces
|
|
86
97
|
def >>(other)
|
|
87
|
-
flat_map { |_| other }
|
|
98
|
+
# Equivalent to flat_map { |_| other }, written directly to avoid the
|
|
99
|
+
# per-call block dispatch.
|
|
100
|
+
Parser.new do |input, pos|
|
|
101
|
+
a = call(input, pos)
|
|
102
|
+
a.success? ? other.call(input, a.pos) : a
|
|
103
|
+
end
|
|
88
104
|
end
|
|
89
105
|
|
|
90
106
|
# Sequence, keeping *both* results (Scala's `~`). Run this parser, then
|
|
91
|
-
# +other+, and on success return the pair +[left, right]+.
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
107
|
+
# +other+, and on success return the pair +[left, right]+. The result type is
|
|
108
|
+
# the product of the operands' types, so `*` (product) is the natural
|
|
109
|
+
# spelling -- and it dovetails with `|` for choice, mirroring how a regular
|
|
110
|
+
# language is a semiring with choice as the sum and sequence as the product.
|
|
111
|
+
#
|
|
112
|
+
# Like Scala's `~` this is left-associative and nests, so `p * q * r` yields
|
|
113
|
+
# `[[a, b], c]`; Ruby's block-parameter destructuring takes them apart the
|
|
114
|
+
# way Scala's `case a ~ b ~ c` does:
|
|
95
115
|
#
|
|
96
|
-
# (p
|
|
97
|
-
def
|
|
98
|
-
flat_map { |x| other.map { |y| [x, y] } }
|
|
116
|
+
# (p * q * r).map { |(a, b), c| ... }
|
|
117
|
+
def *(other)
|
|
118
|
+
# Equivalent to flat_map { |x| other.map { |y| [x, y] } }, written directly
|
|
119
|
+
# so only the result pair (and its Success) is allocated per match, not a
|
|
120
|
+
# fresh intermediate `map` parser.
|
|
121
|
+
Parser.new do |input, pos|
|
|
122
|
+
a = call(input, pos)
|
|
123
|
+
if a.success?
|
|
124
|
+
b = other.call(input, a.pos)
|
|
125
|
+
b.success? ? Success.new([a.value, b.value], b.pos) : b
|
|
126
|
+
else
|
|
127
|
+
a
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Zero or more repetitions (Scala's `rep` / `p.*`). Always succeeds, yielding
|
|
133
|
+
# an array of the collected values (empty when there are no matches). A match
|
|
134
|
+
# that consumes no input stops the loop, so a nullable parser can't spin
|
|
135
|
+
# forever.
|
|
136
|
+
def rep
|
|
137
|
+
Parser.new do |input, pos|
|
|
138
|
+
values = []
|
|
139
|
+
cur = pos
|
|
140
|
+
loop do
|
|
141
|
+
result = call(input, cur)
|
|
142
|
+
break if !result.success? || result.pos == cur
|
|
143
|
+
values << result.value
|
|
144
|
+
cur = result.pos
|
|
145
|
+
end
|
|
146
|
+
Success.new(values, cur)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# One or more repetitions (Scala's `rep1` / `p.+`). Fails if the first match
|
|
151
|
+
# fails; otherwise yields a non-empty array of values.
|
|
152
|
+
def rep1
|
|
153
|
+
flat_map { |first| rep.map { |rest| [first, *rest] } }
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Optional (Scala's `opt` / `p.?`). Yields the parsed value, or nil (consuming
|
|
157
|
+
# nothing) when this parser does not match.
|
|
158
|
+
def opt
|
|
159
|
+
Parser.new do |input, pos|
|
|
160
|
+
result = call(input, pos)
|
|
161
|
+
result.success? ? result : Success.new(nil, pos)
|
|
162
|
+
end
|
|
99
163
|
end
|
|
100
164
|
end
|
|
101
165
|
|
|
@@ -110,20 +174,14 @@ class PackratParser
|
|
|
110
174
|
# Memoizing the *result* per (rule, pos) gives the packrat property: each rule
|
|
111
175
|
# is evaluated at most once per input position, so parsing stays linear.
|
|
112
176
|
#
|
|
113
|
-
# The combinator graph is
|
|
114
|
-
#
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
# NOTE: this rebuild is a workaround for the loop-variable leak in the fork's
|
|
123
|
-
# comprehension (parse.y: new_for_comp_gen leaves the loop var in the
|
|
124
|
-
# surrounding scope, like the legacy `for`). If the comprehension is changed to
|
|
125
|
-
# make loop variables block-local, this clobbering goes away and `built` can be
|
|
126
|
-
# cached once per (owner, name) again -- e.g. `@owner.__built[@name] ||= ...`.
|
|
177
|
+
# The combinator graph is built once per rule and cached on the owner. This
|
|
178
|
+
# relies on the comprehension's loop variables being block-local: each closure
|
|
179
|
+
# built from the rule body owns its loop-variable bindings, so reusing one
|
|
180
|
+
# cached closure across recursive activations (e.g. additive calling additive)
|
|
181
|
+
# is safe. (An earlier fork leaked the loop variables into the rule-method
|
|
182
|
+
# scope, which forced a rebuild per entry so activations would not clobber each
|
|
183
|
+
# other's bindings; that workaround is no longer needed now that the
|
|
184
|
+
# comprehension scopes them.)
|
|
127
185
|
class Rule < Parser
|
|
128
186
|
def initialize(owner, name, body)
|
|
129
187
|
@owner = owner
|
|
@@ -132,11 +190,20 @@ class PackratParser
|
|
|
132
190
|
end
|
|
133
191
|
|
|
134
192
|
def call(input, pos)
|
|
135
|
-
memo
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
memo[
|
|
193
|
+
# Two-level memo table (rule -> pos -> result). Keying on a single [name,
|
|
194
|
+
# pos] Array would allocate one Array per rule invocation and hash it;
|
|
195
|
+
# nesting keeps the per-call key a bare Integer.
|
|
196
|
+
memo = (@owner.__memo[@name] ||= {})
|
|
197
|
+
return memo[pos] if memo.key?(pos)
|
|
198
|
+
combinator = (@owner.__built[@name] ||= @body.bind(@owner).call)
|
|
199
|
+
memo[pos] = combinator.call(input, pos)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Parse +input+ starting from this rule, e.g. +parser.number.parse("123")+.
|
|
203
|
+
# Delegates to the owner so memo reset, whitespace handling, and the
|
|
204
|
+
# full-consumption check are applied exactly as for the start symbol.
|
|
205
|
+
def parse(input)
|
|
206
|
+
@owner.parse(input, @name)
|
|
140
207
|
end
|
|
141
208
|
end
|
|
142
209
|
end
|
metadata
CHANGED
|
@@ -1,52 +1,118 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: packrat_parser
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
- Shugo Maeda
|
|
7
|
+
- Shugo Maeda
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
-
dependencies:
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rake
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
-
|
|
17
|
+
- ~>
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: "13.0"
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
-
|
|
25
|
+
- ~>
|
|
26
|
+
- !ruby/object:Gem::Version
|
|
27
|
+
version: "13.0"
|
|
28
|
+
- !ruby/object:Gem::Dependency
|
|
29
|
+
name: test-unit
|
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
|
31
|
+
requirements:
|
|
32
|
+
-
|
|
33
|
+
- ~>
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: "3.0"
|
|
36
|
+
type: :development
|
|
37
|
+
prerelease: false
|
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
-
|
|
41
|
+
- ~>
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: "3.0"
|
|
44
|
+
- !ruby/object:Gem::Dependency
|
|
45
|
+
name: racc
|
|
46
|
+
requirement: !ruby/object:Gem::Requirement
|
|
47
|
+
requirements:
|
|
48
|
+
-
|
|
49
|
+
- ~>
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: "1.8"
|
|
52
|
+
type: :development
|
|
53
|
+
prerelease: false
|
|
54
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
55
|
+
requirements:
|
|
56
|
+
-
|
|
57
|
+
- ~>
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: "1.8"
|
|
60
|
+
- !ruby/object:Gem::Dependency
|
|
61
|
+
name: parslet
|
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
-
|
|
65
|
+
- ~>
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: "2.0"
|
|
68
|
+
type: :development
|
|
69
|
+
prerelease: false
|
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
-
|
|
73
|
+
- ~>
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: "2.0"
|
|
12
76
|
description: |
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
77
|
+
packrat_parser is a small PEG/packrat parser-combinator library. Grammar
|
|
78
|
+
rules are plain methods that return parsers and can be written using the
|
|
79
|
+
`for ... then` comprehension (flat_map/map/filter), giving a Scala-style
|
|
80
|
+
for-comprehension feel.
|
|
17
81
|
email:
|
|
18
|
-
- shugo.maeda@gmail.com
|
|
82
|
+
- shugo.maeda@gmail.com
|
|
19
83
|
executables: []
|
|
20
84
|
extensions: []
|
|
21
85
|
extra_rdoc_files: []
|
|
22
86
|
files:
|
|
23
|
-
- LICENSE
|
|
24
|
-
- README.md
|
|
25
|
-
- examples/simple_calc.rb
|
|
26
|
-
- lib/packrat_parser.rb
|
|
27
|
-
- lib/packrat_parser/base.rb
|
|
28
|
-
- lib/packrat_parser/parser.rb
|
|
29
|
-
- lib/packrat_parser/result.rb
|
|
30
|
-
- lib/packrat_parser/version.rb
|
|
31
|
-
homepage: https://github.com/shugo/packrat_parser
|
|
87
|
+
- LICENSE
|
|
88
|
+
- README.md
|
|
89
|
+
- examples/simple_calc.rb
|
|
90
|
+
- lib/packrat_parser.rb
|
|
91
|
+
- lib/packrat_parser/base.rb
|
|
92
|
+
- lib/packrat_parser/parser.rb
|
|
93
|
+
- lib/packrat_parser/result.rb
|
|
94
|
+
- lib/packrat_parser/version.rb
|
|
95
|
+
homepage: "https://github.com/shugo/packrat_parser"
|
|
32
96
|
licenses:
|
|
33
|
-
- MIT
|
|
97
|
+
- MIT
|
|
34
98
|
metadata: {}
|
|
35
99
|
rdoc_options: []
|
|
36
100
|
require_paths:
|
|
37
|
-
- lib
|
|
101
|
+
- lib
|
|
38
102
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
39
103
|
requirements:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
104
|
+
-
|
|
105
|
+
- ">="
|
|
106
|
+
- !ruby/object:Gem::Version
|
|
107
|
+
version: 3.0.0
|
|
43
108
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
44
109
|
requirements:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
110
|
+
-
|
|
111
|
+
- ">="
|
|
112
|
+
- !ruby/object:Gem::Version
|
|
113
|
+
version: "0"
|
|
48
114
|
requirements: []
|
|
49
|
-
rubygems_version: 4.0.
|
|
115
|
+
rubygems_version: 4.1.0.dev
|
|
50
116
|
specification_version: 4
|
|
51
117
|
summary: A packrat / PEG parser-combinator library with a Scala-inspired API.
|
|
52
118
|
test_files: []
|