World: r4wp
[#Red] Red language group
older newer | first last |
DocKimbel 13-Apr-2013 [6985x16] | foo: func [[catch]][ print "2" throw 10 print "KO" ] print "1" foo print "3" print ["^/thrown value: " system/thrown lf] |
will output: 123 thrown value: 10 | |
A deeper nested example: foo2: does [ print "3" throw 100 print "KO" ] bar: func [[catch]][ print "2" foo2 print "KO" ] print "1" bar print ["^/thrown value: " system/thrown lf] | |
will output: 123 thrown value: 100 | |
The implementation is done and I will push it in a few minutes. It required about 25 additional LOC to implement all such simple exception system (right, 25!). ;-) | |
So, how does it work? When you need to interrupt the flow of code in a function in Red/System, currently you can just use EXIT/RETURN to make an early exit. But, sometimes, you need to go up through several nested calls, that's where the new THROW function comes handy. It will interrupt the execution and go up the call tree to find the first function that has the CATCH attribut set. It will then just resume execution after the last function call (from which the exception has been generated). If no CATCH attribut is found, it will go up to global code and resume from there. | |
THROW requires an integer! value. Such value represent the exception ID and is user-defined. After the resume from a caught exception, you can use SYSTEM/THROWN to read the passed exception ID and act accordingly (usually using a SWITCH dispatcher). | |
Important thing to note: system/thrown needs to be manually reset, as the last thrown value will stay there if no exception occured. Such reset could be done before each call to a function that could generate an exception or after processing the thrown value. | |
I could have added a much more sophisticated system with a true CATCH function, but this would have made the implementation way more complex and would have taken a lot more time. As I need it only for Red's interpreter, I think this way should be enough and will be usable by other Red/System programmers to enhance their own code. | |
Thinking about it, it might be possible to allow an extended catch attribut with a integer value to specify the barrier value for catching exceptions (and avoid manual re-throwning), something like: foo: func [[catch 100]][...] would catch all thrown exceptions with a value <= 100 and let others pass up to caller. | |
What do you think about all this? | |
BTW, this exception system relies on stack frames unwinding, so it won't work for callbacks (CATCH attribut won't be accepted in callback functions anyway). | |
I've considered using a continuation approach for constant-time exception throwing (would also work from callbacks), but I didn't find a simple enough syntax for defining/using it without having to rely on CPS (Continuation-Passing Style) for every functions... See http://en.wikipedia.org/wiki/Continuation-passing_style | |
Another note: messing up with the stack (using push/pop) in a user-function is fine as long as you returned the stack clean before calling a function that could generate an exception. Failure to do so will result in a crash. It might be possible to make stack-unwinding for exception resistant to such cases, I will investigate that. | |
Exception handling code pushed. Have fun! ;) | |
(only IA-32 for now, will add ARM support in the next days) | |
Gregg 13-Apr-2013 [7001x2] | That sounds great Doc. I collected your comments into a file in %Red/. |
Hmm, shouldn't bar print KO in the second example, from your description? | |
DocKimbel 13-Apr-2013 [7003] | Nope, see first example. This might be a bit non/counter-intuitive, I will see if I can improve that. |
Gregg 13-Apr-2013 [7004] | Ah, I get it now. Yes, that wasn't obvious at first. |
DocKimbel 13-Apr-2013 [7005] | I won't be online tomorrow most of the day, will look into that and will upgrade the documentation tomorrow night. |
Gregg 13-Apr-2013 [7006x2] | So you don't check system/thrown in the func with [catch], correct? |
But in the func that *called* the func with [catch]. | |
DocKimbel 13-Apr-2013 [7008x2] | Right. |
I might adjust that tomorrow if possible to make it more intuitive. | |
PeterWood 13-Apr-2013 [7010] | Absolutely fabulous Nenad!!! |
Arnold 14-Apr-2013 [7011] | Good progress. Nice of you to sum it up Gregg, makes the comments a whole lot more readable. Filename is a bit awkward though: "esceptions" |
Gregg 14-Apr-2013 [7012x2] | Fixed. Thanks Arnold. |
I had a Spanish accent last night. | |
DocKimbel 15-Apr-2013 [7014] | Gregg, I've reconsidered the behavior of the [catch] function attribut, if I change it, some use cases become even less intuitive. For example, the single function use case (my first example above) performs quite intuitively. I think the vague non-intuitive feeling at the beginning comes from our habits with CATCH native in Rebol, after using Red/System's way a bit, that feeling just faded away. Anyway, if you have suggestions for improvements, it's the right time. |
Kaj 15-Apr-2013 [7015] | Doc, any idea how I can convert a string! passed into a routine! to UTF-8, or access a cached UTF-8 value? |
PeterWood 15-Apr-2013 [7016] | Kaj, can you use string/mold inside the routine to create a c-string! ? |
PeterWood 16-Apr-2013 [7017] | The answer is not you can't as mold doesnt output a UTF-8 string. |
DocKimbel 16-Apr-2013 [7018x8] | Kaj: cached UTF-8 string is available using str/cache if str is a red-string! value. |
Only small strings (< 64 bytes) are cached. | |
We haven't yet implemented UTF-8 encoding functions in the standard library. It will be done during the I/O implementation (unless you have a strong need for it, then I'll have a look at it). | |
After trying to use the new exception handling in the interpreter to implement EXIT/RETURN, I realized that the current behavior of the [catch] attribut is not suitable in a intensively recursive environment. So I had to change the moment the exceptions are caught to an earlier time. Now, it works like this: foo: does [ print "3" throw 100 print "KO" ] bar: func [[catch]][ print "2" foo print "4" ] print "1" bar will output: 1234 So, now the [catch] attribut resumes the execution at the current level, instead of the parent caller level like before. | |
The catch flags position on stack has been changed to a safer place, so it's now resistant to a "dirty" stack left by user code (unbalanced PUSH/POP actions at the exception raising point). | |
A [catch] flag will have no effect on exceptions launched from the same level (enables re-throwing exceptions). | |
EXIT and RETURN have been implemented for the interpreter too now. All related tests are now passing. An important fact to note, which differs from the Rebol way: EXIT and RETURN are dialect keywords in Red, not native functions. Both the compiler and the interpreter are processing them as part of function's body dialect. | |
@Ladislav, I know you don't care much about Red, but you might be pleased to see that some of your old propositions for Rebol found their way into Red. ;-) | |
Pekr 16-Apr-2013 [7026x2] | Can't speak for Ladislav, but maybe he is mostly busy with other stuff, so he'll wait, how Red turns out in the end :-) |
So, now as exceptions are working, my typical - what's next? :-) Dyn-lib emmiter? Contexts? IO? :-) | |
DocKimbel 16-Apr-2013 [7028] | Shared lib generation for Mac OS X and Linux. |
PeterWood 16-Apr-2013 [7029] | Does that include ARM? |
DocKimbel 16-Apr-2013 [7030] | Not sure if you mean for exceptions or shared libs, so: - Exceptions: I will push the upgraded ARM version in a few hours - Shared lib: yes, we will support it on ARM too |
PeterWood 16-Apr-2013 [7031] | I meant shared libs |
Ladislav 16-Apr-2013 [7032] | ...you might be pleased to see that some of your old propositions for Rebol found their way into Red - yes, that is great! |
Gregg 16-Apr-2013 [7033] | EXIT from a func exits the console. Is that intended? |
DocKimbel 16-Apr-2013 [7034] | N ope. |
older newer | first last |