Class: C::Node
- Inherits:
-
Object
- Object
- C::Node
- Includes:
- Enumerable
- Defined in:
- lib/cast/node.rb,
lib/cast/to_s.rb,
lib/cast/parse.rb,
lib/cast/c_nodes.rb,
lib/cast/inspect.rb
Overview
Class implementations
Direct Known Subclasses
Declaration, Declarator, Enumerator, Expression, FunctionDef, Label, Member, MemberInit, NodeList, Parameter, Statement, TranslationUnit, Type
Defined Under Namespace
Classes: BadParent, Field, NoParent, Pos
Constant Summary collapse
- INSPECT_TAB =
' '
Instance Attribute Summary collapse
-
#parent ⇒ Object
readonly
The Node's parent.
-
#pos ⇒ Object
The position in the source file the construct this node represents appears at.
-
#subclasses ⇒ Object
readonly
The direct subclasses of this class (an Array of Class).
Class Method Summary collapse
-
.abstract ⇒ Object
Declare this class as abstract.
-
.add_field(newfield) ⇒ Object
Add the Field `newfield' to the list of fields for this class.
-
.child(name, default = nil) ⇒ Object
Declare a child with the given name and default value.
-
.field(name, default = :'no default') ⇒ Object
Declare a field with the given name and default value.
- .fields ⇒ Object
-
.inherited(klass) ⇒ Object
Callback defined in Class.
-
.initializer(*syms) ⇒ Object
Define an initialize method.
- .inspect1(x, prefix = '', indent = 0, is_child = true) ⇒ Object
-
.new_at(pos, *args) ⇒ Object
Like self.new, but the first argument is taken as the position of the Node.
-
.subclasses_recursive ⇒ Object
Return all classes which have this class somewhere in its ancestry (an Array of Class).
Instance Method Summary collapse
-
#==(other) ⇒ Object
True iff both are of the same class, and all fields are #==.
-
#=~(*args) ⇒ Object
Same as #match?.
-
#assert_invariants(testcase) ⇒ Object
Called by the test suite to ensure all invariants are true.
-
#attached? ⇒ Boolean
Return true if this Node is attached (i.e., #parent is nonnil), false otherwise.
-
#clone ⇒ Object
As defined for ::Object, but children are recursively `#clone'd.
-
#depth_first {|:ascending, _self| ... } ⇒ Object
Perform a depth-first walk of the AST, yielding on recursively on each child node:.
-
#detach ⇒ Object
Detach this Node from the tree and return it.
-
#detached? ⇒ Boolean
Return true if this Node is detached (i.e., #parent is nil), false otherwise.
-
#dup ⇒ Object
As defined for ::Object, but children are recursively `#dup'ed.
-
#each(&blk) ⇒ Object
Yield each child in field order.
-
#eql?(other) ⇒ Boolean
Same as #==.
-
#fields ⇒ Object
Return the list of fields this object has.
-
#hash ⇒ Object
#hash, as defined in Object.
-
#insert_next(*newnodes) ⇒ Object
Insert `newnodes' after this node.
-
#insert_prev(*newnodes) ⇒ Object
Insert `newnodes' before this node.
- #inspect ⇒ Object
-
#list_next ⇒ Object
Return the sibling Node that comes after this in the parent NodeList.
-
#list_prev ⇒ Object
Return the sibling Node that comes before this in the parent NodeList.
-
#match?(str, parser = nil) ⇒ Boolean
Return true if
str' is parsed to something==' to this Node. - #method_missing(meth, *args, &blk) ⇒ Object
-
#next ⇒ Object
Return the sibling Node that comes after this in preorder sequence.
-
#node_after(node) ⇒ Object
Return the Node that comes after the given Node in tree preorder.
-
#node_before(node) ⇒ Object
Return the Node that comes before the given Node in tree preorder.
-
#postorder {|_self| ... } ⇒ Object
Perform a postorder walk of the AST, yielding each node in turn.
-
#preorder(&blk) ⇒ Object
Perform a preorder walk of the AST, yielding each node in turn.
-
#prev ⇒ Object
Return the sibling Node that comes before this in preorder sequence.
-
#remove_node(node) ⇒ Object
Remove the given Node.
-
#replace_node(node, newnode = nil) ⇒ Object
Replace
node' withnewnode'. -
#replace_with(*nodes) ⇒ Object
Replace this Node in the tree with the given node(s).
-
#reverse_depth_first {|:ascending, _self| ... } ⇒ Object
Perform a reverse depth-first walk of the AST, yielding on each node:.
-
#reverse_each(&blk) ⇒ Object
Yield each child in reverse field order.
-
#reverse_postorder {|_self| ... } ⇒ Object
Perform a reverse postorder walk of the AST, yielding each node in turn.
-
#reverse_preorder(&blk) ⇒ Object
Perform a reverse preorder walk of the AST, yielding each node in turn.
-
#swap_with(node) ⇒ Object
Swap this node with `node' in their trees.
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(meth, *args, &blk) ⇒ Object
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 |
# File 'lib/cast/node.rb', line 587 def method_missing(meth, *args, &blk) # respond to `Module?' methstr = meth.to_s if methstr =~ /^([A-Z].*)\?$/ && C.const_defined?($1) klass = C.const_get($1) if klass.is_a?(::Module) return self.is_a?(klass) end end begin super rescue NoMethodError => e e.set_backtrace(caller) raise e end end |
Instance Attribute Details
#parent ⇒ Object
The Node's parent.
228 229 230 |
# File 'lib/cast/node.rb', line 228 def parent @parent end |
#pos ⇒ Object
The position in the source file the construct this node represents appears at.
235 236 237 |
# File 'lib/cast/node.rb', line 235 def pos @pos end |
#subclasses ⇒ Object (readonly)
The direct subclasses of this class (an Array of Class).
398 399 400 |
# File 'lib/cast/node.rb', line 398 def subclasses @subclasses end |
Class Method Details
.abstract ⇒ Object
Declare this class as abstract.
422 423 |
# File 'lib/cast/node.rb', line 422 def self.abstract end |
.add_field(newfield) ⇒ Object
Add the Field `newfield' to the list of fields for this class.
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 |
# File 'lib/cast/node.rb', line 449 def self.add_field(newfield) # add the newfield to @fields, and set the index fields = @fields newfield.index = fields.length fields << newfield # getter # define_method(newfield.reader) do # instance_variable_get(newfield.var) # end eval "def #{newfield.reader}; #{newfield.var}; end" # setter if newfield.child? define_method(newfield.writer) do |val| old = send(newfield.reader) return if val.equal? old # detach the old Node old = self.send(newfield.reader) unless old.nil? old.instance_variable_set(:@parent, nil) end # copy val if needed val = val.clone if !val.nil? && val.attached? # set self.instance_variable_set(newfield.var, val) # attach the new Node unless val.nil? val.instance_variable_set(:@parent, self) val.instance_variable_set(:@parent_field, newfield) end end else define_method(newfield.writer) do |val| instance_variable_set(newfield.var, val) end end end |
.child(name, default = nil) ⇒ Object
Declare a child with the given name and default value. The default value is cloned when used (unless cloning is unnecessary).
573 574 575 576 577 |
# File 'lib/cast/node.rb', line 573 def self.child(name, default=nil) field = Field.new(name, default) field.child = true add_field field end |
.field(name, default = :'no default') ⇒ Object
Declare a field with the given name and default value.
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 |
# File 'lib/cast/node.rb', line 548 def self.field(name, default=:'no default') if default == :'no default' if name.to_s[-1] == ?? default = false else default = nil end end # if the field exists, just update its default, otherwise, add # a new field self.fields.each do |field| if field.reader == name field.default = default return end end add_field Field.new(name, default) end |
.fields ⇒ Object
485 486 487 |
# File 'lib/cast/node.rb', line 485 def self.fields @fields end |
.inherited(klass) ⇒ Object
Callback defined in Class.
413 414 415 416 417 |
# File 'lib/cast/node.rb', line 413 def self.inherited(klass) @subclasses << klass klass.instance_variable_set(:@subclasses, []) klass.instance_variable_set(:@fields , []) end |
.initializer(*syms) ⇒ Object
Define an initialize method. The initialize method sets the
fields named in syms' from the arguments given to it. The initialize method also takes named parameters (i.e., an optional Hash as the last argument), which may be used to set fields not even named in syms'. The syms in the optional Hash are the
values of the `init_key' members of the corresponding Field
objects.
As an example for this Node class:
class X < Node
field :x
field :y
child :z
initializer :z, :y
end
...X.new can be called in any of these ways:
X.new # all fields set to default
X.new(1) # .z = 1
X.new(1, 2) # .z = 1, .y = 2
X.new(:x = 1, :y => 2, :z => 3) # .x = 1, .y = 2, .z = 3
X.new(1, :x => 2) # .z = 1, .x = 2
X.new(1, :z => 2) # undefined behaviour!
...etc.
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 |
# File 'lib/cast/node.rb', line 517 def self.initializer(*syms) define_method(:initialize) do |*args| # pop off the opts hash opts = args.last.is_a?(::Hash) ? args.pop : {} # add positional args to opts args.each_with_index do |arg, i| opts[syms[i]] = arg end # set field values fields.each do |field| key = field.init_key if opts.key?(key) send(field.writer, opts[key]) else send(field.writer, field.make_default) end end # pos, parent @pos = nil @parent = nil end end |
.inspect1(x, prefix = '', indent = 0, is_child = true) ⇒ Object
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/cast/inspect.rb', line 14 def Node.inspect1(x, prefix='', indent=0, is_child=true) case x when NodeList if x.empty? return "#{INSPECT_TAB*indent}#{prefix}[]\n" else str = "#{INSPECT_TAB*indent}#{prefix}\n" x.each do |el| str << inspect1(el, "- ", indent+1) end return str end when Node classname = x.class.name.gsub(/^C::/, '') str = "#{INSPECT_TAB*indent}#{prefix}#{classname}" fields = x.fields bools, others = fields.partition{|field| field.reader.to_s[-1] == ??} bools.delete_if{|field| !x.send(field.reader)} bools.map!{|field| field.init_key} unless bools == [] str << " (#{bools.join(' ')})" end str << "\n" others.each do |field| val = x.send(field.reader) next if val == field.make_default || # don't bother with non-child Nodes, since they may cause # loops in the tree (val.is_a?(Node) && !field.child?) str << inspect1(val, "#{field.reader}: ", indent+1, field.child?) end return str when Symbol return "#{INSPECT_TAB*indent}#{prefix}#{x}\n" else return "#{INSPECT_TAB*indent}#{prefix}#{x.inspect}\n" end return s.string end |
.new_at(pos, *args) ⇒ Object
Like self.new, but the first argument is taken as the position of the Node.
30 31 32 33 34 |
# File 'lib/cast/node.rb', line 30 def self.new_at(pos, *args) ret = new(*args) ret.pos = pos return ret end |
.subclasses_recursive ⇒ Object
Return all classes which have this class somewhere in its ancestry (an Array of Class).
404 405 406 407 408 |
# File 'lib/cast/node.rb', line 404 def self.subclasses_recursive ret = @subclasses.dup @subclasses.each{|c| ret.concat(c.subclasses_recursive)} return ret end |
Instance Method Details
#==(other) ⇒ Object
True iff both are of the same class, and all fields are #==.
39 40 41 42 43 44 45 46 47 |
# File 'lib/cast/node.rb', line 39 def ==(other) return false if !other.is_a? self.class fields.all? do |field| mine = self .send(field.reader) yours = other.send(field.reader) mine == yours end end |
#=~(*args) ⇒ Object
Same as #match?.
31 32 33 |
# File 'lib/cast/parse.rb', line 31 def =~(*args) match?(*args) end |
#assert_invariants(testcase) ⇒ Object
Called by the test suite to ensure all invariants are true.
15 16 17 18 19 20 21 22 23 24 |
# File 'lib/cast/node.rb', line 15 def assert_invariants(testcase) fields.each do |field| if val = send(field.reader) assert_same(self, node.parent, "field.reader is #{field.reader}") if field.child? assert_same(field, val.instance_variable_get(:@parent_field), "field.reader is #{field.reader}") end end end end |
#attached? ⇒ Boolean
Return true if this Node is attached (i.e., #parent is nonnil), false otherwise.
This is equal to !detached?
387 388 389 |
# File 'lib/cast/node.rb', line 387 def attached? !@parent.nil? end |
#clone ⇒ Object
As defined for ::Object, but children are recursively `#clone'd.
85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/cast/node.rb', line 85 def clone ret = super ret.instance_variable_set(:@parent, nil) fields.each do |field| next if !field.child? val = instance_variable_get(field.var) val = val.clone unless val.nil? ret.instance_variable_set(field.var, nil) ret.send(field.writer, val) end return ret end |
#depth_first {|:ascending, _self| ... } ⇒ Object
Perform a depth-first walk of the AST, yielding on recursively on each child node:
- (:descending, node) just prior to descending into `node'
- (:ascending, node) just after ascending from `node'
If the block throws :prune while descending, the children of the node that was passed to that block will not be visited.
140 141 142 143 144 145 146 147 |
# File 'lib/cast/node.rb', line 140 def depth_first(&blk) catch :prune do yield :descending, self each{|n| n.depth_first(&blk)} end yield :ascending, self return self end |
#detach ⇒ Object
Detach this Node from the tree and return it.
Raises NoParent if there's no parent.
292 293 294 295 296 |
# File 'lib/cast/node.rb', line 292 def detach @parent or raise NoParent @parent.remove_node(self) return self end |
#detached? ⇒ Boolean
Return true if this Node is detached (i.e., #parent is nil), false otherwise.
This is equal to !attached?
377 378 379 |
# File 'lib/cast/node.rb', line 377 def detached? @parent.nil? end |
#dup ⇒ Object
As defined for ::Object, but children are recursively `#dup'ed.
69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/cast/node.rb', line 69 def dup ret = super ret.instance_variable_set(:@parent, nil) fields.each do |field| next if !field.child? val = instance_variable_get(field.var) val = val.dup unless val.nil? ret.instance_variable_set(field.var, nil) ret.send(field.writer, val) end return ret end |
#each(&blk) ⇒ Object
Yield each child in field order.
107 108 109 110 111 112 113 114 115 |
# File 'lib/cast/node.rb', line 107 def each(&blk) fields.each do |field| if field.child? val = self.send(field.reader) yield val unless val.nil? end end return self end |
#eql?(other) ⇒ Boolean
Same as #==.
52 53 54 |
# File 'lib/cast/node.rb', line 52 def eql?(other) return self == other end |
#fields ⇒ Object
Return the list of fields this object has. Don't modify the returned array!
583 584 585 |
# File 'lib/cast/node.rb', line 583 def fields self.class.fields end |
#hash ⇒ Object
#hash, as defined in Object.
59 60 61 62 63 64 |
# File 'lib/cast/node.rb', line 59 def hash fields.inject(0) do |hash, field| val = send(field.reader) hash ^= val.hash end end |
#insert_next(*newnodes) ⇒ Object
Insert `newnodes' after this node. Return this node.
Raises:
-- NoParent if there's no parent
-- BadParent if the parent is otherwise not a NodeList
364 365 366 367 368 369 |
# File 'lib/cast/node.rb', line 364 def insert_next(*newnodes) @parent or raise NoParent @parent.NodeList? or raise BadParent @parent.insert_after(self, *newnodes) return self end |
#insert_prev(*newnodes) ⇒ Object
Insert `newnodes' before this node. Return this node.
Raises:
-- NoParent if there's no parent
-- BadParent if the parent is otherwise not a NodeList
350 351 352 353 354 355 |
# File 'lib/cast/node.rb', line 350 def insert_prev(*newnodes) @parent or raise NoParent @parent.NodeList? or raise BadParent @parent.insert_before(self, *newnodes) return self end |
#inspect ⇒ Object
10 11 12 |
# File 'lib/cast/inspect.rb', line 10 def inspect return Node.inspect1(self) end |
#list_next ⇒ Object
Return the sibling Node that comes after this in the parent NodeList.
Raises:
-- NoParent if there's no parent
-- BadParent if the parent is otherwise not a NodeList
256 257 258 259 260 |
# File 'lib/cast/node.rb', line 256 def list_next @parent or raise NoParent @parent.NodeList? or raise BadParent return @parent.node_after(self) end |
#list_prev ⇒ Object
Return the sibling Node that comes before this in the parent NodeList.
Raises:
-- NoParent if there's no parent
-- BadParent if the parent is otherwise not a NodeList
281 282 283 284 285 |
# File 'lib/cast/node.rb', line 281 def list_prev @parent or raise NoParent @parent.NodeList? or raise BadParent return @parent.node_before(self) end |
#match?(str, parser = nil) ⇒ Boolean
Return true if str' is parsed to something ==' to this Node.
str is first converted to a String using #to_s, then given to
self.class.parse (along with the optional `parser').
24 25 26 27 |
# File 'lib/cast/parse.rb', line 24 def match?(str, parser=nil) node = self.class.parse(str.to_s, parser) rescue (return false) self == node end |
#next ⇒ Object
Return the sibling Node that comes after this in preorder sequence.
Raises NoParent if there's no parent.
243 244 245 246 |
# File 'lib/cast/node.rb', line 243 def next @parent or raise NoParent return @parent.node_after(self) end |
#node_after(node) ⇒ Object
Return the Node that comes after the given Node in tree preorder.
615 616 617 618 619 620 621 622 623 624 625 626 627 |
# File 'lib/cast/node.rb', line 615 def node_after(node) node.parent.equal? self or raise ArgumentError, "node is not a child" fields = self.fields i = node.instance_variable_get(:@parent_field).index + 1 (i...fields.length).each do |i| f = fields[i] if f.child? && (val = self.send(f.reader)) return val end end return nil end |
#node_before(node) ⇒ Object
Return the Node that comes before the given Node in tree preorder.
633 634 635 636 637 638 639 640 641 642 643 644 645 |
# File 'lib/cast/node.rb', line 633 def node_before(node) node.parent.equal? self or raise ArgumentError, "node is not a child" fields = self.fields i = node.instance_variable_get(:@parent_field).index - 1 i.downto(0) do |i| f = fields[i] if f.child? && (val = self.send(f.reader)) return val end end return nil end |
#postorder {|_self| ... } ⇒ Object
Perform a postorder walk of the AST, yielding each node in turn. Return self.
202 203 204 205 206 |
# File 'lib/cast/node.rb', line 202 def postorder(&blk) each{|n| n.postorder(&blk)} yield self return self end |
#preorder(&blk) ⇒ Object
Perform a preorder walk of the AST, yielding each node in turn. Return self.
If the block throws :prune, the children of the node that was passed to that block will not be visited.
175 176 177 178 179 180 181 |
# File 'lib/cast/node.rb', line 175 def preorder(&blk) catch :prune do yield self each{|n| n.preorder(&blk)} end return self end |
#prev ⇒ Object
Return the sibling Node that comes before this in preorder sequence.
Raises NoParent if there's no parent.
268 269 270 271 |
# File 'lib/cast/node.rb', line 268 def prev @parent or raise NoParent return @parent.node_before(self) end |
#remove_node(node) ⇒ Object
Remove the given Node.
650 651 652 653 654 655 656 657 658 |
# File 'lib/cast/node.rb', line 650 def remove_node(node) node.parent.equal? self or raise ArgumentError, "node is not a child" field = node.instance_variable_get(:@parent_field) node.instance_variable_set(:@parent, nil) node.instance_variable_set(:@parent_field, nil) self.instance_variable_set(field.var, nil) return self end |
#replace_node(node, newnode = nil) ⇒ Object
Replace node' with newnode'.
663 664 665 666 667 668 669 |
# File 'lib/cast/node.rb', line 663 def replace_node(node, newnode=nil) node.parent.equal? self or raise ArgumentError, "node is not a child" field = node.instance_variable_get(:@parent_field) self.send(field.writer, newnode) return self end |
#replace_with(*nodes) ⇒ Object
Replace this Node in the tree with the given node(s). Return this node.
Raises:
-- NoParent if there's no parent
-- BadParent if the parent is otherwise not a NodeList, and
more than one node is given.
307 308 309 310 311 |
# File 'lib/cast/node.rb', line 307 def replace_with(*nodes) @parent or raise NoParent @parent.replace_node(self, *nodes) return self end |
#reverse_depth_first {|:ascending, _self| ... } ⇒ Object
Perform a reverse depth-first walk of the AST, yielding on each node:
- (:descending, node) just prior to descending into `node'
- (:ascending, node) just after ascending from `node'
If the block throws :prune while descending, the children of the node that was passed to that block will not be visited.
159 160 161 162 163 164 165 166 |
# File 'lib/cast/node.rb', line 159 def reverse_depth_first(&blk) catch :prune do yield :descending, self reverse_each{|n| n.reverse_depth_first(&blk)} end yield :ascending, self return self end |
#reverse_each(&blk) ⇒ Object
Yield each child in reverse field order.
120 121 122 123 124 125 126 127 128 |
# File 'lib/cast/node.rb', line 120 def reverse_each(&blk) fields.reverse_each do |field| if field.child? val = self.send(field.reader) yield val unless val.nil? end end return self end |
#reverse_postorder {|_self| ... } ⇒ Object
Perform a reverse postorder walk of the AST, yielding each node in turn. Return self.
212 213 214 215 216 |
# File 'lib/cast/node.rb', line 212 def reverse_postorder(&blk) reverse_each{|n| n.reverse_postorder(&blk)} yield self return self end |
#reverse_preorder(&blk) ⇒ Object
Perform a reverse preorder walk of the AST, yielding each node in turn. Return self.
If the block throws :prune, the children of the node that was passed to that block will not be visited.
190 191 192 193 194 195 196 |
# File 'lib/cast/node.rb', line 190 def reverse_preorder(&blk) catch :prune do yield self reverse_each{|n| n.reverse_preorder(&blk)} end return self end |
#swap_with(node) ⇒ Object
Swap this node with `node' in their trees. If either node is detached, the other will become detached as a result of calling this method.
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
# File 'lib/cast/node.rb', line 318 def swap_with node return self if node.equal? self if self.attached? if node.attached? # both attached -- use placeholder placeholder = Default.new my_parent = @parent my_parent.replace_node(self, placeholder) node.parent.replace_node(node, self) my_parent.replace_node(placeholder, node) else # only `self' attached @parent.replace_node(self, node) end else if node.attached? # only `node' attached node.parent.replace_node(node, self) else # neither attached -- nothing to do end end return self end |