Merge: Introducing the do ... catch ... end structure
authorJean Privat <jean@pryen.org>
Thu, 14 Apr 2016 01:03:06 +0000 (21:03 -0400)
committerJean Privat <jean@pryen.org>
Thu, 14 Apr 2016 01:03:06 +0000 (21:03 -0400)
This PR is a first step in trying to handle exceptions in Nit, replacing the behaviour of `abort` if it happens within a do ... catch ... end.

In the compiler, setjmp() et longjmp() are used to jump directly from the `abort` to the nearest `catch` bloc.

Pull-Request: #2011
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>

1  2 
src/interpreter/naive_interpreter.nit
src/parser/parser_nodes.nit
src/semantize/scope.nit

@@@ -113,13 -113,24 +113,20 @@@ class NaiveInterprete
                return self.modelbuilder.force_get_primitive_method(current_node, name, recv.mclass, self.mainmodule)
        end
  
 -      # Is a return executed?
 -      # Set this mark to skip the evaluation until the end of the specified method frame
 -      var returnmark: nullable FRAME = null
 -
 -      # Is a break or a continue executed?
 +      # Is a return, a break or a continue executed?
        # Set this mark to skip the evaluation until a labeled statement catch it with `is_escape`
        var escapemark: nullable EscapeMark = null
  
+       # Is an abort being executed ?
+       # Set this mark to return to the last `catch` bloc or effectively aborting if there isn't any
+       var catch_mark = new EscapeMark
+       # The count of `catch` blocs that have been encountered and can catch an abort
+       var catch_count = 0
        # Is a return or a break or a continue executed?
        # Use this function to know if you must skip the evaluation of statements
 -      fun is_escaping: Bool do return returnmark != null or escapemark != null
 +      fun is_escaping: Bool do return escapemark != null
  
        # The value associated with the current return/break/continue, if any.
        # Set the value when you set a escapemark.
@@@ -844,8 -855,10 +851,8 @@@ redef class AMethPropde
                var f = v.new_frame(self, mpropdef, args)
                var res = call_commons(v, mpropdef, args, f)
                v.frames.shift
 -              if v.returnmark == f then
 -                      v.returnmark = null
 +              if v.is_escape(self.return_mark) then
                        res = v.escapevalue
 -                      v.escapevalue = null
                        return res
                end
                return res
@@@ -1539,9 -1552,10 +1546,9 @@@ redef class AAttrPropde
                        val = v.expr(nexpr)
                else if nblock != null then
                        v.stmt(nblock)
 -                      assert v.returnmark == f
 +                      assert v.escapemark == return_mark
                        val = v.escapevalue
 -                      v.returnmark = null
 -                      v.escapevalue = null
 +                      v.escapemark = null
                else
                        abort
                end
@@@ -1686,11 -1700,30 +1693,17 @@@ redef class AEscapeExp
        end
  end
  
  redef class AAbortExpr
        redef fun stmt(v)
        do
-               fatal(v, "Aborted")
-               exit(1)
+               # Abort as asked if there is no `catch` bloc
+               if v.catch_count <= 0 then
+                       fatal(v, "Aborted")
+                       exit(1)
+               else
+                       # Abort mode, skipping everything until a `catch` bloc is reached
+                       v.escapemark = v.catch_mark
+               end
        end
  end
  
  redef class ADoExpr
        redef fun stmt(v)
        do
+               # If this bloc has a catch, register it in the counter
+               if self.n_catch != null then v.catch_count += 1
                v.stmt(self.n_block)
                v.is_escape(self.break_mark) # Clear the break (if any)
+               if self.n_catch != null then
+                       v.catch_count -= 1
+                       # Are we in abort mode? then this catch is executing
+                       if v.is_escape(v.catch_mark) then v.stmt(self.n_catch)
+               end
        end
  end
  
@@@ -521,6 -521,11 +521,11 @@@ class TKwd
        super TokenKeyword
  end
  
+ # The keyword `catch`
+ class TKwcatch
+       super TokenKeyword
+ end
  # The keyword `var`
  class TKwvar
        super TokenKeyword
  
  # A `return` statement. eg `return x`
  class AReturnExpr
 -      super AExpr
 +      super AEscapeExpr
  
        # The `return` keyword
        var n_kwreturn: nullable TKwreturn = null is writable
 -
 -      # The return value, if any
 -      var n_expr: nullable AExpr = null is writable
  end
  
  # A `yield` statement. eg `yield x`
@@@ -1844,6 -1852,12 +1849,12 @@@ class ADoExp
  
        # The list of statements of the `do`.
        var n_block: nullable AExpr = null is writable
+       # The `catch` keyword
+       var n_kwcatch: nullable TKwcatch = null is writable
+       # The do catch block
+       var n_catch: nullable AExpr = null is writable
  end
  
  # A `if` statement
diff --combined src/semantize/scope.nit
@@@ -71,9 -71,6 +71,9 @@@ private class ScopeVisito
        # The tool context used to display errors
        var toolcontext: ToolContext
  
 +      # The analysed property
 +      var propdef: APropdef
 +
        var selfvariable = new Variable("self")
  
        init
@@@ -251,13 -248,10 +251,13 @@@ redef class ANod
  end
  
  redef class APropdef
 +      # The break escape mark associated with the return
 +      var return_mark: nullable EscapeMark
 +
        # Entry point of the scope analysis
        fun do_scope(toolcontext: ToolContext)
        do
 -              var v = new ScopeVisitor(toolcontext)
 +              var v = new ScopeVisitor(toolcontext, self)
                v.enter_visit(self)
                v.shift_scope
        end
@@@ -332,21 -326,6 +332,21 @@@ redef class ABreakExp
        end
  end
  
 +redef class AReturnExpr
 +      redef fun accept_scope_visitor(v)
 +      do
 +              super
 +
 +              var escapemark = v.propdef.return_mark
 +              if escapemark == null then
 +                      escapemark = new EscapeMark
 +                      v.propdef.return_mark = escapemark
 +              end
 +
 +              escapemark.escapes.add(self)
 +              self.escapemark = escapemark
 +      end
 +end
  
  redef class ADoExpr
        # The break escape mark associated with the 'do' block
        do
                self.break_mark = v.make_escape_mark(n_label, false)
                v.enter_visit_block(n_block, self.break_mark)
+               v.enter_visit_block(n_catch)
        end
  end