LEOInterpreter

Introduction

Use the links in the table of contents to the left to access the documentation.



Groups

LEOContext calls for call stack management

These are used by the CALL_HANDLER_INSTR and RETURN_FROM_HANDLER_INSTR instructions.

Group members:

 

Static typecasting functions

Group members:

 

LEOContext methods

Group members:


Functions

LEOCastUInt32ToInt32
LEOCastUInt16ToInt16
LEOCastUInt32ToLEONumber
LEOInitInstructionArray
LEOAddInstructionsToInstructionArray
LEOContextCreate
LEOContextRelease
LEOContextRetain
LEORunInContext
LEOResumeContext
LEOPauseContext
LEOContextResumeIfAvailable
LEOPrepareContextForRunning
LEOContinueRunningContext
LEOContextStopWithError
LEOPushValueOnStack
LEOPushIntegerOnStack
LEOPushNumberOnStack
LEOPushBooleanOnStack
LEOPushUnsetValueOnStack
LEOPushEmptyValueOnStack
LEOPushStringValueOnStack
LEOPushStringConstantValueOnStack
LEOCleanUpStackToPtr
LEODebugPrintInstr
LEODebugPrintInstructions
LEODebugPrintContext
LEOContextDebugPrintCallStack
LEOContextPushHandlerScriptReturnAddressAndBasePtr
LEOContextPeekCurrentHandler
LEOContextPeekCurrentScript
LEOContextPeekReturnAddress
LEOContextPeekBasePtr
LEOContextPopHandlerScriptReturnAddressAndBasePtr
LEOFileIDForFileName
LEOFileNameForFileID
LEOSetInstructionIDToDebugPrintBefore
LEOSetInstructionIDToDebugPrintAfter
LEOSetCheckForResumeProc

LEOCastUInt32ToInt32


inline int32_t LEOCastUInt32ToInt32(
    uint32_t inNum ) __attribute__((always_inline));  
Discussion

Reinterpret the given unsigned uint32_t as a signed int32_t. E.g. useful for an instruction's param2 field.


LEOCastUInt16ToInt16


inline int16_t LEOCastUInt16ToInt16(
    uint16_t inNum ) __attribute__((always_inline));  
Discussion

Reinterpret the given unsigned uint16_t as a signed int16_t. E.g. useful for an instruction's param1 field.


LEOCastUInt32ToLEONumber


inline LEONumber LEOCastUInt32ToLEONumber(
    uint32_t inNum ) __attribute__((always_inline));  
Discussion

Reinterpret the given unsigned uint32_t as a LEONumber floating point quantity. E.g. useful for an instruction's param2 field.


LEOInitInstructionArray


void LEOInitInstructionArray(
    void );  
Discussion

Call this method once before calling any other interpreter functions. This initializes the instruction look-up table with the built-in instructions.


LEOAddInstructionsToInstructionArray


void LEOAddInstructionsToInstructionArray(
    struct LEOInstructionEntry *inInstructionArray,
    size_t inNumInstructions,
    size_t *outFirstNewInstruction );  
Discussion

Use this method to register new instructions for your host application. The new entries will be appended to the built-in instruction array (and your array copied), and the index of your first instruction will be returned in outFirstNewInstruction. You can now add this value to your instruction indices to calculate the actual LEOInstructionID you need to generate Leonie bytecode.

See the LEOInstructionEntry documentation on information on how to most easily declare your instruction array.


LEOContextCreate


LEOContext* LEOContextCreate(
    struct LEOContextGroup *inGroup,
    void *inUserData, 
    LEOUserDataCleanUpFuncPtr inCleanUpFunc );  
Discussion

Create a new LEOContext. The context is added to the provided group, and will retain the context group until the context is released by its last client. So once you have added the context to the context group, you can release it if you don't intend to create any other contexts in that group directly. The context starts out with a reference count of 1. When you are done with it, release it using LEOContextRelease().

See Also


LEOContextRelease


void LEOContextRelease(
    LEOContext *inContext );  
Discussion

Release your reference to this context. If you were the last owner of this context, disposes of the given context's associated data structures and releases the reference to its context group that the context holds.

See Also


LEOContextRetain


LEOContext* LEOContextRetain(
    LEOContext *inContext );  // returns inContext. 
Discussion

Acquire a reference to a context. Use this if, for example, you are passing LEOContext objects between functions, or otherwise sharing ownership of a context between several objects. Once you are done with the context, call LEOContextRelease on it to give up your reference.

See Also


LEORunInContext


Discussion

Shorthand for LEOPrepareContextForRunning and a loop of LEOContinueRunningContext.

See Also


LEOResumeContext


void LEOResumeContext(
    LEOContext *inContext );  
Discussion

Queues up a paused context for resumption of execution the next time LEOContextResumeIfAvailable is called.

See Also


LEOPauseContext


void LEOPauseContext(
    LEOContext *inContext );  
Discussion

An instruction can call this to cause the given context so you can do some async work, then later come back to it with LEOResumeContext.

See Also


LEOContextResumeIfAvailable


See Also


LEOPrepareContextForRunning


Discussion

Set the currentInstruction of the given LEOContext to the given instruction array's first instruction, and initialize the Base pointer and stack end pointer and flags etc.

See Also


LEOContinueRunningContext


Discussion

Execute the next instruction in the context. Returns false if the code has finished executing or exited with an error.

See Also


LEOContextStopWithError


void LEOContextStopWithError(
    LEOContext *inContext,
    size_t errLine,
    size_t errOffset,
    uint16_t fileID,
    const char *inErrorFmt,
    ... );  
Discussion

Stop execution in the given context with an error message. Currently sets the errMsg field of the context to the given string and set keepRunningto FALSE.


LEOPushValueOnStack


LEOValuePtr LEOPushValueOnStack(
    LEOContext *theContext,
    LEOValuePtr inValueToCopy );  
Discussion

Push a copy of the given value onto the stack, returning a pointer to it.

See Also


LEOPushIntegerOnStack


LEOValuePtr LEOPushIntegerOnStack(
    LEOContext *theContext,
    LEOInteger inInteger,
    LEOUnit inUnit );  
Discussion

Push a copy of the given integer onto the stack, returning a pointer to it. This is a shorthand for LEOPushValueOnStack and the corresponding LEOInitXXValue call.

See Also


LEOPushNumberOnStack


LEOValuePtr LEOPushNumberOnStack(
    LEOContext *theContext,
    LEONumber inNumber,
    LEOUnit inUnit );  
Discussion

Push a copy of the given number onto the stack, returning a pointer to it. This is a shorthand for LEOPushValueOnStack and the corresponding LEOInitXXValue call.

See Also


LEOPushBooleanOnStack


LEOValuePtr LEOPushBooleanOnStack(
    LEOContext *theContext,
    bool inBoolean );  
Discussion

Push a copy of the given boolean onto the stack, returning a pointer to it. This is a shorthand for LEOPushValueOnStack and the corresponding LEOInitXXValue call.

See Also


LEOPushUnsetValueOnStack


Discussion

Pushes a special empty string on the stack that a user can't generate, and which returns TRUE from LEOGetValueIsUnset(). To the user it looks just like any empty string, but if the user replaces it with any other value (even an empty string), you can detect that. Used e.g. by the message box to detect whether the code the user entered returned a result (i.e. was an expression) or not (i.e. was a command). This is a shorthand for LEOPushValueOnStack and LEOInitUnsetValue.

See Also


LEOPushEmptyValueOnStack


Discussion

Push an empty string onto the stack, returning a pointer to it. This is a shorthand for LEOPushValueOnStack and the corresponding LEInitStringConstantValue("") call.

See Also


LEOPushStringValueOnStack


LEOValuePtr LEOPushStringValueOnStack(
    LEOContext *theContext,
    const char *inString,
    size_t strLen );  
Discussion

Push a copy of the given string onto the stack, returning a pointer to it. This is a shorthand for LEOPushValueOnStack and the corresponding LEOInitXXValue call.

See Also


LEOPushStringConstantValueOnStack


LEOValuePtr LEOPushStringConstantValueOnStack(
    LEOContext *theContext,
    const char *inString );  
Discussion

Push a copy of the given string constant onto the stack, returning a pointer to it. This is a shorthand for LEOPushValueOnStack and the corresponding LEOInitXXValue call.

See Also


LEOCleanUpStackToPtr


void LEOCleanUpStackToPtr(
    LEOContext *theContext,
    union LEOValue *lastItemToDelete );  
