88# as is into the result tree.
99#
1010# This is almost what you would generally do with a tree visitor, except that
11- # you can match several levels of the tree at once.
11+ # you can match several levels of the tree at once.
1212#
1313# As a consequence of this, the resulting tree will contain pieces of the
1414# original tree and new pieces. Most likely, you will want to transform the
1515# original tree wholly, so this isn't a problem.
1616#
1717# You will not be able to create a loop, given that each node will be replaced
1818# only once and then left alone. This means that the results of a replacement
19- # will not be acted upon.
19+ # will not be acted upon.
2020#
21- # Example:
21+ # Example:
2222#
2323# class Example < Parslet::Transform
2424# rule(:string => simple(:x)) { # (1)
3030# rule can be defined by calling #rule with the pattern as argument. The block
3131# given will be called every time the rule matches somewhere in the tree given
3232# to #apply. It is passed a Hash containing all the variable bindings of this
33- # pattern match.
34- #
35- # In the above example, (1) illustrates a simple matching rule.
33+ # pattern match.
34+ #
35+ # In the above example, (1) illustrates a simple matching rule.
3636#
3737# Let's say you want to parse matching parentheses and distill a maximum nest
3838# depth. You would probably write a parser like the one in example/parens.rb;
39- # here's the relevant part:
39+ # here's the relevant part:
4040#
4141# rule(:balanced) {
4242# str('(').as(:l) >> balanced.maybe.as(:m) >> str(')').as(:r)
4343# }
4444#
4545# If you now apply this to a string like '(())', you get a intermediate parse
46- # tree that looks like this:
46+ # tree that looks like this:
4747#
4848# {
49- # l: '(',
49+ # l: '(',
5050# m: {
51- # l: '(',
52- # m: nil,
53- # r: ')'
54- # },
55- # r: ')'
51+ # l: '(',
52+ # m: nil,
53+ # r: ')'
54+ # },
55+ # r: ')'
5656# }
5757#
5858# This parse tree is good for debugging, but what we would really like to have
59- # is just the nesting depth. This transformation rule will produce that:
59+ # is just the nesting depth. This transformation rule will produce that:
6060#
61- # rule(:l => '(', :m => simple(:x), :r => ')') {
61+ # rule(:l => '(', :m => simple(:x), :r => ')') {
6262# # innermost :m will contain nil
6363# x.nil? ? 1 : x+1
6464# }
6767#
6868# There are four ways of using this class. The first one is very much
6969# recommended, followed by the second one for generality. The other ones are
70- # omitted here.
70+ # omitted here.
7171#
72- # Recommended usage is as follows:
72+ # Recommended usage is as follows:
7373#
7474# class MyTransformator < Parslet::Transform
7575# rule(...) { ... }
7878# end
7979# MyTransformator.new.apply(tree)
8080#
81- # Alternatively, you can use the Transform class as follows:
81+ # Alternatively, you can use the Transform class as follows:
8282#
8383# transform = Parslet::Transform.new do
8484# rule(...) { ... }
8787#
8888# = Execution context
8989#
90- # The execution context of action blocks differs depending on the arity of
91- # said blocks. This can be confusing. It is however somewhat intentional. You
92- # should not create fat Transform descendants containing a lot of helper methods,
90+ # The execution context of action blocks differs depending on the arity of
91+ # said blocks. This can be confusing. It is however somewhat intentional. You
92+ # should not create fat Transform descendants containing a lot of helper methods,
9393# instead keep your AST class construction in global scope or make it available
9494# through a factory. The following piece of code illustrates usage of global
95- # scope:
95+ # scope:
9696#
9797# transform = Parslet::Transform.new do
9898# rule(...) { AstNode.new(a_variable) }
109109# transform.apply(tree, :builder => Builder.new)
110110#
111111# As you can see, Transform allows you to inject local context for your rule
112- # action blocks to use.
112+ # action blocks to use.
113113#
114114class Parslet ::Transform
115115 # FIXME: Maybe only part of it? Or maybe only include into constructor
116116 # context?
117- include Parslet
118-
117+ include Parslet
118+
119119 class << self
120120 # FIXME: Only do this for subclasses?
121121 include Parslet
122-
123- # Define a rule for the transform subclass.
122+
123+ # Define a rule for the transform subclass.
124124 #
125125 def rule ( expression , &block )
126126 @__transform_rules ||= [ ]
127127 # Prepend new rules so they have higher precedence than older rules
128128 @__transform_rules . unshift ( [ Parslet ::Pattern . new ( expression ) , block ] )
129129 end
130-
130+
131131 # Allows accessing the class' rules
132132 #
133- def rules
133+ def rules
134134 @__transform_rules ||= [ ]
135135 end
136136
@@ -139,47 +139,47 @@ def inherited(subclass)
139139 subclass . instance_variable_set ( :@__transform_rules , rules . dup )
140140 end
141141 end
142-
143- def initialize ( raise_on_unmatch = false , &block )
142+
143+ def initialize ( raise_on_unmatch = false , &block )
144144 @raise_on_unmatch = raise_on_unmatch
145145 @rules = [ ]
146-
146+
147147 if block
148148 instance_eval ( &block )
149149 end
150150 end
151-
151+
152152 # Defines a rule to be applied whenever apply is called on a tree. A rule
153- # is composed of two parts:
154- #
153+ # is composed of two parts:
154+ #
155155 # * an *expression pattern*
156156 # * a *transformation block*
157157 #
158158 def rule ( expression , &block )
159159 # Prepend new rules so they have higher precedence than older rules
160160 @rules . unshift ( [ Parslet ::Pattern . new ( expression ) , block ] )
161161 end
162-
162+
163163 # Applies the transformation to a tree that is generated by Parslet::Parser
164164 # or a simple parslet. Transformation will proceed down the tree, replacing
165- # parts/all of it with new objects. The resulting object will be returned.
165+ # parts/all of it with new objects. The resulting object will be returned.
166166 #
167167 # Using the context parameter, you can inject bindings for the transformation.
168168 # This can be used to allow access to the outside world from transform blocks,
169169 # like so:
170- #
170+ #
171171 # document = # some class that you act on
172172 # transform.apply(tree, document: document)
173- #
174- # The above will make document available to all your action blocks:
173+ #
174+ # The above will make document available to all your action blocks:
175175 #
176176 # # Variant A
177177 # rule(...) { document.foo(bar) }
178178 # # Variant B
179179 # rule(...) { |d| d[:document].foo(d[:bar]) }
180180 #
181181 # @param obj PORO ast to transform
182- # @param context start context to inject into the bindings.
182+ # @param context start context to inject into the bindings.
183183 #
184184 def apply ( obj , context = nil )
185185 transform_elt (
@@ -190,52 +190,52 @@ def apply(obj, context=nil)
190190 recurse_array ( obj , context )
191191 else
192192 obj
193- end ,
193+ end ,
194194 context
195195 )
196196 end
197-
197+
198198 # Executes the block on the bindings obtained by Pattern#match, if such a match
199- # can be made. Depending on the arity of the given block, it is called in
199+ # can be made. Depending on the arity of the given block, it is called in
200200 # one of two environments: the current one or a clean toplevel environment.
201201 #
202- # If you would like the current environment preserved, please use the
202+ # If you would like the current environment preserved, please use the
203203 # arity 1 variant of the block. Alternatively, you can inject a context object
204204 # and call methods on it (think :ctx => self).
205205 #
206206 # # the local variable a is simulated
207- # t.call_on_match(:a => :b) { a }
207+ # t.call_on_match(:a => :b) { a }
208208 # # no change of environment here
209209 # t.call_on_match(:a => :b) { |d| d[:a] }
210210 #
211211 def call_on_match ( bindings , block )
212212 if block
213213 if block . arity == 1
214- return block . call ( bindings )
214+ return instance_exec ( bindings , & block )
215215 else
216- context = Context . new ( bindings )
216+ context = Context . new ( bindings , self )
217217 return context . instance_eval ( &block )
218218 end
219219 end
220220 end
221-
222- # Allow easy access to all rules, the ones defined in the instance and the
223- # ones predefined in a subclass definition.
221+
222+ # Allow easy access to all rules, the ones defined in the instance and the
223+ # ones predefined in a subclass definition.
224224 #
225- def rules
225+ def rules
226226 self . class . rules + @rules
227227 end
228-
229- # @api private
228+
229+ # @api private
230230 #
231- def transform_elt ( elt , context )
231+ def transform_elt ( elt , context )
232232 rules . each do |pattern , block |
233233 if bindings = pattern . match ( elt , context )
234234 # Produces transformed value
235235 return call_on_match ( bindings , block )
236236 end
237237 end
238-
238+
239239 # No rule matched - element is not transformed
240240 if @raise_on_unmatch && elt . is_a? ( Hash )
241241 elt_types = elt . map do |key , value |
@@ -247,19 +247,19 @@ def transform_elt(elt, context)
247247 end
248248 end
249249
250- # @api private
250+ # @api private
251251 #
252- def recurse_hash ( hsh , ctx )
252+ def recurse_hash ( hsh , ctx )
253253 hsh . inject ( { } ) do |new_hsh , ( k , v ) |
254254 new_hsh [ k ] = apply ( v , ctx )
255255 new_hsh
256256 end
257257 end
258- # @api private
258+ # @api private
259259 #
260- def recurse_array ( ary , ctx )
260+ def recurse_array ( ary , ctx )
261261 ary . map { |elt | apply ( elt , ctx ) }
262262 end
263263end
264264
265- require 'parslet/context'
265+ require 'parslet/context'
0 commit comments