249d2877d578ecd556c7e109274956cb64ebcf90 — Tim Morgan 1 year, 8 months 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 @@
   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 @@
   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 @@
     "\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 @@
     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 @@
       '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 @@
     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 @@
       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