parslet 1.4.0 → 1.5.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.
- data/HISTORY.txt +16 -0
- data/README +1 -1
- data/Rakefile +0 -19
- data/example/calc.rb +153 -0
- data/example/capture.rb +49 -0
- data/example/ignore.rb +2 -2
- data/example/output/calc.out +1 -0
- data/example/output/capture.out +3 -0
- data/example/output/scopes.out +1 -0
- data/example/scopes.rb +15 -0
- data/lib/parslet.rb +33 -17
- data/lib/parslet/atoms.rb +3 -0
- data/lib/parslet/atoms/alternative.rb +2 -2
- data/lib/parslet/atoms/base.rb +50 -21
- data/lib/parslet/atoms/capture.rb +38 -0
- data/lib/parslet/atoms/context.rb +24 -3
- data/lib/parslet/atoms/dsl.rb +11 -0
- data/lib/parslet/atoms/dynamic.rb +32 -0
- data/lib/parslet/atoms/entity.rb +2 -2
- data/lib/parslet/atoms/lookahead.rb +2 -2
- data/lib/parslet/atoms/named.rb +2 -2
- data/lib/parslet/atoms/re.rb +2 -2
- data/lib/parslet/atoms/repetition.rb +18 -3
- data/lib/parslet/atoms/scope.rb +26 -0
- data/lib/parslet/atoms/sequence.rb +12 -5
- data/lib/parslet/atoms/str.rb +4 -3
- data/lib/parslet/convenience.rb +0 -2
- data/lib/parslet/parser.rb +2 -2
- data/lib/parslet/scope.rb +42 -0
- data/lib/parslet/source.rb +0 -4
- metadata +54 -30
data/HISTORY.txt
CHANGED
@@ -3,6 +3,22 @@
|
|
3
3
|
- prsnt? and absnt? are now finally banned into oblivion. Wasting vocals for
|
4
4
|
the win.
|
5
5
|
|
6
|
+
= 1.5 / ??
|
7
|
+
|
8
|
+
+ Handles unconsumed input at end of parse completely differently. Instead
|
9
|
+
of generating a toplevel error, it now raises an error in every branch
|
10
|
+
of the parse. More information in the resulting exception ensues! Thanks
|
11
|
+
again to John Mettraux for inspiration & acceptance specs.
|
12
|
+
|
13
|
+
NOTE that this means that the UnconsumedInput exception is gone, since the
|
14
|
+
unconsumed input case is nothing special anymore.
|
15
|
+
|
16
|
+
* This history now finally reads like the Changelog of the linux kernel.
|
17
|
+
Meaning that probably no one ever reads this.
|
18
|
+
|
19
|
+
+ Captures and parsing subsequent input based on captured values. This has
|
20
|
+
been long overdue - finally you can parse HEREdocs with parslet!
|
21
|
+
|
6
22
|
= 1.4.0 / 25May2012
|
7
23
|
|
8
24
|
+ Revised documentation. A few new API features have finally made it into
|
data/README
CHANGED
data/Rakefile
CHANGED
@@ -16,29 +16,10 @@ end
|
|
16
16
|
|
17
17
|
task :default => :spec
|
18
18
|
|
19
|
-
# Generate documentation
|
20
|
-
RDoc::Task.new do |rdoc|
|
21
|
-
rdoc.title = "parslet - construction of parsers made easy"
|
22
|
-
rdoc.options << '--line-numbers'
|
23
|
-
rdoc.options << '--fmt' << 'shtml' # explictly set shtml generator
|
24
|
-
rdoc.template = 'direct' # lighter template used on railsapi.com
|
25
|
-
rdoc.main = "README"
|
26
|
-
rdoc.rdoc_files.include("README", "lib/**/*.rb")
|
27
|
-
rdoc.rdoc_dir = "rdoc"
|
28
|
-
end
|
29
|
-
|
30
|
-
desc 'Clear out RDoc'
|
31
|
-
task :clean => [:clobber_rdoc, :clobber_package]
|
32
|
-
|
33
19
|
# This task actually builds the gem.
|
34
20
|
task :gem => :spec
|
35
21
|
spec = eval(File.read('parslet.gemspec'))
|
36
22
|
|
37
|
-
desc "Generate the gem package."
|
38
|
-
Gem::PackageTask.new(spec) do |pkg|
|
39
|
-
pkg.gem_spec = spec
|
40
|
-
end
|
41
|
-
|
42
23
|
desc "Prints LOC stats"
|
43
24
|
task :stat do
|
44
25
|
%w(lib spec example).each do |dir|
|
data/example/calc.rb
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
# A simple integer calculator to answer the question about how to do
|
2
|
+
# left and right associativity in parslet (PEG) once and for all.
|
3
|
+
|
4
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
5
|
+
|
6
|
+
require 'rspec'
|
7
|
+
require 'parslet'
|
8
|
+
require 'parslet/rig/rspec'
|
9
|
+
|
10
|
+
# This is the parsing stage. It expresses left associativity by compiling
|
11
|
+
# list of things that have the same associativity.
|
12
|
+
class CalcParser < Parslet::Parser
|
13
|
+
root :addition
|
14
|
+
|
15
|
+
rule(:addition) {
|
16
|
+
multiplication.as(:l) >> (add_op >> multiplication.as(:r)).repeat(1) |
|
17
|
+
multiplication
|
18
|
+
}
|
19
|
+
|
20
|
+
rule(:multiplication) {
|
21
|
+
integer.as(:l) >> (mult_op >> integer.as(:r)).repeat(1) |
|
22
|
+
integer }
|
23
|
+
|
24
|
+
rule(:integer) { digit.repeat(1).as(:i) >> space? }
|
25
|
+
|
26
|
+
rule(:mult_op) { match['*/'].as(:o) >> space? }
|
27
|
+
rule(:add_op) { match['+-'].as(:o) >> space? }
|
28
|
+
|
29
|
+
rule(:digit) { match['0-9'] }
|
30
|
+
rule(:space?) { match['\s'].repeat }
|
31
|
+
end
|
32
|
+
|
33
|
+
# Classes for the abstract syntax tree.
|
34
|
+
Int = Struct.new(:int) {
|
35
|
+
def eval; self end
|
36
|
+
def op(operation, other)
|
37
|
+
left = int
|
38
|
+
right = other.int
|
39
|
+
|
40
|
+
Int.new(
|
41
|
+
case operation
|
42
|
+
when '+'
|
43
|
+
left + right
|
44
|
+
when '-'
|
45
|
+
left - right
|
46
|
+
when '*'
|
47
|
+
left * right
|
48
|
+
when '/'
|
49
|
+
left / right
|
50
|
+
end)
|
51
|
+
end
|
52
|
+
def to_i
|
53
|
+
int
|
54
|
+
end
|
55
|
+
}
|
56
|
+
Seq = Struct.new(:sequence) {
|
57
|
+
def eval
|
58
|
+
sequence.reduce { |accum, operation|
|
59
|
+
operation.call(accum) }
|
60
|
+
end
|
61
|
+
}
|
62
|
+
LeftOp = Struct.new(:operation, :right) {
|
63
|
+
def call(left)
|
64
|
+
left = left.eval
|
65
|
+
right = self.right.eval
|
66
|
+
|
67
|
+
left.op(operation, right)
|
68
|
+
end
|
69
|
+
}
|
70
|
+
|
71
|
+
# Transforming intermediary syntax tree into a real AST.
|
72
|
+
class CalcTransform < Parslet::Transform
|
73
|
+
rule(i: simple(:i)) { Int.new(Integer(i)) }
|
74
|
+
rule(o: simple(:o), r: simple(:i)) { LeftOp.new(o, i) }
|
75
|
+
rule(l: simple(:i)) { i }
|
76
|
+
rule(sequence(:seq)) { Seq.new(seq) }
|
77
|
+
end
|
78
|
+
|
79
|
+
# And this calls everything in the right order.
|
80
|
+
def calculate(str)
|
81
|
+
intermediary_tree = CalcParser.new.parse(str)
|
82
|
+
abstract_tree = CalcTransform.new.apply(intermediary_tree)
|
83
|
+
result = abstract_tree.eval
|
84
|
+
|
85
|
+
result.to_i
|
86
|
+
end
|
87
|
+
|
88
|
+
# A test suite for the above parser
|
89
|
+
describe CalcParser do
|
90
|
+
let(:p) { described_class.new }
|
91
|
+
describe '#integer' do
|
92
|
+
let(:i) { p.integer }
|
93
|
+
it "parses integers" do
|
94
|
+
i.should parse('1')
|
95
|
+
i.should parse('123')
|
96
|
+
end
|
97
|
+
it "consumes trailing white space" do
|
98
|
+
i.should parse('123 ')
|
99
|
+
end
|
100
|
+
it "doesn't parse floats" do
|
101
|
+
i.should_not parse('1.3')
|
102
|
+
end
|
103
|
+
end
|
104
|
+
describe '#multiplication' do
|
105
|
+
let(:m) { p.multiplication }
|
106
|
+
it "parses simple multiplication" do
|
107
|
+
m.should parse('1*2')
|
108
|
+
end
|
109
|
+
it "parses division" do
|
110
|
+
m.should parse('1/2')
|
111
|
+
end
|
112
|
+
end
|
113
|
+
describe '#addition' do
|
114
|
+
let(:a) { p.addition }
|
115
|
+
|
116
|
+
it "parses simple addition" do
|
117
|
+
a.should parse('1+2')
|
118
|
+
a.should parse('1+2+3-4')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
describe CalcTransform do
|
123
|
+
def t(obj)
|
124
|
+
described_class.new.apply(obj)
|
125
|
+
end
|
126
|
+
|
127
|
+
it "transforms integers" do
|
128
|
+
t(i: '1').should == Int.new(1)
|
129
|
+
end
|
130
|
+
it "unwraps left operand" do
|
131
|
+
t(l: :obj).should == :obj
|
132
|
+
end
|
133
|
+
end
|
134
|
+
describe 'whole computation specs' do
|
135
|
+
def self.result_of(str, int)
|
136
|
+
it(str) { calculate(str).should == int }
|
137
|
+
end
|
138
|
+
|
139
|
+
result_of '1+1', 2
|
140
|
+
result_of '1-1-1', -1
|
141
|
+
result_of '1+1+3*5/2', 9
|
142
|
+
result_of '123*2', 246
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
# Enable these if you want to change the code.
|
147
|
+
# RSpec::Core::Runner.run([], $stderr, $stdout)
|
148
|
+
|
149
|
+
str = ARGV.join
|
150
|
+
str = '123*2' if str.match(/^\s*$/)
|
151
|
+
|
152
|
+
print "#{str} (command line): -> "
|
153
|
+
puts calculate(str)
|
data/example/capture.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
|
2
|
+
# This example demonstrates how pieces of input can be captured and matched
|
3
|
+
# against later on. Without this, you cannot match here-documents and other
|
4
|
+
# self-dependent grammars.
|
5
|
+
|
6
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
7
|
+
require 'parslet'
|
8
|
+
require 'parslet/convenience'
|
9
|
+
require 'pp'
|
10
|
+
|
11
|
+
class CapturingParser < Parslet::Parser
|
12
|
+
root :document
|
13
|
+
|
14
|
+
# Introduce a scope for each document. This ensures that documents can be
|
15
|
+
# nested.
|
16
|
+
rule(:document) { scope { doc_start >> text >> doc_end } }
|
17
|
+
|
18
|
+
# Start of a document is a heredoc marker. This is captured in :marker
|
19
|
+
rule(:doc_start) { str('<') >> marker >> newline }
|
20
|
+
rule(:marker) { match['A-Z'].repeat(1).capture(:marker) }
|
21
|
+
|
22
|
+
# The content of a document can be either lines of text or another
|
23
|
+
# document, introduced by <HERE, where HERE is the doc marker.
|
24
|
+
rule(:text) { (document.as(:doc) | text_line.as(:line)).repeat(1) }
|
25
|
+
rule(:text_line) { captured_marker.absent? >> any >>
|
26
|
+
(newline.absent? >> any).repeat >> newline }
|
27
|
+
|
28
|
+
# The end of the document is marked by the marker that was at the beginning
|
29
|
+
# of the document, by itself on a line.
|
30
|
+
rule(:doc_end) { captured_marker }
|
31
|
+
rule(:captured_marker) {
|
32
|
+
dynamic { |source, context|
|
33
|
+
str(context.captures[:marker])
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
rule(:newline) { match["\n"] }
|
38
|
+
end
|
39
|
+
|
40
|
+
parser = CapturingParser.new
|
41
|
+
pp parser.parse_with_debug %Q(<CAPTURE
|
42
|
+
Text1
|
43
|
+
<FOOBAR
|
44
|
+
Text3
|
45
|
+
Text4
|
46
|
+
FOOBAR
|
47
|
+
Text2
|
48
|
+
CAPTURE)
|
49
|
+
|
data/example/ignore.rb
CHANGED
@@ -10,8 +10,8 @@ class IgnoreParslet < Parslet::Atoms::Base
|
|
10
10
|
def to_s_inner(prec)
|
11
11
|
@parslet.to_s(prec)
|
12
12
|
end
|
13
|
-
def try(source, context)
|
14
|
-
success, value = result = @parslet.try(source, context)
|
13
|
+
def try(source, context, consume_all)
|
14
|
+
success, value = result = @parslet.try(source, context, consume_all)
|
15
15
|
|
16
16
|
return succ(nil) if success
|
17
17
|
return result
|
@@ -0,0 +1 @@
|
|
1
|
+
123*2 (command line): -> 246
|
@@ -0,0 +1 @@
|
|
1
|
+
parses 'aba'
|
data/example/scopes.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
3
|
+
require 'parslet'
|
4
|
+
|
5
|
+
include Parslet
|
6
|
+
|
7
|
+
parser = str('a').capture(:a) >> scope { str('b').capture(:a) } >>
|
8
|
+
dynamic { |s,c| str(c.captures[:a]) }
|
9
|
+
|
10
|
+
begin
|
11
|
+
parser.parse('aba')
|
12
|
+
puts "parses 'aba'"
|
13
|
+
rescue
|
14
|
+
puts "exception!"
|
15
|
+
end
|
data/lib/parslet.rb
CHANGED
@@ -83,22 +83,6 @@ module Parslet
|
|
83
83
|
attr_reader :cause
|
84
84
|
end
|
85
85
|
|
86
|
-
# Raised when the parse operation didn't consume all of its input. In this
|
87
|
-
# case, it makes only limited sense to look at the error tree. Maybe the
|
88
|
-
# parser worked just fine, but didn't account for the characters at the tail
|
89
|
-
# of the input?
|
90
|
-
#
|
91
|
-
# str('foo').parse('foobar')
|
92
|
-
# # raises Parslet::UnconsumedInput:
|
93
|
-
# # Don't know what to do with "bar" at line 1 char 4.
|
94
|
-
#
|
95
|
-
# Note that you can have parslet ignore this error:
|
96
|
-
#
|
97
|
-
# str('foo').parse('foobar', prefix: true) # => "foo"@0
|
98
|
-
#
|
99
|
-
class UnconsumedInput < ParseFailed
|
100
|
-
end
|
101
|
-
|
102
86
|
module ClassMethods
|
103
87
|
# Define an entity for the parser. This generates a method of the same
|
104
88
|
# name that can be used as part of other patterns. Those methods can be
|
@@ -185,6 +169,37 @@ module Parslet
|
|
185
169
|
end
|
186
170
|
module_function :any
|
187
171
|
|
172
|
+
# Introduces a new capture scope. This means that all old captures stay
|
173
|
+
# accessible, but new values stored will only be available during the block
|
174
|
+
# given and the old values will be restored after the block.
|
175
|
+
#
|
176
|
+
# Example:
|
177
|
+
# # :a will be available until the end of the block. Afterwards,
|
178
|
+
# # :a from the outer scope will be available again, if such a thing
|
179
|
+
# # exists.
|
180
|
+
# scope { str('a').capture(:a) }
|
181
|
+
#
|
182
|
+
def scope(&block)
|
183
|
+
Parslet::Atoms::Scope.new(block)
|
184
|
+
end
|
185
|
+
module_function :scope
|
186
|
+
|
187
|
+
# Designates a piece of the parser as being dynamic. Dynamic parsers can
|
188
|
+
# either return a parser at runtime, which will be applied on the input, or
|
189
|
+
# return a result from a parse.
|
190
|
+
#
|
191
|
+
# Dynamic parse pieces are never cached and can introduce performance
|
192
|
+
# abnormalitites - use sparingly where other constructs fail.
|
193
|
+
#
|
194
|
+
# Example:
|
195
|
+
# # Parses either 'a' or 'b', depending on the weather
|
196
|
+
# dynamic { rand() < 0.5 ? str('a') : str('b') }
|
197
|
+
#
|
198
|
+
def dynamic(&block)
|
199
|
+
Parslet::Atoms::Dynamic.new(block)
|
200
|
+
end
|
201
|
+
module_function :dynamic
|
202
|
+
|
188
203
|
# A special kind of atom that allows embedding whole treetop expressions
|
189
204
|
# into parslet construction.
|
190
205
|
#
|
@@ -251,4 +266,5 @@ require 'parslet/pattern'
|
|
251
266
|
require 'parslet/pattern/binding'
|
252
267
|
require 'parslet/transform'
|
253
268
|
require 'parslet/parser'
|
254
|
-
require 'parslet/error_reporter'
|
269
|
+
require 'parslet/error_reporter'
|
270
|
+
require 'parslet/scope'
|
data/lib/parslet/atoms.rb
CHANGED
@@ -30,9 +30,9 @@ class Parslet::Atoms::Alternative < Parslet::Atoms::Base
|
|
30
30
|
self.class.new(*@alternatives + [parslet])
|
31
31
|
end
|
32
32
|
|
33
|
-
def try(source, context)
|
33
|
+
def try(source, context, consume_all)
|
34
34
|
errors = alternatives.map { |a|
|
35
|
-
success, value = result = a.apply(source, context)
|
35
|
+
success, value = result = a.apply(source, context, consume_all)
|
36
36
|
return result if success
|
37
37
|
|
38
38
|
# Aggregate all errors
|
data/lib/parslet/atoms/base.rb
CHANGED
@@ -27,7 +27,7 @@ class Parslet::Atoms::Base
|
|
27
27
|
|
28
28
|
# Try to cheat. Assuming that we'll be able to parse the input, don't
|
29
29
|
# run error reporting code.
|
30
|
-
success, value = setup_and_apply(source, nil)
|
30
|
+
success, value = setup_and_apply(source, nil, !options[:prefix])
|
31
31
|
|
32
32
|
# If we didn't succeed the parse, raise an exception for the user.
|
33
33
|
# Stack trace will be off, but the error tree should explain the reason
|
@@ -36,7 +36,8 @@ class Parslet::Atoms::Base
|
|
36
36
|
# Cheating has not paid off. Now pay the cost: Rerun the parse,
|
37
37
|
# gathering error information in the process.
|
38
38
|
reporter = options[:reporter] || Parslet::ErrorReporter::Tree.new
|
39
|
-
|
39
|
+
source.pos = 0
|
40
|
+
success, value = setup_and_apply(source, reporter, !options[:prefix])
|
40
41
|
|
41
42
|
fail "Assertion failed: success was true when parsing with reporter" \
|
42
43
|
if success
|
@@ -48,15 +49,12 @@ class Parslet::Atoms::Base
|
|
48
49
|
end
|
49
50
|
|
50
51
|
# assert: success is true
|
51
|
-
|
52
|
-
#
|
53
|
-
#
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
source, old_pos,
|
58
|
-
"Don't know what to do with #{source.consume(10).to_s.inspect}").
|
59
|
-
raise(Parslet::UnconsumedInput)
|
52
|
+
|
53
|
+
# Extra input is now handled inline with the rest of the parsing. If
|
54
|
+
# really we have success == true, prefix: false and still some input
|
55
|
+
# is left dangling, that is a BUG.
|
56
|
+
if !options[:prefix] && source.chars_left > 0
|
57
|
+
fail "BUG: New error strategy should not reach this point."
|
60
58
|
end
|
61
59
|
|
62
60
|
return flatten(value)
|
@@ -67,21 +65,45 @@ class Parslet::Atoms::Base
|
|
67
65
|
#
|
68
66
|
# @return [<Boolean, Object>] Result of the parse. If the first member is
|
69
67
|
# true, the parse has succeeded.
|
70
|
-
def setup_and_apply(source, error_reporter)
|
68
|
+
def setup_and_apply(source, error_reporter, consume_all)
|
71
69
|
context = Parslet::Atoms::Context.new(error_reporter)
|
72
|
-
apply(source, context)
|
70
|
+
apply(source, context, consume_all)
|
73
71
|
end
|
74
72
|
|
75
|
-
|
76
|
-
#
|
77
|
-
#
|
78
|
-
|
79
|
-
|
73
|
+
# Calls the #try method of this parslet. Success consumes input, error will
|
74
|
+
# rewind the input.
|
75
|
+
#
|
76
|
+
# @param source [Parslet::Source] source to read input from
|
77
|
+
# @param context [Parslet::Atoms::Context] context to use for the parsing
|
78
|
+
# @param consume_all [Boolean] true if the current parse must consume
|
79
|
+
# all input by itself.
|
80
|
+
def apply(source, context, consume_all=false)
|
80
81
|
old_pos = source.pos
|
81
82
|
|
82
|
-
success, value = result = context.try_with_cache(self, source)
|
83
|
+
success, value = result = context.try_with_cache(self, source, consume_all)
|
83
84
|
|
84
|
-
|
85
|
+
if success
|
86
|
+
# If a consume_all parse was made and doesn't result in the consumption
|
87
|
+
# of all the input, that is considered an error.
|
88
|
+
if consume_all && source.chars_left>0
|
89
|
+
# Read 10 characters ahead. Why ten? I don't know.
|
90
|
+
offending_pos = source.pos
|
91
|
+
offending_input = source.consume(10)
|
92
|
+
|
93
|
+
# Rewind input (as happens always in error case)
|
94
|
+
source.pos = old_pos
|
95
|
+
|
96
|
+
return context.err_at(
|
97
|
+
self,
|
98
|
+
source,
|
99
|
+
"Don't know what to do with #{offending_input.to_s.inspect}",
|
100
|
+
offending_pos
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Looks like the parse was successful after all. Don't rewind the input.
|
105
|
+
return result
|
106
|
+
end
|
85
107
|
|
86
108
|
# We only reach this point if the parse has failed. Rewind the input.
|
87
109
|
source.pos = old_pos
|
@@ -91,11 +113,18 @@ class Parslet::Atoms::Base
|
|
91
113
|
# Override this in your Atoms::Base subclasses to implement parsing
|
92
114
|
# behaviour.
|
93
115
|
#
|
94
|
-
def try(source, context)
|
116
|
+
def try(source, context, consume_all)
|
95
117
|
raise NotImplementedError, \
|
96
118
|
"Atoms::Base doesn't have behaviour, please implement #try(source, context)."
|
97
119
|
end
|
98
120
|
|
121
|
+
# Returns true if this atom can be cached in the packrat cache. Most parslet
|
122
|
+
# atoms are cached, so this always returns true, unless overridden.
|
123
|
+
#
|
124
|
+
def cached?
|
125
|
+
true
|
126
|
+
end
|
127
|
+
|
99
128
|
# Debug printing - in Treetop syntax.
|
100
129
|
#
|
101
130
|
def self.precedence(prec)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
# Stores the result of matching an atom against input in the #captures in
|
3
|
+
# parse context. Doing so will allow you to pull parts of the ongoing parse
|
4
|
+
# out later and use them to match other pieces of input.
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
# # After this, context.captures[:an_a] returns 'a'
|
8
|
+
# str('a').capture(:an_a)
|
9
|
+
#
|
10
|
+
# # Capture and use of the capture: (matches either 'aa' or 'bb')
|
11
|
+
# match['ab'].capture(:first) >>
|
12
|
+
# dynamic { |src, ctx| str(ctx.captures[:first]) }
|
13
|
+
#
|
14
|
+
class Parslet::Atoms::Capture < Parslet::Atoms::Base
|
15
|
+
attr_reader :parslet, :name
|
16
|
+
|
17
|
+
def initialize(parslet, name)
|
18
|
+
super()
|
19
|
+
|
20
|
+
@parslet, @name = parslet, name
|
21
|
+
end
|
22
|
+
|
23
|
+
def apply(source, context, consume_all)
|
24
|
+
success, value = result = parslet.apply(source, context, consume_all)
|
25
|
+
|
26
|
+
if success
|
27
|
+
context.captures[name.to_sym] =
|
28
|
+
flatten(value)
|
29
|
+
end
|
30
|
+
|
31
|
+
return result
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s_inner(prec)
|
35
|
+
"(#{name.inspect} = #{parslet.to_s(prec)})"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
@@ -12,6 +12,7 @@ module Parslet::Atoms
|
|
12
12
|
def initialize(reporter=Parslet::ErrorReporter::Tree.new)
|
13
13
|
@cache = Hash.new { |h, k| h[k] = {} }
|
14
14
|
@reporter = reporter
|
15
|
+
@captures = Parslet::Scope.new
|
15
16
|
end
|
16
17
|
|
17
18
|
# Caches a parse answer for obj at source.pos. Applying the same parslet
|
@@ -22,14 +23,17 @@ module Parslet::Atoms
|
|
22
23
|
# were consumed by a successful parse. Imitation of such a parse must
|
23
24
|
# advance the input pos by the same amount of bytes.
|
24
25
|
#
|
25
|
-
def try_with_cache(obj, source)
|
26
|
+
def try_with_cache(obj, source, consume_all)
|
26
27
|
beg = source.pos
|
27
28
|
|
28
29
|
# Not in cache yet? Return early.
|
29
30
|
unless entry = lookup(obj, beg)
|
30
|
-
result = obj.try(source, self)
|
31
|
+
result = obj.try(source, self, consume_all)
|
31
32
|
|
32
|
-
|
33
|
+
if obj.cached?
|
34
|
+
set obj, beg, [result, source.pos-beg]
|
35
|
+
end
|
36
|
+
|
33
37
|
return result
|
34
38
|
end
|
35
39
|
|
@@ -59,6 +63,23 @@ module Parslet::Atoms
|
|
59
63
|
return [false, nil]
|
60
64
|
end
|
61
65
|
|
66
|
+
# Returns the current captures made on the input (see
|
67
|
+
# Parslet::Atoms::Base#capture). Use as follows:
|
68
|
+
#
|
69
|
+
# context.captures[:foobar] # => returns capture :foobar
|
70
|
+
#
|
71
|
+
attr_reader :captures
|
72
|
+
|
73
|
+
# Starts a new scope. Use the #scope method of Parslet::Atoms::DSL
|
74
|
+
# to call this.
|
75
|
+
#
|
76
|
+
def scope
|
77
|
+
captures.push
|
78
|
+
yield
|
79
|
+
ensure
|
80
|
+
captures.pop
|
81
|
+
end
|
82
|
+
|
62
83
|
private
|
63
84
|
def lookup(obj, pos)
|
64
85
|
@cache[pos][obj]
|
data/lib/parslet/atoms/dsl.rb
CHANGED
@@ -95,4 +95,15 @@ module Parslet::Atoms::DSL
|
|
95
95
|
def as(name)
|
96
96
|
Parslet::Atoms::Named.new(self, name)
|
97
97
|
end
|
98
|
+
|
99
|
+
# Captures a part of the input and stores it under the name given. This
|
100
|
+
# is very useful to create self-referential parses. A capture stores
|
101
|
+
# the result of its parse (may be complex) on a successful parse action.
|
102
|
+
#
|
103
|
+
# Example:
|
104
|
+
# str('a').capture(:b) # will store captures[:b] == 'a'
|
105
|
+
#
|
106
|
+
def capture(name)
|
107
|
+
Parslet::Atoms::Capture.new(self, name)
|
108
|
+
end
|
98
109
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Evaluates a block at parse time. The result from the block must be a parser
|
2
|
+
# (something which implements #apply). In the first case, the parser will then
|
3
|
+
# be applied to the input, creating the result.
|
4
|
+
#
|
5
|
+
# Dynamic parses are never cached.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
# dynamic { rand < 0.5 ? str('a') : str('b') }
|
9
|
+
#
|
10
|
+
class Parslet::Atoms::Dynamic < Parslet::Atoms::Base
|
11
|
+
attr_reader :block
|
12
|
+
|
13
|
+
def initialize(block)
|
14
|
+
@block = block
|
15
|
+
end
|
16
|
+
|
17
|
+
def cached?
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
def try(source, context, consume_all)
|
22
|
+
result = block.call(source, context)
|
23
|
+
|
24
|
+
# Result is a parslet atom.
|
25
|
+
return result.apply(source, context, consume_all)
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s_inner(prec)
|
29
|
+
"dynamic { ... }"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
data/lib/parslet/atoms/entity.rb
CHANGED
@@ -21,10 +21,10 @@ class Parslet::Atoms::Lookahead < Parslet::Atoms::Base
|
|
21
21
|
}
|
22
22
|
end
|
23
23
|
|
24
|
-
def try(source, context)
|
24
|
+
def try(source, context, consume_all)
|
25
25
|
pos = source.pos
|
26
26
|
|
27
|
-
success, value = bound_parslet.apply(source, context)
|
27
|
+
success, value = bound_parslet.apply(source, context, consume_all)
|
28
28
|
|
29
29
|
if positive
|
30
30
|
return succ(nil) if success
|
data/lib/parslet/atoms/named.rb
CHANGED
@@ -13,8 +13,8 @@ class Parslet::Atoms::Named < Parslet::Atoms::Base
|
|
13
13
|
@parslet, @name = parslet, name
|
14
14
|
end
|
15
15
|
|
16
|
-
def apply(source, context)
|
17
|
-
success, value = result = parslet.apply(source, context)
|
16
|
+
def apply(source, context, consume_all)
|
17
|
+
success, value = result = parslet.apply(source, context, consume_all)
|
18
18
|
|
19
19
|
return result unless success
|
20
20
|
succ(
|
data/lib/parslet/atoms/re.rb
CHANGED
@@ -20,12 +20,12 @@ class Parslet::Atoms::Re < Parslet::Atoms::Base
|
|
20
20
|
}
|
21
21
|
end
|
22
22
|
|
23
|
-
def try(source, context)
|
23
|
+
def try(source, context, consume_all)
|
24
24
|
return succ(source.consume(1)) if source.matches?(re)
|
25
25
|
|
26
26
|
# No string could be read
|
27
27
|
return context.err(self, source, @error_msgs[:premature]) \
|
28
|
-
if source.
|
28
|
+
if source.chars_left < 1
|
29
29
|
|
30
30
|
# No match
|
31
31
|
return context.err(self, source, @error_msgs[:failed])
|
@@ -15,18 +15,19 @@ class Parslet::Atoms::Repetition < Parslet::Atoms::Base
|
|
15
15
|
@min, @max = min, max
|
16
16
|
@tag = tag
|
17
17
|
@error_msgs = {
|
18
|
-
:minrep => "Expected at least #{min} of #{parslet.inspect}"
|
18
|
+
:minrep => "Expected at least #{min} of #{parslet.inspect}",
|
19
|
+
:unconsumed => "Extra input after last repetition"
|
19
20
|
}
|
20
21
|
end
|
21
22
|
|
22
|
-
def try(source, context)
|
23
|
+
def try(source, context, consume_all)
|
23
24
|
occ = 0
|
24
25
|
accum = [@tag] # initialize the result array with the tag (for flattening)
|
25
26
|
start_pos = source.pos
|
26
27
|
|
27
28
|
break_on = nil
|
28
29
|
loop do
|
29
|
-
success, value = parslet.apply(source, context)
|
30
|
+
success, value = parslet.apply(source, context, false)
|
30
31
|
|
31
32
|
break_on = value
|
32
33
|
break unless success
|
@@ -49,6 +50,20 @@ class Parslet::Atoms::Repetition < Parslet::Atoms::Base
|
|
49
50
|
start_pos,
|
50
51
|
[break_on]) if occ < min
|
51
52
|
|
53
|
+
# consume_all is true, that means that we're inside the part of the parser
|
54
|
+
# that should consume the input completely. Repetition failing here means
|
55
|
+
# probably that we didn't.
|
56
|
+
#
|
57
|
+
# We have a special clause to create an error here because otherwise
|
58
|
+
# break_on would get thrown away. It turns out, that contains very
|
59
|
+
# interesting information in a lot of cases.
|
60
|
+
#
|
61
|
+
return context.err(
|
62
|
+
self,
|
63
|
+
source,
|
64
|
+
@error_msgs[:unconsumed],
|
65
|
+
[break_on]) if consume_all && source.chars_left>0
|
66
|
+
|
52
67
|
return succ(accum)
|
53
68
|
end
|
54
69
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Starts a new scope in the parsing process. Please also see the #captures
|
2
|
+
# method.
|
3
|
+
#
|
4
|
+
class Parslet::Atoms::Scope < Parslet::Atoms::Base
|
5
|
+
attr_reader :block
|
6
|
+
def initialize(block)
|
7
|
+
super()
|
8
|
+
|
9
|
+
@block = block
|
10
|
+
end
|
11
|
+
|
12
|
+
def cached?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
def apply(source, context, consume_all)
|
17
|
+
context.scope do
|
18
|
+
parslet = block.call
|
19
|
+
return parslet.apply(source, context, consume_all)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s_inner(prec)
|
24
|
+
"scope { #{block.call.to_s(prec)} }"
|
25
|
+
end
|
26
|
+
end
|
@@ -19,16 +19,23 @@ class Parslet::Atoms::Sequence < Parslet::Atoms::Base
|
|
19
19
|
self.class.new(* @parslets+[parslet])
|
20
20
|
end
|
21
21
|
|
22
|
-
def try(source, context)
|
23
|
-
|
24
|
-
|
22
|
+
def try(source, context, consume_all)
|
23
|
+
# Presize an array
|
24
|
+
result = Array.new(parslets.size + 1)
|
25
|
+
result[0] = :sequence
|
26
|
+
|
27
|
+
parslets.each_with_index do |p, idx|
|
28
|
+
child_consume_all = consume_all && (idx == parslets.size-1)
|
29
|
+
success, value = p.apply(source, context, child_consume_all)
|
25
30
|
|
26
31
|
unless success
|
27
32
|
return context.err(self, source, @error_msgs[:failed], [value])
|
28
33
|
end
|
29
34
|
|
30
|
-
value
|
31
|
-
|
35
|
+
result[idx+1] = value
|
36
|
+
end
|
37
|
+
|
38
|
+
return succ(result)
|
32
39
|
end
|
33
40
|
|
34
41
|
precedence SEQUENCE
|
data/lib/parslet/atoms/str.rb
CHANGED
@@ -17,13 +17,14 @@ class Parslet::Atoms::Str < Parslet::Atoms::Base
|
|
17
17
|
}
|
18
18
|
end
|
19
19
|
|
20
|
-
def try(source, context)
|
20
|
+
def try(source, context, consume_all)
|
21
21
|
return succ(source.consume(@len)) if source.matches?(str)
|
22
22
|
|
23
|
-
#
|
23
|
+
# Input ending early:
|
24
24
|
return context.err(self, source, @error_msgs[:premature]) \
|
25
25
|
if source.chars_left<@len
|
26
|
-
|
26
|
+
|
27
|
+
# Expected something, but got something else instead:
|
27
28
|
error_pos = source.pos
|
28
29
|
return context.err_at(
|
29
30
|
self, source,
|
data/lib/parslet/convenience.rb
CHANGED
data/lib/parslet/parser.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
class Parslet::Scope
|
2
|
+
# Raised when the accessed slot has never been assigned a value.
|
3
|
+
#
|
4
|
+
class NotFound < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
class Binding
|
8
|
+
attr_reader :parent
|
9
|
+
|
10
|
+
def initialize(parent=nil)
|
11
|
+
@parent = parent
|
12
|
+
@hash = Hash.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](k)
|
16
|
+
@hash.has_key?(k) && @hash[k] ||
|
17
|
+
parent && parent[k] or
|
18
|
+
raise NotFound
|
19
|
+
end
|
20
|
+
def []=(k,v)
|
21
|
+
@hash.store(k,v)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def [](k)
|
26
|
+
@current[k]
|
27
|
+
end
|
28
|
+
def []=(k,v)
|
29
|
+
@current[k] = v
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
@current = Binding.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def push
|
37
|
+
@current = Binding.new(@current)
|
38
|
+
end
|
39
|
+
def pop
|
40
|
+
@current = @current.parent
|
41
|
+
end
|
42
|
+
end
|
data/lib/parslet/source.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parslet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,136 +9,152 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-12-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: blankslate
|
16
|
+
prerelease: false
|
16
17
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: '2.0'
|
22
|
+
none: false
|
22
23
|
type: :runtime
|
23
|
-
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
25
|
requirements:
|
27
26
|
- - ~>
|
28
27
|
- !ruby/object:Gem::Version
|
29
28
|
version: '2.0'
|
29
|
+
none: false
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: rspec
|
32
|
+
prerelease: false
|
32
33
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
34
|
requirements:
|
35
35
|
- - ! '>='
|
36
36
|
- !ruby/object:Gem::Version
|
37
37
|
version: '0'
|
38
|
+
none: false
|
38
39
|
type: :development
|
39
|
-
prerelease: false
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
41
|
requirements:
|
43
42
|
- - ! '>='
|
44
43
|
- !ruby/object:Gem::Version
|
45
44
|
version: '0'
|
45
|
+
none: false
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
47
|
name: flexmock
|
48
|
+
prerelease: false
|
48
49
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
50
|
requirements:
|
51
51
|
- - ! '>='
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: '0'
|
54
|
+
none: false
|
54
55
|
type: :development
|
55
|
-
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
57
|
requirements:
|
59
58
|
- - ! '>='
|
60
59
|
- !ruby/object:Gem::Version
|
61
60
|
version: '0'
|
61
|
+
none: false
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
63
|
name: rdoc
|
64
|
+
prerelease: false
|
64
65
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
66
|
requirements:
|
67
67
|
- - ! '>='
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '0'
|
70
|
+
none: false
|
70
71
|
type: :development
|
71
|
-
prerelease: false
|
72
72
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
73
|
requirements:
|
75
74
|
- - ! '>='
|
76
75
|
- !ruby/object:Gem::Version
|
77
76
|
version: '0'
|
77
|
+
none: false
|
78
78
|
- !ruby/object:Gem::Dependency
|
79
79
|
name: sdoc
|
80
|
+
prerelease: false
|
80
81
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
82
|
requirements:
|
83
83
|
- - ! '>='
|
84
84
|
- !ruby/object:Gem::Version
|
85
85
|
version: '0'
|
86
|
+
none: false
|
86
87
|
type: :development
|
87
|
-
prerelease: false
|
88
88
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
89
|
requirements:
|
91
90
|
- - ! '>='
|
92
91
|
- !ruby/object:Gem::Version
|
93
92
|
version: '0'
|
93
|
+
none: false
|
94
94
|
- !ruby/object:Gem::Dependency
|
95
95
|
name: guard
|
96
|
+
prerelease: false
|
96
97
|
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
98
|
requirements:
|
99
99
|
- - ! '>='
|
100
100
|
- !ruby/object:Gem::Version
|
101
101
|
version: '0'
|
102
|
+
none: false
|
102
103
|
type: :development
|
103
|
-
prerelease: false
|
104
104
|
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
105
|
requirements:
|
107
106
|
- - ! '>='
|
108
107
|
- !ruby/object:Gem::Version
|
109
108
|
version: '0'
|
109
|
+
none: false
|
110
110
|
- !ruby/object:Gem::Dependency
|
111
111
|
name: guard-rspec
|
112
|
+
prerelease: false
|
112
113
|
requirement: !ruby/object:Gem::Requirement
|
113
|
-
none: false
|
114
114
|
requirements:
|
115
115
|
- - ! '>='
|
116
116
|
- !ruby/object:Gem::Version
|
117
117
|
version: '0'
|
118
|
+
none: false
|
118
119
|
type: :development
|
119
|
-
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ! '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
none: false
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: rb-fsevent
|
128
|
+
prerelease: false
|
129
|
+
requirement: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
121
134
|
none: false
|
135
|
+
type: :development
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
137
|
requirements:
|
123
138
|
- - ! '>='
|
124
139
|
- !ruby/object:Gem::Version
|
125
140
|
version: '0'
|
141
|
+
none: false
|
126
142
|
- !ruby/object:Gem::Dependency
|
127
143
|
name: growl
|
144
|
+
prerelease: false
|
128
145
|
requirement: !ruby/object:Gem::Requirement
|
129
|
-
none: false
|
130
146
|
requirements:
|
131
147
|
- - ! '>='
|
132
148
|
- !ruby/object:Gem::Version
|
133
149
|
version: '0'
|
150
|
+
none: false
|
134
151
|
type: :development
|
135
|
-
prerelease: false
|
136
152
|
version_requirements: !ruby/object:Gem::Requirement
|
137
|
-
none: false
|
138
153
|
requirements:
|
139
154
|
- - ! '>='
|
140
155
|
- !ruby/object:Gem::Version
|
141
156
|
version: '0'
|
157
|
+
none: false
|
142
158
|
description:
|
143
159
|
email: kaspar.schiess@absurd.li
|
144
160
|
executables: []
|
@@ -153,13 +169,16 @@ files:
|
|
153
169
|
- lib/parslet/atoms/alternative.rb
|
154
170
|
- lib/parslet/atoms/base.rb
|
155
171
|
- lib/parslet/atoms/can_flatten.rb
|
172
|
+
- lib/parslet/atoms/capture.rb
|
156
173
|
- lib/parslet/atoms/context.rb
|
157
174
|
- lib/parslet/atoms/dsl.rb
|
175
|
+
- lib/parslet/atoms/dynamic.rb
|
158
176
|
- lib/parslet/atoms/entity.rb
|
159
177
|
- lib/parslet/atoms/lookahead.rb
|
160
178
|
- lib/parslet/atoms/named.rb
|
161
179
|
- lib/parslet/atoms/re.rb
|
162
180
|
- lib/parslet/atoms/repetition.rb
|
181
|
+
- lib/parslet/atoms/scope.rb
|
163
182
|
- lib/parslet/atoms/sequence.rb
|
164
183
|
- lib/parslet/atoms/str.rb
|
165
184
|
- lib/parslet/atoms/visitor.rb
|
@@ -176,6 +195,7 @@ files:
|
|
176
195
|
- lib/parslet/pattern/binding.rb
|
177
196
|
- lib/parslet/pattern.rb
|
178
197
|
- lib/parslet/rig/rspec.rb
|
198
|
+
- lib/parslet/scope.rb
|
179
199
|
- lib/parslet/slice.rb
|
180
200
|
- lib/parslet/source/line_cache.rb
|
181
201
|
- lib/parslet/source.rb
|
@@ -183,6 +203,8 @@ files:
|
|
183
203
|
- lib/parslet/transform.rb
|
184
204
|
- lib/parslet.rb
|
185
205
|
- example/boolean_algebra.rb
|
206
|
+
- example/calc.rb
|
207
|
+
- example/capture.rb
|
186
208
|
- example/comments.rb
|
187
209
|
- example/deepest_errors.rb
|
188
210
|
- example/documentation.rb
|
@@ -198,6 +220,8 @@ files:
|
|
198
220
|
- example/modularity.rb
|
199
221
|
- example/nested_errors.rb
|
200
222
|
- example/output/boolean_algebra.out
|
223
|
+
- example/output/calc.out
|
224
|
+
- example/output/capture.out
|
201
225
|
- example/output/comments.out
|
202
226
|
- example/output/deepest_errors.out
|
203
227
|
- example/output/documentation.err
|
@@ -216,12 +240,14 @@ files:
|
|
216
240
|
- example/output/nested_errors.out
|
217
241
|
- example/output/parens.out
|
218
242
|
- example/output/readme.out
|
243
|
+
- example/output/scopes.out
|
219
244
|
- example/output/seasons.out
|
220
245
|
- example/output/sentence.out
|
221
246
|
- example/output/simple_xml.out
|
222
247
|
- example/output/string_parser.out
|
223
248
|
- example/parens.rb
|
224
249
|
- example/readme.rb
|
250
|
+
- example/scopes.rb
|
225
251
|
- example/seasons.rb
|
226
252
|
- example/sentence.rb
|
227
253
|
- example/simple.lit
|
@@ -237,20 +263,17 @@ rdoc_options:
|
|
237
263
|
require_paths:
|
238
264
|
- lib
|
239
265
|
required_ruby_version: !ruby/object:Gem::Requirement
|
240
|
-
none: false
|
241
266
|
requirements:
|
242
267
|
- - ! '>='
|
243
268
|
- !ruby/object:Gem::Version
|
244
269
|
version: '0'
|
245
|
-
segments:
|
246
|
-
- 0
|
247
|
-
hash: 1524575203779308108
|
248
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
249
270
|
none: false
|
271
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
250
272
|
requirements:
|
251
273
|
- - ! '>='
|
252
274
|
- !ruby/object:Gem::Version
|
253
275
|
version: '0'
|
276
|
+
none: false
|
254
277
|
requirements: []
|
255
278
|
rubyforge_project:
|
256
279
|
rubygems_version: 1.8.24
|
@@ -258,3 +281,4 @@ signing_key:
|
|
258
281
|
specification_version: 3
|
259
282
|
summary: Parser construction library with great error reporting in Ruby.
|
260
283
|
test_files: []
|
284
|
+
has_rdoc:
|