class Sexp

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.

Constants

UNASSIGNED

Attributes

comments[RW]

Optional comments above/aside this sexp. Usually set by ruby_parser.

file[RW]

Accessors for the file. Usually set by ruby_parser.

line[W]

A setter for the line this sexp was found on. Usually set by ruby_parser.

line_max[W]

Set the maximum line number for this sexp. Often set by ruby_parser.

Public Class Methods

from_array(a) click to toggle source

Creates a new Sexp from Array a.

# File lib/sexp.rb, line 44
def self.from_array a
  ary = Array === a ? a : [a]

  self.new._concat(ary.map { |x|
             case x
             when Sexp
               x
             when Array
               self.from_array(x)
             else
               x
             end
           })
end
new(*args) click to toggle source

Create a new Sexp containing args.

Calls superclass method
# File lib/sexp.rb, line 35
def initialize *args
  super(args)
end

Public Instance Methods

array_type?() click to toggle source

Returns true if the node_type is array or args.

REFACTOR: to TypedSexp - we only care when we have units.

# File lib/sexp.rb, line 93
def array_type?
  warn "DEPRECATED: please file an issue if you actually use this. from #{caller.first}"
  type = self.sexp_type
  @@array_types.include? type
end
deep_each(&block) click to toggle source

Recursively enumerates the sexp yielding to block for every sub-Sexp.

Returning :skip will stop traversing that subtree:

sexp.deep_each do |s|
  next :skip if s.sexp_type == :if
  # ...
end
# File lib/sexp.rb, line 113
def deep_each &block
  return enum_for(:deep_each) unless block_given?

  self.each_sexp do |sexp|
    next if block[sexp] == :skip
    sexp.deep_each(&block)
  end
end
depth() click to toggle source

Return the maximum depth of the sexp. One-based.

# File lib/sexp.rb, line 125
def depth
  1 + (each_sexp.map(&:depth).max || 0)
end
each_of_type(t) { |sexp| ... } click to toggle source

Enumeratates the sexp yielding to b when the node_type == t.

# File lib/sexp.rb, line 132
def each_of_type t, &b
  return enum_for(:each_of_type, t) unless block_given?

  each_sexp do | sexp |
    sexp.each_of_type(t, &b)
    yield sexp if sexp.sexp_type == t
  end
end
each_sexp() { |sexp| ... } click to toggle source

Enumerates all sub-sexps skipping non-Sexp elements.

# File lib/sexp.rb, line 144
def each_sexp
  return enum_for(:each_sexp) unless block_given?

  self.each do |sexp|
    next unless Sexp === sexp

    yield sexp
  end
end
eql?(o) click to toggle source
Calls superclass method
# File lib/sexp.rb, line 80
def eql? o
  self.class == o.class && super
end
find_and_replace_all(from, to) click to toggle source

Replaces all elements whose node_type is from with to. Used only for the most trivial of rewrites.

# File lib/sexp.rb, line 158
def find_and_replace_all from, to
  each_with_index do | elem, index |
    if Sexp === elem then
      elem.find_and_replace_all(from, to)
    elsif elem == from
      self[index] = to
    end
  end
end
find_nodes(name) click to toggle source

Find every node with type name.

# File lib/sexp.rb, line 217
def find_nodes name
  each_sexp.find_all { |sexp| sexp.sexp_type == name }
end
gsub(pattern, repl) click to toggle source

Replaces all Sexps matching pattern with Sexp repl.

# File lib/sexp.rb, line 171
def gsub pattern, repl
  return repl if pattern == self

  new = self.map { |subset|
    case subset
    when Sexp then
      if Matcher === pattern && pattern.satisfy?(subset) then # TODO: make === be satisfy? maybe?
        repl.dup rescue repl
      else
        subset.gsub pattern, repl
      end
    else
      subset
    end
  }

  Sexp.from_array new
end
hash() click to toggle source
# File lib/sexp.rb, line 84
def hash
  @hash ||= [self.class, *self].hash
end
head()
Alias for: sexp_type
line(n = UNASSIGNED) click to toggle source

