Class: N65::SymbolTable

Inherits:
Object
  • Object
show all
Defined in:
lib/n65/symbol_table.rb

Defined Under Namespace

Classes: CantExitScope, InvalidScope, UndefinedSymbol

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeSymbolTable

Initialize a symbol table that begins in global scope



12
13
14
15
16
17
18
19
# File 'lib/n65/symbol_table.rb', line 12

def initialize
  @symbols = {
    global: {}
  }
  @anonymous_scope_number = 0
  @scope_stack = [:global]
  @subroutine_cycles = {}
end

Instance Attribute Details

#scope_stackObject

Returns the value of attribute scope_stack.



5
6
7
# File 'lib/n65/symbol_table.rb', line 5

def scope_stack
  @scope_stack
end

Instance Method Details

#add_cycles(cycles) ⇒ Object

Add a running cycle count to current top level scopes (ie subroutines)



22
23
24
25
26
27
28
29
# File 'lib/n65/symbol_table.rb', line 22

def add_cycles(cycles)
  cycles ||= 0
  top_level_subroutine = @scope_stack[1]
  return if top_level_subroutine.nil?

  @subroutine_cycles[top_level_subroutine] ||= 0
  @subroutine_cycles[top_level_subroutine] += cycles
end

#define_symbol(symbol, value) ⇒ Object

Define a symbol in the current scope



51
52
53
54
# File 'lib/n65/symbol_table.rb', line 51

def define_symbol(symbol, value)
  scope = current_scope
  scope[symbol.to_sym] = value
end

#enter_scope(name = nil) ⇒ Object

Define a new scope, which can be anonymous or named and switch into that scope

Raises:



33
34
35
36
37
38
39
40
41
# File 'lib/n65/symbol_table.rb', line 33

def enter_scope(name = nil)
  name = generate_name if name.nil?
  name = name.to_sym
  scope = current_scope
  raise(InvalidScope, "Scope: #{name} already exists") if scope.key?(name)

  scope[name] = {}
  @scope_stack.push(name)
end

#exit_scopeObject

Exit the current scope

Raises:



44
45
46
47
48
# File 'lib/n65/symbol_table.rb', line 44

def exit_scope
  raise(CantExitScope, 'You cannot exit global scope') if @scope_stack.size == 1

  @scope_stack.pop
end

#export_cycle_count_yamlObject

Export a cycle count for top level subroutines



127
128
129
# File 'lib/n65/symbol_table.rb', line 127

def export_cycle_count_yaml
  @subroutine_cycles.to_yaml
end

#export_to_yamlObject

Export the symbol table as YAML



119
120
121
122
123
124
# File 'lib/n65/symbol_table.rb', line 119

def export_to_yaml
  @symbols.to_yaml.gsub(/(\d+)$/) do |match|
    integer = match.to_i
    format('0x%.4X', integer)
  end
end

#find_arithmetic(name) ⇒ Object

Separate arithmetic from symbol



57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/n65/symbol_table.rb', line 57

def find_arithmetic(name)
  last_name = name.split('.').last
  md = last_name.match(%r{([+\-*/])(\d+)$})
  f = ->(v) { v }

  unless md.nil?
    full_match, operator, argument = md.to_a
    name = name.gsub(full_match, '')
    f = ->(value) { value.send(operator.to_sym, argument.to_i) }
  end

  [name, f]
end

#resolve_symbol(name) ⇒ Object

Resolve a symbol to its value

Raises:



72
73
74
75
76
77
78
79
80
81
# File 'lib/n65/symbol_table.rb', line 72

def resolve_symbol(name)
  name, arithmetic = find_arithmetic(name)

  method = name.include?('.') ? :resolve_symbol_dot_syntax : :resolve_symbol_scoped
  value = send(method, name)
  value = arithmetic.call(value)
  raise(UndefinedSymbol, name) if value.nil?

  value
end

#resolve_symbol_dot_syntax(name) ⇒ Object

Dot syntax means to check an absolute path to the symbol :global is ignored if it is provided as part of the path



105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/n65/symbol_table.rb', line 105

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.is_a?(Hash) ? v[root] : v
end

#resolve_symbol_scoped(name) ⇒ Object

Resolve symbol by working backwards through each containing scope. Similarly named scopes shadow outer scopes



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/n65/symbol_table.rb', line 85

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.is_a?(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