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 CHANGED
@@ -8,4 +8,8 @@ group :development do
8
8
  gem 'flexmock'
9
9
 
10
10
  gem 'sdoc'
11
+
12
+ gem 'autotest'
13
+ gem 'autotest-fsevent'
14
+ gem 'autotest-growl'
11
15
  end
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 *error reporting* possible
6
- * *not generating* reams of code for you to debug
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 *your job* easier. It allows for
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, two methods:
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
- On the road to 1.0; improving documentation, packaging and upgrading to rspec2.
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.11.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
- # * Transformation of the tree into something useful to you
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
- # and use the second stage to isolate the rest of your code from the changes
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.ascii_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
- # def parse(str)
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. This is essentially a regular
122
- # expression, but you should only match a single character.
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
- def |(parslet)
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
@@ -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
- def flatten_sequence(list)
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
@@ -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
- # a) Avoid infinite recursion during evaluation of the definition
5
- #
6
- # b) Be able to print things by their name, not by their sometimes
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#rule.
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
- # Model positive and negative lookahead by testing this flag.
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
- def initialize(str, opts={})
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) { perhaps.repeat(1).as(:seq) }
14
-
15
- rule(:perhaps) {
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
- string
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 < Parser::Transform
43
- rule(:alt => subtree(:alt)) { Parslet::Atoms::Alternative.new(*alt) }
44
- rule(:seq => sequence(:s)) { Parslet::Atoms::Sequence.new(*s) }
45
- rule(:unwrap => simple(:u)) { u }
46
- rule(:maybe => simple(:m)) { |d| d[:m].maybe }
47
- rule(:string => simple(:s)) { |d| str(d[:s]) }
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
@@ -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
- def call_on_match(tree, bindings, block)
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
- # For a hash to match, all keys must correspond and all values must
137
- # match element wise.
138
- tree.each do |tree_key,tree_value|
139
- return nil unless exp.has_key?(tree_key)
140
-
141
- # We know they both have tk as element.
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 values
145
- unless element_match(tree_value, exp_value, bindings)
146
- # Stop matching early
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) })
@@ -10,7 +10,7 @@ require 'blankslate'
10
10
  # a # => :b
11
11
  # end
12
12
  #
13
- class Parslet::Pattern::Context < BlankSlate
13
+ class Parslet::Pattern::Context < BlankSlate # :nodoc:
14
14
  def initialize(bindings)
15
15
  @bindings = bindings
16
16
  end
@@ -46,10 +46,13 @@ require 'parslet/pattern'
46
46
  # tree that looks like this:
47
47
  #
48
48
  # {
49
- # :l => "(",
50
- # :m => {
51
- # :l=>"(", :m=>nil, :r=>")" },
52
- # :r => ")"
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(elt, bindings, block)
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.11.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-11-25 00:00:00 +01:00
17
+ date: 2010-12-29 00:00:00 +01:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency