fabiokung-sexp_processor 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+