parslet 1.7.1 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/HISTORY.txt +14 -0
- data/lib/parslet.rb +18 -9
- data/lib/parslet/accelerator.rb +1 -0
- data/lib/parslet/atoms.rb +1 -0
- data/lib/parslet/atoms/base.rb +2 -2
- data/lib/parslet/atoms/can_flatten.rb +1 -1
- data/lib/parslet/atoms/dsl.rb +11 -0
- data/lib/parslet/atoms/entity.rb +1 -0
- data/lib/parslet/atoms/ignored.rb +26 -0
- data/lib/parslet/atoms/infix.rb +5 -4
- data/lib/parslet/atoms/lookahead.rb +1 -1
- data/lib/parslet/cause.rb +2 -2
- data/lib/parslet/context.rb +2 -16
- data/lib/parslet/convenience.rb +2 -2
- data/lib/parslet/error_reporter/deepest.rb +1 -1
- data/lib/parslet/parser.rb +2 -1
- data/lib/parslet/pattern.rb +4 -5
- data/lib/parslet/rig/rspec.rb +5 -12
- data/lib/parslet/slice.rb +1 -1
- data/lib/parslet/source/line_cache.rb +1 -0
- data/lib/parslet/transform.rb +11 -3
- data/parslet.gemspec +2 -4
- data/spec/acceptance/examples_spec.rb +2 -2
- data/spec/acceptance/infix_parser_spec.rb +9 -0
- data/spec/parslet/atoms/ignored_spec.rb +15 -0
- data/spec/parslet/atoms_spec.rb +3 -3
- data/spec/parslet/transform/context_spec.rb +37 -16
- data/spec/parslet/transform_spec.rb +23 -5
- data/spec/spec_helper.rb +3 -1
- metadata +7 -25
- data/example/ignore.rb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 930ed2d329af700f3465739d6db3e2897ef64fbc
|
4
|
+
data.tar.gz: ea14cb80ebd03f9b6b9e55aa461021ffaa79a622
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0872f8510339d1dfd97c4060ac4298115f2270765b192804e7722e27f93c90df38b915af6cc867792ecfca3137fd8291b4d7ac854b20849884dd9214185d837f
|
7
|
+
data.tar.gz: 76bb599af2b8df83c53dccbc4bd49652b1a49c3046c9e5bd6f37dd8e30dfe66db07142eb66032886cf0dd78b55bb3763840962d6c41b956e8394e3ca2bd7fcf4
|
data/HISTORY.txt
CHANGED
@@ -1,7 +1,21 @@
|
|
1
|
+
|
1
2
|
= 2.0 / ?? (future release changes, like a reminder to self)
|
2
3
|
|
3
4
|
- prsnt? and absnt? are now finally banned into oblivion. Wasting vocals for
|
4
5
|
the win.
|
6
|
+
|
7
|
+
= 1.8 / 3Apr2017
|
8
|
+
|
9
|
+
+ The `ignored` atom that allows to ignore a part of the matched text.
|
10
|
+
`str('foo').ignore` will match 'foo', but not yield any parse output.
|
11
|
+
Thanks to chrismwendt (Chris Wendt).
|
12
|
+
+ Infix expression parser (arithmetics, anyone?) now supports custom reducers
|
13
|
+
in block form. Thanks to chrismwendt (Chris Wendt).
|
14
|
+
! Small patches to memory footprint (Christophe Bliard).
|
15
|
+
- blankslate dependency removed. You should be good - but if things break,
|
16
|
+
please let us know (Nikita Shilnikov).
|
17
|
+
! Parslet now has `parse_failure_cause`, replaces the earlier `cause`.
|
18
|
+
! Fixes all these interpreter warnings on modern rubies.
|
5
19
|
|
6
20
|
= 1.7 / 12Mar2015
|
7
21
|
|
data/lib/parslet.rb
CHANGED
@@ -55,13 +55,13 @@ module Parslet
|
|
55
55
|
|
56
56
|
# Raised when the parse failed to match. It contains the message that should
|
57
57
|
# be presented to the user. More details can be extracted from the
|
58
|
-
# exceptions #
|
58
|
+
# exceptions #parse_failure_cause member: It contains an instance of {Parslet::Cause} that
|
59
59
|
# stores all the details of your failed parse in a tree structure.
|
60
60
|
#
|
61
61
|
# begin
|
62
62
|
# parslet.parse(str)
|
63
63
|
# rescue Parslet::ParseFailed => failure
|
64
|
-
# puts failure.
|
64
|
+
# puts failure.parse_failure_cause.ascii_tree
|
65
65
|
# end
|
66
66
|
#
|
67
67
|
# Alternatively, you can just require 'parslet/convenience' and call the
|
@@ -72,15 +72,15 @@ module Parslet
|
|
72
72
|
# parslet.parse_with_debug(str)
|
73
73
|
#
|
74
74
|
class ParseFailed < StandardError
|
75
|
-
def initialize(message,
|
75
|
+
def initialize(message, parse_failure_cause=nil)
|
76
76
|
super(message)
|
77
|
-
@
|
77
|
+
@parse_failure_cause = parse_failure_cause
|
78
78
|
end
|
79
79
|
|
80
80
|
# Why the parse failed.
|
81
81
|
#
|
82
82
|
# @return [Parslet::Cause]
|
83
|
-
attr_reader :
|
83
|
+
attr_reader :parse_failure_cause
|
84
84
|
end
|
85
85
|
|
86
86
|
module ClassMethods
|
@@ -100,6 +100,7 @@ module Parslet
|
|
100
100
|
# end
|
101
101
|
#
|
102
102
|
def rule(name, opts={}, &definition)
|
103
|
+
undef_method name if method_defined? name
|
103
104
|
define_method(name) do
|
104
105
|
@rules ||= {} # <name, rule> memoization
|
105
106
|
return @rules[name] if @rules.has_key?(name)
|
@@ -217,18 +218,26 @@ module Parslet
|
|
217
218
|
# associativity is chosen, it would be interpreted as '1 + (2 + 3)'. Note
|
218
219
|
# that the hash trees output reflect that choice as well.
|
219
220
|
#
|
220
|
-
#
|
221
|
+
# An optional block can be provided in order to manipulate the generated tree.
|
222
|
+
# The block will be called on each operator and passed 3 arguments: the left
|
223
|
+
# operand, the operator, and the right operand.
|
224
|
+
#
|
225
|
+
# Examples:
|
221
226
|
# infix_expression(integer, [add_op, 1, :left])
|
222
227
|
# # would parse things like '1 + 2'
|
223
228
|
#
|
229
|
+
# infix_expression(integer, [add_op, 1, :left]) { |l,o,r| { :plus => [l, r] } }
|
230
|
+
# # would parse '1 + 2 + 3' as:
|
231
|
+
# # { :plus => [1, { :plus => [2, 3] }] }
|
232
|
+
#
|
224
233
|
# @param element [Parslet::Atoms::Base] elements that take the NUMBER position
|
225
234
|
# in the expression
|
226
235
|
# @param operations [Array<(Parslet::Atoms::Base, Integer, {:left, :right})>]
|
227
236
|
#
|
228
237
|
# @see Parslet::Atoms::Infix
|
229
238
|
#
|
230
|
-
def infix_expression(element, *operations)
|
231
|
-
Parslet::Atoms::Infix.new(element, operations)
|
239
|
+
def infix_expression(element, *operations, &reducer)
|
240
|
+
Parslet::Atoms::Infix.new(element, operations, &reducer)
|
232
241
|
end
|
233
242
|
module_function :infix_expression
|
234
243
|
|
@@ -299,4 +308,4 @@ require 'parslet/pattern/binding'
|
|
299
308
|
require 'parslet/transform'
|
300
309
|
require 'parslet/parser'
|
301
310
|
require 'parslet/error_reporter'
|
302
|
-
require 'parslet/scope'
|
311
|
+
require 'parslet/scope'
|
data/lib/parslet/accelerator.rb
CHANGED
data/lib/parslet/atoms.rb
CHANGED
@@ -19,6 +19,7 @@ module Parslet::Atoms
|
|
19
19
|
require 'parslet/atoms/context'
|
20
20
|
require 'parslet/atoms/dsl'
|
21
21
|
require 'parslet/atoms/base'
|
22
|
+
require 'parslet/atoms/ignored'
|
22
23
|
require 'parslet/atoms/named'
|
23
24
|
require 'parslet/atoms/lookahead'
|
24
25
|
require 'parslet/atoms/alternative'
|
data/lib/parslet/atoms/base.rb
CHANGED
@@ -83,7 +83,7 @@ class Parslet::Atoms::Base
|
|
83
83
|
def apply(source, context, consume_all=false)
|
84
84
|
old_pos = source.bytepos
|
85
85
|
|
86
|
-
success,
|
86
|
+
success, _ = result = context.try_with_cache(self, source, consume_all)
|
87
87
|
|
88
88
|
if success
|
89
89
|
# Notify context
|
@@ -137,7 +137,7 @@ class Parslet::Atoms::Base
|
|
137
137
|
end
|
138
138
|
precedence BASE
|
139
139
|
def to_s(outer_prec=OUTER)
|
140
|
-
str =
|
140
|
+
str = label || to_s_inner(precedence)
|
141
141
|
if outer_prec < precedence
|
142
142
|
"(#{str})"
|
143
143
|
else
|
@@ -120,7 +120,7 @@ module Parslet::Atoms
|
|
120
120
|
return [] if named && list.empty?
|
121
121
|
|
122
122
|
# If there are only strings, concatenate them and return that.
|
123
|
-
foldl(list) { |s,e| s+e }
|
123
|
+
foldl(list.compact) { |s,e| s+e }
|
124
124
|
end
|
125
125
|
|
126
126
|
# That annoying warning 'Duplicate subtrees while merging result' comes
|
data/lib/parslet/atoms/dsl.rb
CHANGED
@@ -35,6 +35,17 @@ module Parslet::Atoms::DSL
|
|
35
35
|
Parslet::Atoms::Repetition.new(self, 0, 1, :maybe)
|
36
36
|
end
|
37
37
|
|
38
|
+
# Returns a new parslet atom that will not show up in the output. This
|
39
|
+
# is synonymous to calling #repeat(0,1). Generated tree value will always be
|
40
|
+
# nil.
|
41
|
+
#
|
42
|
+
# Example:
|
43
|
+
# str('foo').ignore
|
44
|
+
#
|
45
|
+
def ignore
|
46
|
+
Parslet::Atoms::Ignored.new(self)
|
47
|
+
end
|
48
|
+
|
38
49
|
# Chains two parslet atoms together as a sequence.
|
39
50
|
#
|
40
51
|
# Example:
|
data/lib/parslet/atoms/entity.rb
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
# Ignores the result of a match.
|
2
|
+
#
|
3
|
+
# Example:
|
4
|
+
#
|
5
|
+
# str('foo') # will return 'foo',
|
6
|
+
# str('foo').ignore # will return nil
|
7
|
+
#
|
8
|
+
class Parslet::Atoms::Ignored < Parslet::Atoms::Base
|
9
|
+
attr_reader :parslet
|
10
|
+
def initialize(parslet)
|
11
|
+
super()
|
12
|
+
|
13
|
+
@parslet = parslet
|
14
|
+
end
|
15
|
+
|
16
|
+
def apply(source, context, consume_all)
|
17
|
+
success, _ = result = parslet.apply(source, context, consume_all)
|
18
|
+
|
19
|
+
return result unless success
|
20
|
+
succ(nil)
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s_inner(prec)
|
24
|
+
"ignored(#{parslet.to_s(prec)})"
|
25
|
+
end
|
26
|
+
end
|
data/lib/parslet/atoms/infix.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
class Parslet::Atoms::Infix < Parslet::Atoms::Base
|
2
|
-
attr_reader :element, :operations
|
2
|
+
attr_reader :element, :operations, :reducer
|
3
3
|
|
4
|
-
def initialize(element, operations)
|
4
|
+
def initialize(element, operations, &reducer)
|
5
5
|
super()
|
6
6
|
|
7
7
|
@element = element
|
8
8
|
@operations = operations
|
9
|
+
@reducer = reducer || lambda { |left, op, right| {l: left, o: op, r: right} }
|
9
10
|
end
|
10
11
|
|
11
12
|
def try(source, context, consume_all)
|
@@ -31,9 +32,9 @@ class Parslet::Atoms::Infix < Parslet::Atoms::Base
|
|
31
32
|
|
32
33
|
if right.kind_of? Array
|
33
34
|
# Subexpression -> Subhash
|
34
|
-
left =
|
35
|
+
left = reducer.call(left, op, produce_tree(right))
|
35
36
|
else
|
36
|
-
left =
|
37
|
+
left = reducer.call(left, op, right)
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
@@ -25,7 +25,7 @@ class Parslet::Atoms::Lookahead < Parslet::Atoms::Base
|
|
25
25
|
rewind_pos = source.bytepos
|
26
26
|
error_pos = source.pos
|
27
27
|
|
28
|
-
success,
|
28
|
+
success, _ = bound_parslet.apply(source, context, consume_all)
|
29
29
|
|
30
30
|
if positive
|
31
31
|
return succ(nil) if success
|
data/lib/parslet/cause.rb
CHANGED
@@ -5,8 +5,8 @@ module Parslet
|
|
5
5
|
#
|
6
6
|
class Cause
|
7
7
|
def initialize(message, source, pos, children)
|
8
|
-
@message, @source, @pos, @children =
|
9
|
-
message, source, pos, children
|
8
|
+
@message, @source, @pos, @children, @context =
|
9
|
+
message, source, pos, children, nil
|
10
10
|
end
|
11
11
|
|
12
12
|
# @return [String, Array] A string or an array of message pieces that
|
data/lib/parslet/context.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'blankslate'
|
2
|
-
|
3
1
|
# Provides a context for tree transformations to run in. The context allows
|
4
2
|
# accessing each of the bindings in the bindings hash as local method.
|
5
3
|
#
|
@@ -11,24 +9,12 @@ require 'blankslate'
|
|
11
9
|
# end
|
12
10
|
#
|
13
11
|
# @api private
|
14
|
-
class Parslet::Context
|
15
|
-
reveal :methods
|
16
|
-
reveal :respond_to?
|
17
|
-
reveal :inspect
|
18
|
-
reveal :to_s
|
19
|
-
reveal :instance_variable_set
|
20
|
-
|
12
|
+
class Parslet::Context
|
21
13
|
include Parslet
|
22
|
-
|
23
|
-
def meta_def(name, &body)
|
24
|
-
metaclass = class << self; self; end
|
25
14
|
|
26
|
-
metaclass.send(:define_method, name, &body)
|
27
|
-
end
|
28
|
-
|
29
15
|
def initialize(bindings)
|
30
16
|
bindings.each do |key, value|
|
31
|
-
|
17
|
+
singleton_class.send(:define_method, key) { value }
|
32
18
|
instance_variable_set("@#{key}", value)
|
33
19
|
end
|
34
20
|
end
|
data/lib/parslet/convenience.rb
CHANGED
@@ -5,7 +5,7 @@ class Parslet::Atoms::Base
|
|
5
5
|
# begin
|
6
6
|
# tree = parser.parse('something')
|
7
7
|
# rescue Parslet::ParseFailed => error
|
8
|
-
# puts parser.
|
8
|
+
# puts parser.parse_failure_cause.ascii_tree
|
9
9
|
# end
|
10
10
|
#
|
11
11
|
# into a convenient method.
|
@@ -27,7 +27,7 @@ class Parslet::Atoms::Base
|
|
27
27
|
def parse_with_debug str, opts={}
|
28
28
|
parse str, opts
|
29
29
|
rescue Parslet::ParseFailed => error
|
30
|
-
puts error.
|
30
|
+
puts error.parse_failure_cause.ascii_tree
|
31
31
|
end
|
32
32
|
|
33
33
|
end
|
@@ -64,7 +64,7 @@ module Parslet
|
|
64
64
|
# current deepest error that was saved as a reference.
|
65
65
|
#
|
66
66
|
def deepest(cause)
|
67
|
-
|
67
|
+
_, leaf = deepest_child(cause)
|
68
68
|
|
69
69
|
if !deepest_cause || leaf.pos >= deepest_cause.pos
|
70
70
|
# This error reaches deeper into the input, save it as reference.
|
data/lib/parslet/parser.rb
CHANGED
@@ -51,6 +51,7 @@ class Parslet::Parser < Parslet::Atoms::Base
|
|
51
51
|
# end
|
52
52
|
#
|
53
53
|
def root(name)
|
54
|
+
undef_method :root if method_defined? :root
|
54
55
|
define_method(:root) do
|
55
56
|
self.send(name)
|
56
57
|
end
|
@@ -64,4 +65,4 @@ class Parslet::Parser < Parslet::Atoms::Base
|
|
64
65
|
def to_s_inner(prec)
|
65
66
|
root.to_s(prec)
|
66
67
|
end
|
67
|
-
end
|
68
|
+
end
|
data/lib/parslet/pattern.rb
CHANGED
@@ -48,11 +48,10 @@ class Parslet::Pattern
|
|
48
48
|
#
|
49
49
|
def element_match(tree, exp, bindings)
|
50
50
|
# p [:elm, tree, exp]
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
return element_match_ary_single(tree, exp, bindings)
|
51
|
+
if tree.is_a?(Hash) && exp.is_a?(Hash)
|
52
|
+
return element_match_hash(tree, exp, bindings)
|
53
|
+
elsif tree.is_a?(Array) && exp.is_a?(Array)
|
54
|
+
return element_match_ary_single(tree, exp, bindings)
|
56
55
|
else
|
57
56
|
# If elements match exactly, then that is good enough in all cases
|
58
57
|
return true if exp === tree
|
data/lib/parslet/rig/rspec.rb
CHANGED
@@ -2,13 +2,6 @@ RSpec::Matchers.define(:parse) do |input, opts|
|
|
2
2
|
as = block = nil
|
3
3
|
result = trace = nil
|
4
4
|
|
5
|
-
unless self.respond_to? :failure_message # if RSpec 2.x
|
6
|
-
class << self
|
7
|
-
alias_method :failure_message, :failure_message_for_should
|
8
|
-
alias_method :failure_message_when_negated, :failure_message_for_should_not
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
5
|
match do |parser|
|
13
6
|
begin
|
14
7
|
result = parser.parse(input)
|
@@ -16,12 +9,12 @@ RSpec::Matchers.define(:parse) do |input, opts|
|
|
16
9
|
block.call(result) :
|
17
10
|
(as == result || as.nil?)
|
18
11
|
rescue Parslet::ParseFailed => ex
|
19
|
-
trace = ex.
|
12
|
+
trace = ex.parse_failure_cause.ascii_tree if opts && opts[:trace]
|
20
13
|
false
|
21
14
|
end
|
22
15
|
end
|
23
16
|
|
24
|
-
failure_message do |is|
|
17
|
+
public_send(respond_to?(:failure_message) ? :failure_message : :failure_message_for_should) do |is|
|
25
18
|
if block
|
26
19
|
"expected output of parsing #{input.inspect}" <<
|
27
20
|
" with #{is.inspect} to meet block conditions, but it didn't"
|
@@ -37,7 +30,7 @@ RSpec::Matchers.define(:parse) do |input, opts|
|
|
37
30
|
end
|
38
31
|
end
|
39
32
|
|
40
|
-
failure_message_when_negated do |is|
|
33
|
+
public_send(respond_to?(:failure_message_when_negated) ? :failure_message_when_negated : :failure_message_for_should_not) do |is|
|
41
34
|
if block
|
42
35
|
"expected output of parsing #{input.inspect} with #{is.inspect} not to meet block conditions, but it did"
|
43
36
|
else
|
@@ -52,8 +45,8 @@ RSpec::Matchers.define(:parse) do |input, opts|
|
|
52
45
|
|
53
46
|
# NOTE: This has a nodoc tag since the rdoc parser puts this into
|
54
47
|
# Object, a thing I would never allow.
|
55
|
-
chain :as do |expected_output=nil, &
|
48
|
+
chain :as do |expected_output=nil, &my_block|
|
56
49
|
as = expected_output
|
57
|
-
block =
|
50
|
+
block = my_block
|
58
51
|
end
|
59
52
|
end
|
data/lib/parslet/slice.rb
CHANGED
data/lib/parslet/transform.rb
CHANGED
@@ -131,7 +131,7 @@ class Parslet::Transform
|
|
131
131
|
# Allows accessing the class' rules
|
132
132
|
#
|
133
133
|
def rules
|
134
|
-
@__transform_rules
|
134
|
+
@__transform_rules ||= []
|
135
135
|
end
|
136
136
|
|
137
137
|
def inherited(subclass)
|
@@ -140,7 +140,8 @@ class Parslet::Transform
|
|
140
140
|
end
|
141
141
|
end
|
142
142
|
|
143
|
-
def initialize(&block)
|
143
|
+
def initialize(raise_on_unmatch: false, &block)
|
144
|
+
@raise_on_unmatch = raise_on_unmatch
|
144
145
|
@rules = []
|
145
146
|
|
146
147
|
if block
|
@@ -236,7 +237,14 @@ class Parslet::Transform
|
|
236
237
|
end
|
237
238
|
|
238
239
|
# No rule matched - element is not transformed
|
239
|
-
|
240
|
+
if @raise_on_unmatch && elt.is_a?(Hash)
|
241
|
+
elt_types = elt.map do |key, value|
|
242
|
+
[ key, value.class ]
|
243
|
+
end.to_h
|
244
|
+
raise NotImplementedError, "Failed to match `#{elt_types.inspect}`"
|
245
|
+
else
|
246
|
+
return elt
|
247
|
+
end
|
240
248
|
end
|
241
249
|
|
242
250
|
# @api private
|
data/parslet.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'parslet'
|
5
|
-
s.version = '1.
|
5
|
+
s.version = '1.8.0'
|
6
6
|
|
7
7
|
s.authors = ['Kaspar Schiess']
|
8
8
|
s.email = 'kaspar.schiess@absurd.li'
|
@@ -12,7 +12,5 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.license = 'MIT'
|
13
13
|
s.rdoc_options = ['--main', 'README']
|
14
14
|
s.require_paths = ['lib']
|
15
|
-
s.summary = 'Parser construction library with great error reporting in Ruby.'
|
16
|
-
|
17
|
-
s.add_dependency 'blankslate', '>= 2.0', '<= 4.0'
|
15
|
+
s.summary = 'Parser construction library with great error reporting in Ruby.'
|
18
16
|
end
|
@@ -12,7 +12,7 @@ describe "Regression on" do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
it "runs successfully" do
|
15
|
-
|
15
|
+
_, stdout, stderr = Open3.popen3("ruby #{example}")
|
16
16
|
|
17
17
|
handle_map = {
|
18
18
|
stdout => :out,
|
@@ -21,7 +21,7 @@ describe "Regression on" do
|
|
21
21
|
expectation_found = handle_map.any? do |io, ext|
|
22
22
|
name = product_path(example, ext)
|
23
23
|
|
24
|
-
if File.
|
24
|
+
if File.exist?(name)
|
25
25
|
io.read.strip.should == File.read(name).strip
|
26
26
|
true
|
27
27
|
end
|
@@ -109,4 +109,13 @@ Don't know what to do with "%" at line 1 char 2.
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
end
|
112
|
+
describe "providing a reducer block" do
|
113
|
+
class InfixExpressionReducerParser < Parslet::Parser
|
114
|
+
rule(:top) { infix_expression(str('a'), [str('-'), 1, :right]) { |l,o,r| {:and=>[l,r]} } }
|
115
|
+
end
|
116
|
+
|
117
|
+
it "applies the reducer" do
|
118
|
+
InfixExpressionReducerParser.new.top.parse("a-a-a").should == {:and=>["a", {:and=>["a", "a"]}]}
|
119
|
+
end
|
120
|
+
end
|
112
121
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Parslet::Atoms::Ignored do
|
4
|
+
include Parslet
|
5
|
+
|
6
|
+
describe "ignore" do
|
7
|
+
it "ignores parts of the input" do
|
8
|
+
str('a').ignore.parse('a').should == nil
|
9
|
+
(str('a') >> str('b').ignore >> str('c')).parse('abc').should == 'ac'
|
10
|
+
(str('a') >> str('b').as(:name).ignore >> str('c')).parse('abc').should == 'ac'
|
11
|
+
(str('a') >> str('b').maybe.ignore >> str('c')).parse('abc').should == 'ac'
|
12
|
+
(str('a') >> str('b').maybe.ignore >> str('c')).parse('ac').should == 'ac'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/spec/parslet/atoms_spec.rb
CHANGED
@@ -225,7 +225,7 @@ describe Parslet do
|
|
225
225
|
|
226
226
|
it "should not loop infinitely" do
|
227
227
|
lambda {
|
228
|
-
timeout(1) { parslet.parse('bar') }
|
228
|
+
Timeout.timeout(1) { parslet.parse('bar') }
|
229
229
|
}.should raise_error(Parslet::ParseFailed)
|
230
230
|
end
|
231
231
|
end
|
@@ -398,7 +398,7 @@ describe Parslet do
|
|
398
398
|
end
|
399
399
|
|
400
400
|
describe "combinations thereof (regression)" do
|
401
|
-
|
401
|
+
[
|
402
402
|
[(str('a').repeat >> str('b').repeat), 'aaabbb']
|
403
403
|
].each do |(parslet, input)|
|
404
404
|
describe "#{parslet.inspect} applied to #{input.inspect}" do
|
@@ -408,7 +408,7 @@ describe Parslet do
|
|
408
408
|
end
|
409
409
|
end
|
410
410
|
|
411
|
-
|
411
|
+
[
|
412
412
|
[str('a'), "'a'" ],
|
413
413
|
[(str('a') | str('b')).maybe, "('a' / 'b')?" ],
|
414
414
|
[(str('a') >> str('b')).maybe, "('a' 'b')?" ],
|
@@ -9,22 +9,6 @@ describe Parslet::Context do
|
|
9
9
|
context(:a => 'value').instance_eval { a }.
|
10
10
|
should == 'value'
|
11
11
|
end
|
12
|
-
describe 'when a method in BlankSlate is inherited from the environment somehow' do
|
13
|
-
before(:each) { BlankSlate.send(:define_method, :a) { 'c' } }
|
14
|
-
after(:each) { BlankSlate.send(:undef_method, :a) }
|
15
|
-
|
16
|
-
it "masks what is already on blank slate" do
|
17
|
-
context(:a => 'b').instance_eval { a }.
|
18
|
-
should == 'b'
|
19
|
-
end
|
20
|
-
end
|
21
|
-
it "should not reveal define_singleton_method for all users of blankslate, just for us" do
|
22
|
-
expect {
|
23
|
-
BlankSlate.new.instance_eval {
|
24
|
-
define_singleton_method(:foo) { 'foo' }
|
25
|
-
}
|
26
|
-
}.to raise_error(NoMethodError)
|
27
|
-
end
|
28
12
|
it "one contexts variables aren't the next ones" do
|
29
13
|
ca = context(:a => 'b')
|
30
14
|
cb = context(:b => 'c')
|
@@ -32,4 +16,41 @@ describe Parslet::Context do
|
|
32
16
|
ca.methods.should_not include(:b)
|
33
17
|
cb.methods.should_not include(:a)
|
34
18
|
end
|
19
|
+
|
20
|
+
describe 'works as a Ruby object should' do
|
21
|
+
let(:obj) { context(a: 1) }
|
22
|
+
|
23
|
+
it 'responds_to? :a' do
|
24
|
+
assert obj.respond_to?(:a)
|
25
|
+
end
|
26
|
+
it 'includes :a in #methods' do
|
27
|
+
obj.methods.assert.include?(:a)
|
28
|
+
end
|
29
|
+
it 'allows inspection' do
|
30
|
+
obj.inspect.assert.match(/@a=1/)
|
31
|
+
end
|
32
|
+
it 'allows conversion to string' do
|
33
|
+
obj.to_s.assert.match(/Parslet::Context:0x/)
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when the context is enhanced' do
|
37
|
+
before(:each) do
|
38
|
+
class << obj
|
39
|
+
def foo
|
40
|
+
'foo'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'responds_to correctly' do
|
46
|
+
assert obj.respond_to?(:foo)
|
47
|
+
end
|
48
|
+
it 'includes :foo also in methods' do
|
49
|
+
obj.methods.assert.include?(:foo)
|
50
|
+
end
|
51
|
+
it 'allows calling #foo' do
|
52
|
+
obj.foo.assert == 'foo'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
35
56
|
end
|
@@ -6,10 +6,6 @@ describe Parslet::Transform do
|
|
6
6
|
include Parslet
|
7
7
|
|
8
8
|
let(:transform) { Parslet::Transform.new }
|
9
|
-
attr_reader :transform
|
10
|
-
before(:each) do
|
11
|
-
@transform = Parslet::Transform.new
|
12
|
-
end
|
13
9
|
|
14
10
|
class A < Struct.new(:elt); end
|
15
11
|
class B < Struct.new(:elt); end
|
@@ -77,6 +73,26 @@ describe Parslet::Transform do
|
|
77
73
|
transform.apply(:a => 'a').should == A.new('a')
|
78
74
|
end
|
79
75
|
|
76
|
+
context "optionally raise when no match found" do
|
77
|
+
class BumbleBee < Parslet::Transform
|
78
|
+
def initialize(&block)
|
79
|
+
super(raise_on_unmatch: true, &block)
|
80
|
+
end
|
81
|
+
rule(:a => simple(:x)) { A.new(x) }
|
82
|
+
end
|
83
|
+
let(:transform) { BumbleBee.new }
|
84
|
+
|
85
|
+
it "should evaluate rules" do
|
86
|
+
transform.apply(:a => 'a').should == A.new('a')
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should raise when no rules are matched" do
|
90
|
+
lambda {
|
91
|
+
transform.apply(:z => 'z')
|
92
|
+
}.should raise_error(NotImplementedError, /Failed to match/)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
80
96
|
context "with inheritance" do
|
81
97
|
class OptimusPrimeJunior < OptimusPrime
|
82
98
|
rule(:b => simple(:x)) { B.new(x.upcase) }
|
@@ -137,7 +153,9 @@ describe Parslet::Transform do
|
|
137
153
|
it "should execute in its own context" do
|
138
154
|
@bar = 'test'
|
139
155
|
transform.call_on_match(bindings, proc do
|
140
|
-
@bar
|
156
|
+
if instance_variable_defined?("@bar")
|
157
|
+
instance_variable_get("@bar").should_not == 'test'
|
158
|
+
end
|
141
159
|
end)
|
142
160
|
end
|
143
161
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,6 +5,8 @@ require 'parslet/rig/rspec'
|
|
5
5
|
require 'parslet/atoms/visitor'
|
6
6
|
require 'parslet/export'
|
7
7
|
|
8
|
+
require 'ae'
|
9
|
+
|
8
10
|
RSpec.configure do |config|
|
9
11
|
config.mock_with :flexmock
|
10
12
|
|
@@ -29,7 +31,7 @@ def catch_failed_parse
|
|
29
31
|
yield
|
30
32
|
rescue Parslet::ParseFailed => exception
|
31
33
|
end
|
32
|
-
exception.
|
34
|
+
exception.parse_failure_cause
|
33
35
|
end
|
34
36
|
|
35
37
|
def slet name, &block
|
metadata
CHANGED
@@ -1,35 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parslet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kaspar Schiess
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: blankslate
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '2.0'
|
20
|
-
- - "<="
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: '4.0'
|
23
|
-
type: :runtime
|
24
|
-
prerelease: false
|
25
|
-
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
requirements:
|
27
|
-
- - ">="
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '2.0'
|
30
|
-
- - "<="
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: '4.0'
|
11
|
+
date: 2017-04-03 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
33
13
|
description:
|
34
14
|
email: kaspar.schiess@absurd.li
|
35
15
|
executables: []
|
@@ -51,7 +31,6 @@ files:
|
|
51
31
|
- example/email_parser.rb
|
52
32
|
- example/empty.rb
|
53
33
|
- example/erb.rb
|
54
|
-
- example/ignore.rb
|
55
34
|
- example/ip_address.rb
|
56
35
|
- example/json.rb
|
57
36
|
- example/local.rb
|
@@ -111,6 +90,7 @@ files:
|
|
111
90
|
- lib/parslet/atoms/dsl.rb
|
112
91
|
- lib/parslet/atoms/dynamic.rb
|
113
92
|
- lib/parslet/atoms/entity.rb
|
93
|
+
- lib/parslet/atoms/ignored.rb
|
114
94
|
- lib/parslet/atoms/infix.rb
|
115
95
|
- lib/parslet/atoms/lookahead.rb
|
116
96
|
- lib/parslet/atoms/named.rb
|
@@ -154,6 +134,7 @@ files:
|
|
154
134
|
- spec/parslet/atoms/combinations_spec.rb
|
155
135
|
- spec/parslet/atoms/dsl_spec.rb
|
156
136
|
- spec/parslet/atoms/entity_spec.rb
|
137
|
+
- spec/parslet/atoms/ignored_spec.rb
|
157
138
|
- spec/parslet/atoms/infix_spec.rb
|
158
139
|
- spec/parslet/atoms/lookahead_spec.rb
|
159
140
|
- spec/parslet/atoms/named_spec.rb
|
@@ -206,8 +187,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
206
187
|
version: '0'
|
207
188
|
requirements: []
|
208
189
|
rubyforge_project:
|
209
|
-
rubygems_version: 2.4
|
190
|
+
rubygems_version: 2.6.4
|
210
191
|
signing_key:
|
211
192
|
specification_version: 4
|
212
193
|
summary: Parser construction library with great error reporting in Ruby.
|
213
194
|
test_files: []
|
195
|
+
has_rdoc:
|
data/example/ignore.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
# A small example on how to make parslet ignore parts of the parse tree.
|
2
|
-
|
3
|
-
$:.unshift File.dirname(__FILE__) + "/../lib"
|
4
|
-
require 'parslet'
|
5
|
-
|
6
|
-
class IgnoreParslet < Parslet::Atoms::Base
|
7
|
-
def initialize(parslet)
|
8
|
-
@parslet = parslet
|
9
|
-
end
|
10
|
-
def to_s_inner(prec)
|
11
|
-
@parslet.to_s(prec)
|
12
|
-
end
|
13
|
-
def try(source, context, consume_all)
|
14
|
-
success, value = result = @parslet.try(source, context, consume_all)
|
15
|
-
|
16
|
-
return succ(nil) if success
|
17
|
-
return result
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
21
|
-
module IgnoreDSL
|
22
|
-
def ignore
|
23
|
-
IgnoreParslet.new(self)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
class Parslet::Atoms::Base
|
28
|
-
include IgnoreDSL
|
29
|
-
end
|
30
|
-
|
31
|
-
include Parslet
|
32
|
-
p (str('a') >> str('b').ignore >> str('c')).
|
33
|
-
parse('abc')
|