lib/sexp_processor.rb in ParseTree-1.3.7 vs lib/sexp_processor.rb in ParseTree-1.4.0

- old
+ new

@@ -1,196 +1,19 @@ $TESTING = false unless defined? $TESTING +require 'sexp' + class Object ## # deep_clone is the usual Marshalling hack to make a deep copy. # It is rather slow, so use it sparingly. Helps with debugging # SexpProcessors since you usually shift off sexps. def deep_clone Marshal.load(Marshal.dump(self)) end -end - -## -# Sexps are the basic storage mechanism of SexpProcessor. Sexps have -# a +type+ (to be renamed +node_type+) which is the first element of -# the Sexp. The type is used by SexpProcessor to determine whom to -# dispatch the Sexp to for processing. - -class Sexp < Array # ZenTest FULL - - @@array_types = [ :array, :args, ] - - ## - # Named positional parameters. - # Use with +SexpProcessor.require_empty=false+. - attr_accessor :accessors - - ## - # Create a new Sexp containing +args+. - - def initialize(*args) - @accessors = [] - super(args) - end - - def self.from_array(a) - ary = Array === a ? a : [a] - - result = self.new - - ary.each do |x| - case x - when Sexp - result << x - when Array - result << self.from_array(x) - else - result << x - end - end - - result - end - - ## - # Returns true if the node_type is +array+ or +args+. - # - # REFACTOR: to TypedSexp - we only care when we have units. - - def array_type? - type = self.first - @@array_types.include? type - end - - ## - # Enumeratates the sexp yielding to +b+ when the node_type == +t+. - - def each_of_type(t, &b) - each do | elem | - if Sexp === elem then - elem.each_of_type(t, &b) - b.call(elem) if elem.first == t - end - end - end - - ## - # Replaces all elements whose node_type is +from+ with +to+. Used - # only for the most trivial of rewrites. - - def find_and_replace_all(from, to) - each_with_index do | elem, index | - if Sexp === elem then - elem.find_and_replace_all(from, to) - else - self[index] = to if elem == from - end - end - end - - ## - # Fancy-Schmancy method used to implement named positional accessors - # via +accessors+. - # - # Example: - # - # class MyProcessor < SexpProcessor - # def initialize - # super - # self.require_empty = false - # self.sexp_accessors = { - # :call => [:lhs, :name, :rhs] - # } - # ... - # end - # - # def process_call(exp) - # lhs = exp.lhs - # name = exp.name - # rhs = exp.rhs - # ... - # end - # end - - def method_missing(meth, *a, &b) - super unless @accessors.include? meth - - index = @accessors.index(meth) + 1 # skip type - return self.at(index) - end - - ## - # Returns the Sexp without the node_type. - - def sexp_body - self[1..-1] - end - - ## - # Returnes the bare bones structure of the sexp. - # s(:a, :b, s(:c, :d), :e) => s(:a, s(:c)) - - def structure - result = self.class.new - if Array === self.first then - result = self.first.structure - else - result << self.shift - self.grep(Array).each do |subexp| - result << subexp.structure - end - end - result - end - - def ==(obj) # :nodoc: - case obj - when Sexp - super - else - false - end - end - - def to_a # :nodoc: - self.map { |o| Sexp === o ? o.to_a : o } - end - - def inspect # :nodoc: - sexp_str = self.map {|x|x.inspect}.join(', ') - return "s(#{sexp_str})" - end - - def pretty_print(q) # :nodoc: - q.group(1, 's(', ')') do - q.seplist(self) {|v| q.pp v } - end - end - - def to_s # :nodoc: - inspect - end - - ## - # If run with debug, Sexp will raise if you shift on an empty - # Sexp. Helps with debugging. - - def shift - raise "I'm empty" if self.empty? - super - end if $DEBUG or $TESTING - -end - -## -# This is just a stupid shortcut to make indentation much cleaner. - -def s(*args) - Sexp.new(*args) end ## # SexpProcessor base exception class.