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