LEOScript

Introduction

A script is a reference-counted data type that collects all the methods (functions and commands) of an object. After you create a script and populate it with handlers, it is intended to be immutable. If you want to remove a handler or otherwise change a script afterwards, create a new script.

This way, if a script is changed, any still executing instances of that script can finish on their own. This is especially needed for scripts that modify themselves.

For similar reasons, we reference the owning object using a LEOObjectID and LEOObjectSeed, so a script can delete the object owning it and still finish executing.



Functions

LEOHandlerAddInstruction
LEOHandlerAddVariableNameMapping
LEOHandlerFindVariableByAddress
LEOHandlerFindVariableByName
LEOScriptAddBreakpointAtLine
LEOScriptAddCommandHandlerWithID
LEOScriptAddFunctionHandlerWithID
LEOScriptAddString
LEOScriptAddSyntaxError
LEOScriptCreateForOwner
LEOScriptFindCommandHandlerWithID
LEOScriptFindFunctionHandlerWithID
LEOScriptHasBreakpointAtLine
LEOScriptRelease
LEOScriptRemoveAllBreakpoints
LEOScriptRemoveBreakpointAtLine
LEOScriptRetain

LEOHandlerAddInstruction


void LEOHandlerAddInstruction(
    LEOHandler *inHandler,
    LEOInstructionID instructionID,
    uint16_t param1,
    uint32_t param2 );  
Discussion

Add an instruction with the given instruction ID and parameters to a handler. Use this only when initially setting up a script and parsing/compiling bytecode into it. Pointers to instructions inside a handler may not stay valid after a call to this.

See Also


LEOHandlerAddVariableNameMapping


void LEOHandlerAddVariableNameMapping(
    LEOHandler *inHandler,
    const char *inName,
    const char *inRealName,
    size_t inBPRelativeAddress );  
Parameters
inHandler

The handler to add this variable name mapping to.

inName

