parslet 1.0.1 → 1.1.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 +3 -4
- data/HISTORY.txt +17 -3
- data/README +3 -2
- data/Rakefile +12 -55
- data/example/email_parser.rb +9 -5
- data/example/erb.rb +44 -0
- data/example/minilisp.rb +11 -17
- data/example/parens.rb +3 -1
- data/lib/parslet/atoms/alternative.rb +7 -5
- data/lib/parslet/atoms/base.rb +90 -59
- data/lib/parslet/atoms/context.rb +48 -0
- data/lib/parslet/atoms/entity.rb +2 -2
- data/lib/parslet/atoms/lookahead.rb +17 -29
- data/lib/parslet/atoms/named.rb +10 -5
- data/lib/parslet/atoms/re.rb +16 -7
- data/lib/parslet/atoms/repetition.rb +16 -9
- data/lib/parslet/atoms/sequence.rb +15 -9
- data/lib/parslet/atoms/str.rb +17 -7
- data/lib/parslet/atoms/visitor.rb +75 -0
- data/lib/parslet/atoms.rb +13 -9
- data/lib/parslet/convenience.rb +33 -0
- data/lib/parslet/export.rb +162 -0
- data/lib/parslet/expression.rb +3 -3
- data/lib/parslet/pattern.rb +2 -2
- data/lib/parslet/rig/rspec.rb +2 -2
- data/lib/parslet/source.rb +112 -0
- data/lib/parslet.rb +1 -2
- metadata +24 -7
@@ -9,44 +9,32 @@ class Parslet::Atoms::Lookahead < Parslet::Atoms::Base
|
|
9
9
|
attr_reader :bound_parslet
|
10
10
|
|
11
11
|
def initialize(bound_parslet, positive=true) # :nodoc:
|
12
|
+
super()
|
13
|
+
|
12
14
|
# Model positive and negative lookahead by testing this flag.
|
13
15
|
@positive = positive
|
14
16
|
@bound_parslet = bound_parslet
|
17
|
+
@error_msgs = {
|
18
|
+
:positive => "lookahead: #{bound_parslet.inspect} didn't match, but should have",
|
19
|
+
:negative => "negative lookahead: #{bound_parslet.inspect} matched, but shouldn't have"
|
20
|
+
}
|
15
21
|
end
|
16
22
|
|
17
|
-
def try(
|
18
|
-
pos =
|
19
|
-
|
20
|
-
failed = true
|
21
|
-
catch(:error) {
|
22
|
-
bound_parslet.apply(io)
|
23
|
-
failed = false
|
24
|
-
}
|
25
|
-
return failed ? fail(io) : success(io)
|
23
|
+
def try(source, context) # :nodoc:
|
24
|
+
pos = source.pos
|
26
25
|
|
26
|
+
value = bound_parslet.apply(source, context)
|
27
|
+
return success(nil) if positive ^ value.error?
|
28
|
+
|
29
|
+
return error(source, @error_msgs[:positive]) if positive
|
30
|
+
return error(source, @error_msgs[:negative])
|
31
|
+
|
32
|
+
# This is probably the only parslet that rewinds its input in #try.
|
33
|
+
# Lookaheads NEVER consume their input, even on success, that's why.
|
27
34
|
ensure
|
28
|
-
|
35
|
+
source.pos = pos
|
29
36
|
end
|
30
37
|
|
31
|
-
# TODO Both of these will produce results that could be reduced easily.
|
32
|
-
# Maybe do some shortcut reducing here?
|
33
|
-
def fail(io) # :nodoc:
|
34
|
-
if positive
|
35
|
-
error(io, "lookahead: #{bound_parslet.inspect} didn't match, but should have")
|
36
|
-
else
|
37
|
-
return nil
|
38
|
-
end
|
39
|
-
end
|
40
|
-
def success(io) # :nodoc:
|
41
|
-
if positive
|
42
|
-
return nil
|
43
|
-
else
|
44
|
-
error(
|
45
|
-
io,
|
46
|
-
"negative lookahead: #{bound_parslet.inspect} matched, but shouldn't have")
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
38
|
precedence LOOKAHEAD
|
51
39
|
def to_s_inner(prec) # :nodoc:
|
52
40
|
char = positive ? '&' : '!'
|
data/lib/parslet/atoms/named.rb
CHANGED
@@ -8,13 +8,18 @@
|
|
8
8
|
class Parslet::Atoms::Named < Parslet::Atoms::Base
|
9
9
|
attr_reader :parslet, :name
|
10
10
|
def initialize(parslet, name) # :nodoc:
|
11
|
+
super()
|
12
|
+
|
11
13
|
@parslet, @name = parslet, name
|
12
14
|
end
|
13
15
|
|
14
|
-
def apply(
|
15
|
-
value = parslet.apply(
|
16
|
-
|
17
|
-
|
16
|
+
def apply(source, context) # :nodoc:
|
17
|
+
value = parslet.apply(source, context)
|
18
|
+
|
19
|
+
return value if value.error?
|
20
|
+
success(
|
21
|
+
produce_return_value(
|
22
|
+
value.result))
|
18
23
|
end
|
19
24
|
|
20
25
|
def to_s_inner(prec) # :nodoc:
|
@@ -26,6 +31,6 @@ class Parslet::Atoms::Named < Parslet::Atoms::Base
|
|
26
31
|
end
|
27
32
|
private
|
28
33
|
def produce_return_value(val) # :nodoc:
|
29
|
-
{ name => flatten(val) }
|
34
|
+
{ name => flatten(val, true) }
|
30
35
|
end
|
31
36
|
end
|
data/lib/parslet/atoms/re.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Matches a special kind of regular expression that only ever matches one
|
2
|
-
# character at a time. Useful members of this family are: character
|
3
|
-
#
|
2
|
+
# character at a time. Useful members of this family are: <code>character
|
3
|
+
# ranges, \\w, \\d, \\r, \\n, ...</code>
|
4
4
|
#
|
5
5
|
# Example:
|
6
6
|
#
|
@@ -10,15 +10,24 @@
|
|
10
10
|
class Parslet::Atoms::Re < Parslet::Atoms::Base
|
11
11
|
attr_reader :match, :re
|
12
12
|
def initialize(match) # :nodoc:
|
13
|
+
super()
|
14
|
+
|
13
15
|
@match = match
|
14
16
|
@re = Regexp.new(match, Regexp::MULTILINE)
|
17
|
+
@error_msgs = {
|
18
|
+
:premature => "Premature end of input",
|
19
|
+
:failed => "Failed to match #{match.inspect[1..-2]}"
|
20
|
+
}
|
15
21
|
end
|
16
22
|
|
17
|
-
def try(
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
return s
|
23
|
+
def try(source, context) # :nodoc:
|
24
|
+
error_pos = source.pos
|
25
|
+
s = source.read(1)
|
26
|
+
|
27
|
+
return error(source, @error_msgs[:premature], error_pos) unless s
|
28
|
+
return error(source, @error_msgs[:failed], error_pos) unless s.match(re)
|
29
|
+
|
30
|
+
return success(s)
|
22
31
|
end
|
23
32
|
|
24
33
|
def to_s_inner(prec) # :nodoc:
|
@@ -9,29 +9,36 @@
|
|
9
9
|
class Parslet::Atoms::Repetition < Parslet::Atoms::Base
|
10
10
|
attr_reader :min, :max, :parslet
|
11
11
|
def initialize(parslet, min, max, tag=:repetition)
|
12
|
+
super()
|
13
|
+
|
12
14
|
@parslet = parslet
|
13
15
|
@min, @max = min, max
|
14
16
|
@tag = tag
|
17
|
+
@error_msgs = {
|
18
|
+
:minrep => "Expected at least #{min} of #{parslet.inspect}"
|
19
|
+
}
|
15
20
|
end
|
16
21
|
|
17
|
-
def try(
|
22
|
+
def try(source, context) # :nodoc:
|
18
23
|
occ = 0
|
19
24
|
result = [@tag] # initialize the result array with the tag (for flattening)
|
20
|
-
|
21
|
-
|
25
|
+
start_pos = source.pos
|
26
|
+
loop do
|
27
|
+
value = parslet.apply(source, context)
|
28
|
+
break if value.error?
|
29
|
+
|
22
30
|
occ += 1
|
31
|
+
result << value.result
|
23
32
|
|
24
33
|
# If we're not greedy (max is defined), check if that has been
|
25
34
|
# reached.
|
26
|
-
return result if max && occ>=max
|
27
|
-
|
28
|
-
}
|
35
|
+
return success(result) if max && occ>=max
|
36
|
+
end
|
29
37
|
|
30
38
|
# Greedy matcher has produced a failure. Check if occ (which will
|
31
39
|
# contain the number of sucesses) is in {min, max}.
|
32
|
-
|
33
|
-
|
34
|
-
return result
|
40
|
+
return error(source, @error_msgs[:minrep], start_pos) if occ < min
|
41
|
+
return success(result)
|
35
42
|
end
|
36
43
|
|
37
44
|
precedence REPETITION
|
@@ -7,7 +7,12 @@
|
|
7
7
|
class Parslet::Atoms::Sequence < Parslet::Atoms::Base
|
8
8
|
attr_reader :parslets
|
9
9
|
def initialize(*parslets)
|
10
|
+
super()
|
11
|
+
|
10
12
|
@parslets = parslets
|
13
|
+
@error_msgs = {
|
14
|
+
:failed => "Failed to match sequence (#{self.inspect})"
|
15
|
+
}
|
11
16
|
end
|
12
17
|
|
13
18
|
def >>(parslet) # :nodoc:
|
@@ -15,16 +20,17 @@ class Parslet::Atoms::Sequence < Parslet::Atoms::Base
|
|
15
20
|
self
|
16
21
|
end
|
17
22
|
|
18
|
-
def try(
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
def try(source, context) # :nodoc:
|
24
|
+
success([:sequence]+parslets.map { |p|
|
25
|
+
# Save each parslet as potentially offending (raising an error).
|
26
|
+
@offending_parslet = p
|
27
|
+
|
28
|
+
value = p.apply(source, context)
|
29
|
+
|
30
|
+
return error(source, @error_msgs[:failed]) if value.error?
|
26
31
|
|
27
|
-
|
32
|
+
value.result
|
33
|
+
})
|
28
34
|
end
|
29
35
|
|
30
36
|
precedence SEQUENCE
|
data/lib/parslet/atoms/str.rb
CHANGED
@@ -7,16 +7,26 @@
|
|
7
7
|
class Parslet::Atoms::Str < Parslet::Atoms::Base
|
8
8
|
attr_reader :str
|
9
9
|
def initialize(str)
|
10
|
+
super()
|
11
|
+
|
10
12
|
@str = str
|
13
|
+
@error_msgs = {
|
14
|
+
:premature => "Premature end of input",
|
15
|
+
:failed => "Expected #{str.inspect}, but got "
|
16
|
+
}
|
11
17
|
end
|
12
18
|
|
13
|
-
def try(
|
14
|
-
|
15
|
-
s =
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
19
|
+
def try(source, context) # :nodoc:
|
20
|
+
error_pos = source.pos
|
21
|
+
s = source.read(str.size)
|
22
|
+
|
23
|
+
return success(s) if s == str
|
24
|
+
|
25
|
+
# assert: s != str
|
26
|
+
|
27
|
+
# Failures:
|
28
|
+
return error(source, @error_msgs[:premature]) unless s && s.size==str.size
|
29
|
+
return error(source, @error_msgs[:failed]+s.inspect, error_pos)
|
20
30
|
end
|
21
31
|
|
22
32
|
def to_s_inner(prec) # :nodoc:
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Augments all parslet atoms with an accept method that will call back
|
2
|
+
# to the visitor given.
|
3
|
+
|
4
|
+
#
|
5
|
+
module Parslet::Atoms
|
6
|
+
class Base
|
7
|
+
def accept(visitor)
|
8
|
+
raise NotImplementedError, "No visit method on #{self.class.name}."
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Str
|
13
|
+
# Call back visitors #str method. See parslet/export for an example.
|
14
|
+
#
|
15
|
+
def accept(visitor)
|
16
|
+
visitor.str(str)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Entity
|
21
|
+
# Call back visitors #entity method. See parslet/export for an example.
|
22
|
+
#
|
23
|
+
def accept(visitor)
|
24
|
+
visitor.entity(name, context, block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Named
|
29
|
+
# Call back visitors #named method. See parslet/export for an example.
|
30
|
+
#
|
31
|
+
def accept(visitor)
|
32
|
+
visitor.named(name, parslet)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Sequence
|
37
|
+
# Call back visitors #sequence method. See parslet/export for an example.
|
38
|
+
#
|
39
|
+
def accept(visitor)
|
40
|
+
visitor.sequence(parslets)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Repetition
|
45
|
+
# Call back visitors #repetition method. See parslet/export for an example.
|
46
|
+
#
|
47
|
+
def accept(visitor)
|
48
|
+
visitor.repetition(min, max, parslet)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Alternative
|
53
|
+
# Call back visitors #alternative method. See parslet/export for an example.
|
54
|
+
#
|
55
|
+
def accept(visitor)
|
56
|
+
visitor.alternative(alternatives)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Lookahead
|
61
|
+
# Call back visitors #lookahead method. See parslet/export for an example.
|
62
|
+
#
|
63
|
+
def accept(visitor)
|
64
|
+
visitor.lookahead(positive, bound_parslet)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Re
|
69
|
+
# Call back visitors #re method. See parslet/export for an example.
|
70
|
+
#
|
71
|
+
def accept(visitor)
|
72
|
+
visitor.re(match)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/parslet/atoms.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
|
2
|
+
# This is where parslets name comes from: Small parser atoms.
|
3
|
+
#
|
1
4
|
module Parslet::Atoms
|
2
5
|
# The precedence module controls parenthesis during the #inspect printing
|
3
6
|
# of parslets. It is not relevant to other aspects of the parsing.
|
@@ -12,14 +15,15 @@ module Parslet::Atoms
|
|
12
15
|
OUTER = (prec+=1) # printing is done here.
|
13
16
|
end
|
14
17
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
require 'parslet/atoms/context'
|
19
|
+
require 'parslet/atoms/base'
|
20
|
+
require 'parslet/atoms/named'
|
21
|
+
require 'parslet/atoms/lookahead'
|
22
|
+
require 'parslet/atoms/alternative'
|
23
|
+
require 'parslet/atoms/sequence'
|
24
|
+
require 'parslet/atoms/repetition'
|
25
|
+
require 'parslet/atoms/re'
|
26
|
+
require 'parslet/atoms/str'
|
27
|
+
require 'parslet/atoms/entity'
|
24
28
|
end
|
25
29
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Parslet::Parser
|
2
|
+
|
3
|
+
# Packages the common idiom
|
4
|
+
#
|
5
|
+
# begin
|
6
|
+
# tree = parser.parse('something')
|
7
|
+
# rescue Parslet::ParseFailed => error
|
8
|
+
# puts error
|
9
|
+
# puts parser.root.error_tree
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# into a convenient method.
|
13
|
+
#
|
14
|
+
# Usage:
|
15
|
+
#
|
16
|
+
# require 'parslet'
|
17
|
+
# require 'parslet/convenience'
|
18
|
+
#
|
19
|
+
# class FooParser < Parslet::Parser
|
20
|
+
# rule(:foo) { str('foo') }
|
21
|
+
# root(:foo)
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# FooParser.new.parse_with_debug('bar')
|
25
|
+
#
|
26
|
+
def parse_with_debug str
|
27
|
+
parse str
|
28
|
+
rescue Parslet::ParseFailed => error
|
29
|
+
puts error
|
30
|
+
puts root.error_tree
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
# Allows exporting parslet grammars to other lingos.
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require 'parslet/atoms/visitor'
|
5
|
+
|
6
|
+
class Parslet::Parser
|
7
|
+
module Visitors
|
8
|
+
class Citrus
|
9
|
+
attr_reader :context, :output
|
10
|
+
def initialize(context)
|
11
|
+
@context = context
|
12
|
+
end
|
13
|
+
|
14
|
+
def str(str)
|
15
|
+
"\"#{str.inspect[1..-2]}\""
|
16
|
+
end
|
17
|
+
def re(match)
|
18
|
+
match.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def entity(name, ctx, block)
|
22
|
+
context.deferred(name, [ctx, block])
|
23
|
+
|
24
|
+
"(#{context.mangle_name(name)})"
|
25
|
+
end
|
26
|
+
def named(name, parslet)
|
27
|
+
parslet.accept(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def sequence(parslets)
|
31
|
+
'(' <<
|
32
|
+
parslets.
|
33
|
+
map { |el| el.accept(self) }.
|
34
|
+
join(' ') <<
|
35
|
+
')'
|
36
|
+
end
|
37
|
+
def repetition(min, max, parslet)
|
38
|
+
parslet.accept(self) << "#{min}*#{max}"
|
39
|
+
end
|
40
|
+
def alternative(alternatives)
|
41
|
+
'(' <<
|
42
|
+
alternatives.
|
43
|
+
map { |el| el.accept(self) }.
|
44
|
+
join(' | ') <<
|
45
|
+
')'
|
46
|
+
end
|
47
|
+
|
48
|
+
def lookahead(positive, bound_parslet)
|
49
|
+
(positive ? '&' : '!') <<
|
50
|
+
bound_parslet.accept(self)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Treetop < Citrus
|
55
|
+
def repetition(min, max, parslet)
|
56
|
+
parslet.accept(self) << "#{min}..#{max}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def alternative(alternatives)
|
60
|
+
'(' <<
|
61
|
+
alternatives.
|
62
|
+
map { |el| el.accept(self) }.
|
63
|
+
join(' / ') <<
|
64
|
+
')'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# A helper class that formats Citrus and Treetop grammars as a string.
|
70
|
+
#
|
71
|
+
class PrettyPrinter # :nodoc:
|
72
|
+
attr_reader :visitor
|
73
|
+
def initialize(visitor_klass)
|
74
|
+
@visitor = visitor_klass.new(self)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Pretty prints the given parslet using the visitor that has been
|
78
|
+
# configured in initialize. Returns the string representation of the
|
79
|
+
# Citrus or Treetop grammar.
|
80
|
+
#
|
81
|
+
def pretty_print(name, parslet) # :nodoc:
|
82
|
+
output = "grammar #{name}\n"
|
83
|
+
|
84
|
+
output << rule('root', parslet)
|
85
|
+
|
86
|
+
seen = Set.new
|
87
|
+
loop do
|
88
|
+
# @todo is constantly filled by the visitor (see #deferred). We
|
89
|
+
# keep going until it is empty.
|
90
|
+
break if @todo.empty?
|
91
|
+
name, (context, block) = @todo.shift
|
92
|
+
|
93
|
+
# Track what rules we've already seen. This breaks loops.
|
94
|
+
next if seen.include?(name)
|
95
|
+
seen << name
|
96
|
+
|
97
|
+
output << rule(name, context.instance_eval(&block))
|
98
|
+
end
|
99
|
+
|
100
|
+
output << "end\n"
|
101
|
+
end
|
102
|
+
|
103
|
+
# Formats a rule in either dialect.
|
104
|
+
#
|
105
|
+
def rule(name, parslet)
|
106
|
+
" rule #{mangle_name name}\n" <<
|
107
|
+
" " << parslet.accept(visitor) << "\n" <<
|
108
|
+
" end\n"
|
109
|
+
end
|
110
|
+
|
111
|
+
# Whenever the visitor encounters an rule in a parslet, it defers the
|
112
|
+
# pretty printing of the rule by calling this method.
|
113
|
+
#
|
114
|
+
def deferred(name, content) # :nodoc:
|
115
|
+
@todo ||= []
|
116
|
+
@todo << [name, content]
|
117
|
+
end
|
118
|
+
|
119
|
+
# Mangles names so that Citrus and Treetop can live with it. This mostly
|
120
|
+
# transforms some of the things that Ruby allows into other patterns. If
|
121
|
+
# there is collision, we will not detect it for now.
|
122
|
+
#
|
123
|
+
def mangle_name(str) # :nodoc:
|
124
|
+
str.to_s.sub(/\?$/, '_p')
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Exports the current parser instance as a string in the Citrus dialect.
|
129
|
+
#
|
130
|
+
# Example:
|
131
|
+
#
|
132
|
+
# require 'parslet/export'
|
133
|
+
# class MyParser < Parslet::Parser
|
134
|
+
# root(:expression)
|
135
|
+
# rule(:expression) { str('foo') }
|
136
|
+
# end
|
137
|
+
#
|
138
|
+
# MyParser.new.to_citrus # => a citrus grammar as a string
|
139
|
+
#
|
140
|
+
def to_citrus
|
141
|
+
PrettyPrinter.new(Visitors::Citrus).
|
142
|
+
pretty_print(self.class.name, root)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Exports the current parser instance as a string in the Treetop dialect.
|
146
|
+
#
|
147
|
+
# Example:
|
148
|
+
#
|
149
|
+
# require 'parslet/export'
|
150
|
+
# class MyParser < Parslet::Parser
|
151
|
+
# root(:expression)
|
152
|
+
# rule(:expression) { str('foo') }
|
153
|
+
# end
|
154
|
+
#
|
155
|
+
# MyParser.new.to_treetop # => a treetop grammar as a string
|
156
|
+
#
|
157
|
+
def to_treetop
|
158
|
+
PrettyPrinter.new(Visitors::Treetop).
|
159
|
+
pretty_print(self.class.name, root)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
data/lib/parslet/expression.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
|
2
2
|
# Allows specifying rules as strings using the exact same grammar that treetop
|
3
|
-
# does, minus the actions. This is on one hand a good example of a fully
|
4
|
-
# parser and on the other hand might even turn out really useful.
|
3
|
+
# does, minus the actions. This is on one hand a good example of a fully
|
4
|
+
# fledged parser and on the other hand might even turn out really useful.
|
5
5
|
#
|
6
6
|
# This can be viewed as an extension to parslet and might even be hosted in
|
7
7
|
# its own gem one fine day.
|
8
8
|
#
|
9
9
|
# NOT FINISHED & EXPERIMENTAL
|
10
10
|
#
|
11
|
-
class Parslet::Expression
|
11
|
+
class Parslet::Expression
|
12
12
|
include Parslet
|
13
13
|
|
14
14
|
autoload :Treetop, 'parslet/expression/treetop'
|
data/lib/parslet/pattern.rb
CHANGED
@@ -99,8 +99,8 @@ class Parslet::Pattern
|
|
99
99
|
end
|
100
100
|
|
101
101
|
def element_match_hash(tree, exp, bindings)
|
102
|
-
# Early failure when
|
103
|
-
return false unless exp.
|
102
|
+
# Early failure when one hash is bigger than the other
|
103
|
+
return false unless exp.size == tree.size
|
104
104
|
|
105
105
|
# We iterate over expected pattern, since we demand that the keys that
|
106
106
|
# are there should be in tree as well.
|
data/lib/parslet/rig/rspec.rb
CHANGED
@@ -13,12 +13,12 @@ RSpec::Matchers.define(:parse) do |input|
|
|
13
13
|
failure_message_for_should do |is|
|
14
14
|
"expected " << (@result ?
|
15
15
|
"output of parsing #{input.inspect} with #{is.inspect} to equal #{@as.inspect}, but was #{@result.inspect}" :
|
16
|
-
"
|
16
|
+
"#{is.inspect} to be able to parse #{input.inspect}")
|
17
17
|
end
|
18
18
|
|
19
19
|
failure_message_for_should_not do |is|
|
20
20
|
"expected " << (@as ?
|
21
21
|
"output of parsing #{input.inspect} with #{is.inspect} not to equal #{@as.inspect}" :
|
22
|
-
"
|
22
|
+
"#{is.inspect} to not parse #{input.inspect}, but it did")
|
23
23
|
end
|
24
24
|
end
|