Class: Sexp

Inherits:
Array show all
Defined in:
lib/sexp.rb

Overview

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.

Direct Known Subclasses

SexpMatchSpecial

Constant Summary collapse

@@array_types =
[ :array, :args, ]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ Sexp

Create a new Sexp containing args.



19
20
21
# File 'lib/sexp.rb', line 19

def initialize(*args)
  super(args)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, delete = false) ⇒ Object

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



219
220
221
# File 'lib/sexp.rb', line 219

def method_missing meth, delete = false
  find_node meth, delete
end

Instance Attribute Details

#commentsObject

Returns the value of attribute comments.



12
13
14
# File 'lib/sexp.rb', line 12

def comments
  @comments
end

#fileObject

Returns the value of attribute file.



12
13
14
# File 'lib/sexp.rb', line 12

def file
  @file
end

#line(n = nil) ⇒ Object

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.



193
194
195
196
197
198
199
200
# File 'lib/sexp.rb', line 193

def line(n=nil)
  if n then
    @line = n
    self
  else
    @line ||= nil
  end
end

Class Method Details

.from_array(a) ⇒ Object

Creates a new Sexp from Array a.



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/sexp.rb', line 26

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

Instance Method Details

#==(obj) ⇒ Object

:nodoc:



45
46
47
# File 'lib/sexp.rb', line 45

def ==(obj) # :nodoc:
  obj.class == self.class and super
end

#===(sexp) ⇒ Object

Returns true if this Sexp’s pattern matches sexp.



52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/sexp.rb', line 52

def ===(sexp)
  return nil unless Sexp === sexp
  pattern = self # this is just for my brain

  return true if pattern == sexp

  sexp.each do |subset|
    return true if pattern === subset
  end

  return nil
end

#=~(pattern) ⇒ Object

Returns true if this Sexp matches pattern. (Opposite of #===.)



68
69
70
# File 'lib/sexp.rb', line 68

def =~(pattern)
  return pattern === self
end

#array_type?Boolean

Returns true if the node_type is array or args.

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

Returns:

  • (Boolean)


77
78
79
80
# File 'lib/sexp.rb', line 77

def array_type?
  type = self.first
  @@array_types.include? type
end

#compactObject

:nodoc:



82
83
84
# File 'lib/sexp.rb', line 82

def compact # :nodoc:
  self.delete_if { |o| o.nil? }
end

#deep_each(&block) ⇒ Object

Recursively enumerates the sexp yielding to block for every element.



89
90
91
92
93
94
95
96
# File 'lib/sexp.rb', line 89

def deep_each(&block)
  return enum_for(:deep_each) unless block_given?

  self.each_sexp do |sexp|
    block[sexp]
    sexp.deep_each(&block)
  end
end

#each_of_type(t, &b) ⇒ Object

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



101
102
103
104
105
106
107
108
109
110
# File 'lib/sexp.rb', line 101

def each_of_type(t, &b)
  return enum_for(:each_of_type) unless block_given?

  each do | elem |
    if Sexp === elem then
      elem.each_of_type(t, &b)
      b.call(elem) if elem.first == t
    end
  end
end

#each_sexpObject

Recursively enumerates all sub-sexps skipping non-Sexp elements.



115
116
117
118
119
120
121
122
123
# File 'lib/sexp.rb', line 115

def each_sexp
  return enum_for(:each_sexp) unless block_given?

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

    yield sexp
  end
end

#find_and_replace_all(from, to) ⇒ Object

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



129
130
131
132
133
134
135
136
137
# File 'lib/sexp.rb', line 129

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

#find_node(name, delete = false) ⇒ Object



166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/sexp.rb', line 166

def find_node name, delete = false
  matches = find_nodes name

  case matches.size
  when 0 then
    nil
  when 1 then
    match = matches.first
    delete match if delete
    match
  else
    raise NoMethodError, "multiple nodes for #{name} were found in #{inspect}"
  end
end

#find_nodes(name) ⇒ Object

Find every node with type name.



184
185
186
# File 'lib/sexp.rb', line 184

def find_nodes name
  find_all { | sexp | Sexp === sexp and sexp.first == name }
end

#gsub(pattern, repl) ⇒ Object

Replaces all Sexps matching pattern with Sexp repl.



142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/sexp.rb', line 142

def gsub(pattern, repl)
  return repl if pattern == self

  new = self.map do |subset|
    case subset
    when Sexp then
      subset.gsub(pattern, repl)
    else
      subset
    end
  end

  return Sexp.from_array(new)
end

#inspectObject

:nodoc:



157
158
159
160
161
162
163
164
# File 'lib/sexp.rb', line 157

def inspect # :nodoc:
  sexp_str = self.map {|x|x.inspect}.join(', ')
  if ENV['VERBOSE'] && line then
    "s(#{sexp_str}).line(#{line})"
  else
    "s(#{sexp_str})"
  end
end

#line_maxObject

Returns the maximum line number of the children of self.



205
206
207
# File 'lib/sexp.rb', line 205

def line_max
  self.deep_each.map(&:line).max
end

#massObject

Returns the size of the sexp, flattened.



212
213
214
# File 'lib/sexp.rb', line 212

def mass
  @mass ||= self.structure.flatten.size
end

#pretty_print(q) ⇒ Object

:nodoc:



228
229
230
231
232
233
234
235
# File 'lib/sexp.rb', line 228

def pretty_print(q) # :nodoc:
  nnd = ')'
  nnd << ".line(#{line})" if line && ENV['VERBOSE']

  q.group(1, 's(', nnd) do
    q.seplist(self) {|v| q.pp v }
  end
end

#respond_to?(msg, private = false) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


223
224
225
226
# File 'lib/sexp.rb', line 223

def respond_to? msg, private = false # :nodoc:
  # why do I need this? Because ruby 2.0 is broken. That's why.
  super
end

#sexp_bodyObject Also known as: rest

Returns the Sexp body, ie the values without the node type.



247
248
249
# File 'lib/sexp.rb', line 247

def sexp_body
  self[1..-1]
end

#sexp_typeObject Also known as: head

Returns the node type of the Sexp.



240
241
242
# File 'lib/sexp.rb', line 240

def sexp_type
  first
end

#shiftObject



258
259
260
261
# File 'lib/sexp.rb', line 258

def shift
  raise "I'm empty" if self.empty?
  super
end

#structureObject

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



267
268
269
270
271
272
273
274
275
276
277
# File 'lib/sexp.rb', line 267

def structure
  if Array === self.first then
    s(:bogus, *self).structure # TODO: remove >= 4.2.0
  else
    result = s(self.first)
    self.each do |subexp|
      result << subexp.structure if Sexp === subexp
    end
    result
  end
end

#sub(pattern, repl) ⇒ Object

Replaces the Sexp matching pattern with repl.



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/sexp.rb', line 282

def sub(pattern, repl)
  return repl.dup if pattern == 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
        elsif pattern === subset then
          done = true
          subset.sub pattern, repl
        else
          subset
        end
      else
        subset
      end
    end
  end

  return Sexp.from_array(new)
end

#to_aObject

:nodoc:



311
312
313
# File 'lib/sexp.rb', line 311

def to_a # :nodoc:
  self.map { |o| Sexp === o ? o.to_a : o }
end

#to_sObject

:nodoc:



315
316
317
# File 'lib/sexp.rb', line 315

def to_s # :nodoc:
  inspect
end