Class: RLTK::ASTNode
Overview
This class is a good start for all your abstract syntax tree node needs.
Instance Attribute Summary collapse
-
#parent ⇒ ASTNode
Reference to the parent node.
Class Method Summary collapse
-
.child(name, type) ⇒ 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.
-
.define_accessor(name, type, set_parent = false) ⇒ void
This method defines a type checking accessor named name with type type.
-
.inherited(klass) ⇒ void
Called when the Lexer class is sub-classed, it installes necessary instance class variables.
-
.install_icvars ⇒ void
Installs instance class varialbes into a class.
-
.value(name, type) ⇒ 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 ⇒ Array<ASTNode>
Array of this node’s children.
-
#children=(children) ⇒ void
Assigns an array of AST nodes as the children of this node.
-
#delete_note(key, recursive = true) ⇒ Object
Removes the note key from this node.
-
#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) ⇒ 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 ⇒ Array<Object>
Array of this node’s values.
-
#values=(values) ⇒ Object
Assigns an array of objects as the values of this node.
Constructor Details
#initialize(*objects) ⇒ 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.
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 |
# File 'lib/rltk/ast.rb', line 340 def initialize(*objects) if self.class == RLTK::ASTNode raise 'Attempting to instantiate the RLTK::ASTNode class.' else @notes = Hash.new() @parent = nil # Pad out the objects array with nil values. max_args = self.class.value_names.length + self.class.child_names.length objects.fill(nil, objects.length...max_args) pivot = self.class.value_names.length self.values = objects[0...pivot] self.children = objects[pivot..-1] end end |
Instance Attribute Details
#parent ⇒ ASTNode
Returns Reference to the parent node.
40 41 42 |
# File 'lib/rltk/ast.rb', line 40 def parent @parent end |
Class Method Details
.child(name, type) ⇒ 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.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/rltk/ast.rb', line 81 def child(name, type) 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 define_accessor(name, type, true) end |
.child_names ⇒ Array<Symbol>
Returns Array of the names of this node class’s children.
103 104 105 |
# File 'lib/rltk/ast.rb', line 103 def child_names @child_names 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.
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/rltk/ast.rb', line 115 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| if value.is_a?(type) or value == nil self.instance_variable_set(ivar_name, value) value.parent = self if value else raise TypeMismatch.new(type, value.class) end end else define_method((name.to_s + '=').to_sym) do |value| if value.is_a?(type) or value == nil self.instance_variable_set(ivar_name, value) else raise TypeMismatch.new(type, value.class) end end end else type = type.first if set_parent define_method((name.to_s + '=').to_sym) do |value| if value.inject(true) { |m, o| m and o.is_a?(type) } self.instance_variable_set(ivar_name, value) value.each { |c| c.parent = self } else raise TypeMismatch.new(type, value.class) end end else define_method((name.to_s + '=').to_sym) do |value| if value.inject(true) { |m, o| m and o.is_a?(type) } self.instance_variable_set(ivar_name, value) else raise TypeMismatch.new(type, value.class) end end end end end |
.inherited(klass) ⇒ void
This method returns an undefined value.
Called when the Lexer class is sub-classed, it installes necessary instance class variables.
68 69 70 |
# File 'lib/rltk/ast.rb', line 68 def inherited(klass) klass.install_icvars end |
.install_icvars ⇒ void
This method returns an undefined value.
Installs instance class varialbes into a class.
51 52 53 54 55 56 57 58 59 |
# File 'lib/rltk/ast.rb', line 51 def install_icvars if self.superclass == ASTNode @child_names = Array.new @value_names = Array.new else @child_names = self.superclass.child_names.clone @value_names = self.superclass.value_names.clone end end |
.value(name, type) ⇒ 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. The type of this value must NOT be a subclass of the ASTNode class.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/rltk/ast.rb', line 182 def value(name, type) 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 NOT a subclass of # ASTNode. if t.subclass_of?(ASTNode) raise "A value's type specification must NOT be a subclass of ASTNode." end @value_names << name define_accessor(name, type) end |
.value_names ⇒ Array<Symbol>
Returns Array of the names of this node class’s values.
204 205 206 |
# File 'lib/rltk/ast.rb', line 204 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.
220 221 222 |
# File 'lib/rltk/ast.rb', line 220 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.
225 226 227 |
# File 'lib/rltk/ast.rb', line 225 def [](key) @notes[key] end |
#[]=(key, value) ⇒ Object
Sets the note named key to value.
230 231 232 |
# File 'lib/rltk/ast.rb', line 230 def []=(key, value) @notes[key] = value end |
#children ⇒ Array<ASTNode>
Returns Array of this node’s children.
235 236 237 |
# File 'lib/rltk/ast.rb', line 235 def children self.class.child_names.map { |name| self.send(name) } end |
#children=(children) ⇒ void
This method returns an undefined value.
Assigns an array of AST nodes as the children of this node.
244 245 246 247 248 249 250 251 252 |
# File 'lib/rltk/ast.rb', line 244 def children=(children) 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 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.
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/rltk/ast.rb', line 259 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 |
#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.
283 284 285 286 287 288 289 290 |
# File 'lib/rltk/ast.rb', line 283 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)
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/rltk/ast.rb', line 300 def each(order = :pre, &block) case order when :pre yield self self.children.compact.each { |c| c.each(:pre, &block) } when :post self.children.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.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.
324 325 326 |
# File 'lib/rltk/ast.rb', line 324 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.
366 367 368 369 370 371 |
# File 'lib/rltk/ast.rb', line 366 def map(&block) new_children = self.children.map { |c| if c.nil? then block.call(c) else c.map(&block) end } new_node = self.class.new(*self.values, *new_children) 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.
382 383 384 385 386 |
# File 'lib/rltk/ast.rb', line 382 def map!(&block) self.children = self.children.map { |c| if c.nil? then block.call(c) else c.map!(&block) end } block.call(self) end |
#root ⇒ ASTNode
Returns Root of the abstract syntax tree.
389 390 391 |
# File 'lib/rltk/ast.rb', line 389 def root if @parent then @parent.root else self end end |
#values ⇒ Array<Object>
Returns Array of this node’s values.
394 395 396 |
# File 'lib/rltk/ast.rb', line 394 def values self.class.value_names.map { |name| self.send(name) } end |
#values=(values) ⇒ Object
Assigns an array of objects as the values of this node.
401 402 403 404 405 406 407 408 409 |
# File 'lib/rltk/ast.rb', line 401 def values=(values) 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 end |