mirror of
https://github.com/safiire/n65.git
synced 2025-01-08 19:29:24 +00:00
Linted symbol_table.rb
This commit is contained in:
parent
d3ed4e81ec
commit
34cc52c6a8
@ -1,128 +1,108 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module N65
|
module N65
|
||||||
|
|
||||||
class SymbolTable
|
class SymbolTable
|
||||||
attr_accessor :scope_stack
|
attr_accessor :scope_stack
|
||||||
|
|
||||||
##### Custom Exceptions
|
# Custom Exceptions
|
||||||
class InvalidScope < StandardError; end
|
class InvalidScope < StandardError; end
|
||||||
class UndefinedSymbol < StandardError; end
|
class UndefinedSymbol < StandardError; end
|
||||||
class CantExitScope < StandardError; end
|
class CantExitScope < StandardError; end
|
||||||
|
|
||||||
|
# Initialize a symbol table that begins in global scope
|
||||||
####
|
|
||||||
## Initialize a symbol table that begins in global scope
|
|
||||||
def initialize
|
def initialize
|
||||||
@symbols = {
|
@symbols = {
|
||||||
:global => {}
|
global: {}
|
||||||
}
|
}
|
||||||
@anonymous_scope_number = 0
|
@anonymous_scope_number = 0
|
||||||
@scope_stack = [:global]
|
@scope_stack = [:global]
|
||||||
@subroutine_cycles = {}
|
@subroutine_cycles = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Add a running cycle count to current top level scopes (ie subroutines)
|
||||||
####
|
|
||||||
## Add a running cycle count to current top level scopes (ie subroutines)
|
|
||||||
def add_cycles(cycles)
|
def add_cycles(cycles)
|
||||||
cycles ||= 0
|
cycles ||= 0
|
||||||
top_level_subroutine = @scope_stack[1]
|
top_level_subroutine = @scope_stack[1]
|
||||||
unless top_level_subroutine.nil?
|
return if top_level_subroutine.nil?
|
||||||
@subroutine_cycles[top_level_subroutine] ||= 0
|
|
||||||
@subroutine_cycles[top_level_subroutine] += cycles
|
@subroutine_cycles[top_level_subroutine] ||= 0
|
||||||
end
|
@subroutine_cycles[top_level_subroutine] += cycles
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Define a new scope, which can be anonymous or named
|
||||||
####
|
# and switch into that scope
|
||||||
## Define a new scope, which can be anonymous or named
|
|
||||||
## and switch into that scope
|
|
||||||
def enter_scope(name = nil)
|
def enter_scope(name = nil)
|
||||||
name = generate_name if name.nil?
|
name = generate_name if name.nil?
|
||||||
name = name.to_sym
|
name = name.to_sym
|
||||||
scope = current_scope
|
scope = current_scope
|
||||||
if scope.has_key?(name)
|
raise(InvalidScope, "Scope: #{name} already exists") if scope.key?(name)
|
||||||
fail(InvalidScope, "Scope: #{name} already exists")
|
|
||||||
end
|
|
||||||
scope[name] = {}
|
scope[name] = {}
|
||||||
@scope_stack.push(name)
|
@scope_stack.push(name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Exit the current scope
|
||||||
####
|
|
||||||
## Exit the current scope
|
|
||||||
def exit_scope
|
def exit_scope
|
||||||
if @scope_stack.size == 1
|
raise(CantExitScope, 'You cannot exit global scope') if @scope_stack.size == 1
|
||||||
fail(CantExitScope, "You cannot exit global scope")
|
|
||||||
end
|
|
||||||
@scope_stack.pop
|
@scope_stack.pop
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Define a symbol in the current scope
|
||||||
####
|
|
||||||
## Define a symbol in the current scope
|
|
||||||
def define_symbol(symbol, value)
|
def define_symbol(symbol, value)
|
||||||
scope = current_scope
|
scope = current_scope
|
||||||
scope[symbol.to_sym] = value
|
scope[symbol.to_sym] = value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Separate arithmetic from scope name
|
||||||
####
|
|
||||||
## Separate arithmetic from scope name
|
|
||||||
def find_arithmetic(name)
|
def find_arithmetic(name)
|
||||||
last_name = name.split('.').last
|
last_name = name.split('.').last
|
||||||
md = last_name.match(/([\+\-\*\/])(\d+)$/)
|
md = last_name.match(%r{([+\-*/])(\d+)$})
|
||||||
f = lambda{|v| v}
|
f = ->(v) { v }
|
||||||
|
|
||||||
unless md.nil?
|
unless md.nil?
|
||||||
full_match, operator, argument = md.to_a
|
full_match, operator, argument = md.to_a
|
||||||
name.gsub!(full_match, '')
|
name.gsub!(full_match, '')
|
||||||
f = lambda {|value| value.send(operator.to_sym, argument.to_i) }
|
f = ->(value) { value.send(operator.to_sym, argument.to_i) }
|
||||||
end
|
end
|
||||||
|
|
||||||
[name, f]
|
[name, f]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Resolve a symbol to its value
|
||||||
####
|
|
||||||
## Resolve a symbol to its value
|
|
||||||
def resolve_symbol(name)
|
def resolve_symbol(name)
|
||||||
name, arithmetic = find_arithmetic(name)
|
name, arithmetic = find_arithmetic(name)
|
||||||
|
|
||||||
method = name.include?('.') ? :resolve_symbol_dot_syntax : :resolve_symbol_scoped
|
method = name.include?('.') ? :resolve_symbol_dot_syntax : :resolve_symbol_scoped
|
||||||
value = self.send(method, name)
|
value = send(method, name)
|
||||||
value = arithmetic.call(value)
|
value = arithmetic.call(value)
|
||||||
|
raise(UndefinedSymbol, name) if value.nil?
|
||||||
|
|
||||||
fail(UndefinedSymbol, name) if value.nil?
|
|
||||||
value
|
value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Resolve symbol by working backwards through each
|
||||||
####
|
# containing scope. Similarly named scopes shadow outer scopes
|
||||||
## Resolve symbol by working backwards through each
|
|
||||||
## containing scope. Similarly named scopes shadow outer scopes
|
|
||||||
def resolve_symbol_scoped(name)
|
def resolve_symbol_scoped(name)
|
||||||
root = "-#{name}".to_sym
|
root = "-#{name}".to_sym
|
||||||
stack = @scope_stack.dup
|
stack = @scope_stack.dup
|
||||||
loop do
|
loop do
|
||||||
scope = retreive_scope(stack)
|
scope = retreive_scope(stack)
|
||||||
|
|
||||||
## We see if there is a key either under this name, or root
|
# We see if there is a key either under this name, or root
|
||||||
v = scope[name.to_sym] || scope[root]
|
v = scope[name.to_sym] || scope[root]
|
||||||
v = v.kind_of?(Hash) ? v[root] : v
|
v = v.is_a?(Hash) ? v[root] : v
|
||||||
|
|
||||||
return v unless v.nil?
|
return v unless v.nil?
|
||||||
|
|
||||||
## Pop the stack so we can decend to the parent scope, if any
|
# Pop the stack so we can decend to the parent scope, if any
|
||||||
stack.pop
|
stack.pop
|
||||||
return nil if stack.empty?
|
return nil if stack.empty?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Dot syntax means to check an absolute path to the symbol
|
||||||
####
|
# :global is ignored if it is provided as part of the path
|
||||||
## Dot syntax means to check an absolute path to the symbol
|
|
||||||
## :global is ignored if it is provided as part of the path
|
|
||||||
def resolve_symbol_dot_syntax(name)
|
def resolve_symbol_dot_syntax(name)
|
||||||
path_ary = name.split('.').map(&:to_sym)
|
path_ary = name.split('.').map(&:to_sym)
|
||||||
symbol = path_ary.pop
|
symbol = path_ary.pop
|
||||||
@ -131,40 +111,32 @@ module N65
|
|||||||
|
|
||||||
scope = retreive_scope(path_ary)
|
scope = retreive_scope(path_ary)
|
||||||
|
|
||||||
## We see if there is a key either under this name, or root
|
# We see if there is a key either under this name, or root
|
||||||
v = scope[symbol]
|
v = scope[symbol]
|
||||||
v.kind_of?(Hash) ? v[root] : v
|
v.is_a?(Hash) ? v[root] : v
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Export the symbol table as YAML
|
||||||
####
|
|
||||||
## Export the symbol table as YAML
|
|
||||||
def export_to_yaml
|
def export_to_yaml
|
||||||
@symbols.to_yaml.gsub(/(\d+)$/) do |match|
|
@symbols.to_yaml.gsub(/(\d+)$/) do |match|
|
||||||
integer = match.to_i
|
integer = match.to_i
|
||||||
sprintf("0x%.4X", integer)
|
format('0x%.4X', integer)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Export a cycle count for top level subroutines
|
||||||
####
|
|
||||||
## Export a cycle count for top level subroutines
|
|
||||||
def export_cycle_count_yaml
|
def export_cycle_count_yaml
|
||||||
@subroutine_cycles.to_yaml
|
@subroutine_cycles.to_yaml
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
####
|
# A bit more clearly states to get the current scope
|
||||||
## A bit more clearly states to get the current scope
|
|
||||||
def current_scope
|
def current_scope
|
||||||
retreive_scope
|
retreive_scope
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Retrieve a reference to a scope, current scope by default
|
||||||
####
|
|
||||||
## Retrieve a reference to a scope, current scope by default
|
|
||||||
def retreive_scope(path_ary = @scope_stack)
|
def retreive_scope(path_ary = @scope_stack)
|
||||||
path_ary = path_ary.dup
|
path_ary = path_ary.dup
|
||||||
path_ary.unshift(:global) unless path_ary.first == :global
|
path_ary.unshift(:global) unless path_ary.first == :global
|
||||||
@ -175,28 +147,22 @@ module N65
|
|||||||
if new_scope.nil?
|
if new_scope.nil?
|
||||||
path_string = generate_scope_path(path_ary)
|
path_string = generate_scope_path(path_ary)
|
||||||
message = "Resolving scope: #{path_string} failed at #{path_component}"
|
message = "Resolving scope: #{path_string} failed at #{path_component}"
|
||||||
fail(InvalidScope, message) if new_scope.nil?
|
raise(InvalidScope, message) if new_scope.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
new_scope
|
new_scope
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Generate a scope path from an array
|
||||||
####
|
|
||||||
## Generate a scope path from an array
|
|
||||||
def generate_scope_path(path_ary)
|
def generate_scope_path(path_ary)
|
||||||
path_ary.join('.')
|
path_ary.join('.')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Generate an anonymous scope name
|
||||||
####
|
|
||||||
## Generate an anonymous scope name
|
|
||||||
def generate_name
|
def generate_name
|
||||||
@anonymous_scope_number += 1
|
@anonymous_scope_number += 1
|
||||||
"anonymous_#{@anonymous_scope_number}"
|
"anonymous_#{@anonymous_scope_number}"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user