Discussion

Used by instructions to unwind the stack (e.g. when removing their parameters) and ensure values get destructed correctly.

See Also


LEODebugPrintInstr


void LEODebugPrintInstr(
    LEOInstruction *instruction );  
Discussion

Print the given instruction to the console for debugging purposes.

See Also


LEODebugPrintInstructions


void LEODebugPrintInstructions(
    LEOInstruction instructions[],
    size_t numInstructions );  
Discussion

Print the given array of instructions to the console for debugging purposes using LEODebugPrintInstr.

See Also


LEODebugPrintContext


Discussion

Print the given context to the console for debugging purposes. This includes the stack, and is very useful for debugging new instructions.

See Also


LEOContextDebugPrintCallStack


Discussion

Print the current call stack of the given context to the console for debugging purposes.


LEOContextPushHandlerScriptReturnAddressAndBasePtr


void LEOContextPushHandlerScriptReturnAddressAndBasePtr(
    LEOContext *inContext,
    struct LEOHandler *inHandler,
    struct LEOScript *inScript,
    LEOInstruction *returnAddress,
    LEOValuePtr oldBP );  
Discussion

Push a reference to the current script, the handler in it that is current, the return address and the previous base pointer to restore onto a special stack so the call instruction can retain the script/handler while it is running even if the owning object goes away, and so the return instruction can restore the base pointer and knows what instruction to return to.

See Also


LEOContextPeekCurrentHandler


Discussion

Retrieve the object representing the handler currently in progress, which was pushed last onto a special stack.

See Also


LEOContextPeekCurrentScript


Discussion

Retrieve the object representing the script owning the handler currently in progress, which was pushed last onto a special stack.

See Also


LEOContextPeekReturnAddress


Discussion

Retrieve the address of the instruction that follows the CALL_HANDLER_INSTR instruction that brought you to this handler, and to which we are expected to return after this handler call. Note that this may be NULL to indicate you are the first handler pushed on the call stack (in C that would e.g. be main()).

See Also


LEOContextPeekBasePtr


Discussion

Retrieve the address of the base pointer as it was before this handler was called. Used to restore the calling handler's base pointer when this handler returns.

See Also


LEOContextPopHandlerScriptReturnAddressAndBasePtr


Discussion

Remove the reference to the current handler, current script, return address and base pointer before the current handler was called from the special stack. This is called when returning from the current handler.

See Also


LEOFileIDForFileName


uint16_t LEOFileIDForFileName(
    const char *inFileName );  
Discussion

Generate a file ID for the given file name or file path (or could even be an object 'path' of some sort). This is used e.g. by the debugger to associate a source file with certain instructions.

See Also


LEOFileNameForFileID


const char* LEOFileNameForFileID(
    uint16_t inFileID );  
Discussion

Return the file name that corresponds to the given file ID.

See Also


LEOSetInstructionIDToDebugPrintBefore


Parameters
inID

The instruction ID constant of the instruction you're interested in.

Discussion

Tell the interpreter to log the current context (current instruction and stack) to the console before executing the specified instruction.

See Also


LEOSetInstructionIDToDebugPrintAfter


Parameters
inID

The instruction ID constant of the instruction you're interested in.

Discussion

Tell the interpreter to log the current context (current instruction and stack) to the console after executing the specified instruction.

See Also


LEOSetCheckForResumeProc


void LEOSetCheckForResumeProc(
    void (*checkForResumeProc)() );  
Discussion

A script can be paused mid-instruction and later resumed. This is handy e.g. when an instruction actually involves an asynchronous operation. Once the async operation has finished, it can call to request for the script to be resumed. Since we don't want scripts piling up in async callbacks, scripts are resumed by calling LEOContextResumeIfAvailable() from somewhere farther up the main thread (e.g. where regular event handlers would be triggered). When a resume request is made, this callback is invoked to give you the opportunity to ensure a call to LEOContextResumeIfAvailable() will be triggered by whatever mechanism you choose (queue up an event, signal a semaphore, whatever).


Typedefs

LEOInstructionID
LEOInstructionFuncPtr
LEONonexistentHandlerFuncPtr
LEOUserDataCleanUpFuncPtr
LEOContextCompletionFuncPtr
LEOInstruction
LEOContext