If passed a line number, sets the line and returns self. Otherwise returns the line number. This allows you to do message cascades and still get the sexp back.

# File lib/sexp.rb, line 228
def line n = UNASSIGNED
  if n != UNASSIGNED then
    raise ArgumentError, "setting %p.line %p" % [self, n] unless Integer === n
    @line = n
    self
  else
    @line ||= nil
  end
end
line_max() click to toggle source

Returns the maximum line number of the children of self.

# File lib/sexp.rb, line 241
def line_max
  @line_max ||= self.deep_each.map(&:line).compact.max
end
mass() click to toggle source

Returns the size of the sexp, flattened.

# File lib/sexp.rb, line 248
def mass
  @mass ||= inject(1) { |t, s| Sexp === s ? t + s.mass : t }
end
method_missing(meth, delete = false) click to toggle source

Returns the node named node, deleting it if delete is true.

# File lib/sexp.rb, line 255
def method_missing meth, delete = false
  r = find_node meth, delete
  if ENV["DEBUG"] then
    if r.nil? then
      warn "%p.method_missing(%p) => nil from %s" % [self, meth, caller.first]
    elsif ENV["VERBOSE"]
      warn "%p.method_missing(%p) from %s" % [self, meth, caller.first]
    end
  end
  r
end
new(*body) click to toggle source

Creates a new sexp with the new contents of body, but with the same file, line, and comment as self.

# File lib/sexp.rb, line 63
def new(*body)
  r = self.class.new._concat(body) # ensures a sexp from map
  r.file     = self.file     if self.file
  r.line     = self.line     if self.line
  r.line_max = self.line_max if defined?(@line_max)
  r.comments = self.comments if self.comments
  r
end
rest(from = 1)
Alias for: sexp_body
sexp_body(from = 1) click to toggle source

Returns the Sexp body (starting at from, defaulting to 1), ie the values without the node type.

# File lib/sexp.rb, line 299
def sexp_body from = 1
  self.new._concat(self[from..-1] || [])
end
Also aliased as: rest
sexp_body=(v) click to toggle source

Sets the Sexp body to new content.

# File lib/sexp.rb, line 306
def sexp_body= v
  self[1..-1] = v
end
sexp_type() click to toggle source

Returns the node type of the Sexp.

# File lib/sexp.rb, line 284
def sexp_type
  first
end
Also aliased as: head
sexp_type=(v) click to toggle source

Sets the node type of the Sexp.

# File lib/sexp.rb, line 291
def sexp_type= v
  self[0] = v
end
shift() click to toggle source

If run with debug, Sexp will raise if you shift on an empty Sexp. Helps with debugging.

Calls superclass method
# File lib/sexp.rb, line 317
def shift
  raise "I'm empty" if self.empty?
  super
end
structure() click to toggle source

Returns the bare bones structure of the sexp. s(:a, :b, s(:c, :d), :e) => s(:a, s(:c))

# File lib/sexp.rb, line 326
def structure
  if Array === self.sexp_type then
    warn "NOTE: form s(s(:subsexp)).structure is deprecated. Removing in 5.0"
    s(:bogus, *self).structure # TODO: remove >= 4.2.0
  else
    s(self.sexp_type, *each_sexp.map(&:structure))
  end
end
sub(pattern, repl) click to toggle source

Replaces the Sexp matching pattern with repl.

# File lib/sexp.rb, line 338
def sub pattern, repl
  return repl.dup if pattern == self
  return repl.dup if Matcher === pattern && pattern.satisfy?(self)

  done = false

  new = self.map do |subset|
    if done then
      subset
    else
      case subset
      when Sexp then
        if pattern == subset then
          done = true
          repl.dup rescue repl
        elsif Matcher === pattern && pattern.satisfy?(subset) then
          done = true
          repl.dup rescue repl
        else
          subset.sub pattern, repl
        end
      else
        subset
      end
    end
  end

  Sexp.from_array new
end
value() click to toggle source

Return the value (last item) of a single element sexp (eg ‘s(:lit, 42)`).

# File lib/sexp.rb, line 377
def value
  raise "multi item sexp" if size > 2
  last
end