1. Re: Language Design
- Posted by David Cuny <dcuny at lanset.com> May 29, 2004
- 494 views
Bernie Ryan wrote: > How did you do setjmp/longjmp in euphoria I have been > trying to think of a way to do that for a long time. I don't know enough about the innards of the Euphoria VM to tell how to do it there (I've avoided reading it for obvious reasons), but I can tell how it's done in my Basic interpreter. I'll also note that the Finally clause is what makes Try/Catch difficult to implement, since it's guaranteed to execute to execute - even if you have a Return in a Catch clause. So for now I'll ignore it. Since Try/Catch clause can be nested, each Catch structure needs to be stored on the stack. The structure looks like this: /* a catch */ struct wCatch { int stackCount; /* height of data stack before try */ int callCount; /* height of call stack before try */ int gosubCount; /* height of return stack before try */ int jumpTo; /* opcode to jump to on error */ jmp_buf jumpBuffer; /* state data before jump */ }; Note that all the stacks are stored, so they can be returned to the proper height, and data left on them dereferenced and removed. (The return stack is used to track nesting of the Finally clause). Here's the implementation in the VM: case W_OP_STARTCATCH: /* get the jump offset */ arg1 = pcode[++pc]; aCatch = (wCatch *)wMalloc( sizeof( wCatch ) ); aCatch->stackCount = wTheStack->count; aCatch->callCount = wGrowCount( wTheCallStack ); aCatch->gosubCount = wGrowCount( wTheSubroutineStack ); aCatch->jumpTo = pc+arg1-1; /* store catch data on catch stack */ wGrowAppend( wTheCatchStack, (int)aCatch ); /* set address to return to on error */ if (setjmp(aCatch->jumpBuffer)) { /* print exception */ if (wTheDebugFlag) { wConsoleDebugf("EXCEPTION: %s\n", wTheExceptionText ); } /* restore, just in case */ aCatch = (wCatch *)wGrowPop( wTheCatchStack ); /* need to remove items? */ i = wTheStack->count - aCatch->stackCount; wStackDrop( i ); /* restore the call stack to it's prior size */ wCallRestoreStack( aCatch->callCount ); /* restore the subroutine stack to it's prior size */ i = wTheSubroutineStack->count - aCatch->gosubCount; wGrowDrop( wTheSubroutineStack, i ); /* get the jump address off the stack */ pc = aCatch->jumpTo; /* free the catch */ wFree(aCatch); /* reset the modifier, if set */ modifier = 0; } break; The END_CATCH opcode just drops it from the stack: case W_OP_ENDCATCH: /* get the address to jump to */ arg1 = pcode[++pc]; /* drop the data from the catch stack */ wFree( (void *)wGrowPop( wTheCatchStack )); /* +1 accounts for auto increment of pc */ pc += arg1 - 1; break; Here's the clause in the error handler that throws an error: /* if there is an active error handler, use it */ if (activeHandler) { /* free the error message buffer */ wFree( message ); /* get the jump buffer */ aCatch = (wCatch *)wGrowPeek( wTheCatchStack, 0 ); /* goto jump address */ longjmp(aCatch->jumpBuffer, 1 ); } I don't know if any of this is remotely useful; I'll be glad to answer any questions I can. -- David Cuny