The unique name that is used internally to identify this variable (which e.g. usually includes a prefix for user-defined variables so they can't collide with temporaries the compiler generates).

inRealName

The name the user specified and expects displayed back to it.

inBPRelativeAddress

The BasePointer-relative address on the stack for the variable of this name.

Discussion

Add an entry to this handler so we can display a name for this variable.

See Also


LEOHandlerFindVariableByAddress


void LEOHandlerFindVariableByAddress(
    LEOHandler *inHandler,
    long bpRelativeAddress,
    char **outName,
    char **outRealName,
    LEOContext *inContext );  
Discussion

Find a variable's name in a handler based on its basePointer-relative offset.

See Also


LEOHandlerFindVariableByName


long LEOHandlerFindVariableByName(
    LEOHandler *inHandler,
    const char *inName );  
Discussion

Find a variable's basePointer-relative offset based on its real name.

See Also


LEOScriptAddBreakpointAtLine


size_t LEOScriptAddBreakpointAtLine(
    LEOScript *inScript,
    size_t inLineNumber );  
Discussion

Add a line number to our list of breakpoints. It is the responsibility of the debugger that you use (e.g. LEODebugger.h or LEORemoteDebugger.h) to look at each LINE_MARKER_INSTR instruction and verify whether it matches one of these lines, and to trigger accordingly.

This can be used to e.g. implement clicking in the gutter of a script editor window to set a breakpoint.

See Also


LEOScriptAddCommandHandlerWithID


LEOHandler* LEOScriptAddCommandHandlerWithID(
    LEOScript *inScript,
    LEOHandlerID inHandlerName );  // Invalidates all LEOHandler pointers anyone may have into this script. 
Discussion

Create a new command handler with the given handler ID and return a pointer to it. Use this only when initially setting up a script and parsing/compiling bytecode into it. Whenever you add a new handler to a script, all pointers to existing LEOHandlers in the script may be invalidated, including the ones previously returned by this call.

See Also


LEOScriptAddFunctionHandlerWithID


LEOHandler* LEOScriptAddFunctionHandlerWithID(
    LEOScript *inScript,
    LEOHandlerID inHandlerName );  // Invalidates all LEOHandler pointers anyone may have into this script. 
Discussion

Create a new function handler with the given handler ID and return a pointer to it. Use this only when initially setting up a script and parsing/compiling bytecode into it. Whenever you add a new handler to a script, all pointers to existing LEOHandlers in the script may be invalidated, including the ones previously returned by this call.

See Also


LEOScriptAddString


size_t LEOScriptAddString(
    LEOScript *inScript,
    const char *inString );  
Parameters
inScript

The script to whose strings table you want to add a string.

inString

The string to be copied to the script's strings table.

Return Value

the index into the strings table at which the string can now be found.

Discussion

Add a string to our strings table, so you can push it on the stack using the PUSH_STR_FROM_TABLE_INSTR instruction and operate on it in the script.

See Also


LEOScriptAddSyntaxError


size_t LEOScriptAddSyntaxError(
    LEOScript *inScript,
    const char *inErrMsg,
    uint16_t inFileID,
    size_t inErrorLine,
    size_t inErrorOffset );  
Parameters
inScript

The script to whose strings table you want to add a string.

inErrMsg

The error message to be copied to the script's syntax error table.

inErrorLine

The line number at which the error occurred.

inErrorOffset

If this isn't SIZE_T_MAX, an actual character offset where the parser croaked.

inFileID

The file where the error occurred.

Return Value

the index into the syntax error table at which the string can now be found.

Discussion

Add an error message and position information to our strings table, so it can be reported using the PARSE_ERROR_INSTR instruction.

See Also


LEOScriptCreateForOwner


LEOScript* LEOScriptCreateForOwner(
    LEOObjectID ownerObject,
    LEOObjectSeed ownerSeed,
    LEOGetParentScriptFuncPtr inGetParentScriptFunc );  // Gives referenceCount of 1. 
Discussion

Creates a script referencing the given owner. The LEOScript* is reference- counted and its reference count is set to 1, so when you're done with it, you must release it. When a script is executing, it should usually be retained so that deleting its owner doesn't pull out the rug from under the interpreter.

See Also


LEOScriptFindCommandHandlerWithID


Discussion

Return a pointer to a command handler with the given handler ID (i.e. handler name).

Returns NULL if no such handler exists.

See Also


LEOScriptFindFunctionHandlerWithID


Discussion

Return a pointer to a function handler with the given handler ID (i.e. handler name).

Returns NULL if no such handler exists.

See Also


LEOScriptHasBreakpointAtLine


bool LEOScriptHasBreakpointAtLine(
    LEOScript *inScript,
    size_t inLineNumber );  
Discussion

Returns TRUE if a script has been told a breakpoint exists at the given 1-based line number, FALSE otherwise.

See Also


LEOScriptRelease


void LEOScriptRelease(
    LEOScript *inScript );  // Subtracts 1 from referenceCount. If it hits 0, disposes of inScript. 
Discussion

Give up ownership of the given script. You acquire ownership by either creating the script, or by retaining it. Giving up ownership decreases its reference count by 1. When the last owner releases the object (and the reference count reaches 0), the script is freed.

See Also


LEOScriptRemoveAllBreakpoints


Discussion

Remove all breakpoint for lines in this script to undo what any calls to LEOScriptAddBreakpointAtLine did.

See Also


LEOScriptRemoveBreakpointAtLine


void LEOScriptRemoveBreakpointAtLine(
    LEOScript *inScript,
    size_t inLineNumber );  
Discussion

Remove a breakpoint from a given line, to undo what LEOScriptAddBreakpointAtLine did.

See Also


LEOScriptRetain


LEOScript* LEOScriptRetain(
    LEOScript *inScript );  // Adds 1 to referenceCount. Returns inScript. 
Return Value

Returns inScript so you can assign it to a variable in a struct, or a global, or whatever makes sense.

Discussion

Acquire ownership of the given script, so that when the current owner releases it the object still stays around for your use. Increases the reference count by 1.

See Also


Typedefs

LEOGetParentScriptFuncPtr
LEOHandler
LEOParseErrorEntry
LEOScript
LEOVariableNameMapping

LEOGetParentScriptFuncPtr


// -----------------------------------------------------------------------------  
typedef struct LEOScript* ( *LEOGetParentScriptFuncPtr)(
    struct LEOScript *inScript,
    struct LEOContext *inContext,
    void *inParam );  
Parameters
inScript

The script whose parent needs to be determined.

inContext

The execution context in which the script is being run, with all its globals, references etc.

Discussion

Only the host application can know where in its hierarchy a script resides, and to which script unhandled messages should be forwarded. It can provide a function with this signature to let the interpreter look up the next script in the hierarchy.

See Also


LEOHandler


// -----------------------------------------------------------------------------  
typedef struct LEOHandler { 
    LEOHandlerID handlerName; // Unique ID of handlers with this name. 
    size_t numInstructions; 
    LEOInstruction *instructions; 
    size_t numVariables; 
    LEOVariableNameMapping *varNames; 
} LEOHandler;  
Fields
handlerName

The name of this handler. Case INsensitive.

numInstructions

The number of instructions in the instructions array.

instructions

An array that holds the instructions for this handler.

Discussion

Every method is represented by a struct like this:

See Also


LEOParseErrorEntry


// -----------------------------------------------------------------------------  
typedef struct LEOParseErrorEntry { 
    char *errMsg; 
    size_t errorLine; 
    size_t errorOffset; 
    uint16_t fileID; 
} LEOParseErrorEntry;  
Fields
errMsg

The error message as the compiler would have reported it.

errorLine

The line number where the parse error occurred.

errorOffset

If this isn't SIZE_T_MAX, this is an offset to the actual character offset at which the parser choked.

fileID

The file ID for the file in which this error occurred.

Discussion

We postpone the reporting of parse errors until someone actually attempts to run a script and try to muddle through parsing it until then. These entries hold the error position and message so we can report it then. The advantage of this is that if e.g. a mouseUp handler has a typo, but the mouseWithin handler in the same script is fine, at least the user will not get an error message whenever they move the mouse. Also, if both have a typo but the mouseUp handler comes first, you won't get the error message for the mouseUp handler on mouseWithin. Errors are reported where they occur, as you would expect from an interpreter.

See Also


LEOScript


// -----------------------------------------------------------------------------  
typedef struct LEOScript { 
    size_t referenceCount; 
    LEOObjectID ownerObject; // Deletion-safe reference to owning object. 
    LEOObjectSeed ownerObjectSeed; 
    size_t numFunctions; 
    LEOHandler *functions; 
    size_t numCommands; 
    LEOHandler *commands; 
    size_t numStrings; // Number of items in stringsTable. 
    char **strings; // List of string constants in this script, which we can load. 
    LEOGetParentScriptFuncPtr GetParentScript; 
    size_t numParseErrors; // Number of elements in parseErrors array. 
    LEOParseErrorEntry *parseErrors; // List of errors for the PARSE_ERROR_INSTR instruction to refer to. 
    size_t numBreakpointLines; // Number of elements in breakpointLines array. 
    size_t *breakpointLines; // List of line numbers where the user set a breakpoint. 
} LEOScript;  
Fields
referenceCount

The number of owners this script currently has. Owners are e.g. either contexts running the script or the object the script belongs to.

ownerObject

reference to the object that this script belongs to, i.e. the 'me' or 'self'.

ownerObjectSeed

The 'seed' for the ownerObject slot, to detect the case where the owner has been released and the slot re-used.

numFunctions

The number of function handler entries in the functions array.

functions

An array of handlers implementing the functions this script implements.

numCommands

The number of command handler entries in the commands array.

commands

An array of handlers implementing the commands this script implements.

numStrings

Number of items in strings.

strings

List of string constants in this script, which we can load using ASSIGN_STRING_FROM_TABLE_INSTR.

GetParentScript

A pointer to a function provided by the host that returns a script to which unhandled or passed messages will be forwarded, or NULL if there is no script behind this one to handle those messages.

numParseErrors

Number of elements in parseErrors array.

parseErrors

List of errors for the PARSE_ERROR_INSTR instruction to refer to.

numBreakpointLines

Number of elements in breakpointLines array.

breakpointLines

List of line numbers where the user set a breakpoint, for use by the debugger from a LINE_MARKER_INSTR instruction.

Discussion

Every object has a script, which is a grouping of functions and commands:

See Also

LEOScriptCreateForOwner

LEOScriptAddCommandHandlerWithID

LEOScriptAddFunctionHandlerWithID

LEOScriptFindCommandHandlerWithID

LEOScriptFindFunctionHandlerWithID

LEOScriptAddString

LEOGetParentScriptFuncPtr

LEOScriptAddSyntaxError

LEOScriptAddBreakpointAtLine

LEOScriptRemoveBreakpointAtLine

LEOScriptHasBreakpointAtLine

LEODebugPrintScript


LEOVariableNameMapping


// -----------------------------------------------------------------------------  
typedef struct LEOVariableNameMapping { 
    char variableName[DBG_VAR_NAME_SIZE]; 
    char realVariableName[DBG_VAR_NAME_SIZE]; // Name as the user sees it. 
    long bpRelativeAddress; 
} LEOVariableNameMapping;  
Fields
variableName

Name of the variable as Leonie sees it internally. User-defined variables get a prefix so they can't collide with temporaries the compiler generates.

realVariableName

The name that the user chose for a variable when defining it. Identical to variableName if not a user-defined variable.

bpRelativeAddress

The basepointer-relative address of this variable on the stack.

Discussion

This struct is used to keep information for the debugger and for error display about each local variable, and each global imported into local scope by adding a local variable containing a reference to the global.

See Also