parslet 0.11.0 → 1.0.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/Gemfile +4 -0
- data/HISTORY.txt +8 -0
- data/README +7 -11
- data/Rakefile +3 -1
- data/lib/parslet.rb +39 -16
- data/lib/parslet/atoms.rb +1 -1
- data/lib/parslet/atoms/alternative.rb +17 -6
- data/lib/parslet/atoms/base.rb +72 -11
- data/lib/parslet/atoms/entity.rb +9 -10
- data/lib/parslet/atoms/lookahead.rb +7 -7
- data/lib/parslet/atoms/named.rb +5 -5
- data/lib/parslet/atoms/re.rb +3 -3
- data/lib/parslet/atoms/repetition.rb +4 -4
- data/lib/parslet/atoms/sequence.rb +4 -4
- data/lib/parslet/atoms/str.rb +2 -2
- data/lib/parslet/error_tree.rb +3 -3
- data/lib/parslet/expression.rb +11 -2
- data/lib/parslet/expression/treetop.rb +54 -13
- data/lib/parslet/pattern.rb +17 -55
- data/lib/parslet/pattern/binding.rb +3 -3
- data/lib/parslet/pattern/context.rb +1 -1
- data/lib/parslet/transform.rb +24 -11
- metadata +3 -3
data/Gemfile
CHANGED
data/HISTORY.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
= 1.0.0 / 29Dez2010
|
2
|
+
|
3
|
+
- #each_match was removed. There was some duplication of code that even
|
4
|
+
confused me - and we should not have 2 methods of achieving the same
|
5
|
+
goal.
|
6
|
+
|
7
|
+
+ Full documentation. Fixed sdoc.
|
8
|
+
|
1
9
|
= 0.11.0 / 25Nov2010
|
2
10
|
|
3
11
|
! Bugfixes to tree handling. Let's hope that was the last such significant
|
data/README
CHANGED
@@ -2,10 +2,10 @@ INTRODUCTION
|
|
2
2
|
|
3
3
|
Parslet makes developing complex parsers easy. It does so by
|
4
4
|
|
5
|
-
* providing the best
|
6
|
-
*
|
5
|
+
* providing the best <b>error reporting</b> possible
|
6
|
+
* <b>not generating</b> reams of code for you to debug
|
7
7
|
|
8
|
-
Parslet takes the long way around to make
|
8
|
+
Parslet takes the long way around to make <b>your job</b> easier. It allows for
|
9
9
|
incremental language construction. Often, you start out small, implementing
|
10
10
|
the atoms of your language first; _parslet_ takes pride in making this
|
11
11
|
possible.
|
@@ -33,14 +33,8 @@ SYNOPSIS
|
|
33
33
|
|
34
34
|
tree # => {:string=>"This is a \\\"String\\\" in which you can escape stuff"}
|
35
35
|
|
36
|
-
# Here's how you can grab results from that tree
|
36
|
+
# Here's how you can grab results from that tree:
|
37
37
|
|
38
|
-
# 1)
|
39
|
-
Pattern.new(:string => simple(:x)).each_match(tree) do |dictionary|
|
40
|
-
puts "String contents (method 1): #{dictionary[:x]}"
|
41
|
-
end
|
42
|
-
|
43
|
-
# 2)
|
44
38
|
transform = Parslet::Transform.new do
|
45
39
|
rule(:string => simple(:x)) {
|
46
40
|
puts "String contents (method 2): #{x}" }
|
@@ -53,6 +47,8 @@ This library should work with both ruby 1.8 and ruby 1.9.
|
|
53
47
|
|
54
48
|
STATUS
|
55
49
|
|
56
|
-
|
50
|
+
0.12.0
|
51
|
+
|
52
|
+
On the road to 1.0; improving documentation, trying to ease access to the API.
|
57
53
|
|
58
54
|
(c) 2010 Kaspar Schiess
|
data/Rakefile
CHANGED
@@ -18,7 +18,7 @@ spec = Gem::Specification.new do |s|
|
|
18
18
|
|
19
19
|
# Change these as appropriate
|
20
20
|
s.name = "parslet"
|
21
|
-
s.version = "0.
|
21
|
+
s.version = "1.0.0"
|
22
22
|
s.summary = "Parser construction library with great error reporting in Ruby."
|
23
23
|
s.author = "Kaspar Schiess"
|
24
24
|
s.email = "kaspar.schiess@absurd.li"
|
@@ -64,6 +64,8 @@ require 'sdoc'
|
|
64
64
|
|
65
65
|
# Generate documentation
|
66
66
|
Rake::RDocTask.new do |rdoc|
|
67
|
+
rdoc.title = "parslet - construction of parsers made easy"
|
68
|
+
rdoc.options << '--line-numbers'
|
67
69
|
rdoc.options << '--fmt' << 'shtml' # explictly set shtml generator
|
68
70
|
rdoc.template = 'direct' # lighter template used on railsapi.com
|
69
71
|
rdoc.main = "README"
|
data/lib/parslet.rb
CHANGED
@@ -21,18 +21,34 @@ require 'stringio'
|
|
21
21
|
# Parslet is typically used in stages:
|
22
22
|
#
|
23
23
|
#
|
24
|
-
# * Parsing the input string; this yields an intermediary tree
|
25
|
-
#
|
24
|
+
# * Parsing the input string; this yields an intermediary tree, see
|
25
|
+
# Parslet.any, Parslet.match, Parslet.str, Parslet::ClassMethods#rule and
|
26
|
+
# Parslet::ClassMethods#root.
|
27
|
+
# * Transformation of the tree into something useful to you, see
|
28
|
+
# Parslet::Transform, Parslet.simple, Parslet.sequence and Parslet.subtree.
|
26
29
|
#
|
27
30
|
# The first stage is traditionally intermingled with the second stage; output
|
28
31
|
# from the second stage is usually called the 'Abstract Syntax Tree' or AST.
|
29
32
|
#
|
30
|
-
# The stages are completely decoupled; You can change your grammar around
|
31
|
-
#
|
33
|
+
# The stages are completely decoupled; You can change your grammar around and
|
34
|
+
# use the second stage to isolate the rest of your code from the changes
|
32
35
|
# you've effected.
|
33
36
|
#
|
37
|
+
# == Further reading
|
38
|
+
#
|
39
|
+
# All parslet atoms are subclasses of Parslet::Atoms::Base. You might want to
|
40
|
+
# look at all of those: Parslet::Atoms::Re, Parslet::Atoms::Str,
|
41
|
+
# Parslet::Atoms::Repetition, Parslet::Atoms::Sequence,
|
42
|
+
# Parslet::Atoms::Alternative.
|
43
|
+
#
|
44
|
+
# == When things go wrong
|
45
|
+
#
|
46
|
+
# A parse that fails will raise Parslet::ParseFailed. A detailed explanation
|
47
|
+
# of what went wrong can be obtained from the parslet involved or the root of
|
48
|
+
# the parser instance.
|
49
|
+
#
|
34
50
|
module Parslet
|
35
|
-
def self.included(base)
|
51
|
+
def self.included(base) # :nodoc:
|
36
52
|
base.extend(ClassMethods)
|
37
53
|
end
|
38
54
|
|
@@ -47,7 +63,7 @@ module Parslet
|
|
47
63
|
# begin
|
48
64
|
# parslet.parse(str)
|
49
65
|
# rescue Parslet::ParseFailed => failure
|
50
|
-
# puts parslet.error_tree
|
66
|
+
# puts parslet.error_tree
|
51
67
|
# end
|
52
68
|
#
|
53
69
|
class ParseFailed < Exception
|
@@ -96,9 +112,7 @@ module Parslet
|
|
96
112
|
# bar >> bar
|
97
113
|
# end
|
98
114
|
#
|
99
|
-
#
|
100
|
-
# twobar.parse(str)
|
101
|
-
# end
|
115
|
+
# root :twobar
|
102
116
|
# end
|
103
117
|
#
|
104
118
|
def rule(name, &definition)
|
@@ -109,23 +123,28 @@ module Parslet
|
|
109
123
|
end
|
110
124
|
end
|
111
125
|
end
|
112
|
-
|
113
|
-
# Allows for delayed construction of #match.
|
126
|
+
|
127
|
+
# Allows for delayed construction of #match. See also Parslet.match.
|
114
128
|
#
|
115
|
-
class DelayedMatchConstructor
|
129
|
+
class DelayedMatchConstructor # :nodoc:
|
116
130
|
def [](str)
|
117
131
|
Atoms::Re.new("[" + str + "]")
|
118
132
|
end
|
119
133
|
end
|
120
134
|
|
121
|
-
# Returns an atom matching a character class.
|
122
|
-
#
|
135
|
+
# Returns an atom matching a character class. All regular expressions can be
|
136
|
+
# used, as long as they match only a single character at a time.
|
123
137
|
#
|
124
138
|
# Example:
|
125
139
|
#
|
126
140
|
# match('[ab]') # will match either 'a' or 'b'
|
127
141
|
# match('[\n\s]') # will match newlines and spaces
|
128
142
|
#
|
143
|
+
# There is also another (convenience) form of this method:
|
144
|
+
#
|
145
|
+
# match['a-z'] # synonymous to match('[a-z]')
|
146
|
+
# match['\n'] # synonymous to match('[\n]')
|
147
|
+
#
|
129
148
|
def match(str=nil)
|
130
149
|
return DelayedMatchConstructor.new unless str
|
131
150
|
|
@@ -144,7 +163,10 @@ module Parslet
|
|
144
163
|
end
|
145
164
|
module_function :str
|
146
165
|
|
147
|
-
# Returns an atom matching any character.
|
166
|
+
# Returns an atom matching any character. It acts like the '.' (dot)
|
167
|
+
# character in regular expressions.
|
168
|
+
#
|
169
|
+
# any.parse('a') # => 'a'
|
148
170
|
#
|
149
171
|
def any
|
150
172
|
Atoms::Re.new('.')
|
@@ -158,7 +180,7 @@ module Parslet
|
|
158
180
|
#
|
159
181
|
# exp(%Q("a" "b"?)) # => returns the same as str('a') >> str('b').maybe
|
160
182
|
#
|
161
|
-
def exp(str)
|
183
|
+
def exp(str) # :nodoc:
|
162
184
|
Parslet::Expression.new(str).to_parslet
|
163
185
|
end
|
164
186
|
module_function :exp
|
@@ -202,6 +224,7 @@ module Parslet
|
|
202
224
|
def subtree(symbol)
|
203
225
|
Pattern::SubtreeBind.new(symbol)
|
204
226
|
end
|
227
|
+
module_function :subtree
|
205
228
|
|
206
229
|
autoload :Expression, 'parslet/expression'
|
207
230
|
end
|
data/lib/parslet/atoms.rb
CHANGED
@@ -2,7 +2,7 @@ module Parslet::Atoms
|
|
2
2
|
# The precedence module controls parenthesis during the #inspect printing
|
3
3
|
# of parslets. It is not relevant to other aspects of the parsing.
|
4
4
|
#
|
5
|
-
module Precedence
|
5
|
+
module Precedence # :nodoc:
|
6
6
|
prec = 0
|
7
7
|
BASE = (prec+=1) # everything else
|
8
8
|
LOOKAHEAD = (prec+=1) # &SOMETHING
|
@@ -8,16 +8,27 @@
|
|
8
8
|
#
|
9
9
|
class Parslet::Atoms::Alternative < Parslet::Atoms::Base
|
10
10
|
attr_reader :alternatives
|
11
|
+
|
12
|
+
# Constructs an Alternative instance using all given parslets in the order
|
13
|
+
# given. This is what happens if you call '|' on existing parslets, like
|
14
|
+
# this:
|
15
|
+
#
|
16
|
+
# str('a') | str('b')
|
17
|
+
#
|
11
18
|
def initialize(*alternatives)
|
12
19
|
@alternatives = alternatives
|
13
20
|
end
|
14
|
-
|
15
|
-
|
21
|
+
|
22
|
+
#---
|
23
|
+
# Don't construct a hanging tree of Alternative parslets, instead store them
|
24
|
+
# all here. This reduces the number of objects created.
|
25
|
+
#+++
|
26
|
+
def |(parslet) # :nodoc:
|
16
27
|
@alternatives << parslet
|
17
28
|
self
|
18
29
|
end
|
19
30
|
|
20
|
-
def try(io)
|
31
|
+
def try(io) # :nodoc:
|
21
32
|
alternatives.each { |a|
|
22
33
|
begin
|
23
34
|
return a.apply(io)
|
@@ -29,11 +40,11 @@ class Parslet::Atoms::Alternative < Parslet::Atoms::Base
|
|
29
40
|
end
|
30
41
|
|
31
42
|
precedence ALTERNATE
|
32
|
-
def to_s_inner(prec)
|
33
|
-
alternatives.map { |a| a.to_s(prec) }.join('
|
43
|
+
def to_s_inner(prec) # :nodoc:
|
44
|
+
alternatives.map { |a| a.to_s(prec) }.join(' / ')
|
34
45
|
end
|
35
46
|
|
36
|
-
def error_tree
|
47
|
+
def error_tree # :nodoc:
|
37
48
|
Parslet::ErrorTree.new(self, *alternatives.
|
38
49
|
map { |child| child.error_tree })
|
39
50
|
end
|
data/lib/parslet/atoms/base.rb
CHANGED
@@ -4,6 +4,10 @@
|
|
4
4
|
class Parslet::Atoms::Base
|
5
5
|
include Parslet::Atoms::Precedence
|
6
6
|
|
7
|
+
# Given a string or an IO object, this will attempt a parse of its contents
|
8
|
+
# and return a result. If the parse fails, a Parslet::ParseFailed exception
|
9
|
+
# will be thrown.
|
10
|
+
#
|
7
11
|
def parse(io)
|
8
12
|
if io.respond_to? :to_str
|
9
13
|
io = StringIO.new(io)
|
@@ -27,7 +31,7 @@ class Parslet::Atoms::Base
|
|
27
31
|
return flatten(result)
|
28
32
|
end
|
29
33
|
|
30
|
-
def apply(io)
|
34
|
+
def apply(io) # :nodoc:
|
31
35
|
# p [:start, self, io.string[io.pos, 10]]
|
32
36
|
|
33
37
|
old_pos = io.pos
|
@@ -43,30 +47,86 @@ class Parslet::Atoms::Base
|
|
43
47
|
io.pos = old_pos; raise ex
|
44
48
|
end
|
45
49
|
end
|
46
|
-
|
50
|
+
|
51
|
+
# Construct a new atom that repeats the current atom min times at least and
|
52
|
+
# at most max times. max can be nil to indicate that no maximum is present.
|
53
|
+
#
|
54
|
+
# Example:
|
55
|
+
# # match any number of 'a's
|
56
|
+
# str('a').repeat
|
57
|
+
#
|
58
|
+
# # match between 1 and 3 'a's
|
59
|
+
# str('a').repeat(1,3)
|
60
|
+
#
|
47
61
|
def repeat(min=0, max=nil)
|
48
62
|
Parslet::Atoms::Repetition.new(self, min, max)
|
49
63
|
end
|
64
|
+
|
65
|
+
# Returns a new parslet atom that is only maybe present in the input. This
|
66
|
+
# is synonymous to calling #repeat(0,1). Generated tree value will be
|
67
|
+
# either nil (if atom is not present in the input) or the matched subtree.
|
68
|
+
#
|
69
|
+
# Example:
|
70
|
+
# str('foo').maybe
|
71
|
+
#
|
50
72
|
def maybe
|
51
73
|
Parslet::Atoms::Repetition.new(self, 0, 1, :maybe)
|
52
74
|
end
|
75
|
+
|
76
|
+
# Chains two parslet atoms together as a sequence.
|
77
|
+
#
|
78
|
+
# Example:
|
79
|
+
# str('a') >> str('b')
|
80
|
+
#
|
53
81
|
def >>(parslet)
|
54
82
|
Parslet::Atoms::Sequence.new(self, parslet)
|
55
83
|
end
|
84
|
+
|
85
|
+
# Chains two parslet atoms together to express alternation. A match will
|
86
|
+
# always be attempted with the parslet on the left side first. If it doesn't
|
87
|
+
# match, the right side will be tried.
|
88
|
+
#
|
89
|
+
# Example:
|
90
|
+
# # matches either 'a' OR 'b'
|
91
|
+
# str('a') | str('b')
|
92
|
+
#
|
56
93
|
def |(parslet)
|
57
94
|
Parslet::Atoms::Alternative.new(self, parslet)
|
58
95
|
end
|
96
|
+
|
97
|
+
# Tests for absence of a parslet atom in the input stream without consuming
|
98
|
+
# it.
|
99
|
+
#
|
100
|
+
# Example:
|
101
|
+
# # Only proceed the parse if 'a' is absent.
|
102
|
+
# str('a').absnt?
|
103
|
+
#
|
59
104
|
def absnt?
|
60
105
|
Parslet::Atoms::Lookahead.new(self, false)
|
61
106
|
end
|
107
|
+
|
108
|
+
# Tests for presence of a parslet atom in the input stream without consuming
|
109
|
+
# it.
|
110
|
+
#
|
111
|
+
# Example:
|
112
|
+
# # Only proceed the parse if 'a' is present.
|
113
|
+
# str('a').prsnt?
|
114
|
+
#
|
62
115
|
def prsnt?
|
63
116
|
Parslet::Atoms::Lookahead.new(self, true)
|
64
117
|
end
|
118
|
+
|
119
|
+
# Marks a parslet atom as important for the tree output. This must be used
|
120
|
+
# to achieve meaningful output from the #parse method.
|
121
|
+
#
|
122
|
+
# Example:
|
123
|
+
# str('a').as(:b) # will produce {:b => 'a'}
|
124
|
+
#
|
65
125
|
def as(name)
|
66
126
|
Parslet::Atoms::Named.new(self, name)
|
67
127
|
end
|
68
128
|
|
69
|
-
def flatten(value)
|
129
|
+
def flatten(value) # :nodoc:
|
70
130
|
# Passes through everything that isn't an array of things
|
71
131
|
return value unless value.instance_of? Array
|
72
132
|
|
@@ -88,12 +148,13 @@ class Parslet::Atoms::Base
|
|
88
148
|
|
89
149
|
fail "BUG: Unknown tag #{tag.inspect}."
|
90
150
|
end
|
91
|
-
|
151
|
+
|
152
|
+
def flatten_sequence(list) # :nodoc:
|
92
153
|
list.compact.inject('') { |r, e| # and then merge flat elements
|
93
154
|
merge_fold(r, e)
|
94
155
|
}
|
95
156
|
end
|
96
|
-
def merge_fold(l, r)
|
157
|
+
def merge_fold(l, r) # :nodoc:
|
97
158
|
# equal pairs: merge.
|
98
159
|
if l.class == r.class
|
99
160
|
if l.is_a?(Hash)
|
@@ -117,7 +178,7 @@ class Parslet::Atoms::Base
|
|
117
178
|
fail "Unhandled case when foldr'ing sequence."
|
118
179
|
end
|
119
180
|
|
120
|
-
def flatten_repetition(list)
|
181
|
+
def flatten_repetition(list) # :nodoc:
|
121
182
|
if list.any? { |e| e.instance_of?(Hash) }
|
122
183
|
# If keyed subtrees are in the array, we'll want to discard all
|
123
184
|
# strings inbetween. To keep them, name them.
|
@@ -136,18 +197,18 @@ class Parslet::Atoms::Base
|
|
136
197
|
list.inject('') { |s,e| s<<(e||'') }
|
137
198
|
end
|
138
199
|
|
139
|
-
def self.precedence(prec)
|
200
|
+
def self.precedence(prec) # :nodoc:
|
140
201
|
define_method(:precedence) { prec }
|
141
202
|
end
|
142
203
|
precedence BASE
|
143
|
-
def to_s(outer_prec)
|
204
|
+
def to_s(outer_prec=OUTER) # :nodoc:
|
144
205
|
if outer_prec < precedence
|
145
206
|
"("+to_s_inner(precedence)+")"
|
146
207
|
else
|
147
208
|
to_s_inner(precedence)
|
148
209
|
end
|
149
210
|
end
|
150
|
-
def inspect
|
211
|
+
def inspect # :nodoc:
|
151
212
|
to_s(OUTER)
|
152
213
|
end
|
153
214
|
|
@@ -155,7 +216,7 @@ class Parslet::Atoms::Base
|
|
155
216
|
# of what went wrong with the parse. Not relevant if the parse succeeds,
|
156
217
|
# but needed for clever error reports.
|
157
218
|
#
|
158
|
-
def cause
|
219
|
+
def cause # :nodoc:
|
159
220
|
@last_cause
|
160
221
|
end
|
161
222
|
|
@@ -166,7 +227,7 @@ class Parslet::Atoms::Base
|
|
166
227
|
def error_tree
|
167
228
|
Parslet::ErrorTree.new(self) if cause?
|
168
229
|
end
|
169
|
-
def cause?
|
230
|
+
def cause? # :nodoc:
|
170
231
|
not @last_cause.nil?
|
171
232
|
end
|
172
233
|
private
|
data/lib/parslet/atoms/entity.rb
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
# This wraps pieces of parslet definition and gives them a name. The wrapped
|
2
2
|
# piece is lazily evaluated and cached. This has two purposes:
|
3
3
|
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# complicated content.
|
4
|
+
# * Avoid infinite recursion during evaluation of the definition
|
5
|
+
# * Be able to print things by their name, not by their sometimes
|
6
|
+
# complicated content.
|
8
7
|
#
|
9
8
|
# You don't normally use this directly, instead you should generated it by
|
10
|
-
# using the structuring method Parslet
|
9
|
+
# using the structuring method Parslet.rule.
|
11
10
|
#
|
12
11
|
class Parslet::Atoms::Entity < Parslet::Atoms::Base
|
13
12
|
attr_reader :name, :context, :block
|
14
|
-
def initialize(name, context, block)
|
13
|
+
def initialize(name, context, block) # :nodoc:
|
15
14
|
super()
|
16
15
|
|
17
16
|
@name = name
|
@@ -19,7 +18,7 @@ class Parslet::Atoms::Entity < Parslet::Atoms::Base
|
|
19
18
|
@block = block
|
20
19
|
end
|
21
20
|
|
22
|
-
def try(io)
|
21
|
+
def try(io) # :nodoc:
|
23
22
|
parslet.apply(io)
|
24
23
|
end
|
25
24
|
|
@@ -29,16 +28,16 @@ class Parslet::Atoms::Entity < Parslet::Atoms::Base
|
|
29
28
|
}
|
30
29
|
end
|
31
30
|
|
32
|
-
def to_s_inner(prec)
|
31
|
+
def to_s_inner(prec) # :nodoc:
|
33
32
|
name.to_s.upcase
|
34
33
|
end
|
35
34
|
|
36
|
-
def error_tree
|
35
|
+
def error_tree # :nodoc:
|
37
36
|
parslet.error_tree
|
38
37
|
end
|
39
38
|
|
40
39
|
private
|
41
|
-
def raise_not_implemented
|
40
|
+
def raise_not_implemented # :nodoc:
|
42
41
|
trace = caller.reject {|l| l =~ %r{#{Regexp.escape(__FILE__)}}} # blatantly stolen from dependencies.rb in activesupport
|
43
42
|
exception = NotImplementedError.new("rule(#{name.inspect}) { ... } returns nil. Still not implemented, but already used?")
|
44
43
|
exception.set_backtrace(trace)
|
@@ -8,13 +8,13 @@ class Parslet::Atoms::Lookahead < Parslet::Atoms::Base
|
|
8
8
|
attr_reader :positive
|
9
9
|
attr_reader :bound_parslet
|
10
10
|
|
11
|
-
def initialize(bound_parslet, positive=true)
|
12
|
-
#
|
11
|
+
def initialize(bound_parslet, positive=true) # :nodoc:
|
12
|
+
# Model positive and negative lookahead by testing this flag.
|
13
13
|
@positive = positive
|
14
14
|
@bound_parslet = bound_parslet
|
15
15
|
end
|
16
16
|
|
17
|
-
def try(io)
|
17
|
+
def try(io) # :nodoc:
|
18
18
|
pos = io.pos
|
19
19
|
begin
|
20
20
|
bound_parslet.apply(io)
|
@@ -26,7 +26,7 @@ class Parslet::Atoms::Lookahead < Parslet::Atoms::Base
|
|
26
26
|
return success(io)
|
27
27
|
end
|
28
28
|
|
29
|
-
def fail(io)
|
29
|
+
def fail(io) # :nodoc:
|
30
30
|
if positive
|
31
31
|
error(io, "lookahead: #{bound_parslet.inspect} didn't match, but should have")
|
32
32
|
else
|
@@ -34,7 +34,7 @@ class Parslet::Atoms::Lookahead < Parslet::Atoms::Base
|
|
34
34
|
return nil
|
35
35
|
end
|
36
36
|
end
|
37
|
-
def success(io)
|
37
|
+
def success(io) # :nodoc:
|
38
38
|
if positive
|
39
39
|
return nil # see above, TODO
|
40
40
|
else
|
@@ -45,13 +45,13 @@ class Parslet::Atoms::Lookahead < Parslet::Atoms::Base
|
|
45
45
|
end
|
46
46
|
|
47
47
|
precedence LOOKAHEAD
|
48
|
-
def to_s_inner(prec)
|
48
|
+
def to_s_inner(prec) # :nodoc:
|
49
49
|
char = positive ? '&' : '!'
|
50
50
|
|
51
51
|
"#{char}#{bound_parslet.to_s(prec)}"
|
52
52
|
end
|
53
53
|
|
54
|
-
def error_tree
|
54
|
+
def error_tree # :nodoc:
|
55
55
|
bound_parslet.error_tree
|
56
56
|
end
|
57
57
|
end
|
data/lib/parslet/atoms/named.rb
CHANGED
@@ -7,25 +7,25 @@
|
|
7
7
|
#
|
8
8
|
class Parslet::Atoms::Named < Parslet::Atoms::Base
|
9
9
|
attr_reader :parslet, :name
|
10
|
-
def initialize(parslet, name)
|
10
|
+
def initialize(parslet, name) # :nodoc:
|
11
11
|
@parslet, @name = parslet, name
|
12
12
|
end
|
13
13
|
|
14
|
-
def apply(io)
|
14
|
+
def apply(io) # :nodoc:
|
15
15
|
value = parslet.apply(io)
|
16
16
|
|
17
17
|
produce_return_value value
|
18
18
|
end
|
19
19
|
|
20
|
-
def to_s_inner(prec)
|
20
|
+
def to_s_inner(prec) # :nodoc:
|
21
21
|
"#{name}:#{parslet.to_s(prec)}"
|
22
22
|
end
|
23
23
|
|
24
|
-
def error_tree
|
24
|
+
def error_tree # :nodoc:
|
25
25
|
parslet.error_tree
|
26
26
|
end
|
27
27
|
private
|
28
|
-
def produce_return_value(val)
|
28
|
+
def produce_return_value(val) # :nodoc:
|
29
29
|
{ name => flatten(val) }
|
30
30
|
end
|
31
31
|
end
|
data/lib/parslet/atoms/re.rb
CHANGED
@@ -9,11 +9,11 @@
|
|
9
9
|
#
|
10
10
|
class Parslet::Atoms::Re < Parslet::Atoms::Base
|
11
11
|
attr_reader :match
|
12
|
-
def initialize(match)
|
12
|
+
def initialize(match) # :nodoc:
|
13
13
|
@match = match
|
14
14
|
end
|
15
15
|
|
16
|
-
def try(io)
|
16
|
+
def try(io) # :nodoc:
|
17
17
|
r = Regexp.new(match, Regexp::MULTILINE)
|
18
18
|
s = io.read(1)
|
19
19
|
error(io, "Premature end of input") unless s
|
@@ -21,7 +21,7 @@ class Parslet::Atoms::Re < Parslet::Atoms::Base
|
|
21
21
|
return s
|
22
22
|
end
|
23
23
|
|
24
|
-
def to_s_inner(prec)
|
24
|
+
def to_s_inner(prec) # :nodoc:
|
25
25
|
match.inspect[1..-2]
|
26
26
|
end
|
27
27
|
end
|
@@ -14,7 +14,7 @@ class Parslet::Atoms::Repetition < Parslet::Atoms::Base
|
|
14
14
|
@tag = tag
|
15
15
|
end
|
16
16
|
|
17
|
-
def try(io)
|
17
|
+
def try(io) # :nodoc:
|
18
18
|
occ = 0
|
19
19
|
result = [@tag] # initialize the result array with the tag (for flattening)
|
20
20
|
loop do
|
@@ -36,18 +36,18 @@ class Parslet::Atoms::Repetition < Parslet::Atoms::Base
|
|
36
36
|
end
|
37
37
|
|
38
38
|
precedence REPETITION
|
39
|
-
def to_s_inner(prec)
|
39
|
+
def to_s_inner(prec) # :nodoc:
|
40
40
|
minmax = "{#{min}, #{max}}"
|
41
41
|
minmax = '?' if min == 0 && max == 1
|
42
42
|
|
43
43
|
parslet.to_s(prec) + minmax
|
44
44
|
end
|
45
45
|
|
46
|
-
def cause
|
46
|
+
def cause # :nodoc:
|
47
47
|
# Either the repetition failed or the parslet inside failed to repeat.
|
48
48
|
super || parslet.cause
|
49
49
|
end
|
50
|
-
def error_tree
|
50
|
+
def error_tree # :nodoc:
|
51
51
|
if cause?
|
52
52
|
Parslet::ErrorTree.new(self, parslet.error_tree)
|
53
53
|
else
|
@@ -10,12 +10,12 @@ class Parslet::Atoms::Sequence < Parslet::Atoms::Base
|
|
10
10
|
@parslets = parslets
|
11
11
|
end
|
12
12
|
|
13
|
-
def >>(parslet)
|
13
|
+
def >>(parslet) # :nodoc:
|
14
14
|
@parslets << parslet
|
15
15
|
self
|
16
16
|
end
|
17
17
|
|
18
|
-
def try(io)
|
18
|
+
def try(io) # :nodoc:
|
19
19
|
[:sequence]+parslets.map { |p|
|
20
20
|
# Save each parslet as potentially offending (raising an error).
|
21
21
|
@offending_parslet = p
|
@@ -26,11 +26,11 @@ class Parslet::Atoms::Sequence < Parslet::Atoms::Base
|
|
26
26
|
end
|
27
27
|
|
28
28
|
precedence SEQUENCE
|
29
|
-
def to_s_inner(prec)
|
29
|
+
def to_s_inner(prec) # :nodoc:
|
30
30
|
parslets.map { |p| p.to_s(prec) }.join(' ')
|
31
31
|
end
|
32
32
|
|
33
|
-
def error_tree
|
33
|
+
def error_tree # :nodoc:
|
34
34
|
Parslet::ErrorTree.new(self).tap { |t|
|
35
35
|
t.children << @offending_parslet.error_tree if @offending_parslet }
|
36
36
|
end
|
data/lib/parslet/atoms/str.rb
CHANGED
@@ -10,7 +10,7 @@ class Parslet::Atoms::Str < Parslet::Atoms::Base
|
|
10
10
|
@str = str
|
11
11
|
end
|
12
12
|
|
13
|
-
def try(io)
|
13
|
+
def try(io) # :nodoc:
|
14
14
|
old_pos = io.pos
|
15
15
|
s = io.read(str.size)
|
16
16
|
error(io, "Premature end of input") unless s && s.size==str.size
|
@@ -19,7 +19,7 @@ class Parslet::Atoms::Str < Parslet::Atoms::Base
|
|
19
19
|
return s
|
20
20
|
end
|
21
21
|
|
22
|
-
def to_s_inner(prec)
|
22
|
+
def to_s_inner(prec) # :nodoc:
|
23
23
|
"'#{str}'"
|
24
24
|
end
|
25
25
|
end
|
data/lib/parslet/error_tree.rb
CHANGED
@@ -8,7 +8,7 @@ class Parslet::ErrorTree
|
|
8
8
|
# All errors that were encountered when parsing part of this +parslet+.
|
9
9
|
attr_reader :children
|
10
10
|
|
11
|
-
def initialize(parslet, *children)
|
11
|
+
def initialize(parslet, *children) # :nodoc:
|
12
12
|
@parslet = parslet
|
13
13
|
@children = children.compact
|
14
14
|
end
|
@@ -31,7 +31,7 @@ class Parslet::ErrorTree
|
|
31
31
|
end
|
32
32
|
alias to_s ascii_tree
|
33
33
|
private
|
34
|
-
def recursive_ascii_tree(node, stream, curved)
|
34
|
+
def recursive_ascii_tree(node, stream, curved) # :nodoc:
|
35
35
|
append_prefix(stream, curved)
|
36
36
|
stream.puts node.cause
|
37
37
|
|
@@ -41,7 +41,7 @@ private
|
|
41
41
|
recursive_ascii_tree(child, stream, curved + [last_child])
|
42
42
|
end
|
43
43
|
end
|
44
|
-
def append_prefix(stream, curved)
|
44
|
+
def append_prefix(stream, curved) # :nodoc:
|
45
45
|
curved[0..-2].each do |c|
|
46
46
|
stream.print c ? " " : "| "
|
47
47
|
end
|
data/lib/parslet/expression.rb
CHANGED
@@ -8,12 +8,18 @@
|
|
8
8
|
#
|
9
9
|
# NOT FINISHED & EXPERIMENTAL
|
10
10
|
#
|
11
|
-
class Parslet::Expression
|
11
|
+
class Parslet::Expression # :nodoc:
|
12
12
|
include Parslet
|
13
13
|
|
14
14
|
autoload :Treetop, 'parslet/expression/treetop'
|
15
15
|
|
16
|
-
|
16
|
+
# Creates a parslet from a foreign language expression.
|
17
|
+
#
|
18
|
+
# Example:
|
19
|
+
#
|
20
|
+
# Parslet::Expression.new("'a' 'b'")
|
21
|
+
#
|
22
|
+
def initialize(str, opts={}, context=self)
|
17
23
|
@type = opts[:type] || :treetop
|
18
24
|
@exp = str
|
19
25
|
@parslet = transform(
|
@@ -27,6 +33,9 @@ class Parslet::Expression
|
|
27
33
|
|
28
34
|
# pp tree
|
29
35
|
transform.apply(tree)
|
36
|
+
rescue
|
37
|
+
warn "Could not transform: " + tree.inspect
|
38
|
+
raise
|
30
39
|
end
|
31
40
|
|
32
41
|
# Parses the string and returns a parse tree.
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class Parslet::Expression::Treetop
|
2
|
-
class Parser < Parslet::Parser
|
2
|
+
class Parser < Parslet::Parser # :nodoc:
|
3
3
|
root(:expression)
|
4
4
|
|
5
5
|
rule(:expression) { alternatives }
|
@@ -10,18 +10,37 @@ class Parslet::Expression::Treetop
|
|
10
10
|
}
|
11
11
|
|
12
12
|
# sequence by simple concatenation 'a' 'b'
|
13
|
-
rule(:simple) {
|
14
|
-
|
15
|
-
|
13
|
+
rule(:simple) { occurrence.repeat(1).as(:seq) }
|
14
|
+
|
15
|
+
# occurrence modifiers
|
16
|
+
rule(:occurrence) {
|
17
|
+
atom.as(:repetition) >> spaced('*').as(:sign) |
|
18
|
+
atom.as(:repetition) >> spaced('+').as(:sign) |
|
19
|
+
atom.as(:repetition) >> repetition_spec |
|
20
|
+
|
16
21
|
atom.as(:maybe) >> spaced('?') |
|
17
22
|
atom
|
18
23
|
}
|
19
|
-
|
24
|
+
|
20
25
|
rule(:atom) {
|
21
26
|
spaced('(') >> expression.as(:unwrap) >> spaced(')') |
|
22
|
-
|
27
|
+
dot |
|
28
|
+
string |
|
29
|
+
char_class
|
23
30
|
}
|
24
|
-
|
31
|
+
|
32
|
+
# a character class
|
33
|
+
rule(:char_class) {
|
34
|
+
(str('[') >>
|
35
|
+
(str('\\') >> any |
|
36
|
+
str(']').absnt? >> any).repeat(1) >>
|
37
|
+
str(']')).as(:match) >> space?
|
38
|
+
}
|
39
|
+
|
40
|
+
# anything at all
|
41
|
+
rule(:dot) { spaced('.').as(:any) }
|
42
|
+
|
43
|
+
# recognizing strings
|
25
44
|
rule(:string) {
|
26
45
|
str('\'') >>
|
27
46
|
(
|
@@ -31,6 +50,17 @@ class Parslet::Expression::Treetop
|
|
31
50
|
str('\'') >> space?
|
32
51
|
}
|
33
52
|
|
53
|
+
# repetition specification like {1, 2}
|
54
|
+
rule(:repetition_spec) {
|
55
|
+
spaced('{') >>
|
56
|
+
integer.maybe.as(:min) >> spaced(',') >>
|
57
|
+
integer.maybe.as(:max) >> spaced('}')
|
58
|
+
}
|
59
|
+
rule(:integer) {
|
60
|
+
match['0-9'].repeat(1)
|
61
|
+
}
|
62
|
+
|
63
|
+
# whitespace handling
|
34
64
|
rule(:space) { match("\s").repeat(1) }
|
35
65
|
rule(:space?) { space.maybe }
|
36
66
|
|
@@ -39,12 +69,23 @@ class Parslet::Expression::Treetop
|
|
39
69
|
end
|
40
70
|
end
|
41
71
|
|
42
|
-
class Transform <
|
43
|
-
|
44
|
-
rule(:
|
45
|
-
|
46
|
-
|
47
|
-
rule(:
|
72
|
+
class Transform < Parslet::Transform # :nodoc:
|
73
|
+
|
74
|
+
rule(:repetition => simple(:rep), :sign => simple(:sign)) {
|
75
|
+
min = sign=='+' ? 1 : 0
|
76
|
+
Parslet::Atoms::Repetition.new(rep, min, nil) }
|
77
|
+
rule(:repetition => simple(:rep), :min => simple(:min), :max => simple(:max)) {
|
78
|
+
Parslet::Atoms::Repetition.new(rep,
|
79
|
+
Integer(min || 0),
|
80
|
+
max && Integer(max) || nil) }
|
81
|
+
|
82
|
+
rule(:alt => subtree(:alt)) { Parslet::Atoms::Alternative.new(*alt) }
|
83
|
+
rule(:seq => sequence(:s)) { Parslet::Atoms::Sequence.new(*s) }
|
84
|
+
rule(:unwrap => simple(:u)) { u }
|
85
|
+
rule(:maybe => simple(:m)) { |d| d[:m].maybe }
|
86
|
+
rule(:string => simple(:s)) { Parslet::Atoms::Str.new(s) }
|
87
|
+
rule(:match => simple(:m)) { Parslet::Atoms::Re.new(m) }
|
88
|
+
rule(:any => simple(:a)) { Parslet::Atoms::Re.new('.') }
|
48
89
|
end
|
49
90
|
|
50
91
|
end
|
data/lib/parslet/pattern.rb
CHANGED
@@ -25,29 +25,6 @@ class Parslet::Pattern
|
|
25
25
|
@pattern = pattern
|
26
26
|
end
|
27
27
|
|
28
|
-
# Searches the given +tree+ for this pattern, yielding the subtrees that
|
29
|
-
# match to the block.
|
30
|
-
#
|
31
|
-
# Example:
|
32
|
-
#
|
33
|
-
# tree = parslet.apply(input)
|
34
|
-
# pat = Parslet::Pattern.new(:_x)
|
35
|
-
# pat.each_match(tree) do |subtree|
|
36
|
-
# # do something with the matching subtree here
|
37
|
-
# end
|
38
|
-
#
|
39
|
-
def each_match(tree, &block) # :yield: subtree
|
40
|
-
raise ArgumentError, "Must pass a block" unless block
|
41
|
-
|
42
|
-
recurse_into(tree) do |subtree|
|
43
|
-
if bindings=match(subtree)
|
44
|
-
call_on_match(subtree, bindings, block)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
return nil
|
49
|
-
end
|
50
|
-
|
51
28
|
# Decides if the given subtree matches this pattern. Returns the bindings
|
52
29
|
# made on a successful match or nil if the match fails.
|
53
30
|
#
|
@@ -60,7 +37,10 @@ class Parslet::Pattern
|
|
60
37
|
# can be made. Contains the logic that will switch to instance variables
|
61
38
|
# depending on the arity of the block.
|
62
39
|
#
|
63
|
-
|
40
|
+
#---
|
41
|
+
# TODO This method should be in Transform.
|
42
|
+
#
|
43
|
+
def call_on_match(bindings, block)
|
64
44
|
if block
|
65
45
|
if block.arity == 1
|
66
46
|
return block.call(bindings)
|
@@ -71,23 +51,9 @@ class Parslet::Pattern
|
|
71
51
|
end
|
72
52
|
end
|
73
53
|
|
74
|
-
# Handles preorder, depth-first recursion through the +expr+ given.
|
75
|
-
#
|
76
|
-
def recurse_into(expr, &block)
|
77
|
-
# p [:attempt_match, expr]
|
78
|
-
block.call(expr)
|
79
|
-
|
80
|
-
case expr
|
81
|
-
when Array
|
82
|
-
expr.each { |y| recurse_into(y, &block) }
|
83
|
-
when Hash
|
84
|
-
expr.each { |k,v| recurse_into(v, &block) }
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
54
|
# Returns true if the tree element given by +tree+ matches the expression
|
89
55
|
# given by +exp+. This match must respect bindings already made in
|
90
|
-
# +bindings+.
|
56
|
+
# +bindings+. Note that bindings is carried along and modified.
|
91
57
|
#
|
92
58
|
def element_match(tree, exp, bindings)
|
93
59
|
# p [:elm, tree, exp]
|
@@ -111,7 +77,7 @@ class Parslet::Pattern
|
|
111
77
|
end
|
112
78
|
end
|
113
79
|
|
114
|
-
def element_match_binding(tree, exp, bindings)
|
80
|
+
def element_match_binding(tree, exp, bindings) # :nodoc:
|
115
81
|
var_name = exp.variable_name
|
116
82
|
|
117
83
|
# TODO test for the hidden :_ feature.
|
@@ -125,7 +91,7 @@ class Parslet::Pattern
|
|
125
91
|
return true
|
126
92
|
end
|
127
93
|
|
128
|
-
def element_match_ary_single(sequence, exp, bindings)
|
94
|
+
def element_match_ary_single(sequence, exp, bindings) # :nodoc:
|
129
95
|
return false if sequence.size != exp.size
|
130
96
|
|
131
97
|
return sequence.zip(exp).all? { |elt, subexp|
|
@@ -133,22 +99,18 @@ class Parslet::Pattern
|
|
133
99
|
end
|
134
100
|
|
135
101
|
def element_match_hash(tree, exp, bindings)
|
136
|
-
#
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
exp_value = exp[tree_key]
|
102
|
+
# p [:emh, tree, exp, bindings]
|
103
|
+
|
104
|
+
# We iterate over expected pattern, since we demand that the keys that
|
105
|
+
# are there should be in tree as well.
|
106
|
+
exp.each do |expected_key, expected_value|
|
107
|
+
return false unless tree.has_key? expected_key
|
143
108
|
|
144
|
-
# Recurse into the
|
145
|
-
|
146
|
-
|
147
|
-
return false
|
148
|
-
end
|
109
|
+
# Recurse into the value and stop early on failure
|
110
|
+
value = tree[expected_key]
|
111
|
+
return false unless element_match(value, expected_value, bindings)
|
149
112
|
end
|
150
113
|
|
151
|
-
# Match succeeds
|
152
114
|
return true
|
153
|
-
end
|
115
|
+
end
|
154
116
|
end
|
@@ -5,7 +5,7 @@
|
|
5
5
|
# It defines the most permissive kind of bind, the one that matches any subtree
|
6
6
|
# whatever it looks like.
|
7
7
|
#
|
8
|
-
class Parslet::Pattern::SubtreeBind < Struct.new(:symbol)
|
8
|
+
class Parslet::Pattern::SubtreeBind < Struct.new(:symbol) # :nodoc:
|
9
9
|
def variable_name
|
10
10
|
symbol
|
11
11
|
end
|
@@ -33,7 +33,7 @@ end
|
|
33
33
|
# Binds a symbol to a simple subtree, one that is not either a sequence of
|
34
34
|
# elements or a collection of attributes.
|
35
35
|
#
|
36
|
-
class Parslet::Pattern::SimpleBind < Parslet::Pattern::SubtreeBind
|
36
|
+
class Parslet::Pattern::SimpleBind < Parslet::Pattern::SubtreeBind # :nodoc:
|
37
37
|
def can_bind?(subtree)
|
38
38
|
not [Hash, Array].include?(subtree.class)
|
39
39
|
end
|
@@ -41,7 +41,7 @@ end
|
|
41
41
|
|
42
42
|
# Binds a symbol to a sequence of simple leafs ([element1, element2, ...])
|
43
43
|
#
|
44
|
-
class Parslet::Pattern::SequenceBind < Parslet::Pattern::SubtreeBind
|
44
|
+
class Parslet::Pattern::SequenceBind < Parslet::Pattern::SubtreeBind # :nodoc:
|
45
45
|
def can_bind?(subtree)
|
46
46
|
subtree.kind_of?(Array) &&
|
47
47
|
(not subtree.any? { |el| [Hash, Array].include?(el.class) })
|
data/lib/parslet/transform.rb
CHANGED
@@ -46,10 +46,13 @@ require 'parslet/pattern'
|
|
46
46
|
# tree that looks like this:
|
47
47
|
#
|
48
48
|
# {
|
49
|
-
# :
|
50
|
-
# :
|
51
|
-
#
|
52
|
-
#
|
49
|
+
# l: '(',
|
50
|
+
# m: {
|
51
|
+
# l: '(',
|
52
|
+
# m: nil,
|
53
|
+
# r: ')'
|
54
|
+
# },
|
55
|
+
# r: ')'
|
53
56
|
# }
|
54
57
|
#
|
55
58
|
# This parse tree is good for debugging, but what we would really like to have
|
@@ -100,12 +103,12 @@ class Parslet::Transform
|
|
100
103
|
|
101
104
|
# Allows accessing the class' rules
|
102
105
|
#
|
103
|
-
def rules
|
106
|
+
def rules # :nodoc:
|
104
107
|
@__transform_rules || []
|
105
108
|
end
|
106
109
|
end
|
107
110
|
|
108
|
-
def initialize(&block)
|
111
|
+
def initialize(&block) # :nodoc:
|
109
112
|
@rules = []
|
110
113
|
|
111
114
|
if block
|
@@ -113,6 +116,12 @@ class Parslet::Transform
|
|
113
116
|
end
|
114
117
|
end
|
115
118
|
|
119
|
+
# Defines a rule to be applied whenever apply is called on a tree. A rule
|
120
|
+
# is composed of two parts:
|
121
|
+
#
|
122
|
+
# * an *expression pattern*
|
123
|
+
# * a *transformation block*
|
124
|
+
#
|
116
125
|
def rule(expression, &block)
|
117
126
|
@rules << [
|
118
127
|
Parslet::Pattern.new(expression),
|
@@ -120,6 +129,10 @@ class Parslet::Transform
|
|
120
129
|
]
|
121
130
|
end
|
122
131
|
|
132
|
+
# Applies the transformation to a tree that is generated by Parslet::Parser
|
133
|
+
# or a simple parslet. Transformation will proceed down the tree, replacing
|
134
|
+
# parts/all of it with new objects. The resulting object will be returned.
|
135
|
+
#
|
123
136
|
def apply(obj)
|
124
137
|
transform_elt(
|
125
138
|
case obj
|
@@ -136,28 +149,28 @@ class Parslet::Transform
|
|
136
149
|
# Allow easy access to all rules, the ones defined in the instance and the
|
137
150
|
# ones predefined in a subclass definition.
|
138
151
|
#
|
139
|
-
def rules
|
152
|
+
def rules # :nodoc:
|
140
153
|
self.class.rules + @rules
|
141
154
|
end
|
142
155
|
|
143
|
-
def transform_elt(elt)
|
156
|
+
def transform_elt(elt) # :nodoc:
|
144
157
|
rules.each do |pattern, block|
|
145
158
|
if bindings=pattern.match(elt)
|
146
159
|
# Produces transformed value
|
147
|
-
return pattern.call_on_match(
|
160
|
+
return pattern.call_on_match(bindings, block)
|
148
161
|
end
|
149
162
|
end
|
150
163
|
|
151
164
|
# No rule matched - element is not transformed
|
152
165
|
return elt
|
153
166
|
end
|
154
|
-
def recurse_hash(hsh)
|
167
|
+
def recurse_hash(hsh) # :nodoc:
|
155
168
|
hsh.inject({}) do |new_hsh, (k,v)|
|
156
169
|
new_hsh[k] = apply(v)
|
157
170
|
new_hsh
|
158
171
|
end
|
159
172
|
end
|
160
|
-
def recurse_array(ary)
|
173
|
+
def recurse_array(ary) # :nodoc:
|
161
174
|
ary.map { |elt| apply(elt) }
|
162
175
|
end
|
163
176
|
end
|
metadata
CHANGED
@@ -3,10 +3,10 @@ name: parslet
|
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
|
+
- 1
|
6
7
|
- 0
|
7
|
-
- 11
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 1.0.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Kaspar Schiess
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-12-29 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|