Class: RLTK::ASTNode

Inherits:
Object show all
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

Class Method Summary collapse

Instance Method Summary collapse

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

Parameters:

  • objects (Array<Object>)

    The values and children of this node.



337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
# File 'lib/rltk/ast.rb', line 337

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

#parentASTNode

Returns Reference to the parent node.

Returns:

  • (ASTNode)

    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.

Parameters:

  • name (String, Symbol)

    Name of child node.

  • type (Class)

    Type of child node. Must be a subclass of ASTNode.



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_namesArray<Symbol>

Returns Array of the names of this node class’s children.

Returns:

  • (Array<Symbol>)

    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.

Parameters:

  • name (String, Symbol)

    Name of accessor.

  • type (Class)

    Class used for type checking.

  • set_parent (Boolean) (defaults to: false)

    Set the parent variable or not.



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.

Parameters:

  • klass (Class)

    The class is inheriting from this class.



68
69
70
# File 'lib/rltk/ast.rb', line 68

def inherited(klass)
	klass.install_icvars
end

.install_icvarsvoid

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.

Parameters:

  • name (String, Symbol)

    Name of value.

  • type (Class)

    Type of value. Must NOT be a subclass of ASTNode.



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_namesArray<Symbol>

Returns Array of the names of this node class’s values.

Returns:

  • (Array<Symbol>)

    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.

Parameters:

  • other (ASTNode)

    The ASTNode to compare to.

Returns:

  • (Boolean)


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.

Returns:

  • (Object)

    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

#childrenArray<ASTNode>

Returns Array of this node’s children.

Returns:

  • (Array<ASTNode>)

    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.

Parameters:

  • children (Array<ASTNode>)

    Children to be assigned to 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.

Parameters:

  • key (Object)

    The key of the note to remove.

  • recursive (Boolean) (defaults to: true)

    Do a recursive removal or not.



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.

Parameters:

  • dest (nil, IO, String) (defaults to: nil)

    Where the serialized version of the AST will end up. If nil, this method will return the AST as a string.

  • limit (Fixnum) (defaults to: -1))

    Recursion depth. If -1 is specified there is no limit on the recursion depth.

Returns:

  • (void, String)

    String if dest is nil, void otherwise.



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.

Returns:

  • (Boolean)


324
325
326
# File 'lib/rltk/ast.rb', line 324

def has_note?(key)
	@notes.has_key?(key)
end

#mapvoid

This method returns an undefined value.

Maps the children of the ASTNode from one value to another.



358
359
360
# File 'lib/rltk/ast.rb', line 358

def map
	self.children = self.children.map { |c| yield c }
end

#rootASTNode

Returns Root of the abstract syntax tree.

Returns:

  • (ASTNode)

    Root of the abstract syntax tree.



363
364
365
# File 'lib/rltk/ast.rb', line 363

def root
	if @parent then @parent.root else self end
end

#valuesArray<Object>

Returns Array of this node’s values.

Returns:

  • (Array<Object>)

    Array of this node’s values.



368
369
370
# File 'lib/rltk/ast.rb', line 368

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.

Parameters:

  • values (Array<Object>)

    The values to be assigned to this node.



375
376
377
378
379
380
381
382
383
# File 'lib/rltk/ast.rb', line 375

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