1. Re: Language Design

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

new topic     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu