class N65::SymbolTable
Attributes
scope_stack[RW]
Public Class Methods
new()
click to toggle source
Initialize a symbol table that begins in global scope
# File lib/n65/symbol_table.rb, line 15 def initialize @symbols = { :global => {} } @anonymous_scope_number = 0 @scope_stack = [:global] end
Public Instance Methods
define_symbol(symbol, value)
click to toggle source
Define a symbol in the current scope
# File lib/n65/symbol_table.rb, line 52 def define_symbol(symbol, value) scope = current_scope scope[symbol.to_sym] = value end
enter_scope(name = nil)
click to toggle source
Define a new scope, which can be anonymous or named and switch into that scope
# File lib/n65/symbol_table.rb, line 27 def enter_scope(name = nil) name = generate_name if name.nil? name = name.to_sym scope = current_scope if scope.has_key?(name) #path_string = generate_scope_path(path_ary) fail(InvalidScope, "Scope: #{name} already exists") end scope[name] = {} @scope_stack.push(name) end
exit_scope()
click to toggle source
Exit the current scope
# File lib/n65/symbol_table.rb, line 42 def exit_scope if @scope_stack.size == 1 fail(CantExitScope, "You cannot exit global scope") end @scope_stack.pop end
export_to_yaml()
click to toggle source
Export the symbol table as YAML
# File lib/n65/symbol_table.rb, line 145 def export_to_yaml @symbols.to_yaml.gsub(/(\d+)$/) do |match| integer = match.to_i sprintf("0x%.4X", integer) end end
resolve_symbol(name)
click to toggle source
# File lib/n65/symbol_table.rb, line 95 def resolve_symbol(name) method = name.include?('.') ? :resolve_symbol_dot_syntax : :resolve_symbol_scoped value = self.send(method, name) fail(UndefinedSymbol, name) if value.nil? value end
resolve_symbol_dot_syntax(name)
click to toggle source
Dot syntax means to check an absolute path to the symbol :global is ignored if it is provided as part of the path
# File lib/n65/symbol_table.rb, line 129 def resolve_symbol_dot_syntax(name) path_ary = name.split('.').map(&:to_sym) symbol = path_ary.pop root = "-#{symbol}".to_sym path_ary.shift if path_ary.first == :global scope = retreive_scope(path_ary) ## We see if there is a key either under this name, or root v = scope[symbol] v.kind_of?(Hash) ? v[root] : v end
resolve_symbol_scoped(name)
click to toggle source
Resolve symbol by working backwards through each containing scope. Similarly named scopes shadow outer scopes
# File lib/n65/symbol_table.rb, line 107 def resolve_symbol_scoped(name) root = "-#{name}".to_sym stack = @scope_stack.dup loop do scope = retreive_scope(stack) ## We see if there is a key either under this name, or root v = scope[name.to_sym] || scope[root] v = v.kind_of?(Hash) ? v[root] : v return v unless v.nil? ## Pop the stack so we can decend to the parent scope, if any stack.pop return nil if stack.empty? end end
Private Instance Methods
current_scope()
click to toggle source
A bit more clearly states to get the current scope
# File lib/n65/symbol_table.rb, line 157 def current_scope retreive_scope end
generate_name()
click to toggle source
Generate an anonymous scope name
# File lib/n65/symbol_table.rb, line 191 def generate_name @anonymous_scope_number += 1 "anonymous_#{@anonymous_scope_number}" end
generate_scope_path(path_ary)
click to toggle source
Generate a scope path from an array
# File lib/n65/symbol_table.rb, line 184 def generate_scope_path(path_ary) path_ary.join('.') end
retreive_scope(path_ary = @scope_stack)
click to toggle source
Retrieve a reference to a scope, current scope by default
# File lib/n65/symbol_table.rb, line 164 def retreive_scope(path_ary = @scope_stack) path_ary = path_ary.dup path_ary.unshift(:global) unless path_ary.first == :global path_ary.inject(@symbols) do |scope, path_component| new_scope = scope[path_component.to_sym] if new_scope.nil? path_string = generate_scope_path(path_ary) message = "Resolving scope: #{path_string} failed at #{path_component}" fail(InvalidScope, message) if new_scope.nil? end new_scope end end