sexp_processor 4.9.0 → 4.10.0b1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  $TESTING = false unless defined? $TESTING
2
2
 
3
- require 'sexp'
3
+ require "sexp"
4
4
 
5
5
  ##
6
6
  # SexpProcessor provides a uniform interface to process Sexps.
@@ -33,7 +33,8 @@ require 'sexp'
33
33
 
34
34
  class SexpProcessor
35
35
 
36
- VERSION = "4.9.0"
36
+ # duh
37
+ VERSION = "4.10.0b1"
37
38
 
38
39
  ##
39
40
  # Automatically shifts off the Sexp type before handing the
@@ -102,7 +103,7 @@ class SexpProcessor
102
103
 
103
104
  dirs.flatten.map { |p|
104
105
  if File.directory? p then
105
- Dir[File.join(p, '**', "*.{#{extensions.join(',')}}")]
106
+ Dir[File.join(p, "**", "*.{#{extensions.join ","}}")]
106
107
  else
107
108
  p
108
109
  end
@@ -160,7 +161,10 @@ class SexpProcessor
160
161
  end
161
162
  end
162
163
 
163
- def assert_empty(meth, exp, exp_orig)
164
+ ##
165
+ # Raise if +exp+ is not empty.
166
+
167
+ def assert_empty meth, exp, exp_orig
164
168
  unless exp.empty? then
165
169
  msg = "exp not empty after #{self.class}.#{meth} on #{exp.inspect}"
166
170
  msg += " from #{exp_orig.inspect}" if $DEBUG
@@ -168,30 +172,39 @@ class SexpProcessor
168
172
  end
169
173
  end
170
174
 
171
- def rewrite(exp)
172
- type = exp.first
175
+ ##
176
+ # Rewrite +exp+ using rewrite_* method for +exp+'s sexp_type, if one
177
+ # exists.
178
+
179
+ def rewrite exp
180
+ type = exp.sexp_type
181
+
182
+ comments = exp.comments
173
183
 
174
- if @debug.has_key? type then
184
+ if @debug.key? type then
175
185
  str = exp.inspect
176
186
  puts "// DEBUG (original ): #{str}" if str =~ @debug[type]
177
187
  end
178
188
 
179
189
  in_context type do
180
- exp.map! { |sub| Array === sub ? rewrite(sub) : sub }
190
+ exp = exp.map { |sub| Array === sub ? rewrite(sub) : sub }
181
191
  end
182
192
 
183
- begin
193
+ loop do
184
194
  meth = @rewriters[type]
185
195
  exp = self.send(meth, exp) if meth
186
196
  break unless Sexp === exp
187
197
 
188
- if @debug.has_key? type then
198
+ if @debug.key? type then
189
199
  str = exp.inspect
190
200
  puts "// DEBUG (rewritten): #{str}" if str =~ @debug[type]
191
201
  end
192
202
 
193
- old_type, type = type, exp.first
194
- end until old_type == type
203
+ old_type, type = type, exp.sexp_type
204
+ break if old_type == type
205
+ end
206
+
207
+ exp.comments = comments
195
208
 
196
209
  exp
197
210
  end
@@ -201,8 +214,13 @@ class SexpProcessor
201
214
  # the Sexp type given. Performs additional checks as specified by
202
215
  # the initializer.
203
216
 
204
- def process(exp)
217
+ def process exp
205
218
  return nil if exp.nil?
219
+
220
+ unless Sexp === exp then
221
+ raise SexpTypeError, "exp must be a Sexp, was #{exp.class}:#{exp.inspect}"
222
+ end
223
+
206
224
  if self.context.empty? then
207
225
  p :rewriting unless debug.empty?
208
226
  exp = self.rewrite(exp)
@@ -210,7 +228,7 @@ class SexpProcessor
210
228
  end
211
229
 
212
230
  unless @unsupported_checked then
213
- m = public_methods.grep(/^process_/) { |o| o.to_s.sub(/^process_/, '').to_sym }
231
+ m = public_methods.grep(/^process_/) { |o| o.to_s.sub(/^process_/, "").to_sym }
214
232
  supported = m - (m - @unsupported)
215
233
 
216
234
  raise UnsupportedNodeError, "#{supported.inspect} shouldn't be in @unsupported" unless supported.empty?
@@ -220,19 +238,19 @@ class SexpProcessor
220
238
 
221
239
  result = self.expected.new
222
240
 
223
- type = exp.first
241
+ type = exp.sexp_type
224
242
  raise "type should be a Symbol, not: #{exp.first.inspect}" unless
225
243
  Symbol === type
226
244
 
227
245
  in_context type do
228
- if @debug.has_key? type then
246
+ if @debug.key? type then
229
247
  str = exp.inspect
230
248
  puts "// DEBUG:(original ): #{str}" if str =~ @debug[type]
231
249
  end
232
250
 
233
251
  exp_orig = nil
234
252
  exp_orig = exp.deep_clone if $DEBUG or
235
- @debug.has_key? type or @exceptions.has_key?(type)
253
+ @debug.key? type or @exceptions.key?(type)
236
254
 
237
255
  raise UnsupportedNodeError, "'#{type}' is not a supported node type" if
238
256
  @unsupported.include? type
@@ -245,40 +263,44 @@ class SexpProcessor
245
263
  warn "WARNING: Using default method #{meth} for #{type}"
246
264
  end
247
265
 
248
- exp.shift if @auto_shift_type and meth != @default_method
266
+ exp = exp.sexp_body if @auto_shift_type and meth != @default_method # HACK
249
267
 
250
- result = error_handler(type, exp_orig) do
251
- self.send(meth, exp)
252
- end
268
+ result = error_handler(type, exp_orig) {
269
+ self.send meth, exp
270
+ }
253
271
 
254
- if @debug.has_key? type then
272
+ if @debug.key? type then
255
273
  str = exp.inspect
256
274
  puts "// DEBUG (processed): #{str}" if str =~ @debug[type]
257
275
  end
258
276
 
259
- raise SexpTypeError, "Result must be a #{@expected}, was #{result.class}:#{result.inspect}" unless @expected === result
277
+ raise SexpTypeError, "Result of #{type} must be a #{@expected}, was #{result.class}:#{result.inspect}" unless
278
+ @expected === result
260
279
 
261
280
  self.assert_empty(meth, exp, exp_orig) if @require_empty
262
281
  else
263
282
  unless @strict then
264
283
  until exp.empty? do
265
- sub_exp = exp.shift
284
+ sub_exp, *exp = exp # HACK
266
285
  sub_result = nil
267
286
  if Array === sub_exp then
268
287
  sub_result = error_handler(type, exp_orig) do
269
288
  process(sub_exp)
270
289
  end
271
290
  raise "Result is a bad type" unless Array === sub_exp
272
- raise "Result does not have a type in front: #{sub_exp.inspect}" unless Symbol === sub_exp.first unless sub_exp.empty?
291
+ raise "Result does not have a type in front: #{sub_exp.inspect}" unless
292
+ Symbol === sub_exp.sexp_type unless
293
+ sub_exp.empty?
273
294
  else
274
295
  sub_result = sub_exp
275
296
  end
276
- result << sub_result
297
+ # result << sub_result
298
+ result = result.class.new(*result, sub_result) # HACK
277
299
  end
278
300
 
279
301
  # NOTE: this is costly, but we are in the generic processor
280
302
  # so we shouldn't hit it too much with RubyToC stuff at least.
281
- result.sexp_type = exp.sexp_type if Sexp === exp and exp.sexp_type
303
+ result.c_type ||= exp.c_type if Sexp === exp and exp.respond_to?(:c_type)
282
304
  else
283
305
  msg = "Bug! Unknown node-type #{type.inspect} to #{self.class}"
284
306
  msg += " in #{exp_orig.inspect} from #{caller.inspect}" if $DEBUG
@@ -293,28 +315,26 @@ class SexpProcessor
293
315
  ##
294
316
  # Raises unless the Sexp type for +list+ matches +typ+
295
317
 
296
- def assert_type(list, typ)
318
+ def assert_type list, typ
297
319
  raise SexpTypeError, "Expected type #{typ.inspect} in #{list.inspect}" if
298
320
  not Array === list or list.first != typ
299
321
  end
300
322
 
301
- def error_handler(type, exp=nil) # :nodoc:
302
- begin
303
- return yield
304
- rescue StandardError => err
305
- if @exceptions.has_key? type then
306
- return @exceptions[type].call(self, exp, err)
307
- else
308
- warn "#{err.class} Exception thrown while processing #{type} for sexp #{exp.inspect} #{caller.inspect}" if $DEBUG
309
- raise
310
- end
311
- end
323
+ def error_handler type, exp = nil # :nodoc:
324
+ yield
325
+ rescue StandardError => err
326
+ return @exceptions[type].call self, exp, err if @exceptions.key? type
327
+
328
+ warn "#{err.class} Exception thrown while processing #{type} for sexp #{exp.inspect} #{caller.inspect}" if
329
+ $DEBUG
330
+
331
+ raise
312
332
  end
313
333
 
314
334
  ##
315
335
  # Registers an error handler for +node+
316
336
 
317
- def on_error_in(node_type, &block)
337
+ def on_error_in node_type, &block
318
338
  @exceptions[node_type] = block
319
339
  end
320
340
 
@@ -329,13 +349,9 @@ class SexpProcessor
329
349
  # return s(:dummy, process(exp), s(:extra, 42))
330
350
  # end
331
351
 
332
- def process_dummy(exp)
352
+ def process_dummy exp
333
353
  result = @expected.new(:dummy) rescue @expected.new
334
-
335
- until exp.empty? do
336
- result << self.process(exp.shift)
337
- end
338
-
354
+ result << self.process(exp.shift) until exp.empty?
339
355
  result
340
356
  end
341
357
 
@@ -362,11 +378,16 @@ class SexpProcessor
362
378
  env.scope(&block)
363
379
  end
364
380
 
381
+ ##
382
+ # Track a stack of contexts that the processor is in, pushing on
383
+ # +type+ yielding, and then removing the context from the stack.
384
+
365
385
  def in_context type
366
386
  self.context.unshift type
367
387
 
368
388
  yield
369
389
 
390
+ ensure
370
391
  self.context.shift
371
392
  end
372
393
 
@@ -376,35 +397,55 @@ class SexpProcessor
376
397
  # itches too much...
377
398
 
378
399
  class Environment
379
- def initialize
400
+ def initialize #:nodoc:
380
401
  @env = []
381
402
  @env.unshift({})
382
403
  end
383
404
 
405
+ ##
406
+ # Flatten out all scopes and return all key/value pairs.
407
+
384
408
  def all
385
409
  @env.reverse.inject { |env, scope| env.merge scope }
386
410
  end
387
411
 
412
+ ##
413
+ # Return the current number of scopes.
414
+
388
415
  def depth
389
416
  @env.length
390
417
  end
391
418
 
392
419
  # TODO: depth_of
393
420
 
421
+ ##
422
+ # Get +name+ from env at whatever scope it is defined in, or return nil.
423
+
394
424
  def [] name
395
- hash = @env.find { |closure| closure.has_key? name }
425
+ hash = @env.find { |closure| closure.key? name }
396
426
  hash[name] if hash
397
427
  end
398
428
 
429
+ ##
430
+ # If +name+ exists in the env, set it to +val+ in whatever scope
431
+ # it is in. If it doesn't exist, set +name+ to +val+ in the
432
+ # current scope.
433
+
399
434
  def []= name, val
400
- hash = @env.find { |closure| closure.has_key? name } || current
435
+ hash = @env.find { |closure| closure.key? name } || current
401
436
  hash[name] = val
402
437
  end
403
438
 
439
+ ##
440
+ # Get the current/top environment.
441
+
404
442
  def current
405
443
  @env.first
406
444
  end
407
445
 
446
+ ##
447
+ # Create a new scope and yield to the block passed.
448
+
408
449
  def scope
409
450
  @env.unshift({})
410
451
  begin
@@ -423,7 +464,7 @@ end
423
464
  # AKA, an interpreter.
424
465
 
425
466
  class SexpInterpreter < SexpProcessor
426
- def initialize
467
+ def initialize #:nodoc:
427
468
  super
428
469
 
429
470
  self.expected = Object
@@ -442,9 +483,30 @@ class MethodBasedSexpProcessor < SexpProcessor
442
483
  @@no_class = :main
443
484
  @@no_method = :none
444
485
 
445
- attr_reader :class_stack, :method_stack, :sclass, :method_locations
486
+ ##
487
+ # A stack of the classes/modules that are being processed
446
488
 
447
- def initialize
489
+ attr_reader :class_stack
490
+
491
+ ##
492
+ # A stack of the methods that are being processed. You'd think it'd
493
+ # only ever be 1 deep, but you'd be wrong. People do terrible things
494
+ # in/to ruby.
495
+
496
+ attr_reader :method_stack
497
+
498
+ ##
499
+ # A stack of the singleton classes that are being processed.
500
+
501
+ attr_reader :sclass
502
+
503
+ ##
504
+ # A lookup table of all the method locations that have been
505
+ # processed so far.
506
+
507
+ attr_reader :method_locations
508
+
509
+ def initialize #:nodoc:
448
510
  super
449
511
  @sclass = []
450
512
  @class_stack = []
@@ -458,7 +520,7 @@ class MethodBasedSexpProcessor < SexpProcessor
458
520
 
459
521
  def in_klass name
460
522
  if Sexp === name then
461
- name = case name.first
523
+ name = case name.sexp_type
462
524
  when :colon2 then
463
525
  name = name.flatten
464
526
  name.delete :const
@@ -483,7 +545,7 @@ class MethodBasedSexpProcessor < SexpProcessor
483
545
  ##
484
546
  # Adds name to the method stack, for the duration of the block
485
547
 
486
- def in_method name, file, line, line_max=nil
548
+ def in_method name, file, line, line_max = nil
487
549
  method_name = Regexp === name ? name.inspect : name.to_s
488
550
  @method_stack.unshift method_name
489
551
  line_max = "-#{line_max}" if line_max
@@ -514,10 +576,10 @@ class MethodBasedSexpProcessor < SexpProcessor
514
576
  def klass_name
515
577
  name = @class_stack.first
516
578
 
517
- if Sexp === name then
518
- raise "you shouldn't see me"
519
- elsif @class_stack.any?
520
- @class_stack.reverse.join("::").sub(/\([^\)]+\)$/, '')
579
+ raise "you shouldn't see me" if Sexp === name
580
+
581
+ if @class_stack.any?
582
+ @class_stack.reverse.join("::").sub(/\([^\)]+\)$/, "")
521
583
  else
522
584
  @@no_class
523
585
  end
@@ -535,10 +597,10 @@ class MethodBasedSexpProcessor < SexpProcessor
535
597
 
536
598
  ##
537
599
  # Process a class node until empty. Tracks all nesting. If you have
538
- # to subclass and override this method, you can clall super with a
600
+ # to subclass and override this method, you can call super with a
539
601
  # block.
540
602
 
541
- def process_class(exp)
603
+ def process_class exp
542
604
  exp.shift unless auto_shift_type # node type
543
605
  in_klass exp.shift do
544
606
  if block_given? then
@@ -555,7 +617,7 @@ class MethodBasedSexpProcessor < SexpProcessor
555
617
  # have to subclass and override this method, you can clall super
556
618
  # with a block.
557
619
 
558
- def process_defn(exp)
620
+ def process_defn exp
559
621
  exp.shift unless auto_shift_type # node type
560
622
  name = @sclass.empty? ? exp.shift : "::#{exp.shift}"
561
623
 
@@ -574,7 +636,7 @@ class MethodBasedSexpProcessor < SexpProcessor
574
636
  # If you have to subclass and override this method, you can clall
575
637
  # super with a block.
576
638
 
577
- def process_defs(exp)
639
+ def process_defs exp
578
640
  exp.shift unless auto_shift_type # node type
579
641
  process exp.shift # recv
580
642
  in_method "::#{exp.shift}", exp.file, exp.line, exp.line_max do
@@ -592,7 +654,7 @@ class MethodBasedSexpProcessor < SexpProcessor
592
654
  # to subclass and override this method, you can clall super with a
593
655
  # block.
594
656
 
595
- def process_module(exp)
657
+ def process_module exp
596
658
  exp.shift unless auto_shift_type # node type
597
659
  in_klass exp.shift do
598
660
  if block_given? then
@@ -609,7 +671,7 @@ class MethodBasedSexpProcessor < SexpProcessor
609
671
  # you have to subclass and override this method, you can clall super
610
672
  # with a block.
611
673
 
612
- def process_sclass(exp)
674
+ def process_sclass exp
613
675
  exp.shift unless auto_shift_type # node type
614
676
  in_sklass do
615
677
  if block_given? then
@@ -651,7 +713,7 @@ class MethodBasedSexpProcessor < SexpProcessor
651
713
  end
652
714
  end
653
715
 
654
- class Object
716
+ class Object # :nodoc:
655
717
 
656
718
  ##
657
719
  # deep_clone is the usual Marshalling hack to make a deep copy.
@@ -0,0 +1,126 @@
1
+ # :stopdoc:
2
+
3
+ ##
4
+ # I'm starting to warm up to this idea!
5
+ # ENV["STRICT_SEXP"] turns on various levels of conformance checking
6
+ #
7
+ # 1 = sexp[0] => sexp_type
8
+ # 1 = sexp.first => sexp_type
9
+ # 1 = sexp[0] = x => sexp_type = x
10
+ # 1 = sexp[1..-1] => sexp_body
11
+ # 1 = sexp[1..-1] = x => sexp_body = x
12
+ # 1 = sexp[-1] => last
13
+ # 2 = sexp[1] => no
14
+ # 2 = sexp[1] = x => no
15
+ # 3 = sexp[n] => no
16
+ # 3 = sexp[n] = x => no
17
+ # 3 = sexp.node_name => no (ie, method_missing)
18
+ # 4 = sexp.replace x => no
19
+ # 4 = sexp.concat x => no
20
+ # 4 = sexp.collect! => no
21
+ # 4 = sexp.compact! => no
22
+ # 4 = sexp.flatten! => no
23
+ # 4 = sexp.map! => no
24
+ # 4 = sexp.reject! => no
25
+ # 4 = sexp.reverse! => no
26
+ # 4 = sexp.rotate! => no
27
+ # 4 = sexp.select! => no
28
+ # 4 = sexp.shuffle! => no
29
+ # 4 = sexp.slice! => no
30
+ # 4 = sexp.sort! => no
31
+ # 4 = sexp.sort_by! => no
32
+ # 4 = sexp.uniq! => no
33
+ # 4 = sexp.unshift => no
34
+ # 4 = sexp.push => no
35
+ # 4 = sexp.pop => no
36
+ # 4 = sexp << => no
37
+
38
+ class Sexp
39
+ alias :safe_idx :[]
40
+ alias :safe_asgn :[]=
41
+ alias :sexp_type= :sexp_type=
42
+ alias :sexp_body= :sexp_body=
43
+ alias :shift :shift
44
+
45
+ def self.nuke_method name, level
46
+ define_method name do |*args|
47
+ raise "no: %p.%s %p" % [self, name, args]
48
+ end if __strict >= level
49
+ end
50
+
51
+ def self.__strict
52
+ ENV["STRICT_SEXP"].to_i
53
+ end
54
+
55
+ def __strict
56
+ self.class.__strict
57
+ end
58
+
59
+ undef_method :method_missing if __strict > 2
60
+
61
+ def method_missing msg, *args
62
+ raise "don't call method_missing on Sexps: %p.(%s)" % [msg, args.inspect[1..-2]]
63
+ end if __strict > 2
64
+
65
+ def [] i
66
+ raise "no idx: #{inspect}[#{i}]" if __strict > 2
67
+ raise "no idx>1: #{inspect}[#{i}]" if Integer === i && i > 1 if __strict > 1
68
+ raise "use sexp_type" if i == 0
69
+ raise "use sexp_body" if i == (1..-1)
70
+ raise "use last" if i == -1
71
+ self.safe_idx i
72
+ end
73
+
74
+ def []= i, v
75
+ raise "use sexp_type=" if i == 0
76
+ raise "use sexp_body=" if i == (1..-1)
77
+ raise "no asgn>1: #{inspect}[#{i}] = #{v.inspect}" if Integer === i && i > 1 if
78
+ __strict > 1
79
+ raise "no asgn: #{inspect}[#{i}] = #{v.inspect}" if
80
+ __strict > 2
81
+ self.safe_asgn i, v
82
+ end
83
+
84
+ def first
85
+ raise "use sexp_type"
86
+ end
87
+
88
+ nuke_method :collect!, 4
89
+ nuke_method :compact!, 4
90
+ nuke_method :concat, 4
91
+ nuke_method :flatten!, 4
92
+ nuke_method :map!, 4
93
+ nuke_method :pop, 4
94
+ nuke_method :push, 4
95
+ nuke_method :reject!, 4
96
+ nuke_method :replace, 4
97
+ nuke_method :reverse!, 4
98
+ nuke_method :rotate!, 4
99
+ nuke_method :select!, 4
100
+ nuke_method :shuffle!, 4
101
+ nuke_method :slice!, 4
102
+ nuke_method :sort!, 4
103
+ nuke_method :sort_by!, 4
104
+ nuke_method :uniq!, 4
105
+ nuke_method :unshift, 4
106
+ nuke_method :<<, 5
107
+ nuke_method :shift, 5
108
+
109
+ def sexp_type
110
+ safe_idx 0
111
+ end
112
+
113
+ def sexp_body from = 1
114
+ safe_idx from..-1
115
+ end
116
+
117
+ def sexp_type= v
118
+ self.safe_asgn 0, v
119
+ end
120
+
121
+ def sexp_body= v
122
+ self.safe_asgn 1..-1, v
123
+ end
124
+ end unless Sexp.new.respond_to? :safe_asgn if ENV["STRICT_SEXP"]
125
+
126
+ # :startdoc: