added system.onRaise to support a condition system

This commit is contained in:
Araq 2012-11-05 08:36:44 +01:00
commit 865d9cc6e6
7 changed files with 100 additions and 36 deletions

View file

@ -157,10 +157,8 @@ proc trackPragmaStmt(tracked: PEffects, n: PNode) =
var it = n.sons[i]
if whichPragma(it) == wEffects:
# list the computed effects up to here:
pushInfoContext(n.info)
listEffects(tracked)
popInfoContext()
proc raisesSpec*(n: PNode): PNode =
for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i]

View file

@ -1969,29 +1969,6 @@ a special syntactic extension, the ``when`` construct is also available
within ``object`` definitions.
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.
Try statement
~~~~~~~~~~~~~
@ -2060,6 +2037,48 @@ in an implicit try block:
...
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
~~~~~~~~~~~~~~~~

View file

@ -1941,6 +1941,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
prev: PSafePoint # points to next safe point ON THE STACK
status: int
context: C_JmpBuf
raiseAction: proc (e: ref E_Base): bool {.closure.}
when defined(initAllocator):
initAllocator()
@ -2040,6 +2041,14 @@ when not defined(EcmaScript) and not defined(NimrodVM):
var e = getCurrentException()
return if e == nil: "" else: e.msg
proc onRaise*(action: proc(e: ref E_Base): bool{.closure.}) =
## can be used in a ``try`` statement to setup a Lisp-like
## `condition system`:idx:\: This prevents the 'raise' statement to
## raise an exception but instead calls ``action``.
## If ``action`` returns false, the exception has been handled and
## does not propagate further through the call stack.
if not isNil(excHandler): excHandler.raiseAction = action
{.push stack_trace: off, profiler:off.}
when defined(endb):
include "system/debugger"

View file

@ -52,6 +52,7 @@ proc setFrame(s: PFrame) {.compilerRtl, inl.} =
framePtr = s
proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
s.raiseAction = nil
s.prev = excHandler
excHandler = s
@ -198,8 +199,9 @@ proc raiseExceptionAux(e: ref E_Base) =
if globalRaiseHook != nil:
if not globalRaiseHook(e): return
if excHandler != nil:
pushCurrentException(e)
c_longjmp(excHandler.context, 1)
if isNil(excHandler.raiseAction) or excHandler.raiseAction(e):
pushCurrentException(e)
c_longjmp(excHandler.context, 1)
elif e[] of EOutOfMemory:
writeToStdErr(e.name)
quitOrDebug()

34
tests/run/tonraise.nim Normal file
View file

@ -0,0 +1,34 @@
discard """
output: '''i: 1
success'''
"""
type
ESomething = object of E_Base
ESomeOtherErr = object of E_Base
proc genErrors(s: string) =
if s == "error!":
raise newException(ESomething, "Test")
else:
raise newException(EsomeotherErr, "bla")
proc foo() =
var i = 0
try:
inc i
onRaise(proc (e: ref E_Base): bool =
echo "i: ", i)
genErrors("errssor!")
except ESomething:
echo("ESomething happened")
except:
echo("Some other error happened")
# test that raise handler is gone:
try:
genErrors("error!")
except ESomething:
echo "success"
foo()

View file

@ -1,7 +1,7 @@
version 0.9.2
=============
- implement/generalize the effect system; checked exceptions
- implement/generalize the effect system; document checked exceptions
- overloading based on ASTs: 'constraint' should not be in PType but for the
parameter *symbol*
@ -96,8 +96,6 @@ Library
Low priority
------------
- make 'raiseHook' take a closure and provide push and pop for this
--> Lisp-style condition system
- change how comments are part of the AST
- ``with proc `+`(x, y: T): T`` for generic code
- new feature: ``distinct T with operations``

View file

@ -2,10 +2,10 @@
News
====
2012-XX-XX Version 0.9.XX released
==================================
2012-XX-XX Version 0.9.2 released
=================================
Version 0.8.XX has been released! Get it `here <download.html>`_.
Version 0.9.2 has been released! Get it `here <download.html>`_.
Bugfixes
--------
@ -15,6 +15,7 @@ Bugfixes
Library Additions
-----------------
- Added ``system.onRaise`` to support a condition system.
Changes affecting backwards compatibility
@ -31,8 +32,11 @@ Language Additions
------------------
- ``case expressions`` are now supported.
- table constructors now mimic more closely the syntax of case... of...
- Nimrod can now infer the return type of a proc from its body
- Table constructors now mimic more closely the syntax of the ``case``
statement.
- Nimrod can now infer the return type of a proc from its body.
- Exception tracking has been added and the ``doc2`` command annotates possible
exceptions for you.
2012-09-23 Version 0.9.0 released