Class: Sexp
Overview
:nodoc:
Direct Known Subclasses
Defined Under Namespace
Classes: All, Any, Atom, Child, Include, MatchCollection, Matcher, Not, Pattern, Remaining, Sibling, Type, Wild
Constant Summary collapse
- @@array_types =
TODO: remove
[ :array, :args ]
Instance Attribute Summary collapse
-
#comments ⇒ Object
Optional comments above/aside this sexp.
-
#file ⇒ Object
Accessors for the file.
-
#line(n = nil) ⇒ Object
If passed a line number, sets the line and returns self.
Class Method Summary collapse
-
._ ⇒ Object
Matches any single item.
-
.___ ⇒ Object
Matches all remaining input.
-
.all(*args) ⇒ Object
Matches only when all sub-expressions match.
-
.any(*args) ⇒ Object
Matches when any of the sub-expressions match.
-
.atom ⇒ Object
Matches any atom.
-
.child(child) ⇒ Object
Matches anything that has a child matching the sub-expression.
-
.from_array(a) ⇒ Object
Creates a new Sexp from Array
a
. -
.include(child) ⇒ Object
Matches an expression or any expression that includes the child.
-
.m(*values) ⇒ Object
Matches any atom who’s string representation matches the patterns passed in.
-
.not?(arg) ⇒ Boolean
Matches when sub-expression does not match.
-
.s(*args) ⇒ Object
Matches an S-Expression.
-
.t(name) ⇒ Object
Matches anything having the same sexp_type, which is the first value in a Sexp.
Instance Method Summary collapse
-
#/(pattern) ⇒ Object
Verifies that
pattern
is a Matcher and then dispatches to its #/ method. -
#==(obj) ⇒ Object
:nodoc:.
-
#=~(pattern) ⇒ Object
Verifies that
pattern
is a Matcher and then dispatches to its #=~ method. -
#array_type? ⇒ Boolean
Returns true if the node_type is
array
orargs
. -
#compact ⇒ Object
:nodoc:.
-
#deep_each(&block) ⇒ Object
Recursively enumerates the sexp yielding to
block
for every element. -
#depth ⇒ Object
Return the maximum depth of the sexp.
-
#each_of_type(t, &b) ⇒ Object
Enumeratates the sexp yielding to
b
when the node_type ==t
. -
#each_sexp ⇒ Object
Recursively enumerates all sub-sexps skipping non-Sexp elements.
-
#find_and_replace_all(from, to) ⇒ Object
Replaces all elements whose node_type is
from
withto
. -
#find_node(name, delete = false) ⇒ Object
:nodoc:.
-
#find_nodes(name) ⇒ Object
Find every node with type
name
. -
#gsub(pattern, repl) ⇒ Object
Replaces all Sexps matching
pattern
with Sexprepl
. -
#initialize(*args) ⇒ Sexp
constructor
Create a new Sexp containing
args
. -
#inspect ⇒ Object
(also: #to_s)
:nodoc:.
-
#line_max ⇒ Object
Returns the maximum line number of the children of self.
-
#map(&blk) ⇒ Object
:nodoc:.
-
#mass ⇒ Object
Returns the size of the sexp, flattened.
-
#method_missing(meth, delete = false) ⇒ Object
Returns the node named
node
, deleting it ifdelete
is true. -
#new(*body) ⇒ Object
Creates a new sexp with the new contents of
body
, but with the samefile
,line
, andcomment
as self. -
#pretty_print(q) ⇒ Object
:nodoc:.
-
#replace_sexp(pattern, &block) ⇒ Object
Recursively searches for the
pattern
yielding each match, and replacing it with the result of the block. -
#respond_to?(msg, private = false) ⇒ Boolean
:nodoc:.
-
#satisfy?(pattern) ⇒ Boolean
Verifies that
pattern
is a Matcher and then dispatches to its #satisfy? method. -
#search_each(pattern, &block) ⇒ Object
Recursively searches for the
pattern
yielding the matches. -
#sexp_body(from = 1) ⇒ Object
(also: #rest)
Returns the Sexp body (starting at
from
, defaulting to 1), ie the values without the node type. -
#sexp_body=(v) ⇒ Object
Returns the Sexp body, ie the values without the node type.
-
#sexp_type ⇒ Object
(also: #head)
Returns the node type of the Sexp.
-
#sexp_type=(v) ⇒ Object
Sets the node type of the Sexp.
-
#shift ⇒ Object
If run with debug, Sexp will raise if you shift on an empty Sexp.
-
#structure ⇒ Object
Returns the bare bones structure of the sexp.
-
#sub(pattern, repl) ⇒ Object
Replaces the Sexp matching
pattern
withrepl
. -
#to_a ⇒ Object
:nodoc:.
Constructor Details
#initialize(*args) ⇒ Sexp
Create a new Sexp containing args
.
30 31 32 |
# File 'lib/sexp.rb', line 30 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.
236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/sexp.rb', line 236 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 |
Instance Attribute Details
#comments ⇒ Object
Optional comments above/aside this sexp. Usually set by ruby_parser.
23 24 25 |
# File 'lib/sexp.rb', line 23 def comments @comments end |
#file ⇒ Object
Accessors for the file. Usually set by ruby_parser.
18 19 20 |
# File 'lib/sexp.rb', line 18 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.
210 211 212 213 214 215 216 217 |
# File 'lib/sexp.rb', line 210 def line n = nil if n then @line = n self else @line ||= nil end end |
Class Method Details
._ ⇒ Object
Matches any single item.
See Wild for examples.
455 456 457 |
# File 'lib/sexp.rb', line 455 def self._ Wild.new end |
.___ ⇒ Object
Matches all remaining input.
See Remaining for examples.
466 467 468 |
# File 'lib/sexp.rb', line 466 def self.___ Remaining.new end |
.all(*args) ⇒ Object
Matches only when all sub-expressions match.
This is also available via Matcher#&.
See All for examples.
506 507 508 |
# File 'lib/sexp.rb', line 506 def self.all *args All.new(*args) end |
.any(*args) ⇒ Object
Matches when any of the sub-expressions match.
This is also available via Matcher#|.
See Any for examples.
495 496 497 |
# File 'lib/sexp.rb', line 495 def self.any *args Any.new(*args) end |
.atom ⇒ Object
Matches any atom.
See Atom for examples.
484 485 486 |
# File 'lib/sexp.rb', line 484 def self.atom Atom.new end |
.child(child) ⇒ Object
Matches anything that has a child matching the sub-expression.
See Child for examples.
528 529 530 |
# File 'lib/sexp.rb', line 528 def self.child child Child.new child end |
.from_array(a) ⇒ Object
Creates a new Sexp from Array a
.
37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/sexp.rb', line 37 def self.from_array a ary = Array === a ? a : [a] self.new(*ary.map { |x| case x when Sexp x when Array self.from_array(x) else x end }) end |
.include(child) ⇒ Object
Matches an expression or any expression that includes the child.
See Include for examples.
475 476 477 |
# File 'lib/sexp.rb', line 475 def self.include child # TODO: rename, name is generic ruby Include.new(child) end |
.m(*values) ⇒ Object
Matches any atom who’s string representation matches the patterns passed in.
See Pattern for examples.
548 549 550 551 552 553 554 555 556 557 558 559 |
# File 'lib/sexp.rb', line 548 def self.m *values res = values.map { |value| case value when Regexp then value else re = Regexp.escape value.to_s Regexp.new "\\A%s\\Z" % re end } Pattern.new Regexp.union(*res) end |
.not?(arg) ⇒ Boolean
Matches when sub-expression does not match.
This is also available via Matcher#-@.
See Not for examples.
517 518 519 |
# File 'lib/sexp.rb', line 517 def self.not? arg Not.new arg end |
Instance Method Details
#/(pattern) ⇒ Object
Verifies that pattern
is a Matcher and then dispatches to its #/ method.
TODO: rename grep? match_all ? find_all ?
398 399 400 401 |
# File 'lib/sexp.rb', line 398 def / pattern raise ArgumentError, "Not a pattern: %p" % [pattern] unless Matcher === pattern pattern / self end |
#==(obj) ⇒ Object
:nodoc:
68 69 70 |
# File 'lib/sexp.rb', line 68 def == obj # :nodoc: obj.class == self.class and super # only because of a bug in ruby end |
#=~(pattern) ⇒ Object
Verifies that pattern
is a Matcher and then dispatches to its #=~ method.
See Matcher.=~
376 377 378 379 |
# File 'lib/sexp.rb', line 376 def =~ pattern raise ArgumentError, "Not a pattern: %p" % [pattern] unless Matcher === pattern 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.
77 78 79 80 81 |
# File 'lib/sexp.rb', line 77 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 |
#compact ⇒ Object
:nodoc:
83 84 85 |
# File 'lib/sexp.rb', line 83 def compact # :nodoc: self.delete_if(&:nil?) end |
#deep_each(&block) ⇒ Object
97 98 99 100 101 102 103 104 |
# File 'lib/sexp.rb', line 97 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 ⇒ Object
Return the maximum depth of the sexp. One-based.
109 110 111 |
# File 'lib/sexp.rb', line 109 def depth 1 + (each_sexp.map(&:depth).max || 0) end |
#each_of_type(t, &b) ⇒ Object
Enumeratates the sexp yielding to b
when the node_type == t
.
116 117 118 119 120 121 122 123 |
# File 'lib/sexp.rb', line 116 def each_of_type t, &b return enum_for(:each_of_type) unless block_given? each_sexp do | sexp | sexp.each_of_type(t, &b) yield sexp if sexp.sexp_type == t end end |
#each_sexp ⇒ Object
Recursively enumerates all sub-sexps skipping non-Sexp elements.
128 129 130 131 132 133 134 135 136 |
# File 'lib/sexp.rb', line 128 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.
142 143 144 145 146 147 148 149 150 |
# File 'lib/sexp.rb', line 142 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_node(name, delete = false) ⇒ Object
:nodoc:
183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/sexp.rb', line 183 def find_node name, delete = false # :nodoc: 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
.
201 202 203 |
# File 'lib/sexp.rb', line 201 def find_nodes name each_sexp.find_all { |sexp| sexp.sexp_type == name } end |
#gsub(pattern, repl) ⇒ Object
Replaces all Sexps matching pattern
with Sexp repl
.
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/sexp.rb', line 155 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 |
#inspect ⇒ Object Also known as: to_s
:nodoc:
174 175 176 177 178 179 180 181 |
# File 'lib/sexp.rb', line 174 def inspect # :nodoc: sexp_str = self.map(&:inspect).join ", " if ENV["VERBOSE"] && line then "s(#{sexp_str}).line(#{line})" else "s(#{sexp_str})" end end |
#line_max ⇒ Object
Returns the maximum line number of the children of self.
222 223 224 |
# File 'lib/sexp.rb', line 222 def line_max @line_max ||= self.deep_each.map(&:line).max end |
#map(&blk) ⇒ Object
:nodoc:
64 65 66 |
# File 'lib/sexp.rb', line 64 def map &blk # :nodoc: self.new(*super(&blk)) # ensures a sexp from map end |
#mass ⇒ Object
Returns the size of the sexp, flattened.
229 230 231 |
# File 'lib/sexp.rb', line 229 def mass @mass ||= inject(1) { |t, s| Sexp === s ? t + s.mass : t } end |
#new(*body) ⇒ Object
Creates a new sexp with the new contents of body
, but with the same file
, line
, and comment
as self.
56 57 58 59 60 61 62 |
# File 'lib/sexp.rb', line 56 def new(*body) r = self.class.new(*body) # ensures a sexp from map r.file = self.file if self.file r.line = self.line if self.line r.comments = self.comments if self.comments r end |
#pretty_print(q) ⇒ Object
:nodoc:
253 254 255 256 257 258 259 260 |
# File 'lib/sexp.rb', line 253 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 |
#replace_sexp(pattern, &block) ⇒ Object
Recursively searches for the pattern
yielding each match, and replacing it with the result of the block.
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 |
# File 'lib/sexp.rb', line 425 def replace_sexp pattern, &block # TODO: rename to gsub? raise ArgumentError, "Needs a pattern" unless pattern.kind_of? Matcher return yield self if pattern.satisfy? self # TODO: Needs #new_from(*new_body) to copy file/line/comment self.class.new(*self.map { |subset| case subset when Sexp then subset.replace_sexp pattern, &block else subset end }) end |
#respond_to?(msg, private = false) ⇒ Boolean
:nodoc:
248 249 250 251 |
# File 'lib/sexp.rb', line 248 def respond_to? msg, private = false # :nodoc: # why do I need this? Because ruby 2.0 is broken. That's why. super end |
#satisfy?(pattern) ⇒ Boolean
Verifies that pattern
is a Matcher and then dispatches to its #satisfy? method.
TODO: rename match?
387 388 389 390 |
# File 'lib/sexp.rb', line 387 def satisfy? pattern raise ArgumentError, "Not a pattern: %p" % [pattern] unless Matcher === pattern pattern.satisfy? self end |
#search_each(pattern, &block) ⇒ Object
Recursively searches for the pattern
yielding the matches.
406 407 408 409 410 411 412 413 414 415 416 417 418 |
# File 'lib/sexp.rb', line 406 def search_each pattern, &block # TODO: rename to grep? raise ArgumentError, "Needs a pattern" unless pattern.kind_of? Matcher return enum_for(:search_each, pattern) unless block_given? if pattern.satisfy? self then yield self end self.each_sexp do |subset| subset.search_each pattern, &block end end |
#sexp_body(from = 1) ⇒ Object Also known as: rest
Returns the Sexp body (starting at from
, defaulting to 1), ie the values without the node type.
280 281 282 |
# File 'lib/sexp.rb', line 280 def sexp_body from = 1 self.new(*self[from..-1]) end |
#sexp_body=(v) ⇒ Object
Returns the Sexp body, ie the values without the node type.
287 288 289 |
# File 'lib/sexp.rb', line 287 def sexp_body= v self[1..-1] = v end |
#sexp_type ⇒ Object Also known as: head
Returns the node type of the Sexp.
265 266 267 |
# File 'lib/sexp.rb', line 265 def sexp_type first end |
#sexp_type=(v) ⇒ Object
Sets the node type of the Sexp.
272 273 274 |
# File 'lib/sexp.rb', line 272 def sexp_type= v self[0] = v end |
#shift ⇒ Object
If run with debug, Sexp will raise if you shift on an empty Sexp. Helps with debugging.
298 299 300 301 |
# File 'lib/sexp.rb', line 298 def shift raise "I'm empty" if self.empty? super end |
#structure ⇒ Object
Returns the bare bones structure of the sexp. s(:a, :b, s(:c, :d), :e) => s(:a, s(:c))
307 308 309 310 311 312 313 314 |
# File 'lib/sexp.rb', line 307 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) ⇒ Object
Replaces the Sexp matching pattern
with repl
.
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
# File 'lib/sexp.rb', line 319 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 |