system.onRaise improvements
This commit is contained in:
parent
865d9cc6e6
commit
e54425b068
3 changed files with 133 additions and 114 deletions
236
doc/manual.txt
236
doc/manual.txt
|
|
@ -1969,116 +1969,6 @@ a special syntactic extension, the ``when`` construct is also available
|
||||||
within ``object`` definitions.
|
within ``object`` definitions.
|
||||||
|
|
||||||
|
|
||||||
Try statement
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Syntax::
|
|
||||||
|
|
||||||
qualifiedIdent ::= symbol ['.' symbol]
|
|
||||||
exceptList ::= [qualifiedIdent (comma qualifiedIdent)* [comma]]
|
|
||||||
tryStmt ::= 'try' ':' stmt
|
|
||||||
('except' exceptList ':' stmt)*
|
|
||||||
['finally' ':' stmt]
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
.. code-block:: nimrod
|
|
||||||
# read the first two lines of a text file that should contain numbers
|
|
||||||
# and tries to add them
|
|
||||||
var
|
|
||||||
f: TFile
|
|
||||||
if open(f, "numbers.txt"):
|
|
||||||
try:
|
|
||||||
var a = readLine(f)
|
|
||||||
var b = readLine(f)
|
|
||||||
echo("sum: " & $(parseInt(a) + parseInt(b)))
|
|
||||||
except EOverflow:
|
|
||||||
echo("overflow!")
|
|
||||||
except EInvalidValue:
|
|
||||||
echo("could not convert string to integer")
|
|
||||||
except EIO:
|
|
||||||
echo("IO error!")
|
|
||||||
except:
|
|
||||||
echo("Unknown exception!")
|
|
||||||
finally:
|
|
||||||
close(f)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The statements after the `try`:idx: are executed in sequential order unless
|
|
||||||
an exception ``e`` is raised. If the exception type of ``e`` matches any
|
|
||||||
of the list ``exceptlist`` the corresponding statements are executed.
|
|
||||||
The statements following the ``except`` clauses are called
|
|
||||||
`exception handlers`:idx:.
|
|
||||||
|
|
||||||
The empty `except`:idx: clause is executed if there is an exception that is
|
|
||||||
in no list. It is similar to an ``else`` clause in ``if`` statements.
|
|
||||||
|
|
||||||
If there is a `finally`:idx: clause, it is always executed after the
|
|
||||||
exception handlers.
|
|
||||||
|
|
||||||
The exception is *consumed* in an exception handler. However, an
|
|
||||||
exception handler may raise another exception. If the exception is not
|
|
||||||
handled, it is propagated through the call stack. This means that often
|
|
||||||
the rest of the procedure - that is not within a ``finally`` clause -
|
|
||||||
is not executed (if an exception occurs).
|
|
||||||
|
|
||||||
|
|
||||||
Except and finally statements
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
`except`:idx: and `finally`:idx: can also be used as a stand-alone statements.
|
|
||||||
Any statements following them in the current block will be considered to be
|
|
||||||
in an implicit try block:
|
|
||||||
|
|
||||||
.. code-block:: nimrod
|
|
||||||
var f = open("numbers.txt")
|
|
||||||
finally: close(f)
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
Raise statement
|
|
||||||
~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Syntax::
|
|
||||||
|
|
||||||
raiseStmt ::= 'raise' [expr]
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
.. code-block:: nimrod
|
|
||||||
raise newEOS("operating system failed")
|
|
||||||
|
|
||||||
Apart from built-in operations like array indexing, memory allocation, etc.
|
|
||||||
the ``raise`` statement is the only way to raise an exception.
|
|
||||||
|
|
||||||
.. XXX document this better!
|
|
||||||
|
|
||||||
If no exception name is given, the current exception is `re-raised`:idx:. The
|
|
||||||
`ENoExceptionToReraise`:idx: exception is raised if there is no exception to
|
|
||||||
re-raise. It follows that the ``raise`` statement *always* raises an
|
|
||||||
exception (unless a raise hook has been provided).
|
|
||||||
|
|
||||||
|
|
||||||
OnRaise builtin
|
|
||||||
~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
``system.onRaise`` can be used to override the behaviour of ``raise`` for a
|
|
||||||
single ``try`` statement. `onRaise`:idx: has to be called within the ``try``
|
|
||||||
statement that should be affected.
|
|
||||||
|
|
||||||
This allows for a Lisp-like `condition system`:idx:\:
|
|
||||||
|
|
||||||
.. code-block:: nimrod
|
|
||||||
var myFile = open("broken.txt", fmWrite)
|
|
||||||
try:
|
|
||||||
onRaise(proc (e: ref E_Base): bool =
|
|
||||||
stdout.writeln "ok, writing to stdout instead")
|
|
||||||
myFile.writeln "writing to broken file"
|
|
||||||
finally:
|
|
||||||
myFile.close()
|
|
||||||
|
|
||||||
|
|
||||||
Return statement
|
Return statement
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
@ -2781,6 +2671,132 @@ can be recursive or even mutually recursive. Mutually recursive types are only
|
||||||
possible within a single ``type`` section.
|
possible within a single ``type`` section.
|
||||||
|
|
||||||
|
|
||||||
|
Exception handling
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Try statement
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Syntax::
|
||||||
|
|
||||||
|
qualifiedIdent ::= symbol ['.' symbol]
|
||||||
|
exceptList ::= [qualifiedIdent (comma qualifiedIdent)* [comma]]
|
||||||
|
tryStmt ::= 'try' ':' stmt
|
||||||
|
('except' exceptList ':' stmt)*
|
||||||
|
['finally' ':' stmt]
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
# read the first two lines of a text file that should contain numbers
|
||||||
|
# and tries to add them
|
||||||
|
var
|
||||||
|
f: TFile
|
||||||
|
if open(f, "numbers.txt"):
|
||||||
|
try:
|
||||||
|
var a = readLine(f)
|
||||||
|
var b = readLine(f)
|
||||||
|
echo("sum: " & $(parseInt(a) + parseInt(b)))
|
||||||
|
except EOverflow:
|
||||||
|
echo("overflow!")
|
||||||
|
except EInvalidValue:
|
||||||
|
echo("could not convert string to integer")
|
||||||
|
except EIO:
|
||||||
|
echo("IO error!")
|
||||||
|
except:
|
||||||
|
echo("Unknown exception!")
|
||||||
|
finally:
|
||||||
|
close(f)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The statements after the `try`:idx: are executed in sequential order unless
|
||||||
|
an exception ``e`` is raised. If the exception type of ``e`` matches any
|
||||||
|
of the list ``exceptlist`` the corresponding statements are executed.
|
||||||
|
The statements following the ``except`` clauses are called
|
||||||
|
`exception handlers`:idx:.
|
||||||
|
|
||||||
|
The empty `except`:idx: clause is executed if there is an exception that is
|
||||||
|
in no list. It is similar to an ``else`` clause in ``if`` statements.
|
||||||
|
|
||||||
|
If there is a `finally`:idx: clause, it is always executed after the
|
||||||
|
exception handlers.
|
||||||
|
|
||||||
|
The exception is *consumed* in an exception handler. However, an
|
||||||
|
exception handler may raise another exception. If the exception is not
|
||||||
|
handled, it is propagated through the call stack. This means that often
|
||||||
|
the rest of the procedure - that is not within a ``finally`` clause -
|
||||||
|
is not executed (if an exception occurs).
|
||||||
|
|
||||||
|
|
||||||
|
Except and finally statements
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
`except`:idx: and `finally`:idx: can also be used as a stand-alone statements.
|
||||||
|
Any statements following them in the current block will be considered to be
|
||||||
|
in an implicit try block:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
var f = open("numbers.txt")
|
||||||
|
finally: close(f)
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
Raise statement
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Syntax::
|
||||||
|
|
||||||
|
raiseStmt ::= 'raise' [expr]
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
raise newEOS("operating system failed")
|
||||||
|
|
||||||
|
Apart from built-in operations like array indexing, memory allocation, etc.
|
||||||
|
the ``raise`` statement is the only way to raise an exception.
|
||||||
|
|
||||||
|
.. XXX document this better!
|
||||||
|
|
||||||
|
If no exception name is given, the current exception is `re-raised`:idx:. The
|
||||||
|
`ENoExceptionToReraise`:idx: exception is raised if there is no exception to
|
||||||
|
re-raise. It follows that the ``raise`` statement *always* raises an
|
||||||
|
exception (unless a raise hook has been provided).
|
||||||
|
|
||||||
|
|
||||||
|
OnRaise builtin
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``system.onRaise`` can be used to override the behaviour of ``raise`` for a
|
||||||
|
single ``try`` statement. `onRaise`:idx: has to be called within the ``try``
|
||||||
|
statement that should be affected.
|
||||||
|
|
||||||
|
This allows for a Lisp-like `condition system`:idx:\:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
var myFile = open("broken.txt", fmWrite)
|
||||||
|
try:
|
||||||
|
onRaise(proc (e: ref E_Base): bool =
|
||||||
|
stdout.writeln "ok, writing to stdout instead")
|
||||||
|
myFile.writeln "writing to broken file"
|
||||||
|
finally:
|
||||||
|
myFile.close()
|
||||||
|
|
||||||
|
|
||||||
|
Exception tracking
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Nimrod supports `exception tracking`:idx:. XXX
|
||||||
|
|
||||||
|
- Raises pragma
|
||||||
|
- Inference rules
|
||||||
|
* Forwarding rule
|
||||||
|
* Multi-method rule
|
||||||
|
* proc type rule
|
||||||
|
- Effects pragma
|
||||||
|
|
||||||
|
|
||||||
Generics
|
Generics
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1941,6 +1941,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
prev: PSafePoint # points to next safe point ON THE STACK
|
prev: PSafePoint # points to next safe point ON THE STACK
|
||||||
status: int
|
status: int
|
||||||
context: C_JmpBuf
|
context: C_JmpBuf
|
||||||
|
hasRaiseAction: bool
|
||||||
raiseAction: proc (e: ref E_Base): bool {.closure.}
|
raiseAction: proc (e: ref E_Base): bool {.closure.}
|
||||||
|
|
||||||
when defined(initAllocator):
|
when defined(initAllocator):
|
||||||
|
|
@ -2047,7 +2048,9 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
## raise an exception but instead calls ``action``.
|
## raise an exception but instead calls ``action``.
|
||||||
## If ``action`` returns false, the exception has been handled and
|
## If ``action`` returns false, the exception has been handled and
|
||||||
## does not propagate further through the call stack.
|
## does not propagate further through the call stack.
|
||||||
if not isNil(excHandler): excHandler.raiseAction = action
|
if not isNil(excHandler):
|
||||||
|
excHandler.hasRaiseAction = true
|
||||||
|
excHandler.raiseAction = action
|
||||||
|
|
||||||
{.push stack_trace: off, profiler:off.}
|
{.push stack_trace: off, profiler:off.}
|
||||||
when defined(endb):
|
when defined(endb):
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ proc setFrame(s: PFrame) {.compilerRtl, inl.} =
|
||||||
framePtr = s
|
framePtr = s
|
||||||
|
|
||||||
proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
|
proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
|
||||||
s.raiseAction = nil
|
s.hasRaiseAction = false
|
||||||
s.prev = excHandler
|
s.prev = excHandler
|
||||||
excHandler = s
|
excHandler = s
|
||||||
|
|
||||||
|
|
@ -199,7 +199,7 @@ proc raiseExceptionAux(e: ref E_Base) =
|
||||||
if globalRaiseHook != nil:
|
if globalRaiseHook != nil:
|
||||||
if not globalRaiseHook(e): return
|
if not globalRaiseHook(e): return
|
||||||
if excHandler != nil:
|
if excHandler != nil:
|
||||||
if isNil(excHandler.raiseAction) or excHandler.raiseAction(e):
|
if not excHandler.hasRaiseAction or excHandler.raiseAction(e):
|
||||||
pushCurrentException(e)
|
pushCurrentException(e)
|
||||||
c_longjmp(excHandler.context, 1)
|
c_longjmp(excHandler.context, 1)
|
||||||
elif e[] of EOutOfMemory:
|
elif e[] of EOutOfMemory:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue