Extends the `do catch` construct to catch all run time errors.
The interpreter also keep the error information if one wants it.
Pull-Request: #2411
<context id="keywords" style-ref="keyword">
<keyword>do</keyword>
<keyword>end</keyword>
+ <keyword>catch</keyword>
<keyword>intrude</keyword>
<keyword>private</keyword>
<keyword>if</keyword>
Keywords={
{ Id=1,
- List={"abort", "abstract", "as", "assert", "break", "continue", "do", "else", "end", "enum", "extern", "for", "if", "import", "in", "interface", "intern", "intrude", "is", "isa", "isset", "new", "label", "loop", "private", "protected", "readable", "return", "super", "then", "while", "false", "null", "nullable", "redef", "self", "true", "and", "not", "or", "fun", "var", "type", "init", "class", "package", "module", "special", "universal", "writable"},
+ List={"abort", "abstract", "as", "assert", "break", "continue", "do", "else", "end", "catch", "enum", "extern", "for", "if", "import", "in", "interface", "intern", "intrude", "is", "isa", "isset", "new", "label", "loop", "private", "protected", "readable", "return", "super", "then", "while", "false", "null", "nullable", "redef", "self", "true", "and", "not", "or", "fun", "var", "type", "init", "class", "package", "module", "special", "universal", "writable"},
},
{ Id=2,
Regex=[[[A-Z][\/\w]+]],
specialchar = '\\.'
end
-keyword = "abort|abstract|as|assert|break|continue|do|else|end|enum|extern|for|if|import|in|interface|intern|intrude|is|isa|isset|new|label|loop|private|protected|readable|return|super|then|while|false|null|nullable|redef|self|true|and|not|or|fun|var|type|init|class|package|module|special|universal|writable"
+keyword = "abort|abstract|as|assert|break|continue|do|else|end|catch|enum|extern|for|if|import|in|interface|intern|intrude|is|isa|isset|new|label|loop|private|protected|readable|return|super|then|while|false|null|nullable|redef|self|true|and|not|or|fun|var|type|init|class|package|module|special|universal|writable"
type = '[[:upper:]]([[:word:]]*)'
function Brush()
{
- var keywords = 'abort abstract and as assert break class continue do else end enum extern false for fun' +
+ var keywords = 'abort abstract and as assert break class continue do else end catch enum extern false for fun' +
'if import in init interface intrude is isa isset label loop module new null nullable not' +
'once or protected private redef return self super then type true universal var' +
'when while writable';
setlocal nocindent
setlocal autoindent
setlocal comments=:#
-setlocal indentkeys+==end,=else,=do,=var,0!,=then,=loop,=special,=class,=interface,=universal
+setlocal indentkeys+==end,=else,=catch,=do,=var,0!,=then,=loop,=special,=class,=interface,=universal
" Only define the function once.
if exists("*GetNITIndent")
endif
" Indent after
-let s:relative_indent = '\<\(do\|loop\|then\|else\|if\)\s*\(#\|$\)\|^\s*\(\<\(redef\|private\)\>\s*\)\?\(\<abstract\>\s*\)\?\<\(class\|interface\|universal\|special\)\>'
+let s:relative_indent = '\<\(do\|loop\|then\|else\|catch\|if\)\s*\(#\|$\)\|^\s*\(\<\(redef\|private\)\>\s*\)\?\(\<abstract\>\s*\)\?\<\(class\|interface\|universal\|special\)\>'
" Unindent on them
-let s:outdent = '^\s*\(else\|then\|end\)\>'
+let s:outdent = '^\s*\(catch\|else\|then\|end\)\>'
" At 0
let s:no_indent = '^\s*\(class\|import\|special\)\>'
" Fallback highlight keywords
syn match NITNull "\<\(null\)\>"
-syn match NITControl "\<\(init\|end\|not null\|not\|var\|do\|then\|else\|loop\|is\)\>"
+syn match NITControl "\<\(init\|end\|not null\|not\|var\|do\|then\|catch\|else\|loop\|is\)\>"
syn match NITKeyword "\<\(super\)\>"
" Unmatchning error
syn match Error "\<end\>"
# used by aborts, asserts, casts, etc.
fun add_abort(message: String)
do
+ add_raw_throw
+ self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
+ add_raw_abort
+ end
+
+ # Generate a long jump if there is a catch block.
+ #
+ # This method should be called before the error messages and before a `add_raw_abort`.
+ fun add_raw_throw
+ do
self.add("if(catchStack.cursor >= 0)\{")
self.add("longjmp(catchStack.envs[catchStack.cursor], 1);")
self.add("\}")
- self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
- add_raw_abort
end
+ # Generate abort without a message.
+ #
+ # Used when one need a more complex message.
+ # Do not forget to call `add_raw_abort` before the display of a custom user message.
fun add_raw_abort
do
- if self.current_node != null and self.current_node.location.file != null and
- self.current_node.location.file.mmodule != null then
+ var current_node = self.current_node
+ if current_node != null and current_node.location.file != null and
+ current_node.location.file.mmodule != null then
var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
self.require_declaration(f)
self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
do
var res = self.type_test(value, mtype, tag)
self.add("if (unlikely(!{res})) \{")
+ self.add_raw_throw
var cn = self.class_name_string(value)
self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
self.add_raw_abort
var node = modelbuilder.mpropdef2node(self)
if is_abstract then
+ v.add_raw_throw
var cn = v.class_name_string(arguments.first)
v.current_node = node
v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
end
# We have a problem
+ v.add_raw_throw
var cn = v.class_name_string(arguments.first)
v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
v.add_raw_abort
var escapemark: nullable EscapeMark = null
# The count of `catch` blocs that have been encountered and can catch an abort
- var catch_count = 0
+ var catch_count = 0 is writable
+
+ # The last error thrown on abort/runtime error where catch_count > 0
+ var last_error: nullable FatalError = null
# Is a return or a break or a continue executed?
# Use this function to know if you must skip the evaluation of statements
var error_instance = new MutableInstance(modelbuilder.model.null_type) is lazy
end
+# A runtime error
+class FatalError
+ # The error message
+ var message: String
+
+ # The problematic node, if any
+ var node: nullable ANode
+end
+
# An instance represents a value of the executed program.
abstract class Instance
# The dynamic type of the instance
# `v` is used to know if a colored message is displayed or not
fun fatal(v: NaiveInterpreter, message: String)
do
+ # Abort if there is a `catch` block
+ if v.catch_count > 0 then
+ v.last_error = new FatalError(message, self)
+ abort
+ end
+
if v.modelbuilder.toolcontext.opt_no_color.value == true then
sys.stderr.write("Runtime error: {message} ({location.file.filename}:{location.line_start})\n")
else
redef class AAbortExpr
redef fun stmt(v)
do
- # Abort as asked if there is no `catch` bloc
- if v.catch_count <= 0 then
- fatal(v, "Aborted")
- exit(1)
- else
- abort
- end
+ fatal(v, "Aborted")
+ exit(1)
end
end
--- /dev/null
+1
+1
+2
+2
+3
+3
+4
+4
+5
+5
+6
+6
+7
+7
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import core::kernel
+
+fun foo is abstract
+fun bar is intern
+
+class A
+ var a: A is noautoinit
+ fun foo do 0.output
+end
+
+var a = new A
+var n: nullable A = null
+var o: Object = 1
+
+do
+ 1.output
+ if true then abort
+ 666.output
+catch
+ 1.output
+end
+
+do
+ 2.output
+ n.foo
+ 666.output
+catch
+ 2.output
+end
+
+do
+ 3.output
+ foo
+ 666.output
+catch
+ 3.output
+end
+
+do
+ 4.output
+ assert false
+ 666.output
+catch
+ 4.output
+end
+
+do
+ 5.output
+ o.as(A).foo
+ 666.output
+catch
+ 5.output
+end
+
+do
+ 6.output
+ a.a.foo
+ 666.output
+catch
+ 6.output
+end
+
+do
+ 7.output
+ bar
+ 666.output
+catch
+ 7.output
+end