From: Jean Privat Date: Thu, 14 Apr 2016 01:03:06 +0000 (-0400) Subject: Merge: Introducing the do ... catch ... end structure X-Git-Url: http://nitlanguage.org?hp=-c Merge: Introducing the do ... catch ... end structure 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 Reviewed-by: Alexis Laferrière --- 18633a190ffb0929bb4bd56729909d80ebef5280 diff --combined src/interpreter/naive_interpreter.nit index 7fc2355,c70dc83..0028df4 --- a/src/interpreter/naive_interpreter.nit +++ b/src/interpreter/naive_interpreter.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 @@@ -1734,8 -1767,15 +1747,15 @@@ en 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 diff --combined src/parser/parser_nodes.nit index ba5b8f3,477b11b..261694a --- a/src/parser/parser_nodes.nit +++ b/src/parser/parser_nodes.nit @@@ -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 @@@ -1776,10 -1781,13 +1781,10 @@@ en # 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 index a0205a5,0c70a16..3374951 --- a/src/semantize/scope.nit +++ b/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 @@@ -356,6 -335,7 +356,7 @@@ 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