LEOInstructionID


typedef uint16_t LEOInstructionID;  
Discussion

Index into our instruction function array. Index 0 contains the "unimplemented" error exit function.

Note that instruction IDs are sequential and not intended to be saved to disk. If you want to save bytecode to disk, you should save a look-up-table from some persistent instruction opcode along to the file and then when loading fix up all the instruction IDs based on that name.


LEOInstructionFuncPtr


typedef void ( *LEOInstructionFuncPtr)(
    struct LEOContext *inContext );  
Discussion

All instructions are implemented as functions with the following signature: Apart from branching instructions, every instruction has to increment the inContext->currentInstruction so it'll advance to the next instruction. The instruction functions are looked up in the gInstructions array.


LEONonexistentHandlerFuncPtr


typedef void ( *LEONonexistentHandlerFuncPtr)(
    struct LEOContext *inContext,
    LEOHandlerID inHandler );  
Discussion

If a handler call can't find the handler in the current script and no parent for that script exists that handles the call, this function is called to allow the host application to provide default behaviour for unhandled messages.


LEOUserDataCleanUpFuncPtr


typedef void ( *LEOUserDataCleanUpFuncPtr)(
    void *inUserData );  
Discussion

Callback you can give to a context when you attach user data to it, which it will call when it is cleaned up to allow disposing of the user data.


LEOContextCompletionFuncPtr


typedef void ( *LEOContextCompletionFuncPtr)(
    struct LEOContext *inContext );  
Discussion

This function is called when a context finishes execution, either because of an error, or because the outermost function finished executing. You can now look at the return value or pass the message on to the next object in the chain.


LEOInstruction


typedef struct LEOInstruction { 
    LEOInstructionID instructionID; 
    uint16_t param1; 
    uint32_t param2; 
} LEOInstruction;  
Discussion

A LEOInstruction is how an instruction looks in bytecode. It has the instruction's ID as the first item, and then one 16-bit and one 32-bit argument that depends on the particular instruction.


LEOContext


typedef struct LEOContext { 
    size_t referenceCount; 
    struct LEOContextGroup *group; // The group this context belongs to, containing its global state, references etc. 
    LEOContextFlags flags; // But flags for flow control etc. 
    char errMsg[1024]; // Error message to display when kLEOContextKeepRunning flag has been set to FALSE. 
    size_t errLine; // Line on which the error occurred. Or SIZE_T_MAX if we don't know. 
    size_t errOffset; // Byte offset in script at which the error occurred, or SIZE_T_MAX if we don't know. If this isn't present, errLine might still be set. 
    uint16_t errFileID; // ID of the file in which the error occurred. 
    char itemDelimiter; // item delimiter to use for chunk expressions in values. 
    size_t numCallStackEntries; // Number of items in callStackEntries. 
    LEOCallStackEntry *callStackEntries; // Array of call stack entries to allow showing a simple backtrace and picking handlers from the current script. 
    LEOInstructionFuncPtr preInstructionProc; // For each instruction, this function gets called, to let you do idle processing, hook in a debugger etc. This should NOT be an instruction, as that would advance the PC and screw up the call of the actual instruction. 
    LEOInstructionFuncPtr promptProc; // On certain errors, this function is called to enter into the debugger prompt. 
    LEONonexistentHandlerFuncPtr callNonexistentHandlerProc; // When a handler is called that doesn't exist, and a script has no parent, this function is called (e.g. to display an error message or call an XCMD). 
    size_t numSteps; // Used by LEODebugger's PreInstructionProc to implement single-stepping. 
    LEOInstruction *currentInstruction; // PC 
    union LEOValue *stackBasePtr; // BP 
    union LEOValue *stackEndPtr; // SP (always points at element after last element) 
    union LEOValue stack[LEO_STACK_SIZE]; // The stack. 
    void *userData; 
    LEOUserDataCleanUpFuncPtr cleanUpUserData; 
    LEOContextCompletionFuncPtr contextCompleted; 
} LEOContext;  
Fields
group

The LEOContextGroup that collects the global state shared between this context and any others in its group.

flags

flags that let you pause or exit scripts.

errMsg

Error message to display when the kLEOContextKeepRunning flag has been set to FALSE. If this is an empty string, it was a benign exit.

itemDelimiter

