~tim/scheme-vm

249d2877d578ecd556c7e109274956cb64ebcf90 — Tim Morgan 2 years ago c511010
Show better error when calling undefined
3 files changed, 30 insertions(+), 6 deletions(-)

M program.rb
M vm/exceptions.rb
M vm/operations.rb
M program.rb => program.rb +18 -4
@@ 6,6 6,7 @@ class Program
  EXIT_CODE_VAR_UNDEFINED  = 1
  EXIT_CODE_STACK_TOO_DEEP = 2
  EXIT_CODE_SYNTAX_ERROR   = 3
  EXIT_CODE_FATAL_ERROR    = 4

  def initialize(code, filename: '(unknown)', args: [], stdout: $stdout)
    @filename = filename


@@ 38,6 39,9 @@ class Program
  rescue VM::CallStackTooDeep => e
    print_call_stack_too_deep_error(e)
    EXIT_CODE_STACK_TOO_DEEP
  rescue VM::FatalError => e
    print_fatal_error(e)
    EXIT_CODE_FATAL_ERROR
  end

  def filename=(f)


@@ 72,10 76,8 @@ class Program
    "\n\n#{line}\n\n#{code}\n#{pointer}"
  end

  def print_call_stack_too_deep_error(e)
    message = "Error: #{e.message}"
    @stdout.puts(message)
    e.call_stack.reverse.each do |frame|
  def print_call_stack(call_stack)
    call_stack.reverse.each do |frame|
      next unless (name = frame[:name])
      @stdout.puts "#{name.filename}##{name.line}"
      code = @compiler.source[name.filename].split("\n")[name.line - 1]


@@ 84,11 86,23 @@ class Program
    end
  end

  def print_call_stack_too_deep_error(e)
    message = "Error: #{e.message}"
    @stdout.puts(message)
    print_call_stack(e.call_stack)
  end

  def print_syntax_error(e)
    message = 'Syntax Error:' + error_details_to_s(e, @code)
    @stdout.puts(message)
  end

  def print_fatal_error(e)
    message = "Fatal Error: #{e.message}"
    @stdout.puts(message)
    print_call_stack(e.call_stack)
  end

  def print_timings
    puts "parse:   #{@total_parse}"
    puts "compile: #{@total_compile}"

M vm/exceptions.rb => vm/exceptions.rb +9 -0
@@ 39,4 39,13 @@ class VM
      'call stack too deep'
    end
  end

  class FatalError < StandardError
    attr_reader :call_stack, :message

    def initialize(call_stack, message = 'fatal error')
      @call_stack = call_stack
      @message = message
    end
  end
end

M vm/operations.rb => vm/operations.rb +3 -2
@@ 148,8 148,9 @@ class VM
    end

    def do_call(new_ip = pop)
      return do_call_continuation(new_ip) if heap[new_ip].is_a?(Continuation)
      raise VM::FatalError.new(@call_stack, "tried to call undefined value: #{@last_atom}") if new_ip.nil?
      raise "ip #{new_ip.inspect} is invalid" unless new_ip.is_a?(Integer)
      return do_call_continuation(new_ip) if heap[new_ip].is_a?(Continuation)
      name = @last_atom if find_address_for_name(@last_atom) == new_ip
      if @heap[@ip] == RETURN
        @call_stack.last[:func] = new_ip


@@ 158,7 159,7 @@ class VM
      else
        @call_stack << { name: name, func: new_ip, return: @ip, args: @call_args, named_args: {} }
      end
      raise CallStackTooDeep, @call_stack if @call_stack.size > MAX_CALL_DEPTH
      raise CallStackTooDeep.new(@call_stack) if @call_stack.size > MAX_CALL_DEPTH
      @ip = new_ip
    end