fabiokung-sexp_processor 3.0.1

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/History.txt ADDED
@@ -0,0 +1,23 @@
1
+ === 3.0.1 / 2009-01-20
2
+
3
+ * 3 minor enhancements:
4
+
5
+ * Filled out README
6
+ * Promoted file/line/comments from ruby_parser.
7
+ * Added sexp_type to compliment sexp_body.
8
+
9
+ === 3.0.0 / 2008-10-22
10
+
11
+ * 2 major enhancements:
12
+
13
+ * Released as its own project, splitting from ParseTree
14
+ * Added Environment to SexpProcessor and built it in. YAY!
15
+
16
+ * 6 minor enhancements:
17
+
18
+ * Allowed CompositeSexpProcessor to be more ducktypey.
19
+ * Refactored Sexp#method_missing into find_node and find_nodes.
20
+ * Removed Sexp#for and other PT specific code.
21
+ * SexpProcessor#process now runs rewriters before everything else.
22
+ * SexpProcessor#rewrite context only for subs, EMPTY for top level rewrites.
23
+ * SexpProcessor#rewrite will stop iterating if the result isn't another Sexp.
data/Manifest.txt ADDED
@@ -0,0 +1,11 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/composite_sexp_processor.rb
6
+ lib/sexp.rb
7
+ lib/sexp_processor.rb
8
+ test/test_composite_sexp_processor.rb
9
+ test/test_environment.rb
10
+ test/test_sexp.rb
11
+ test/test_sexp_processor.rb
data/README.txt ADDED
@@ -0,0 +1,61 @@
1
+ = SexpProcessor
2
+
3
+ * http://rubyforge.org/projects/parsetree/
4
+
5
+ == DESCRIPTION:
6
+
7
+ sexp_processor branches from ParseTree bringing all the generic sexp
8
+ processing tools with it. Sexp, SexpProcessor, Environment, etc... all
9
+ for your language processing pleasure.
10
+
11
+ == FEATURES/PROBLEMS:
12
+
13
+ * Includes SexpProcessor and CompositeSexpProcessor.
14
+
15
+ * Allows you to write very clean filters.
16
+
17
+ == SYNOPSIS:
18
+
19
+ class MyProcessor < SexpProcessor
20
+ def initialize
21
+ super
22
+ self.strict = false
23
+ end
24
+ def process_lit(exp)
25
+ val = exp.shift
26
+ return val
27
+ end
28
+ end
29
+
30
+ == REQUIREMENTS:
31
+
32
+ * rubygems
33
+
34
+ == INSTALL:
35
+
36
+ * sudo gem install sexp_processor
37
+
38
+ == LICENSE:
39
+
40
+ (The MIT License)
41
+
42
+ Copyright (c) 2008 Ryan Davis, Seattle.rb
43
+
44
+ Permission is hereby granted, free of charge, to any person obtaining
45
+ a copy of this software and associated documentation files (the
46
+ 'Software'), to deal in the Software without restriction, including
47
+ without limitation the rights to use, copy, modify, merge, publish,
48
+ distribute, sublicense, and/or sell copies of the Software, and to
49
+ permit persons to whom the Software is furnished to do so, subject to
50
+ the following conditions:
51
+
52
+ The above copyright notice and this permission notice shall be
53
+ included in all copies or substantial portions of the Software.
54
+
55
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
56
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
57
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
58
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
59
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
60
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
61
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ Hoe.add_include_dirs "lib"
7
+
8
+ require './lib/sexp_processor.rb'
9
+
10
+ Hoe.new('sexp_processor', SexpProcessor::VERSION) do |sexp|
11
+ sexp.rubyforge_name = 'parsetree'
12
+ sexp.developer('Ryan Davis', 'ryand-ruby@zenspider.com')
13
+ sexp.developer('Fabio Kung', 'fabio.kung@gmail.com')
14
+ end
15
+
16
+ # vim: syntax=Ruby
@@ -0,0 +1,49 @@
1
+ require 'sexp_processor'
2
+
3
+ ##
4
+ # Implements the Composite pattern on SexpProcessor. Need we say more?
5
+ #
6
+ # Yeah... probably. Implements a SexpProcessor of SexpProcessors so
7
+ # you can easily chain multiple to each other. At some stage we plan
8
+ # on having all of them run +process+ and but only ever output
9
+ # something when +generate+ is called, allowing for deferred final
10
+ # processing.
11
+
12
+ class CompositeSexpProcessor < SexpProcessor
13
+
14
+ ##
15
+ # The list o' processors to run.
16
+
17
+ attr_reader :processors
18
+
19
+ def initialize # :nodoc:
20
+ super
21
+ @processors = []
22
+ end
23
+
24
+ ##
25
+ # Add a +processor+ to the list of processors to run.
26
+
27
+ def <<(processor)
28
+ raise ArgumentError, "Can only add sexp processors" unless
29
+ SexpProcessor === processor || processor.respond_to?(:process)
30
+ @processors << processor
31
+ end
32
+
33
+ ##
34
+ # Run +exp+ through all of the processors, returning the final
35
+ # result.
36
+
37
+ def process(exp)
38
+ @processors.each do |processor|
39
+ exp = processor.process(exp)
40
+ end
41
+ exp
42
+ end
43
+
44
+ def on_error_in(node_type, &block)
45
+ @processors.each do |processor|
46
+ processor.on_error_in(node_type, &block)
47
+ end
48
+ end
49
+ end
data/lib/sexp.rb ADDED
@@ -0,0 +1,316 @@
1
+
2
+ $TESTING ||= false # unless defined $TESTING
3
+
4
+ ##
5
+ # Sexps are the basic storage mechanism of SexpProcessor. Sexps have
6
+ # a +type+ (to be renamed +node_type+) which is the first element of
7
+ # the Sexp. The type is used by SexpProcessor to determine whom to
8
+ # dispatch the Sexp to for processing.
9
+
10
+ class Sexp < Array # ZenTest FULL
11
+
12
+ attr_writer :line
13
+ attr_writer :endline
14
+ attr_accessor :file, :comments
15
+
16
+ @@array_types = [ :array, :args, ]
17
+
18
+ ##
19
+ # Create a new Sexp containing +args+.
20
+
21
+ def initialize(*args)
22
+ super(args)
23
+ end
24
+
25
+ ##
26
+ # Creates a new Sexp from Array +a+.
27
+
28
+ def self.from_array(a)
29
+ ary = Array === a ? a : [a]
30
+
31
+ result = self.new
32
+
33
+ ary.each do |x|
34
+ case x
35
+ when Sexp
36
+ result << x
37
+ when Array
38
+ result << self.from_array(x)
39
+ else
40
+ result << x
41
+ end
42
+ end
43
+
44
+ result
45
+ end
46
+
47
+ def ==(obj) # :nodoc:
48
+ if obj.class == self.class then
49
+ super
50
+ else
51
+ false
52
+ end
53
+ end
54
+
55
+ ##
56
+ # Returns true if this Sexp's pattern matches +sexp+.
57
+
58
+ def ===(sexp)
59
+ return nil unless Sexp === sexp
60
+ pattern = self # this is just for my brain
61
+
62
+ return true if pattern == sexp
63
+
64
+ sexp.each do |subset|
65
+ return true if pattern === subset
66
+ end
67
+
68
+ return nil
69
+ end
70
+
71
+ ##
72
+ # Returns true if this Sexp matches +pattern+. (Opposite of #===.)
73
+
74
+ def =~(pattern)
75
+ return pattern === self
76
+ end
77
+
78
+ ##
79
+ # Returns true if the node_type is +array+ or +args+.
80
+ #
81
+ # REFACTOR: to TypedSexp - we only care when we have units.
82
+
83
+ def array_type?
84
+ type = self.first
85
+ @@array_types.include? type
86
+ end
87
+
88
+ def compact # :nodoc:
89
+ self.delete_if { |o| o.nil? }
90
+ end
91
+
92
+ ##
93
+ # Enumeratates the sexp yielding to +b+ when the node_type == +t+.
94
+
95
+ def each_of_type(t, &b)
96
+ each do | elem |
97
+ if Sexp === elem then
98
+ elem.each_of_type(t, &b)
99
+ b.call(elem) if elem.first == t
100
+ end
101
+ end
102
+ end
103
+
104
+ ##
105
+ # Replaces all elements whose node_type is +from+ with +to+. Used
106
+ # only for the most trivial of rewrites.
107
+
108
+ def find_and_replace_all(from, to)
109
+ each_with_index do | elem, index |
110
+ if Sexp === elem then
111
+ elem.find_and_replace_all(from, to)
112
+ else
113
+ self[index] = to if elem == from
114
+ end
115
+ end
116
+ end
117
+
118
+ ##
119
+ # Replaces all Sexps matching +pattern+ with Sexp +repl+.
120
+
121
+ def gsub(pattern, repl)
122
+ return repl if pattern == self
123
+
124
+ new = self.map do |subset|
125
+ case subset
126
+ when Sexp then
127
+ subset.gsub(pattern, repl)
128
+ else
129
+ subset
130
+ end
131
+ end
132
+
133
+ return Sexp.from_array(new)
134
+ end
135
+
136
+ def inspect # :nodoc:
137
+ sexp_str = self.map {|x|x.inspect}.join(', ')
138
+ if line && ENV['VERBOSE'] then
139
+ "s(#{sexp_str}).line(#{line})"
140
+ else
141
+ "s(#{sexp_str})"
142
+ end
143
+ end
144
+
145
+ def find_node name, delete = false
146
+ matches = find_nodes name
147
+
148
+ case matches.size
149
+ when 0 then
150
+ nil
151
+ when 1 then
152
+ match = matches.first
153
+ delete match if delete
154
+ match
155
+ else
156
+ raise NoMethodError, "multiple nodes for #{name} were found in #{inspect}"
157
+ end
158
+ end
159
+
160
+ ##
161
+ # Find every node with type +name+.
162
+
163
+ def find_nodes name
164
+ find_all { | sexp | Sexp === sexp and sexp.first == name }
165
+ end
166
+
167
+ ##
168
+ # If passed a line number, sets the line and returns self. Otherwise
169
+ # returns the line number. This allows you to do message cascades
170
+ # and still get the sexp back.
171
+
172
+ def line(n=nil)
173
+ if n then
174
+ @line = n
175
+ self
176
+ else
177
+ @line ||= nil
178
+ end
179
+ end
180
+
181
+ ##
182
+ # If passed a line number, sets the endline and returns self. Otherwise
183
+ # returns the endline number. This allows you to do message cascades
184
+ # and still get the sexp back.
185
+
186
+ def endline(n=nil)
187
+ if n then
188
+ @endline = n
189
+ self
190
+ else
191
+ @endline ||= nil
192
+ end
193
+ end
194
+
195
+ ##
196
+ # Returns the node named +node+, deleting it if +delete+ is true.
197
+
198
+ def method_missing meth, delete = false
199
+ find_node meth, delete
200
+ end
201
+
202
+ def pretty_print(q) # :nodoc:
203
+ q.group(1, 's(', ')') do
204
+ q.seplist(self) {|v| q.pp v }
205
+ end
206
+ end
207
+
208
+ ##
209
+ # Returns the node type of the Sexp.
210
+
211
+ def sexp_type
212
+ first
213
+ end
214
+
215
+ ##
216
+ # Returns the Sexp body, ie the values without the node type.
217
+
218
+ def sexp_body
219
+ self[1..-1]
220
+ end
221
+
222
+ ##
223
+ # If run with debug, Sexp will raise if you shift on an empty
224
+ # Sexp. Helps with debugging.
225
+
226
+ def shift
227
+ raise "I'm empty" if self.empty?
228
+ super
229
+ end if $DEBUG or $TESTING
230
+
231
+ ##
232
+ # Returns the bare bones structure of the sexp.
233
+ # s(:a, :b, s(:c, :d), :e) => s(:a, s(:c))
234
+
235
+ def structure
236
+ result = self.class.new
237
+ if Array === self.first then
238
+ result = self.first.structure
239
+ else
240
+ result << self.first
241
+ self.grep(Array).each do |subexp|
242
+ result << subexp.structure
243
+ end
244
+ end
245
+ result
246
+ end
247
+
248
+ ##
249
+ # Replaces the Sexp matching +pattern+ with +repl+.
250
+
251
+ def sub(pattern, repl)
252
+ return repl.dup if pattern == self
253
+
254
+ done = false
255
+
256
+ new = self.map do |subset|
257
+ if done then
258
+ subset
259
+ else
260
+ case subset
261
+ when Sexp then
262
+ if pattern == subset then
263
+ done = true
264
+ repl.dup
265
+ elsif pattern === subset then
266
+ done = true
267
+ subset.sub pattern, repl
268
+ else
269
+ subset
270
+ end
271
+ else
272
+ subset
273
+ end
274
+ end
275
+ end
276
+
277
+ return Sexp.from_array(new)
278
+ end
279
+
280
+ def to_a # :nodoc:
281
+ self.map { |o| Sexp === o ? o.to_a : o }
282
+ end
283
+
284
+ def to_s # :nodoc:
285
+ inspect
286
+ end
287
+
288
+ end
289
+
290
+ class SexpMatchSpecial < Sexp; end
291
+
292
+ class SexpAny < SexpMatchSpecial
293
+ def ==(o)
294
+ Sexp === o
295
+ end
296
+
297
+ def ===(o)
298
+ return Sexp === o
299
+ end
300
+
301
+ def inspect
302
+ "ANY"
303
+ end
304
+ end
305
+
306
+ module SexpMatchSpecials
307
+ def ANY(); return SexpAny.new; end
308
+ end
309
+
310
+ ##
311
+ # This is just a stupid shortcut to make indentation much cleaner.
312
+
313
+ def s(*args)
314
+ Sexp.new(*args)
315
+ end
316
+