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.



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

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

#massObject

Returns the size of the sexp, flattened.



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

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

#pretty_print(q) ⇒ Object

:nodoc:



221
222
223
224
225
226
227
228
# File 'lib/sexp.rb', line 221

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)


216
217
218
219
# File 'lib/sexp.rb', line 216

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.



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

def sexp_body
  self[1..-1]
end

#sexp_typeObject Also known as: head

Returns the node type of the Sexp.



233
234
235
# File 'lib/sexp.rb', line 233

def sexp_type
  first
end

#shiftObject



251
252
253
254
# File 'lib/sexp.rb', line 251

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))



260
261
262
263
264
265
266
267
268
269
270
# File 'lib/sexp.rb', line 260

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.



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/sexp.rb', line 275

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:



304
305
306
# File 'lib/sexp.rb', line 304

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

#to_sObject

:nodoc:



308
309
310
# File 'lib/sexp.rb', line 308

def to_s # :nodoc:
  inspect
end