ghazel-parslet 1.4.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.txt +195 -0
- data/LICENSE +23 -0
- data/README +70 -0
- data/Rakefile +49 -0
- data/example/boolean_algebra.rb +70 -0
- data/example/calc.rb +153 -0
- data/example/comments.rb +35 -0
- data/example/deepest_errors.rb +131 -0
- data/example/documentation.rb +18 -0
- data/example/email_parser.rb +52 -0
- data/example/empty.rb +13 -0
- data/example/erb.rb +47 -0
- data/example/ignore.rb +33 -0
- data/example/ip_address.rb +125 -0
- data/example/json.rb +128 -0
- data/example/local.rb +34 -0
- data/example/mathn.rb +44 -0
- data/example/minilisp.rb +94 -0
- data/example/modularity.rb +47 -0
- data/example/nested_errors.rb +132 -0
- data/example/output/boolean_algebra.out +4 -0
- data/example/output/calc.out +1 -0
- data/example/output/comments.out +8 -0
- data/example/output/deepest_errors.out +54 -0
- data/example/output/documentation.err +4 -0
- data/example/output/documentation.out +1 -0
- data/example/output/email_parser.out +2 -0
- data/example/output/empty.err +1 -0
- data/example/output/erb.out +7 -0
- data/example/output/ignore.out +1 -0
- data/example/output/ignore_whitespace.out +1 -0
- data/example/output/ip_address.out +9 -0
- data/example/output/json.out +5 -0
- data/example/output/local.out +3 -0
- data/example/output/mathn.out +4 -0
- data/example/output/minilisp.out +5 -0
- data/example/output/modularity.out +0 -0
- data/example/output/nested_errors.out +54 -0
- data/example/output/parens.out +8 -0
- data/example/output/readme.out +1 -0
- data/example/output/seasons.out +28 -0
- data/example/output/sentence.out +1 -0
- data/example/output/simple_xml.out +2 -0
- data/example/output/string_parser.out +3 -0
- data/example/parens.rb +42 -0
- data/example/readme.rb +30 -0
- data/example/seasons.rb +46 -0
- data/example/sentence.rb +36 -0
- data/example/simple.lit +3 -0
- data/example/simple_xml.rb +54 -0
- data/example/string_parser.rb +77 -0
- data/example/test.lit +4 -0
- data/lib/parslet.rb +254 -0
- data/lib/parslet/atoms.rb +32 -0
- data/lib/parslet/atoms/alternative.rb +50 -0
- data/lib/parslet/atoms/base.rb +124 -0
- data/lib/parslet/atoms/can_flatten.rb +137 -0
- data/lib/parslet/atoms/context.rb +94 -0
- data/lib/parslet/atoms/dsl.rb +98 -0
- data/lib/parslet/atoms/entity.rb +41 -0
- data/lib/parslet/atoms/lookahead.rb +49 -0
- data/lib/parslet/atoms/named.rb +32 -0
- data/lib/parslet/atoms/re.rb +38 -0
- data/lib/parslet/atoms/repetition.rb +63 -0
- data/lib/parslet/atoms/rule.rb +12 -0
- data/lib/parslet/atoms/rule/position.rb +143 -0
- data/lib/parslet/atoms/sequence.rb +38 -0
- data/lib/parslet/atoms/str.rb +37 -0
- data/lib/parslet/atoms/visitor.rb +89 -0
- data/lib/parslet/cause.rb +94 -0
- data/lib/parslet/convenience.rb +35 -0
- data/lib/parslet/error_reporter.rb +7 -0
- data/lib/parslet/error_reporter/deepest.rb +95 -0
- data/lib/parslet/error_reporter/tree.rb +57 -0
- data/lib/parslet/export.rb +162 -0
- data/lib/parslet/expression.rb +51 -0
- data/lib/parslet/expression/treetop.rb +92 -0
- data/lib/parslet/parser.rb +67 -0
- data/lib/parslet/pattern.rb +114 -0
- data/lib/parslet/pattern/binding.rb +49 -0
- data/lib/parslet/rig/rspec.rb +51 -0
- data/lib/parslet/slice.rb +101 -0
- data/lib/parslet/source.rb +62 -0
- data/lib/parslet/source/line_cache.rb +95 -0
- data/lib/parslet/transform.rb +236 -0
- data/lib/parslet/transform/context.rb +32 -0
- metadata +264 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
class Parslet::Atoms::Base
|
2
|
+
|
3
|
+
# Packages the common idiom
|
4
|
+
#
|
5
|
+
# begin
|
6
|
+
# tree = parser.parse('something')
|
7
|
+
# rescue Parslet::ParseFailed => error
|
8
|
+
# puts parser.cause.ascii_tree
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# into a convenient method.
|
12
|
+
#
|
13
|
+
# Usage:
|
14
|
+
#
|
15
|
+
# require 'parslet'
|
16
|
+
# require 'parslet/convenience'
|
17
|
+
#
|
18
|
+
# class FooParser < Parslet::Parser
|
19
|
+
# rule(:foo) { str('foo') }
|
20
|
+
# root(:foo)
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# FooParser.new.parse_with_debug('bar')
|
24
|
+
#
|
25
|
+
# @see Parslet::Atoms::Base#parse
|
26
|
+
#
|
27
|
+
def parse_with_debug str, opts={}
|
28
|
+
parse str, opts
|
29
|
+
rescue Parslet::UnconsumedInput => error
|
30
|
+
puts error
|
31
|
+
rescue Parslet::ParseFailed => error
|
32
|
+
puts error.cause.ascii_tree
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Parslet
|
2
|
+
module ErrorReporter
|
3
|
+
# Instead of reporting the latest error that happens like {Tree} does,
|
4
|
+
# this class reports the deepest error. Depth is defined here as how
|
5
|
+
# advanced into the input an error happens. The errors close to the
|
6
|
+
# greatest depth tend to be more relevant to the end user, since they
|
7
|
+
# specify what could be done to make them go away.
|
8
|
+
#
|
9
|
+
# More specifically, errors produced by this reporter won't be related to
|
10
|
+
# the structure of the grammar at all. The positions of the errors will
|
11
|
+
# be advanced and convey at every grammar level what the deepest rule
|
12
|
+
# was to fail.
|
13
|
+
#
|
14
|
+
class Deepest
|
15
|
+
def initialize
|
16
|
+
@deepest_cause = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
# Produces an error cause that combines the message at the current level
|
20
|
+
# with the errors that happened at a level below (children).
|
21
|
+
#
|
22
|
+
# @param atom [Parslet::Atoms::Base] parslet that failed
|
23
|
+
# @param source [Source] Source that we're using for this parse. (line
|
24
|
+
# number information...)
|
25
|
+
# @param message [String, Array] Error message at this level.
|
26
|
+
# @param children [Array] A list of errors from a deeper level (or nil).
|
27
|
+
# @return [Cause] An error tree combining children with message.
|
28
|
+
#
|
29
|
+
def err(atom, source, message, children=nil)
|
30
|
+
position = source.pos
|
31
|
+
cause = Cause.format(source, position, message, children)
|
32
|
+
return deepest(cause)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Produces an error cause that combines the message at the current level
|
36
|
+
# with the errors that happened at a level below (children).
|
37
|
+
#
|
38
|
+
# @param atom [Parslet::Atoms::Base] parslet that failed
|
39
|
+
# @param source [Source] Source that we're using for this parse. (line
|
40
|
+
# number information...)
|
41
|
+
# @param message [String, Array] Error message at this level.
|
42
|
+
# @param pos [Fixnum] The real position of the error.
|
43
|
+
# @param children [Array] A list of errors from a deeper level (or nil).
|
44
|
+
# @return [Cause] An error tree combining children with message.
|
45
|
+
#
|
46
|
+
def err_at(atom, source, message, pos, children=nil)
|
47
|
+
position = pos
|
48
|
+
cause = Cause.format(source, position, message, children)
|
49
|
+
return deepest(cause)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the cause that is currently deepest. Mainly for specs.
|
53
|
+
#
|
54
|
+
attr_reader :deepest_cause
|
55
|
+
|
56
|
+
# Checks to see if the lineage of the cause given includes a cause with
|
57
|
+
# an error position deeper than the current deepest cause stored. If
|
58
|
+
# yes, it passes the cause through to the caller. If no, it returns the
|
59
|
+
# current deepest error that was saved as a reference.
|
60
|
+
#
|
61
|
+
def deepest(cause)
|
62
|
+
rank, leaf = deepest_child(cause)
|
63
|
+
|
64
|
+
if !deepest_cause || leaf.pos >= deepest_cause.pos
|
65
|
+
# This error reaches deeper into the input, save it as reference.
|
66
|
+
@deepest_cause = leaf
|
67
|
+
return cause
|
68
|
+
end
|
69
|
+
|
70
|
+
return deepest_cause
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
# Returns the leaf from a given error tree with the biggest rank.
|
75
|
+
#
|
76
|
+
def deepest_child(cause, rank=0)
|
77
|
+
max_child = cause
|
78
|
+
max_rank = rank
|
79
|
+
|
80
|
+
if cause.children && !cause.children.empty?
|
81
|
+
cause.children.each do |child|
|
82
|
+
c_rank, c_cause = deepest_child(child, rank+1)
|
83
|
+
|
84
|
+
if c_rank > max_rank
|
85
|
+
max_rank = c_rank
|
86
|
+
max_child = c_cause
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
return max_rank, max_child
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Parslet
|
2
|
+
module ErrorReporter
|
3
|
+
# An error reporter has two central methods, one for reporting errors at
|
4
|
+
# the current parse position (#err) and one for reporting errors at a
|
5
|
+
# given parse position (#err_at). The reporter can return an object (a
|
6
|
+
# 'cause') that will be returned to the caller along with the information
|
7
|
+
# that the parse failed.
|
8
|
+
#
|
9
|
+
# When reporting errors on the outer levels of your parser, these methods
|
10
|
+
# get passed a list of error objects ('causes') from the inner levels. In
|
11
|
+
# this default implementation, the inner levels are considered error
|
12
|
+
# subtrees and are appended to the generated tree node at each level,
|
13
|
+
# thereby constructing an error tree.
|
14
|
+
#
|
15
|
+
# This error tree will report in parallel with the grammar structure that
|
16
|
+
# failed. A one-to-one correspondence exists between each error in the
|
17
|
+
# tree and the parslet atom that produced that error.
|
18
|
+
#
|
19
|
+
# The implementor is really free to use these return values as he sees
|
20
|
+
# fit. One example would be to return an error state object from these
|
21
|
+
# methods that is then updated as errors cascade up the parse derivation
|
22
|
+
# tree.
|
23
|
+
#
|
24
|
+
class Tree
|
25
|
+
# Produces an error cause that combines the message at the current level
|
26
|
+
# with the errors that happened at a level below (children).
|
27
|
+
#
|
28
|
+
# @param atom [Parslet::Atoms::Base] parslet that failed
|
29
|
+
# @param source [Source] Source that we're using for this parse. (line
|
30
|
+
# number information...)
|
31
|
+
# @param message [String, Array] Error message at this level.
|
32
|
+
# @param children [Array] A list of errors from a deeper level (or nil).
|
33
|
+
# @return [Cause] An error tree combining children with message.
|
34
|
+
#
|
35
|
+
def err(atom, source, message, children=nil)
|
36
|
+
position = source.pos
|
37
|
+
Cause.format(source, position, message, children)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Produces an error cause that combines the message at the current level
|
41
|
+
# with the errors that happened at a level below (children).
|
42
|
+
#
|
43
|
+
# @param atom [Parslet::Atoms::Base] parslet that failed
|
44
|
+
# @param source [Source] Source that we're using for this parse. (line
|
45
|
+
# number information...)
|
46
|
+
# @param message [String, Array] Error message at this level.
|
47
|
+
# @param pos [Fixnum] The real position of the error.
|
48
|
+
# @param children [Array] A list of errors from a deeper level (or nil).
|
49
|
+
# @return [Cause] An error tree combining children with message.
|
50
|
+
#
|
51
|
+
def err_at(atom, source, message, pos, children=nil)
|
52
|
+
position = pos
|
53
|
+
Cause.format(source, position, message, children)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
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 visit_str(str)
|
15
|
+
"\"#{str.inspect[1..-2]}\""
|
16
|
+
end
|
17
|
+
def visit_re(match)
|
18
|
+
match.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def visit_entity(name, block)
|
22
|
+
context.deferred(name, block)
|
23
|
+
|
24
|
+
"(#{context.mangle_name(name)})"
|
25
|
+
end
|
26
|
+
def visit_named(name, parslet)
|
27
|
+
parslet.accept(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def visit_sequence(parslets)
|
31
|
+
'(' <<
|
32
|
+
parslets.
|
33
|
+
map { |el| el.accept(self) }.
|
34
|
+
join(' ') <<
|
35
|
+
')'
|
36
|
+
end
|
37
|
+
def visit_repetition(tag, min, max, parslet)
|
38
|
+
parslet.accept(self) << "#{min}*#{max}"
|
39
|
+
end
|
40
|
+
def visit_alternative(alternatives)
|
41
|
+
'(' <<
|
42
|
+
alternatives.
|
43
|
+
map { |el| el.accept(self) }.
|
44
|
+
join(' | ') <<
|
45
|
+
')'
|
46
|
+
end
|
47
|
+
|
48
|
+
def visit_lookahead(positive, bound_parslet)
|
49
|
+
(positive ? '&' : '!') <<
|
50
|
+
bound_parslet.accept(self)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Treetop < Citrus
|
55
|
+
def visit_repetition(tag, min, max, parslet)
|
56
|
+
parslet.accept(self) << "#{min}..#{max}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def visit_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
|
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)
|
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, 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, block.call)
|
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)
|
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)
|
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
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
|
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
|
+
# fledged parser and on the other hand might even turn out really useful.
|
5
|
+
#
|
6
|
+
# This can be viewed as an extension to parslet and might even be hosted in
|
7
|
+
# its own gem one fine day.
|
8
|
+
#
|
9
|
+
class Parslet::Expression
|
10
|
+
include Parslet
|
11
|
+
|
12
|
+
autoload :Treetop, 'parslet/expression/treetop'
|
13
|
+
|
14
|
+
# Creates a parslet from a foreign language expression.
|
15
|
+
#
|
16
|
+
# Example:
|
17
|
+
#
|
18
|
+
# Parslet::Expression.new("'a' 'b'")
|
19
|
+
#
|
20
|
+
def initialize(str, opts={}, context=self)
|
21
|
+
@type = opts[:type] || :treetop
|
22
|
+
@exp = str
|
23
|
+
@parslet = transform(
|
24
|
+
parse(str))
|
25
|
+
end
|
26
|
+
|
27
|
+
# Transforms the parse tree into a parslet expression.
|
28
|
+
#
|
29
|
+
def transform(tree)
|
30
|
+
transform = Treetop::Transform.new
|
31
|
+
|
32
|
+
# pp tree
|
33
|
+
transform.apply(tree)
|
34
|
+
rescue
|
35
|
+
warn "Could not transform: " + tree.inspect
|
36
|
+
raise
|
37
|
+
end
|
38
|
+
|
39
|
+
# Parses the string and returns a parse tree.
|
40
|
+
#
|
41
|
+
def parse(str)
|
42
|
+
parser = Treetop::Parser.new
|
43
|
+
parser.parse(str)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Turns this expression into a parslet.
|
47
|
+
#
|
48
|
+
def to_parslet
|
49
|
+
@parslet
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
class Parslet::Expression::Treetop
|
2
|
+
class Parser < Parslet::Parser
|
3
|
+
root(:expression)
|
4
|
+
|
5
|
+
rule(:expression) { alternatives }
|
6
|
+
|
7
|
+
# alternative 'a' / 'b'
|
8
|
+
rule(:alternatives) {
|
9
|
+
(simple >> (spaced('/') >> simple).repeat).as(:alt)
|
10
|
+
}
|
11
|
+
|
12
|
+
# sequence by simple concatenation 'a' 'b'
|
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
|
+
|
21
|
+
atom.as(:maybe) >> spaced('?') |
|
22
|
+
atom
|
23
|
+
}
|
24
|
+
|
25
|
+
rule(:atom) {
|
26
|
+
spaced('(') >> expression.as(:unwrap) >> spaced(')') |
|
27
|
+
dot |
|
28
|
+
string |
|
29
|
+
char_class
|
30
|
+
}
|
31
|
+
|
32
|
+
# a character class
|
33
|
+
rule(:char_class) {
|
34
|
+
(str('[') >>
|
35
|
+
(str('\\') >> any |
|
36
|
+
str(']').absent? >> any).repeat(1) >>
|
37
|
+
str(']')).as(:match) >> space?
|
38
|
+
}
|
39
|
+
|
40
|
+
# anything at all
|
41
|
+
rule(:dot) { spaced('.').as(:any) }
|
42
|
+
|
43
|
+
# recognizing strings
|
44
|
+
rule(:string) {
|
45
|
+
str('\'') >>
|
46
|
+
(
|
47
|
+
(str('\\') >> any) |
|
48
|
+
(str("'").absent? >> any)
|
49
|
+
).repeat.as(:string) >>
|
50
|
+
str('\'') >> space?
|
51
|
+
}
|
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
|
64
|
+
rule(:space) { match("\s").repeat(1) }
|
65
|
+
rule(:space?) { space.maybe }
|
66
|
+
|
67
|
+
def spaced(str)
|
68
|
+
str(str) >> space?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class Transform < Parslet::Transform
|
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('.') }
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|