Class: RLTK::ASTNode
- Inherits:
-
Object
- Object
- RLTK::ASTNode
- Extended by:
- Filigree::AbstractClass, Filigree::Destructurable
- Includes:
- Filigree::Visitable
- Defined in:
- lib/rltk/ast.rb
Overview
This class is a good start for all your abstract syntax tree node needs.
Instance Attribute Summary collapse
-
#notes ⇒ Hash
The notes hash for this node.
-
#parent ⇒ ASTNode
Reference to the parent node.
Class Method Summary collapse
-
.array_members ⇒ Array<Symbol>
List of members (children and values) that have array types.
-
.check_odr(name) ⇒ Object
Check to make sure a name isn’t re-defining a value or child.
-
.child(name, type, omit = false) ⇒ void
Defined a child for this AST class and its subclasses.
-
.child_names ⇒ Array<Symbol>
Array of the names of this node class’s children.
-
.custom_order(*members) ⇒ void
Define a custom ordering for the class to use when building the default constructor and destructurer.
-
.def_order ⇒ Array<Symbol>
Array of names of values/children in the order they were defined.
-
.define_accessor(name, type, set_parent = false) ⇒ void
This method defines a type checking accessor named name with type type.
-
.inc_children ⇒ Array<Symbol>
Array of the names of children that should be included in the constructor.
-
.inc_values ⇒ Array<Symbol>
Array of the names of values that should be included in the constructor.
-
.inherited(klass) ⇒ void
Called when the Lexer class is sub-classed, it installes necessary instance class variables.
-
.install_icvars ⇒ void
Installs instance class variables into a class.
-
.member_order(val = nil) ⇒ :values, ...
(also: order)
A getter and setter for a class’s initialization order.
-
.value(name, type, omit = false) ⇒ void
Defined a value for this AST class and its subclasses.
-
.value_names ⇒ Array<Symbol>
Array of the names of this node class’s values.
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
Used for AST comparison, this function will return true if the two nodes are of the same class and all of their values and children are equal.
-
#[](key) ⇒ Object
Note with the name key.
-
#[]=(key, value) ⇒ Object
Sets the note named key to value.
-
#children(as = Array) ⇒ Array<ASTNode>, Hash{Symbol => ASTNode}
Array or Hash of this node’s children.
-
#children=(children) ⇒ void
Assigns an array or hash of AST nodes as the children of this node.
-
#copy ⇒ ASTNode
Produce an exact copy of this tree.
-
#delete_note(key, recursive = true) ⇒ Object
Removes the note key from this node.
-
#destructure(arity) ⇒ Object
This method allows ASTNodes to be destructured for pattern matching.
-
#dump(dest = nil, limit = -1)) ⇒ void, String
This method is a simple wrapper around Marshal.dump, and is used to serialize an AST.
-
#each(order = :pre, &block) ⇒ void
An iterator over the node’s children.
-
#has_note?(key) ⇒ Boolean
(also: #note?)
Tests to see if a note named key is present at this node.
-
#initialize(*objects, &block) ⇒ ASTNode
constructor
Instantiates a new ASTNode object.
-
#map(&block) ⇒ Object
Create a new tree by using the provided Proc object to map the nodes of this tree to new nodes.
-
#map!(&block) ⇒ Object
Map the nodes in an AST to new nodes using the provided Proc object.
-
#root ⇒ ASTNode
Root of the abstract syntax tree.
-
#values(as = Array) ⇒ Array<Object>, Hash{Symbol => Object}
Array or Hash of this node’s values.
-
#values=(values) ⇒ Object
Assigns an array or hash of objects as the values of this node.
Constructor Details
#initialize(*objects, &block) ⇒ ASTNode
Instantiates a new ASTNode object. The arguments to this method are split into two lists: the set of values for this node and a list of its children. If the node has 2 values and 3 children you would pass the values in as the first two arguments (in the order they were declared) and then the children as the remaining arguments (in the order they were declared).
If a node has 2 values and 2 children and is passed only a single value the remaining values and children are assumed to be nil or empty arrays, depending on the declared type of the value or child.
If a block is passed to initialize the block will be executed in the conext of the new object.
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 |
# File 'lib/rltk/ast.rb', line 445 def initialize(*objects, &block) @notes = Hash.new() @parent = nil pairs = case self.class.member_order when :values then (self.class.inc_values + self.class.inc_children) when :children then (self.class.inc_children + self.class.inc_values) when :def then self.class.def_order when Array then self.class.member_order end.zip(objects).first(objects.length) pairs.each do |name, value| self.send("#{name}=", value) end self.class.array_members.each do |member| ivar_name = '@' + member.to_s self.instance_variable_set(ivar_name, []) if self.instance_variable_get(ivar_name).nil? end self.instance_exec(&block) if not block.nil? end |
Instance Attribute Details
#notes ⇒ Hash
Returns The notes hash for this node.
34 35 36 |
# File 'lib/rltk/ast.rb', line 34 def notes @notes end |
#parent ⇒ ASTNode
Returns Reference to the parent node.
31 32 33 |
# File 'lib/rltk/ast.rb', line 31 def parent @parent end |
Class Method Details
.array_members ⇒ Array<Symbol>
Returns List of members (children and values) that have array types.
43 44 45 |
# File 'lib/rltk/ast.rb', line 43 def array_members @array_members end |
.check_odr(name) ⇒ Object
Check to make sure a name isn’t re-defining a value or child.
50 51 52 53 54 55 56 57 58 |
# File 'lib/rltk/ast.rb', line 50 def check_odr(name) if @child_names.include? name raise ArgumentError, "Class #{self} or one of its superclasses already defines a child named #{name}" end if @value_names.include?(name) raise ArgumentError, "Class #{self} or one of its superclasses already defines a value named #{name}" end end |
.child(name, type, omit = false) ⇒ void
This method returns an undefined value.
Defined a child for this AST class and its subclasses. The name of the child will be used to define accessor methods that include type checking. The type of this child must be a subclass of the ASTNode class.
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/rltk/ast.rb', line 106 def child(name, type, omit = false) check_odr(name) if type.is_a?(Array) and type.length == 1 t = type.first elsif type.is_a?(Class) t = type else raise 'Child and Value types must be a class name or an array with a single class name element.' end # Check to make sure that type is a subclass of ASTNode. if not t.subclass_of?(ASTNode) raise "A child's type specification must be a subclass of ASTNode." end @child_names << name @array_members << name if type.is_a?(Array) if not omit @def_order << name @inc_children << name end define_accessor(name, type, true) end |
.child_names ⇒ Array<Symbol>
Returns Array of the names of this node class’s children.
136 137 138 |
# File 'lib/rltk/ast.rb', line 136 def child_names @child_names end |
.custom_order(*members) ⇒ void
This method returns an undefined value.
Define a custom ordering for the class to use when building the default constructor and destructurer.
195 196 197 |
# File 'lib/rltk/ast.rb', line 195 def custom_order(*members) @member_order = members end |
.def_order ⇒ Array<Symbol>
Returns Array of names of values/children in the order they were defined.
141 142 143 |
# File 'lib/rltk/ast.rb', line 141 def def_order @def_order end |
.define_accessor(name, type, set_parent = false) ⇒ void
This method returns an undefined value.
This method defines a type checking accessor named name with type type.
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/rltk/ast.rb', line 153 def define_accessor(name, type, set_parent = false) ivar_name = ('@' + name.to_s).to_sym define_method(name) do self.instance_variable_get(ivar_name) end if type.is_a?(Class) if set_parent define_method((name.to_s + '=').to_sym) do |value| self.instance_variable_set(ivar_name, check_type(value, type, nil, true)) value.parent = self if value end else define_method((name.to_s + '=').to_sym) do |value| self.instance_variable_set(ivar_name, check_type(value, type, nil, true)) end end else if set_parent define_method((name.to_s + '=').to_sym) do |value| self.instance_variable_set(ivar_name, check_array_type(value, type.first, nil, true)) value.each { |c| c.parent = self } end else define_method((name.to_s + '=').to_sym) do |value| self.instance_variable_set(ivar_name, check_array_type(value, type.first, nil, true)) end end end end |
.inc_children ⇒ Array<Symbol>
Returns Array of the names of children that should be included in the constructor.
200 201 202 |
# File 'lib/rltk/ast.rb', line 200 def inc_children @inc_children end |
.inc_values ⇒ Array<Symbol>
Returns Array of the names of values that should be included in the constructor.
205 206 207 |
# File 'lib/rltk/ast.rb', line 205 def inc_values @inc_values end |
.inherited(klass) ⇒ void
This method returns an undefined value.
Called when the Lexer class is sub-classed, it installes necessary instance class variables.
92 93 94 |
# File 'lib/rltk/ast.rb', line 92 def inherited(klass) klass.install_icvars end |
.install_icvars ⇒ void
This method returns an undefined value.
Installs instance class variables into a class.
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/rltk/ast.rb', line 63 def install_icvars if self.superclass == ASTNode @child_names = Array.new @value_names = Array.new @array_members = Array.new @member_order = :values @def_order = Array.new @inc_children = Array.new @inc_values = Array.new else @child_names = self.superclass.child_names.clone @value_names = self.superclass.value_names.clone @array_members = self.superclass.array_members.clone @member_order = (v = self.superclass.member_order).is_a?(Symbol) ? v : v.clone @def_order = self.superclass.def_order.clone @inc_children = self.superclass.inc_children.clone @inc_values = self.superclass.inc_values.clone end end |
.member_order(val = nil) ⇒ :values, ... Also known as: order
A getter and setter for a class’s initialization order. If the order value is ‘:values` the constructor will expect all of the values and then the children. If it is `:children` then the constructor expects children and then values. If it is `:def` the constructor expects to values and children in the order that they were defined. If val is nil the current value will be returned.
The default ordering is ‘:values`, which matches the behavior of previous versions of RLTK.
223 224 225 226 227 228 229 |
# File 'lib/rltk/ast.rb', line 223 def member_order(val = nil) if val @member_order = val else @member_order end end |
.value(name, type, omit = false) ⇒ void
This method returns an undefined value.
Defined a value for this AST class and its subclasses. The name of the value will be used to define accessor methods that include type checking.
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/rltk/ast.rb', line 241 def value(name, type, omit = false) check_odr(name) if not (type.is_a?(Class) or (type.is_a?(Array) and type.length == 1)) raise 'Child and Value types must be a class name or an array with a single class name element.' end @value_names << name @array_members << name if type.is_a?(Array) if not omit @def_order << name @inc_values << name end define_accessor(name, type) end |
.value_names ⇒ Array<Symbol>
Returns Array of the names of this node class’s values.
260 261 262 |
# File 'lib/rltk/ast.rb', line 260 def value_names @value_names end |
Instance Method Details
#==(other) ⇒ Boolean
Used for AST comparison, this function will return true if the two nodes are of the same class and all of their values and children are equal.
276 277 278 |
# File 'lib/rltk/ast.rb', line 276 def ==(other) self.class == other.class and self.values == other.values and self.children == other.children end |
#[](key) ⇒ Object
Returns Note with the name key.
281 282 283 |
# File 'lib/rltk/ast.rb', line 281 def [](key) @notes[key] end |
#[]=(key, value) ⇒ Object
Sets the note named key to value.
286 287 288 |
# File 'lib/rltk/ast.rb', line 286 def []=(key, value) @notes[key] = value end |
#children(as = Array) ⇒ Array<ASTNode>, Hash{Symbol => ASTNode}
Returns Array or Hash of this node’s children.
303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/rltk/ast.rb', line 303 def children(as = Array) if as == Array self.class.child_names.map { |name| self.send(name) } elsif as == Hash self.class.child_names.inject(Hash.new) { |h, name| h[name] = self.send(name); h } else raise 'Children can only be returned as an Array or a Hash.' end end |
#children=(children) ⇒ void
This method returns an undefined value.
Assigns an array or hash of AST nodes as the children of this node. If a hash is provided as an argument the key is used as the name of the child a object should be assigned to.
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 |
# File 'lib/rltk/ast.rb', line 322 def children=(children) case children when Array if children.length != self.class.child_names.length raise 'Wrong number of children specified.' end self.class.child_names.each_with_index do |name, i| self.send((name.to_s + '=').to_sym, children[i]) end when Hash children.each do |name, val| if self.class.child_names.include?(name) self.send((name.to_s + '=').to_sym, val) else raise "ASTNode subclass #{self.class.name} does not have a child named #{name}." end end end end |
#copy ⇒ ASTNode
Produce an exact copy of this tree.
347 348 349 |
# File 'lib/rltk/ast.rb', line 347 def copy self.map { |c| c } end |
#delete_note(key, recursive = true) ⇒ Object
Removes the note key from this node. If the recursive argument is true it will also remove the note from the node’s children.
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 |
# File 'lib/rltk/ast.rb', line 356 def delete_note(key, recursive = true) if recursive self.children.each do |child| next if not child if child.is_a?(Array) child.each { |c| c.delete_note(key, true) } else child.delete_note(key, true) end end end @notes.delete(key) end |
#destructure(arity) ⇒ Object
This method allows ASTNodes to be destructured for pattern matching.
291 292 293 294 295 296 297 298 |
# File 'lib/rltk/ast.rb', line 291 def destructure(arity) case self.class.member_order when :values then (self.class.inc_values + self.class.inc_children) when :children then (self.class.inc_children + self.class.inc_values) when :def then self.class.def_order when Array then self.class.member_order end.map { |m| self.send m } end |
#dump(dest = nil, limit = -1)) ⇒ void, String
This method is a simple wrapper around Marshal.dump, and is used to serialize an AST. You can use Marshal.load to reconstruct a serialized AST.
381 382 383 384 385 386 387 388 |
# File 'lib/rltk/ast.rb', line 381 def dump(dest = nil, limit = -1) case dest when nil then Marshal.dump(self, limit) when String then File.open(dest, 'w') { |f| Marshal.dump(self, f, limit) } when IO then Marshal.dump(self, dest, limit) else raise TypeError, "AST#dump expects nil, a String, or an IO object for the dest parameter." end end |
#each(order = :pre, &block) ⇒ void
This method returns an undefined value.
An iterator over the node’s children. The AST may be traversed in the following orders:
-
Pre-order (:pre)
-
Post-order (:post)
-
Level-order (:level)
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 |
# File 'lib/rltk/ast.rb', line 400 def each(order = :pre, &block) case order when :pre yield self self.children.flatten.compact.each { |c| c.each(:pre, &block) } when :post self.children.flatten.compact.each { |c| c.each(:post, &block) } yield self when :level level_queue = [self] while node = level_queue.shift yield node level_queue += node.children.flatten.compact end end end |
#has_note?(key) ⇒ Boolean Also known as: note?
Tests to see if a note named key is present at this node.
424 425 426 |
# File 'lib/rltk/ast.rb', line 424 def has_note?(key) @notes.has_key?(key) end |
#map(&block) ⇒ Object
This does not modify the current tree.
Create a new tree by using the provided Proc object to map the nodes of this tree to new nodes. This is always done in post-order, meaning that all children of a node are visited before the node itself.
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 |
# File 'lib/rltk/ast.rb', line 477 def map(&block) new_values = self.values.map { |v| v.clone } new_children = self.children.map do |c0| case c0 when Array then c0.map { |c1| c1.map(&block) } when ASTNode then c0.map(&block) when NilClass then nil end end new_node = self.class.new(*new_values, *new_children) new_node.notes = self.notes block.call(new_node) end |
#map!(&block) ⇒ Object
The root node can not be replaced and as such the result of
Map the nodes in an AST to new nodes using the provided Proc object. This is always done in post-order, meaning that all children of a node are visited before the node itself.
calling the provided block on the root node is used as the return value.
504 505 506 507 508 509 510 511 512 513 514 515 |
# File 'lib/rltk/ast.rb', line 504 def map!(&block) self.children = self.children.map do |c0| case c0 when Array then c0.map { |c1| c1.map!(&block) } when ASTNode then c0.map!(&block) when NilClass then nil end end block.call(self) end |
#root ⇒ ASTNode
Returns Root of the abstract syntax tree.
527 528 529 |
# File 'lib/rltk/ast.rb', line 527 def root if @parent then @parent.root else self end end |
#values(as = Array) ⇒ Array<Object>, Hash{Symbol => Object}
Returns Array or Hash of this node’s values.
534 535 536 537 538 539 540 541 542 543 544 |
# File 'lib/rltk/ast.rb', line 534 def values(as = Array) if as == Array self.class.value_names.map { |name| self.send(name) } elsif as == Hash self.class.value_names.inject(Hash.new) { |h, name| h[name] = self.send(name); h } else raise 'Values can only be returned as an Array or a Hash.' end end |
#values=(values) ⇒ Object
Assigns an array or hash of objects as the values of this node. If a hash is provided as an argument the key is used as the name of the value an object should be assigned to.
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 |
# File 'lib/rltk/ast.rb', line 551 def values=(values) case values when Array if values.length != self.class.value_names.length raise 'Wrong number of values specified.' end self.class.value_names.each_with_index do |name, i| self.send((name.to_s + '=').to_sym, values[i]) end when Hash values.each do |name, val| if self.class.value_names.include?(name) self.send((name.to_s + '=').to_sym, val) else raise "ASTNode subclass #{self.class.name} does not have a value named #{name}." end end end end |