The delimiter to use for the "item" chunk expression. Defaults to comma (',').

preInstructionProc

A function to call on each instruction before it is executed. Useful as a hook-up-point for a debugger, or to process events while a script is running (e.g. user cancellation).

callNonexistentHandlerProc

When a handler is called that doesn't exist, and a script has no parent, this function is called (e.g. to display an error message or call an XCMD or equivalent third-party plugin).

numSteps

Used by LEODebugger's PreInstructionProc to implement single-stepping.

currentInstruction

The instruction currently being executed. Essentially the Program Counter of our virtual CPU.

stackBasePtr

Base pointer into stack, used during function calls to find parameters & start of local variable section.

stackEndPtr

Stack pointer indicating used size of our stack. Always points at element after last element.

stack

The stack containing all our local variables, parameters etc.

userData

A pointer for use by the client, for storing additional information in.

cleanUpUserData

A function that will be passed the userData pointer and called right before we free the context's memory, which you can provide to free any memory used by the userData.

contextCompleted

A function that is called once execution aborts due to an error or the outermost function completes. It can retrieve result values from the context and free its memory.

Discussion

A LEOContext encapsulates all execution state needed to run bytecode. Speaking in CPU terms, it encapsulates the registers, the call stack, and a few thread-globals. Hence, each thread in which you want to run bytecode needs its own LEOContext.

See Also


Structs and Unions

LEOInstructionEntry

LEOInstructionEntry


struct LEOInstructionEntry { 
    LEOInstructionFuncPtr proc;
    const char *name;
};  
Fields
proc

The function to call for this instruction

name

String representation of the function's name. *must be* a constant or otherwise guaranteed to stay around as long as Leonie needs it.

Discussion

A LEOInstructionEntry is how we look up the function associated with an instruction ID. We keep a master table of all instructions we know, and you can add your own instructions by simply adding to that list. You generally do not use this directly. Instead, you use the LEOINSTR_START, LEOINSTR, and LEOINSTR_LAST macros below to declare a global array:

LEOINSTR_START(FrobnitzWebService,NUM_WEBSERVICE_INSTRUCTIONS) LEOINSTR(MyWebServiceStartInstruction) LEOINSTR_LAST(MyWebServiceStopInstruction)

and pass that to LEOAddInstructionsToInstructionArray:

size_t outFirstNewInstruction = 0; LEOAddInstructionsToInstructionArray( gFrobnitzWebServiceInstructions, NUM_WEBSERVICE_INSTRUCTIONS, &outFirstNewInstruction );

Assuming you've declared your instruction IDs in an enum like this:

enum { MY_WEBSERVICE_START_INSTR = 0, MY_WEBSERVICE_STOP_INSTR, NUM_WEBSERVICE_INSTRUCTIONS };

The instruction IDs to generate can now be calculated like:

LEOHandlerAddInstruction( theHandler, outFirstNewInstruction +MY_WEB_SERVICE_START_INSTR, 0, 0 );

or the likes.


Enumerated Types

Possible

Possible


enum { 
    kLEOContextKeepRunning = (
        1 << 0),
    kLEOContextPause = (
        1 << 1),
    kLEOContextResuming = (
        1 << 2) 
};  
Constants
kLEOContextKeepRunning

Clear this bit to stop script execution. Used on errors and for ExitToTop.

kLEOContextPause

Set by the current instruction when it wants to pause the current context (e.g. to perform some async tasks which should appear synchronous to scripts). The instruction should not advance the PC until the context is resumed, which it can detect by looking at the kLEOContextResuming flag.

kLEOContextResuming

Context was just resumed from being paused. The current instruction can now finish its work, advance the PC and return.

Discussion

values for the 'flags' field in the LEOContext.


Macro Definitions

LEO_STACK_SIZE
BACK_OF_STACK

LEO_STACK_SIZE


#define LEO_STACK_SIZE 1024 
Discussion

How many LEOValues can be on the stack before we run out of stack space.


BACK_OF_STACK


#define BACK_OF_STACK  
Discussion

Pass this as param1 to some instructions that take a basePtr-relative address to make it pop the last value off the stack instead of reading a value from that address. Since they reinterpret the number as an int16_t, it needs to be a value in the middle of the unsigned range.

Value

0x8000 (32768)