parslet 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|