From: John Hodge Date: Sat, 17 Sep 2011 10:21:24 +0000 (+0800) Subject: SpiderScript - Huge changes to introduce bytecode support X-Git-Tag: rel0.11~93 X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=1e45480331132c75898cbdd761ddd1fa48739e54;p=tpg%2Facess2.git SpiderScript - Huge changes to introduce bytecode support - Scripts can be compiled into bytecode (last I checked) - Still leaks memory - Bytecode does not yet run --- diff --git a/Usermode/Libraries/libspiderscript.so_src/ast.c b/Usermode/Libraries/libspiderscript.so_src/ast.c index 94d697cc..ce04195d 100644 --- a/Usermode/Libraries/libspiderscript.so_src/ast.c +++ b/Usermode/Libraries/libspiderscript.so_src/ast.c @@ -35,6 +35,7 @@ tAST_Function *AST_AppendFunction(tAST_Script *Script, const char *Name, int Ret strcpy(ret->Name, Name); ret->Code = NULL; ret->Arguments = NULL; + ret->ArgumentCount = 0; ret->ReturnType = ReturnType; if(Script->LastFunction == NULL) { @@ -57,6 +58,7 @@ void AST_AppendFunctionArg(tAST_Function *Function, tAST_Node *Node) Function->Arguments_Last->NextSibling = Node; Function->Arguments_Last = Node; } + Function->ArgumentCount ++; } /** @@ -193,6 +195,7 @@ size_t AST_WriteNode(void *Buffer, size_t Offset, tAST_Node *Node) // Looping Construct (For loop node) case NODETYPE_LOOP: WRITE_8(Buffer, Offset, Node->For.bCheckAfter); +// printf("Node %p, Loop Tag %p\n", Node, Node->For.Tag); WRITE_STR(Buffer, Offset, Node->For.Tag); Offset += AST_WriteNode(Buffer, Offset, Node->For.Init); Offset += AST_WriteNode(Buffer, Offset, Node->For.Condition); @@ -411,7 +414,7 @@ void AST_FreeNode(tAST_Node *Node) case NODETYPE_INTEGER: case NODETYPE_REAL: if( Node->ValueCache ) - Object_Dereference(Node->ValueCache); + SpiderScript_DereferenceValue(Node->ValueCache); Node->ValueCache = NULL; break; } diff --git a/Usermode/Libraries/libspiderscript.so_src/ast.h b/Usermode/Libraries/libspiderscript.so_src/ast.h index 67f77277..aa448625 100644 --- a/Usermode/Libraries/libspiderscript.so_src/ast.h +++ b/Usermode/Libraries/libspiderscript.so_src/ast.h @@ -97,6 +97,7 @@ struct sAST_Function tAST_Node *Code; //!< Function Code tAST_Node *Arguments; // HACKJOB (Only NODETYPE_DEFVAR is allowed) tAST_Node *Arguments_Last; + int ArgumentCount; char Name[]; //!< Function Name }; diff --git a/Usermode/Libraries/libspiderscript.so_src/ast_to_bytecode.c b/Usermode/Libraries/libspiderscript.so_src/ast_to_bytecode.c new file mode 100644 index 00000000..5cffd796 --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/ast_to_bytecode.c @@ -0,0 +1,575 @@ +/* + * SpiderScript Library + * + * AST to Bytecode Conversion + */ +#include +#include +#include +#include +#include "ast.h" +#include "bytecode_gen.h" +#include "bytecode_ops.h" + +#define TRACE_VAR_LOOKUPS 0 +#define TRACE_NODE_RETURNS 0 + +// === IMPORTS === +extern tSpiderFunction *gpExports_First; + +// === TYPES === +typedef struct sAST_BlockInfo +{ + struct sAST_BlockInfo *Parent; + void *Handle; + const char *Tag; + + int BreakTarget; + int ContinueTarget; +} tAST_BlockInfo; + +// === PROTOTYPES === +// Node Traversal + int AST_ConvertNode(tAST_BlockInfo *Block, tAST_Node *Node); +// Variables + int BC_Variable_Define(tAST_BlockInfo *Block, int Type, const char *Name); + int BC_Variable_SetValue(tAST_BlockInfo *Block, tAST_Node *VarNode); + int BC_Variable_GetValue(tAST_BlockInfo *Block, tAST_Node *VarNode); +// - Errors +void AST_RuntimeMessage(tAST_Node *Node, const char *Type, const char *Format, ...); +void AST_RuntimeError(tAST_Node *Node, const char *Format, ...); + +// === GLOBALS === +// int giNextBlockIdent = 1; + +// === CODE === +/** + * \brief Convert a function into bytecode + */ +tBC_Function *Bytecode_ConvertFunction(tAST_Function *ASTFcn) +{ + tBC_Function *ret; + tAST_BlockInfo bi = {0}; + + { + tAST_Node *arg; + int i; + char *arg_names[ASTFcn->ArgumentCount]; + int arg_types[ASTFcn->ArgumentCount]; + + for(arg = ASTFcn->Arguments; arg; arg = arg->NextSibling) + { + arg_names[i] = arg->DefVar.Name; + arg_types[i] = arg->DefVar.DataType; + } + + ret = Bytecode_CreateFunction(ASTFcn->Name, ASTFcn->ArgumentCount, arg_names, arg_types); + if(!ret) return NULL; + } + + bi.Handle = ret; + if( AST_ConvertNode(&bi, ASTFcn->Code) ) + { + Bytecode_DeleteFunction(ret); + return NULL; + } + return ret; +} + +/** + * \brief Convert a node into bytecode + * \param Block Execution context + * \param Node Node to execute + */ +int AST_ConvertNode(tAST_BlockInfo *Block, tAST_Node *Node) +{ + tAST_Node *node; + int ret = 0; + int i, op = 0; + + switch(Node->Type) + { + // No Operation + case NODETYPE_NOP: + break; + + // Code block + case NODETYPE_BLOCK: + Bytecode_AppendEnterContext(Block->Handle); // Create a new block + { + tAST_BlockInfo blockInfo; + blockInfo.Parent = Block; + blockInfo.Handle = Block->Handle; + blockInfo.ContinueTarget = 0; + blockInfo.BreakTarget = 0; + // Loop over all nodes, or until the return value is set + for(node = Node->Block.FirstChild; + node; + node = node->NextSibling ) + { + AST_ConvertNode(Block, node); + } + } + Bytecode_AppendLeaveContext(Block->Handle); // Leave this context + break; + + // Assignment + case NODETYPE_ASSIGN: + // TODO: Support assigning to object attributes + if( Node->Assign.Dest->Type != NODETYPE_VARIABLE ) { + AST_RuntimeError(Node, "LVALUE of assignment is not a variable"); + return -1; + } + ret = AST_ConvertNode(Block, Node->Assign.Value); + if(ret) return ret; + + // Perform assignment operation + if( Node->Assign.Operation != NODETYPE_NOP ) + { + + ret = BC_Variable_GetValue(Block, Node->Assign.Dest); + if(ret) return ret; + Bytecode_AppendBinOp(Block->Handle, Node->Assign.Operation); + } + + // Set the variable value + ret = BC_Variable_SetValue( Block, Node->Assign.Dest ); + break; + + // Post increment/decrement + case NODETYPE_POSTINC: + case NODETYPE_POSTDEC: + Bytecode_AppendConstInt(Block->Handle, 1); + + // TODO: Support assigning to object attributes + if( Node->UniOp.Value->Type != NODETYPE_VARIABLE ) { + AST_RuntimeError(Node, "LVALUE of assignment is not a variable"); + return -1; + } + + ret = BC_Variable_GetValue(Block, Node->UniOp.Value); + if(ret) return ret; + + if( Node->Type == NODETYPE_POSTDEC ) + Bytecode_AppendBinOp(Block->Handle, NODETYPE_SUBTRACT); + else + Bytecode_AppendBinOp(Block->Handle, NODETYPE_ADD); + if(ret) return ret; + + ret = BC_Variable_SetValue(Block, Node->UniOp.Value); + if(ret) return ret; + break; + + // Function Call + case NODETYPE_METHODCALL: + case NODETYPE_FUNCTIONCALL: + case NODETYPE_CREATEOBJECT: + i = 0; + for(node = Node->FunctionCall.FirstArg; node; node = node->NextSibling) + { + ret = AST_ConvertNode(Block, node); + if(ret) return ret; + i ++; + } + + // Call the function + if( Node->Type == NODETYPE_CREATEOBJECT ) + { + // TODO: Sanity check stack top + Bytecode_AppendCreateObj(Block->Handle, Node->FunctionCall.Name, i); + } + else if( Node->Type == NODETYPE_METHODCALL ) + { + // TODO: Sanity check stack top + ret = AST_ConvertNode(Block, Node->FunctionCall.Object); + if(ret) return ret; + Bytecode_AppendMethodCall(Block->Handle, Node->FunctionCall.Name, i); + } + else + { + Bytecode_AppendFunctionCall(Block->Handle, Node->FunctionCall.Name, i); + } + break; + + // Conditional + case NODETYPE_IF: { + int if_true, if_end; + ret = AST_ConvertNode(Block, Node->If.Condition); + if(ret) return ret; + + if_true = Bytecode_AllocateLabel(Block->Handle); + if_end = Bytecode_AllocateLabel(Block->Handle); + + Bytecode_AppendCondJump(Block->Handle, if_true); + + // False + ret = AST_ConvertNode(Block, Node->If.False); + if(ret) return ret; + Bytecode_AppendJump(Block->Handle, if_end); + + // True + Bytecode_SetLabel(Block->Handle, if_true); + ret = AST_ConvertNode(Block, Node->If.True); + if(ret) return ret; + + // End + Bytecode_SetLabel(Block->Handle, if_end); + } break; + + // Loop + case NODETYPE_LOOP: { + int loop_start, loop_end; + int saved_break, saved_continue; + const char *saved_tag; + + // Initialise + ret = AST_ConvertNode(Block, Node->For.Init); + if(ret) return ret; + + loop_start = Bytecode_AllocateLabel(Block->Handle); + loop_end = Bytecode_AllocateLabel(Block->Handle); + + saved_break = Block->BreakTarget; + saved_continue = Block->ContinueTarget; + saved_tag = Block->Tag; + Block->BreakTarget = loop_end; + Block->ContinueTarget = loop_end; + Block->Tag = Node->For.Tag; + + Bytecode_SetLabel(Block->Handle, loop_start); + + // Check initial condition + if( !Node->For.bCheckAfter ) + { + ret = AST_ConvertNode(Block, Node->For.Condition); + if(ret) return ret; + Bytecode_AppendUniOp(Block->Handle, NODETYPE_LOGICALNOT); + Bytecode_AppendCondJump(Block->Handle, loop_end); + } + + // Code + ret = AST_ConvertNode(Block, Node->For.Code); + if(ret) return ret; + + // Increment + ret = AST_ConvertNode(Block, Node->For.Increment); + if(ret) return ret; + + // Tail check + if( Node->For.bCheckAfter ) + { + ret = AST_ConvertNode(Block, Node->For.Condition); + if(ret) return ret; + Bytecode_AppendCondJump(Block->Handle, loop_start); + } + + Bytecode_SetLabel(Block->Handle, loop_end); + + Block->BreakTarget = saved_break; + Block->ContinueTarget = saved_continue; + Block->Tag = saved_tag; + } break; + + // Return + case NODETYPE_RETURN: + ret = AST_ConvertNode(Block, Node->UniOp.Value); + if(ret) return ret; + Bytecode_AppendReturn(Block->Handle); + break; + + case NODETYPE_BREAK: + case NODETYPE_CONTINUE: { + tAST_BlockInfo *bi = Block; + if( Node->Variable.Name[0] ) { + while(bi && strcmp(bi->Tag, Node->Variable.Name) == 0) bi = bi->Parent; + } + if( !bi ) return 1; + // TODO: Check if BreakTarget/ContinueTarget are valid + if( Node->Type == NODETYPE_BREAK ) + Bytecode_AppendJump(Block->Handle, bi->BreakTarget); + else + Bytecode_AppendJump(Block->Handle, bi->ContinueTarget); + } break; + + // Define a variable + case NODETYPE_DEFVAR: + ret = BC_Variable_Define(Block, Node->DefVar.DataType, Node->DefVar.Name); + if(ret) return ret; + + if( Node->DefVar.InitialValue ) + { + ret = AST_ConvertNode(Block, Node->DefVar.InitialValue); + if(ret) return ret; + Bytecode_AppendSaveVar(Block->Handle, Node->DefVar.Name); + } + break; + + // Scope + case NODETYPE_SCOPE: + Bytecode_AppendSubNamespace(Block->Handle, Node->Scope.Name); + ret = AST_ConvertNode(Block, Node->Scope.Element); + break; + + // Variable + case NODETYPE_VARIABLE: + ret = BC_Variable_GetValue( Block, Node ); + break; + + // Element of an Object + case NODETYPE_ELEMENT: + ret = AST_ConvertNode( Block, Node->Scope.Element ); + if(ret) return ret; + + Bytecode_AppendElement(Block->Handle, Node->Scope.Name); + break; + + // Cast a value to another + case NODETYPE_CAST: + ret = AST_ConvertNode(Block, Node->Cast.Value); + if(ret) return ret; + Bytecode_AppendCast(Block->Handle, Node->Cast.DataType); + break; + + // Index into an array + case NODETYPE_INDEX: + ret = AST_ConvertNode(Block, Node->BinOp.Left); // Array + if(ret) return ret; + ret = AST_ConvertNode(Block, Node->BinOp.Right); // Offset + if(ret) return ret; + + Bytecode_AppendIndex(Block->Handle); + break; + + // TODO: Implement runtime constants + case NODETYPE_CONSTANT: + // TODO: Scan namespace for constant name + AST_RuntimeError(Node, "TODO - Runtime Constants"); + ret = -1; + break; + + // Constant Values + case NODETYPE_STRING: + Bytecode_AppendConstString(Block->Handle, Node->Constant.String.Data, Node->Constant.String.Length); + break; + case NODETYPE_INTEGER: + Bytecode_AppendConstInt(Block->Handle, Node->Constant.Integer); + break; + case NODETYPE_REAL: + Bytecode_AppendConstInt(Block->Handle, Node->Constant.Real); + break; + + // --- Operations --- + // Boolean Operations + case NODETYPE_LOGICALNOT: // Logical NOT (!) + if(!op) op = BC_OP_LOGICNOT; + case NODETYPE_BWNOT: // Bitwise NOT (~) + if(!op) op = BC_OP_BITNOT; + case NODETYPE_NEGATE: // Negation (-) + if(!op) op = BC_OP_NEG; + ret = AST_ConvertNode(Block, Node->UniOp.Value); + if(ret) return ret; + Bytecode_AppendUniOp(Block->Handle, op); + break; + + case NODETYPE_LOGICALAND: // Logical AND (&&) + if(!op) op = BC_OP_LOGICAND; + case NODETYPE_LOGICALOR: // Logical OR (||) + if(!op) op = BC_OP_LOGICOR; + case NODETYPE_LOGICALXOR: // Logical XOR (^^) + if(!op) op = BC_OP_LOGICXOR; + // Comparisons + case NODETYPE_EQUALS: if(!op) op = BC_OP_EQUALS; + case NODETYPE_LESSTHAN: if(!op) op = BC_OP_LESSTHAN; + case NODETYPE_GREATERTHAN: if(!op) op = BC_OP_GREATERTHAN; + case NODETYPE_LESSTHANEQUAL: if(!op) op = BC_OP_LESSTHANOREQUAL; + case NODETYPE_GREATERTHANEQUAL: if(!op) op = BC_OP_GREATERTHANOREQUAL; + // General Binary Operations + case NODETYPE_ADD: if(!op) op = BC_OP_ADD; + case NODETYPE_SUBTRACT: if(!op) op = BC_OP_SUBTRACT; + case NODETYPE_MULTIPLY: if(!op) op = BC_OP_MULTIPLY; + case NODETYPE_DIVIDE: if(!op) op = BC_OP_DIVIDE; + case NODETYPE_MODULO: if(!op) op = BC_OP_MODULO; + case NODETYPE_BWAND: if(!op) op = BC_OP_BITAND; + case NODETYPE_BWOR: if(!op) op = BC_OP_BITOR; + case NODETYPE_BWXOR: if(!op) op = BC_OP_BITXOR; + case NODETYPE_BITSHIFTLEFT: if(!op) op = BC_OP_BITSHIFTLEFT; + case NODETYPE_BITSHIFTRIGHT: if(!op) op = BC_OP_BITSHIFTRIGHT; + case NODETYPE_BITROTATELEFT: if(!op) op = BC_OP_BITROTATELEFT; + ret = AST_ConvertNode(Block, Node->BinOp.Left); + if(ret) return ret; + ret = AST_ConvertNode(Block, Node->BinOp.Right); + if(ret) return ret; + + Bytecode_AppendBinOp(Block->Handle, op); + break; + + //default: + // ret = NULL; + // AST_RuntimeError(Node, "BUG - SpiderScript AST_ConvertNode Unimplemented %i", Node->Type); + // break; + } + + #if TRACE_NODE_RETURNS + if(ret && ret != ERRPTR) { + AST_RuntimeError(Node, "Ret type of %p %i is %i", Node, Node->Type, ret->Type); + } + else { + AST_RuntimeError(Node, "Ret type of %p %i is %p", Node, Node->Type, ret); + } + #endif + + return ret; +} + +/** + * \brief Define a variable + * \param Block Current block state + * \param Type Type of the variable + * \param Name Name of the variable + * \return Boolean Failure + */ +int BC_Variable_Define(tAST_BlockInfo *Block, int Type, const char *Name) +{ + #if 0 + tAST_Variable *var, *prev = NULL; + + for( var = Block->FirstVar; var; prev = var, var = var->Next ) + { + if( strcmp(var->Name, Name) == 0 ) { + AST_RuntimeError(NULL, "Redefinition of variable '%s'", Name); + return ERRPTR; + } + } + + var = malloc( sizeof(tAST_Variable) + strlen(Name) + 1 ); + var->Next = NULL; + var->Type = Type; + strcpy(var->Name, Name); + + if(prev) prev->Next = var; + else Block->FirstVar = var; + + return var; + #else + Bytecode_AppendDefineVar(Block->Handle, Name, Type); + return 0; + #endif +} + +tAST_Variable *BC_Variable_Lookup(tAST_BlockInfo *Block, tAST_Node *VarNode, int CreateType) +{ + #if 0 + tAST_Variable *var = NULL; + + // Speed hack + if( VarNode->BlockState == Block && VarNode->BlockIdent == Block->Ident ) { + var = VarNode->ValueCache; + #if TRACE_VAR_LOOKUPS + AST_RuntimeMessage(VarNode, "debug", "Fast var fetch on '%s' %p (%p:%i)", + VarNode->Variable.Name, var, + VarNode->BlockState, VarNode->BlockIdent + ); + #endif + } + else + { + tAST_BlockInfo *bs; + for( bs = Block; bs; bs = bs->Parent ) + { + for( var = bs->FirstVar; var; var = var->Next ) + { + if( strcmp(var->Name, VarNode->Variable.Name) == 0 ) + break; + } + if(var) break; + } + + if( !var ) + { + if( Block->Script->Variant->bDyamicTyped && CreateType != SS_DATATYPE_UNDEF ) { + // Define variable + var = BC_Variable_Define(Block, CreateType, VarNode->Variable.Name, NULL); + } + else + { + AST_RuntimeError(VarNode, "Variable '%s' is undefined", VarNode->Variable.Name); + return NULL; + } + } + + #if TRACE_VAR_LOOKUPS + AST_RuntimeMessage(VarNode, "debug", "Saved variable lookup of '%s' %p (%p:%i)", + VarNode->Variable.Name, var, + Block, Block->Ident); + #endif + + VarNode->ValueCache = var; + VarNode->BlockState = Block; + VarNode->BlockIdent = Block->Ident; + } + + return var; + #else + return (void*)1; + #endif +} + +/** + * \brief Set the value of a variable + * \return Boolean Failure + */ +int BC_Variable_SetValue(tAST_BlockInfo *Block, tAST_Node *VarNode) +{ + tAST_Variable *var; + + var = BC_Variable_Lookup(Block, VarNode, SS_DATATYPE_UNDEF); + if(!var) return -1; + + // TODO: Check types + + Bytecode_AppendSaveVar(Block->Handle, VarNode->Variable.Name); + return 0; +} + +/** + * \brief Get the value of a variable + */ +int BC_Variable_GetValue(tAST_BlockInfo *Block, tAST_Node *VarNode) +{ + tAST_Variable *var; + + var = BC_Variable_Lookup(Block, VarNode, 0); + if(!var) return -1; + + Bytecode_AppendLoadVar(Block->Handle, VarNode->Variable.Name); + return 0; +} + +#if 0 +void AST_RuntimeMessage(tAST_Node *Node, const char *Type, const char *Format, ...) +{ + va_list args; + + if(Node) { + fprintf(stderr, "%s:%i: ", Node->File, Node->Line); + } + fprintf(stderr, "%s: ", Type); + va_start(args, Format); + vfprintf(stderr, Format, args); + va_end(args); + fprintf(stderr, "\n"); +} +void AST_RuntimeError(tAST_Node *Node, const char *Format, ...) +{ + va_list args; + + if(Node) { + fprintf(stderr, "%s:%i: ", Node->File, Node->Line); + } + fprintf(stderr, "error: "); + va_start(args, Format); + vfprintf(stderr, Format, args); + va_end(args); + fprintf(stderr, "\n"); +} +#endif diff --git a/Usermode/Libraries/libspiderscript.so_src/bytecode_gen.c b/Usermode/Libraries/libspiderscript.so_src/bytecode_gen.c new file mode 100644 index 00000000..8ae7f32a --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/bytecode_gen.c @@ -0,0 +1,459 @@ +/* + * SpiderScript Library + * by John Hodge (thePowersGang) + * + * bytecode_gen.c + * - Generate bytecode + */ +#include +#include +#include "bytecode_ops.h" +#include +#include "bytecode_gen.h" +#include + +// === IMPORTS === + +// === STRUCTURES === +typedef struct sBC_Op tBC_Op; + +struct sBC_Op +{ + tBC_Op *Next; + int Operation; + int bUseInteger; + union { + struct { + const char *String; + int Integer; + } StringInt; + + uint64_t Integer; + double Real; + } Content; +}; + +struct sBC_Function +{ + const char *Name; + + int LabelCount; + int LabelSpace; + tBC_Op **Labels; + + int MaxVariableCount; + // NOTE: These fields are invalid after compilation + int VariableCount; + int VariableSpace; + const char **VariableNames; // Only type needs to be stored + int CurContextDepth; // Used to get the real var count + + int OperationCount; + tBC_Op *Operations; + tBC_Op *OperationsEnd; + + int ArgumentCount; + struct { + char *Name; + int Type; + } Arguments[]; +}; + +// === PROTOTYPES === +tBC_Op *Bytecode_int_AllocateOp(int Operation); + +// === GLOBALS === + +// === CODE === +tBC_Op *Bytecode_int_AllocateOp(int Operation) +{ + tBC_Op *ret; + + ret = malloc(sizeof(tBC_Op)); + if(!ret) return NULL; + + ret->Next = NULL; + ret->Operation = Operation; + ret->bUseInteger = 0; + + return ret; +} + +tBC_Function *Bytecode_CreateFunction(const char *Name, int ArgCount, char **ArgNames, int *ArgTypes) +{ + tBC_Function *ret; + int i; + + ret = malloc(sizeof(tBC_Function) + ArgCount*sizeof(ret->Arguments[0])); + if(!ret) return NULL; + + ret->Name = Name; + ret->LabelSpace = ret->LabelCount = 0; + ret->Labels = NULL; + + ret->VariableCount = ret->VariableSpace = 0; + ret->VariableNames = NULL; + + ret->OperationCount = 0; + ret->Operations = NULL; + ret->OperationsEnd = (void*)&ret->Operations; + + ret->ArgumentCount = ArgCount; + for( i = 0; i < ArgCount; i ++ ) + { + ret->Arguments[i].Name = strdup(ArgNames[i]); + ret->Arguments[i].Type = ArgTypes[i]; + } + + return ret; +} + +void Bytecode_DeleteFunction(tBC_Function *Fcn) +{ + tBC_Op *op; + int i; + for( i = 0; i < Fcn->ArgumentCount; i ++ ) + { + free(Fcn->Arguments[i].Name); + } + for( op = Fcn->Operations; op; ) + { + tBC_Op *nextop = op->Next; + free(op); + op = nextop; + } + free(Fcn->VariableNames); + free(Fcn->Labels); + free(Fcn); +} + +int StringList_GetString(tStringList *List, const char *String, int Length) +{ + int strIdx = 0; + tString *ent; + for(ent = List->Head; ent; ent = ent->Next, strIdx ++) + { + if(ent->Length == Length && memcmp(ent->Data, String, Length) == 0) break; + } + if( ent ) { + ent->RefCount ++; + } + else { + ent = malloc(sizeof(tString) + Length + 1); + if(!ent) return -1; + ent->Next = NULL; + ent->Length = Length; + ent->RefCount = 1; + memcpy(ent->Data, String, Length); + ent->Data[Length] = '\0'; + + if(List->Head) + List->Tail->Next = ent; + else + List->Head = ent; + List->Tail = ent; + List->Count ++; + } + return strIdx; +} + +int Bytecode_int_Serialize(const tBC_Function *Function, void *Output, int *LabelOffsets, tStringList *Strings) +{ + tBC_Op *op; + int len = 0, idx = 0; + int i; + + void _put_byte(uint8_t byte) + { + uint8_t *buf = Output; + if(Output) buf[len] = byte; + len ++; + } + + void _put_dword(uint32_t value) + { + uint8_t *buf = Output; + if(Output) { + buf[len+0] = value & 0xFF; + buf[len+1] = value >> 8; + buf[len+2] = value >> 16; + buf[len+3] = value >> 24; + } + len += 4; + } + + void _put_qword(uint64_t value) + { + _put_dword(value & 0xFFFFFFFF); + _put_dword(value >> 32); + } + + void _put_double(double value) + { + // TODO: Machine agnostic + if(Output) { + *(double*)( (char*)Output + len ) = value; + } + len += sizeof(double); + } + + void _put_string(const char *str, int len) + { + int strIdx = 0; + if( Output ) { + strIdx = StringList_GetString(Strings, str, len); + } + + // TODO: Relocations + _put_dword(strIdx); + } + + for( op = Function->Operations; op; op = op->Next, idx ++ ) + { + // If first run, convert labels into byte offsets + if( !Output ) + { + for( i = 0; i < Function->LabelCount; i ++ ) + { + if(LabelOffsets[i]) continue; + if(op != Function->Labels[i]) continue; + + LabelOffsets[i] = len; + } + } + + _put_byte(op->Operation); + switch(op->Operation) + { + // Relocate jumps (the value only matters if `Output` is non-NULL) + case BC_OP_JUMP: + case BC_OP_JUMPIF: + case BC_OP_JUMPIFNOT: + // TODO: Relocations? + _put_dword( LabelOffsets[op->Content.StringInt.Integer] ); + break; + // Special case for inline values + case BC_OP_LOADINT: + _put_qword(op->Content.Integer); + break; + case BC_OP_LOADREAL: + _put_double(op->Content.Real); + break; + case BC_OP_LOADSTR: + _put_string(op->Content.StringInt.String, op->Content.StringInt.Integer); + break; + // Everthing else just gets handled nicely + default: + if( op->Content.StringInt.String ) + _put_string(op->Content.StringInt.String, strlen(op->Content.StringInt.String)); + if( op->bUseInteger ) + _put_dword(op->Content.StringInt.Integer); + break; + } + } + + return len; +} + +char *Bytecode_SerialiseFunction(const tBC_Function *Function, int *Length, tStringList *Strings) +{ + int len; + int *label_offsets; + char *code; + + label_offsets = calloc( sizeof(int), Function->LabelCount ); + if(!label_offsets) return NULL; + + len = Bytecode_int_Serialize(Function, NULL, label_offsets, Strings); + + code = malloc(len); + + Bytecode_int_Serialize(Function, code, label_offsets, Strings); + + free(label_offsets); + + *Length = len; + + return code; +} + +int Bytecode_AllocateLabel(tBC_Function *Handle) +{ + int ret; + + if( Handle->LabelCount == Handle->LabelSpace ) { + void *tmp; + Handle->LabelSpace += 20; // TODO: Don't hardcode increment + tmp = realloc(Handle->Labels, Handle->LabelSpace * sizeof(Handle->Labels[0])); + if( !tmp ) { + Handle->LabelSpace -= 20; + return -1; + } + Handle->Labels = tmp; + } + ret = Handle->LabelCount ++; + Handle->Labels[ret] = 0; + return ret; +} + +void Bytecode_SetLabel(tBC_Function *Handle, int Label) +{ + if(Label < 0) return ; + + if(Label >= Handle->LabelCount) return ; + + Handle->Labels[Label] = Handle->OperationsEnd; + return ; +} + +void Bytecode_int_AppendOp(tBC_Function *Fcn, tBC_Op *Op) +{ + Op->Next = NULL; + if( Fcn->Operations ) + Fcn->OperationsEnd->Next = Op; + else + Fcn->Operations = Op; + Fcn->OperationsEnd = Op; +} + +void Bytecode_int_AddVariable(tBC_Function *Handle, const char *Name) +{ + if(Handle->VariableCount == Handle->VariableSpace) { + void *tmp; + Handle->VariableSpace += 10; + tmp = realloc(Handle->VariableNames, Handle->VariableSpace * sizeof(Handle->VariableNames[0])); + if(!tmp) return ; // TODO: Error + Handle->VariableNames = tmp; + } + Handle->VariableNames[Handle->VariableCount] = Name; + Handle->VariableCount ++; + // Get max count (used when executing to get the frame size) + if(Handle->VariableCount - Handle->CurContextDepth >= Handle->MaxVariableCount) + Handle->MaxVariableCount = Handle->VariableCount - Handle->CurContextDepth; +} + +int Bytecode_int_GetVarIndex(tBC_Function *Handle, const char *Name) +{ + int i; + // Get the start of this context + for( i = Handle->VariableCount; i --; ) + { + if( Handle->VariableNames[i] == NULL ) break; + } + // Check for duplicate allocation + for( ; i < Handle->VariableCount; i ++ ) + { + if( strcmp(Name, Handle->VariableNames[i]) == 0 ) + return i; + } + return -1; +} + +#define DEF_BC_NONE(_op) { \ + tBC_Op *op = Bytecode_int_AllocateOp(_op); \ + op->Content.Integer = 0; \ + op->bUseInteger = 0; \ + Bytecode_int_AppendOp(Handle, op);\ +} + +#define DEF_BC_STRINT(_op, _str, _int) { \ + tBC_Op *op = Bytecode_int_AllocateOp(_op);\ + op->Content.StringInt.Integer = _int;\ + op->Content.StringInt.String = _str;\ + op->bUseInteger = 1;\ + Bytecode_int_AppendOp(Handle, op);\ +} +#define DEF_BC_STR(_op, _str) {\ + tBC_Op *op = Bytecode_int_AllocateOp(_op);\ + op->Content.StringInt.String = _str;\ + op->bUseInteger = 0;\ + Bytecode_int_AppendOp(Handle, op);\ +} + +// --- Flow Control +void Bytecode_AppendJump(tBC_Function *Handle, int Label) + DEF_BC_STRINT(BC_OP_JUMP, NULL, Label) +void Bytecode_AppendCondJump(tBC_Function *Handle, int Label) + DEF_BC_STRINT(BC_OP_JUMPIF, NULL, Label) +void Bytecode_AppendReturn(tBC_Function *Handle) + DEF_BC_NONE(BC_OP_RETURN); + +// --- Variables +void Bytecode_AppendLoadVar(tBC_Function *Handle, const char *Name) + DEF_BC_STRINT(BC_OP_LOADVAR, NULL, Bytecode_int_GetVarIndex(Handle, Name)) +// DEF_BC_STR(BC_OP_LOADVAR, Name) +void Bytecode_AppendSaveVar(tBC_Function *Handle, const char *Name) // (Obj->)?var = + DEF_BC_STRINT(BC_OP_SAVEVAR, NULL, Bytecode_int_GetVarIndex(Handle, Name)) +// DEF_BC_STR(BC_OP_SAVEVAR, Name) + +// --- Constants +void Bytecode_AppendConstInt(tBC_Function *Handle, uint64_t Value) +{ + tBC_Op *op = Bytecode_int_AllocateOp(BC_OP_LOADINT); + op->Content.Integer = Value; + Bytecode_int_AppendOp(Handle, op); +} +void Bytecode_AppendConstReal(tBC_Function *Handle, double Value) +{ + tBC_Op *op = Bytecode_int_AllocateOp(BC_OP_LOADREAL); + op->Content.Real = Value; + Bytecode_int_AppendOp(Handle, op); +} +void Bytecode_AppendConstString(tBC_Function *Handle, const void *Data, size_t Length) + DEF_BC_STRINT(BC_OP_LOADSTR, Data, Length) + +// --- Indexing / Scoping +void Bytecode_AppendSubNamespace(tBC_Function *Handle, const char *Name) + DEF_BC_STR(BC_OP_SCOPE, Name) +void Bytecode_AppendElement(tBC_Function *Handle, const char *Name) + DEF_BC_STR(BC_OP_ELEMENT, Name) +void Bytecode_AppendIndex(tBC_Function *Handle) + DEF_BC_NONE(BC_OP_INDEX) + +void Bytecode_AppendCreateObj(tBC_Function *Handle, const char *Name, int ArgumentCount) + DEF_BC_STRINT(BC_OP_CREATEOBJ, Name, ArgumentCount) +void Bytecode_AppendMethodCall(tBC_Function *Handle, const char *Name, int ArgumentCount) + DEF_BC_STRINT(BC_OP_CALLMETHOD, Name, ArgumentCount) +void Bytecode_AppendFunctionCall(tBC_Function *Handle, const char *Name, int ArgumentCount) + DEF_BC_STRINT(BC_OP_CALLFUNCTION, Name, ArgumentCount) + +void Bytecode_AppendBinOp(tBC_Function *Handle, int Operation) + DEF_BC_NONE(Operation) +void Bytecode_AppendUniOp(tBC_Function *Handle, int Operation) + DEF_BC_NONE(Operation) +void Bytecode_AppendCast(tBC_Function *Handle, int Type) + DEF_BC_STRINT(BC_OP_CAST, NULL, Type) + +// Does some bookeeping to allocate variable slots at compile time +void Bytecode_AppendEnterContext(tBC_Function *Handle) +{ + Handle->CurContextDepth ++; + Bytecode_int_AddVariable(Handle, NULL); // NULL to record the extent of this + + DEF_BC_NONE(BC_OP_ENTERCONTEXT) +} +void Bytecode_AppendLeaveContext(tBC_Function *Handle) +{ + int i; + for( i = Handle->VariableCount; i --; ) + { + if( Handle->VariableNames[i] == NULL ) break; + } + Handle->CurContextDepth --; + + DEF_BC_NONE(BC_OP_LEAVECONTEXT) +} +//void Bytecode_AppendImportNamespace(tBC_Function *Handle, const char *Name); +// DEF_BC_STRINT(BC_OP_IMPORTNS, Name, 0) +void Bytecode_AppendDefineVar(tBC_Function *Handle, const char *Name, int Type) +{ + #if 1 + // Check for duplicates + if( Bytecode_int_GetVarIndex(Handle, Name) ) + return ; // TODO: Error + #endif + + Bytecode_int_AddVariable(Handle, Name); + + DEF_BC_STRINT(BC_OP_DEFINEVAR, Name, Type) +} diff --git a/Usermode/Libraries/libspiderscript.so_src/bytecode_gen.h b/Usermode/Libraries/libspiderscript.so_src/bytecode_gen.h new file mode 100644 index 00000000..b2f19cf2 --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/bytecode_gen.h @@ -0,0 +1,77 @@ +/* + * SpiderScript Library + * - By John Hodge (thePowersGang) + * + * bytecode_gen.h + * - Bytecode Generation header + */ +#ifndef _BYTECODE_GEN_H_ +#define _BYTECODE_GEN_H_ + +#include "ast.h" + +typedef struct sBC_Function tBC_Function; +typedef struct sStringList tStringList; +typedef struct sString tString; +//typedef struct sAST_Function tAST_Function; + +struct sString +{ + tString *Next; + int Length; + int RefCount; + char Data[]; +}; + +struct sStringList +{ + tString *Head; + tString *Tail; + int Count; +}; + + +extern int Bytecode_ConvertScript(tSpiderScript *Script, const char *DestFile); +extern tBC_Function *Bytecode_ConvertFunction(tAST_Function *ASTFcn); +extern tBC_Function *Bytecode_NewBlankFunction(void); +extern void Bytecode_DeleteFunction(tBC_Function *Fcn); + +extern char *Bytecode_SerialiseFunction(const tBC_Function *Function, int *Length, tStringList *Strings); +extern int StringList_GetString(tStringList *List, const char *String, int Length); +extern tBC_Function *Bytecode_CreateFunction(const char *Name, int ArgCount, char **ArgNames, int *ArgTypes); + +extern int Bytecode_AllocateLabel(tBC_Function *Handle); +extern void Bytecode_SetLabel(tBC_Function *Handle, int Label); +// Bytecode adding +// - Flow Control +extern void Bytecode_AppendJump(tBC_Function *Handle, int Label); +extern void Bytecode_AppendCondJump(tBC_Function *Handle, int Label); +extern void Bytecode_AppendReturn(tBC_Function *Handle); +// - Operation Stack +// > Load/Store +extern void Bytecode_AppendLoadVar(tBC_Function *Handle, const char *Name); +extern void Bytecode_AppendSaveVar(tBC_Function *Handle, const char *Name); // (Obj->)?var = +extern void Bytecode_AppendConstInt(tBC_Function *Handle, uint64_t Value); +extern void Bytecode_AppendConstReal(tBC_Function *Handle, double Value); +extern void Bytecode_AppendConstString(tBC_Function *Handle, const void *Data, size_t Length); +// > Scoping +extern void Bytecode_AppendSubNamespace(tBC_Function *Handle, const char *Name); // Pop NS from stack, go to child, push ("" = root) +extern void Bytecode_AppendElement(tBC_Function *Handle, const char *Name); // Obj->SubObj +extern void Bytecode_AppendIndex(tBC_Function *Handle); // Index into an array +// > Function Calls +extern void Bytecode_AppendCreateObj(tBC_Function *Handle, const char *Name, int ArgumentCount); +extern void Bytecode_AppendMethodCall(tBC_Function *Handle, const char *Name, int ArgumentCount); +extern void Bytecode_AppendFunctionCall(tBC_Function *Handle, const char *Name, int ArgumentCount); +// > Manipulation +extern void Bytecode_AppendBinOp(tBC_Function *Handle, int Operation); +extern void Bytecode_AppendUniOp(tBC_Function *Handle, int Operation); +extern void Bytecode_AppendCast(tBC_Function *Handlde, int Type); +// - Context +// TODO: Are contexts needed? Should variables be allocated like labels? +extern void Bytecode_AppendEnterContext(tBC_Function *Handle); +extern void Bytecode_AppendLeaveContext(tBC_Function *Handle); +//extern void Bytecode_AppendImportNamespace(tBC_Function *Handle, const char *Name); +extern void Bytecode_AppendDefineVar(tBC_Function *Handle, const char *Name, int Type); + +#endif + diff --git a/Usermode/Libraries/libspiderscript.so_src/bytecode_makefile.c b/Usermode/Libraries/libspiderscript.so_src/bytecode_makefile.c new file mode 100644 index 00000000..a4c9b717 --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/bytecode_makefile.c @@ -0,0 +1,127 @@ +/* + * SpiderScript Library + * by John Hodge (thePowersGang) + * + * bytecode_makefile.c + * - Generate a bytecode file + */ +#include +#include "ast.h" +#include "bytecode_gen.h" +#include +#include + +// === IMPORTS === + +// === PROTOTYPES === + +// === GLOBALS === + +// === CODE === +int Bytecode_ConvertScript(tSpiderScript *Script, const char *DestFile) +{ + tStringList strings = {0}; + tAST_Function *fcn; + FILE *fp; + int fcn_hdr_offset = 0; + int fcn_count = 0; + int strtab_ofs; + + void _put8(uint8_t val) + { + fwrite(&val, 1, 1, fp); + } + void _put32(uint32_t val) + { + _put8(val & 0xFF); + _put8(val >> 8); + _put8(val >> 16); + _put8(val >> 24); + } + + fp = fopen(DestFile, "wb"); + if(!fp) return 1; + + // Create header + fwrite("SSBC\r\n\xBC\x55", 8, 1, fp); + _put32(0); // Function count, to be filled + _put32(0); // String count + _put32(0); // String table offset + // TODO: Variant info + + fcn_hdr_offset = ftell(fp); + + // Create function descriptors + for(fcn = Script->Script->Functions; fcn; fcn = fcn->Next, fcn_count ++) + { + tAST_Node *arg; + + _put32( StringList_GetString(&strings, fcn->Name, strlen(fcn->Name)) ); + _put32( 0 ); // Code offset + _put8( fcn->ReturnType ); + + if(fcn->ArgumentCount > 255) { + // ERROR: Too many args + return 2; + } + _put8( fcn->ArgumentCount ); + + // Argument types? + for(arg = fcn->Arguments; arg; arg = arg->NextSibling) + { + _put32( StringList_GetString(&strings, arg->DefVar.Name, strlen(arg->DefVar.Name)) ); + _put8( arg->DefVar.DataType ); + } + } + + // Put function code in + for(fcn = Script->Script->Functions; fcn; fcn = fcn->Next) + { + char *code; + int len, code_pos; + tBC_Function *bc_fcn; + + // Fix header + code_pos = ftell(fp); + fseek(fp, SEEK_SET, fcn_hdr_offset + 4); + _put32( code_pos ); + fseek(fp, SEEK_SET, code_pos ); + + fcn_hdr_offset += 4+4+1+1+(4+1)*fcn->ArgumentCount; + + // Write code + bc_fcn = Bytecode_ConvertFunction(fcn); + code = Bytecode_SerialiseFunction(bc_fcn, &len, &strings); + Bytecode_DeleteFunction(bc_fcn); + fwrite(code, len, 1, fp); + } + + // String table + strtab_ofs = ftell(fp); + { + int string_offset = strtab_ofs + (4+4)*strings.Count; + tString *str; + // Array + for(str = strings.Head; str; str = str->Next) + { + _put32(str->Length); + _put32(string_offset); + string_offset += str->Length + 1; + } + // Data + for(str = strings.Head; str; str = str->Next) + { + fwrite(str->Data, str->Length, 1, fp); + _put8(0); + } + } + + // Fix header + fseek(fp, 8, SEEK_SET); + _put32(fcn_count); + _put32(strings.Count); + _put32(strtab_ofs); + + return 0; +} + diff --git a/Usermode/Libraries/libspiderscript.so_src/bytecode_ops.h b/Usermode/Libraries/libspiderscript.so_src/bytecode_ops.h new file mode 100644 index 00000000..420684c9 --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/bytecode_ops.h @@ -0,0 +1,66 @@ +/** + */ +#ifndef _BYTECODE_OPS_H_ +#define _BYTECODE_OPS_H_ + +enum eBC_Ops +{ + BC_OP_NOP, + + BC_OP_JUMP, + BC_OP_JUMPIF, + BC_OP_JUMPIFNOT, + + BC_OP_RETURN, + BC_OP_CALLFUNCTION, + BC_OP_CALLMETHOD, + BC_OP_CREATEOBJ, + + BC_OP_LOADVAR, + BC_OP_SAVEVAR, + + BC_OP_LOADINT, + BC_OP_LOADREAL, + BC_OP_LOADSTR, + + BC_OP_CAST, + + BC_OP_SCOPE, + BC_OP_ELEMENT, + BC_OP_INDEX, + + BC_OP_ENTERCONTEXT, + BC_OP_LEAVECONTEXT, + BC_OP_DEFINEVAR, + + // Operations + BC_OP_LOGICNOT, + BC_OP_LOGICAND, + BC_OP_LOGICOR, + BC_OP_LOGICXOR, + + BC_OP_BITNOT, + BC_OP_BITAND, + BC_OP_BITOR, + BC_OP_BITXOR, + + BC_OP_BITSHIFTLEFT, + BC_OP_BITSHIFTRIGHT, + BC_OP_BITROTATELEFT, + + BC_OP_NEG, + BC_OP_ADD, + BC_OP_SUBTRACT, + BC_OP_MULTIPLY, + BC_OP_DIVIDE, + BC_OP_MODULO, + + BC_OP_EQUALS, + BC_OP_NOTEQUALS, + BC_OP_LESSTHAN, + BC_OP_LESSTHANOREQUAL, + BC_OP_GREATERTHAN, + BC_OP_GREATERTHANOREQUAL +}; + +#endif diff --git a/Usermode/Libraries/libspiderscript.so_src/bytecode_optimise.c b/Usermode/Libraries/libspiderscript.so_src/bytecode_optimise.c new file mode 100644 index 00000000..3da6aa5a --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/bytecode_optimise.c @@ -0,0 +1,19 @@ +/* + * SpiderScript Library + * by John Hodge (thePowersGang) + * + * bytecode_gen.c + * - Generate bytecode + */ +#include +#include "bytecode_ops.h" + +// Patterns: +// TODO: Figure out what optimisations can be done + +int Bytecode_OptimizeFunction(tBC_Function *Function) +{ + for( op = Function->Operations; op; op = op->Next, idx ++ ) + { + } +} diff --git a/Usermode/Libraries/libspiderscript.so_src/exec_ast.c b/Usermode/Libraries/libspiderscript.so_src/exec_ast.c index 5ba15540..0c1a4021 100644 --- a/Usermode/Libraries/libspiderscript.so_src/exec_ast.c +++ b/Usermode/Libraries/libspiderscript.so_src/exec_ast.c @@ -16,16 +16,6 @@ extern tSpiderFunction *gpExports_First; // === PROTOTYPES === -// - Values -void Object_Dereference(tSpiderValue *Object); -void Object_Reference(tSpiderValue *Object); -tSpiderValue *SpiderScript_CreateInteger(uint64_t Value); -tSpiderValue *SpiderScript_CreateReal(double Value); -tSpiderValue *SpiderScript_CreateString(int Length, const char *Data); -tSpiderValue *SpiderScript_CastValueTo(int Type, tSpiderValue *Source); - int SpiderScript_IsValueTrue(tSpiderValue *Value); -void SpiderScript_FreeValue(tSpiderValue *Value); -char *SpiderScript_DumpValue(tSpiderValue *Value); // - Node Execution tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node); tSpiderValue *AST_ExecuteNode_BinOp(tAST_BlockState *Block, tAST_Node *Node, int Operation, tSpiderValue *Left, tSpiderValue *Right); @@ -43,365 +33,6 @@ void AST_RuntimeError(tAST_Node *Node, const char *Format, ...); int giNextBlockIdent = 1; // === CODE === -/** - * \brief Dereference a created object - */ -void Object_Dereference(tSpiderValue *Object) -{ - if(!Object) return ; - if(Object == ERRPTR) return ; - Object->ReferenceCount --; -// printf("%p Dereferenced (%i)\n", Object, Object->ReferenceCount); - if( Object->ReferenceCount == 0 ) { - switch( (enum eSpiderScript_DataTypes) Object->Type ) - { - case SS_DATATYPE_OBJECT: - Object->Object->Type->Destructor( Object->Object ); - break; - case SS_DATATYPE_OPAQUE: - Object->Opaque.Destroy( Object->Opaque.Data ); - break; - default: - break; - } - free(Object); - } -} - -void Object_Reference(tSpiderValue *Object) -{ - if(!Object) return ; - Object->ReferenceCount ++; -// printf("%p Referenced (%i)\n", Object, Object->ReferenceCount); -} - -/** - * \brief Allocate and initialise a SpiderScript object - */ -tSpiderObject *SpiderScript_AllocateObject(tSpiderObjectDef *Class, int ExtraBytes) -{ - int size = sizeof(tSpiderObject) + Class->NAttributes * sizeof(tSpiderValue*) + ExtraBytes; - tSpiderObject *ret = malloc(size); - - ret->Type = Class; - ret->ReferenceCount = 1; - ret->OpaqueData = &ret->Attributes[ Class->NAttributes ]; - memset( ret->Attributes, 0, Class->NAttributes * sizeof(tSpiderValue*) ); - - return ret; -} - -/** - * \brief Create an integer object - */ -tSpiderValue *SpiderScript_CreateInteger(uint64_t Value) -{ - tSpiderValue *ret = malloc( sizeof(tSpiderValue) ); - ret->Type = SS_DATATYPE_INTEGER; - ret->ReferenceCount = 1; - ret->Integer = Value; - return ret; -} - -/** - * \brief Create an real number object - */ -tSpiderValue *SpiderScript_CreateReal(double Value) -{ - tSpiderValue *ret = malloc( sizeof(tSpiderValue) ); - ret->Type = SS_DATATYPE_REAL; - ret->ReferenceCount = 1; - ret->Real = Value; - return ret; -} - -/** - * \brief Create an string object - */ -tSpiderValue *SpiderScript_CreateString(int Length, const char *Data) -{ - tSpiderValue *ret = malloc( sizeof(tSpiderValue) + Length + 1 ); - ret->Type = SS_DATATYPE_STRING; - ret->ReferenceCount = 1; - ret->String.Length = Length; - if( Data ) - memcpy(ret->String.Data, Data, Length); - else - memset(ret->String.Data, 0, Length); - ret->String.Data[Length] = '\0'; - return ret; -} - -/** - * \brief Concatenate two strings - */ -tSpiderValue *Object_StringConcat(const tSpiderValue *Str1, const tSpiderValue *Str2) -{ - int newLen = 0; - tSpiderValue *ret; - if(Str1) newLen += Str1->String.Length; - if(Str2) newLen += Str2->String.Length; - ret = malloc( sizeof(tSpiderValue) + newLen + 1 ); - ret->Type = SS_DATATYPE_STRING; - ret->ReferenceCount = 1; - ret->String.Length = newLen; - if(Str1) - memcpy(ret->String.Data, Str1->String.Data, Str1->String.Length); - if(Str2) { - if(Str1) - memcpy(ret->String.Data+Str1->String.Length, Str2->String.Data, Str2->String.Length); - else - memcpy(ret->String.Data, Str2->String.Data, Str2->String.Length); - } - ret->String.Data[ newLen ] = '\0'; - return ret; -} - -/** - * \brief Cast one object to another - * \brief Type Destination type - * \brief Source Input data - */ -tSpiderValue *SpiderScript_CastValueTo(int Type, tSpiderValue *Source) -{ - tSpiderValue *ret = ERRPTR; - int len = 0; - - if( !Source ) - { - switch(Type) - { - case SS_DATATYPE_INTEGER: return SpiderScript_CreateInteger(0); - case SS_DATATYPE_REAL: return SpiderScript_CreateReal(0); - case SS_DATATYPE_STRING: return SpiderScript_CreateString(4, "null"); - } - return NULL; - } - - // Check if anything needs to be done - if( Source->Type == Type ) { - Object_Reference(Source); - return Source; - } - - // Debug - #if 0 - { - printf("Casting %i ", Source->Type); - switch(Source->Type) - { - case SS_DATATYPE_INTEGER: printf("0x%lx", Source->Integer); break; - case SS_DATATYPE_STRING: printf("\"%s\"", Source->String.Data); break; - case SS_DATATYPE_REAL: printf("%f", Source->Real); break; - default: break; - } - printf(" to %i\n", Type); - } - #endif - - // Object casts - #if 0 - if( Source->Type == SS_DATATYPE_OBJECT ) - { - const char *name = NULL; - switch(Type) - { - case SS_DATATYPE_INTEGER: name = "cast Integer"; break; - case SS_DATATYPE_REAL: name = "cast Real"; break; - case SS_DATATYPE_STRING: name = "cast String"; break; - case SS_DATATYPE_ARRAY: name = "cast Array"; break; - default: - AST_RuntimeError(NULL, "Invalid cast to %i from Object", Type); - return ERRPTR; - } - if( fcnname ) - { - ret = Object_ExecuteMethod(Left->Object, fcnname, Right); - if( ret != ERRPTR ) - return ret; - // Fall through and try casting (which will usually fail) - } - } - #endif - - switch( (enum eSpiderScript_DataTypes)Type ) - { - case SS_DATATYPE_UNDEF: - case SS_DATATYPE_ARRAY: - case SS_DATATYPE_OPAQUE: - AST_RuntimeError(NULL, "Invalid cast to %i", Type); - return ERRPTR; - case SS_DATATYPE_OBJECT: - // TODO: - AST_RuntimeError(NULL, "Invalid cast to %i", Type); - return ERRPTR; - - case SS_DATATYPE_INTEGER: - ret = malloc(sizeof(tSpiderValue)); - ret->Type = SS_DATATYPE_INTEGER; - ret->ReferenceCount = 1; - switch(Source->Type) - { - case SS_DATATYPE_INTEGER: break; // Handled above - case SS_DATATYPE_STRING: ret->Integer = atoi(Source->String.Data); break; - case SS_DATATYPE_REAL: ret->Integer = Source->Real; break; - default: - AST_RuntimeError(NULL, "Invalid cast from %i to Integer", Source->Type); - free(ret); - ret = ERRPTR; - break; - } - break; - - case SS_DATATYPE_REAL: - ret = malloc(sizeof(tSpiderValue)); - ret->Type = SS_DATATYPE_REAL; - ret->ReferenceCount = 1; - switch(Source->Type) - { - case SS_DATATYPE_STRING: ret->Real = atof(Source->String.Data); break; - case SS_DATATYPE_INTEGER: ret->Real = Source->Integer; break; - default: - AST_RuntimeError(NULL, "Invalid cast from %i to Real", Source->Type); - free(ret); - ret = ERRPTR; - break; - } - break; - - case SS_DATATYPE_STRING: - switch(Source->Type) - { - case SS_DATATYPE_INTEGER: len = snprintf(NULL, 0, "%li", Source->Integer); break; - case SS_DATATYPE_REAL: len = snprintf(NULL, 0, "%g", Source->Real); break; - default: break; - } - ret = malloc(sizeof(tSpiderValue) + len + 1); - ret->Type = SS_DATATYPE_STRING; - ret->ReferenceCount = 1; - ret->String.Length = len; - switch(Source->Type) - { - case SS_DATATYPE_INTEGER: sprintf(ret->String.Data, "%li", Source->Integer); break; - case SS_DATATYPE_REAL: - sprintf(ret->String.Data, "%g", Source->Real); break; - default: - AST_RuntimeError(NULL, "Invalid cast from %i to String", Source->Type); - free(ret); - ret = ERRPTR; - break; - } - break; - - default: - AST_RuntimeError(NULL, "BUG - BUG REPORT: Unimplemented cast target %i", Type); - ret = ERRPTR; - break; - } - - return ret; -} - -/** - * \brief Condenses a value down to a boolean - */ -int SpiderScript_IsValueTrue(tSpiderValue *Value) -{ - if( Value == ERRPTR ) return 0; - if( Value == NULL ) return 0; - - switch( (enum eSpiderScript_DataTypes)Value->Type ) - { - case SS_DATATYPE_UNDEF: - return 0; - - case SS_DATATYPE_INTEGER: - return !!Value->Integer; - - case SS_DATATYPE_REAL: - return (-.5f < Value->Real && Value->Real < 0.5f); - - case SS_DATATYPE_STRING: - return Value->String.Length > 0; - - case SS_DATATYPE_OBJECT: - return Value->Object != NULL; - - case SS_DATATYPE_OPAQUE: - return Value->Opaque.Data != NULL; - - case SS_DATATYPE_ARRAY: - return Value->Array.Length > 0; - default: - AST_RuntimeError(NULL, "Unknown type %i in SpiderScript_IsValueTrue", Value->Type); - return 0; - } - return 0; -} - -/** - * \brief Free a value - * \note Just calls Object_Dereference - */ -void SpiderScript_FreeValue(tSpiderValue *Value) -{ - Object_Dereference(Value); -} - -/** - * \brief Dump a value into a string - * \return Heap string - */ -char *SpiderScript_DumpValue(tSpiderValue *Value) -{ - char *ret; - if( Value == ERRPTR ) - return strdup("ERRPTR"); - if( Value == NULL ) - return strdup("null"); - - switch( (enum eSpiderScript_DataTypes)Value->Type ) - { - case SS_DATATYPE_UNDEF: return strdup("undefined"); - - case SS_DATATYPE_INTEGER: - ret = malloc( sizeof(Value->Integer)*2 + 3 ); - sprintf(ret, "0x%lx", Value->Integer); - return ret; - - case SS_DATATYPE_REAL: - ret = malloc( sprintf(NULL, "%f", Value->Real) + 1 ); - sprintf(ret, "%f", Value->Real); - return ret; - - case SS_DATATYPE_STRING: - ret = malloc( Value->String.Length + 3 ); - ret[0] = '"'; - strcpy(ret+1, Value->String.Data); - ret[Value->String.Length+1] = '"'; - ret[Value->String.Length+2] = '\0'; - return ret; - - case SS_DATATYPE_OBJECT: - ret = malloc( sprintf(NULL, "{%s *%p}", Value->Object->Type->Name, Value->Object) + 1 ); - sprintf(ret, "{%s *%p}", Value->Object->Type->Name, Value->Object); - return ret; - - case SS_DATATYPE_OPAQUE: - ret = malloc( sprintf(NULL, "*%p", Value->Opaque.Data) + 1 ); - sprintf(ret, "*%p", Value->Opaque.Data); - return ret; - - case SS_DATATYPE_ARRAY: - return strdup("Array"); - - default: - AST_RuntimeError(NULL, "Unknown type %i in Object_Dump", Value->Type); - return NULL; - } - -} - /** * \brief Execute a script function * \param Script Script context to execute in @@ -456,7 +87,7 @@ tSpiderValue *SpiderScript_ExecuteFunction(tSpiderScript *Script, ret = AST_ExecuteNode(&bs, astFcn->Code); if(ret != ERRPTR) { - Object_Dereference(ret); // Dereference output of last block statement + SpiderScript_DereferenceValue(ret); // Dereference output of last block statement ret = bs.RetVal; // Set to return value of block } bFound = 1; @@ -640,7 +271,7 @@ tSpiderValue *SpiderScript_CreateObject(tSpiderScript *Script, ret = AST_ExecuteNode(&bs, astFcn->Code); if( ret != ERRPTR ) { - Object_Dereference(ret); // Dereference output of last block statement + SpiderScript_DereferenceValue(ret); // Dereference output of last block statement ret = bs.RetVal; // Set to return value of block } bFound = 1; @@ -768,7 +399,7 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) { ret = AST_ExecuteNode(&blockInfo, node); if(ret == ERRPTR) break; // Error check - if(ret != NULL) Object_Dereference(ret); // Free unused value + if(ret != NULL) SpiderScript_DereferenceValue(ret); // Free unused value } // Clean up variables while(blockInfo.FirstVar) @@ -813,7 +444,7 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) #if 0 #else if(varVal && varVal->ReferenceCount == 2) { - Object_Dereference(varVal); + SpiderScript_DereferenceValue(varVal); // printf("pre: (%s) varVal->ReferenceCount = %i\n", // Node->Assign.Dest->Variable.Name, // varVal->ReferenceCount); @@ -822,12 +453,12 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) value = AST_ExecuteNode_BinOp(Block, Node, Node->Assign.Operation, varVal, ret); if(value == ERRPTR) return ERRPTR; - if(ret) Object_Dereference(ret); + if(ret) SpiderScript_DereferenceValue(ret); #if 0 - if(varVal) Object_Dereference(varVal); + if(varVal) SpiderScript_DereferenceValue(varVal); #else if(varVal && varVal->ReferenceCount == 1) { - Object_Reference(varVal); + SpiderScript_ReferenceValue(varVal); // printf("post: varVal->ReferenceCount = %i\n", varVal->ReferenceCount); break; // If varVal was non-null, it has been updated by _BinOp } @@ -838,7 +469,7 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) // Set the variable value if( Variable_SetValue( Block, Node->Assign.Dest, ret ) ) { - Object_Dereference( ret ); + SpiderScript_DereferenceValue( ret ); return ERRPTR; } break; @@ -873,10 +504,10 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) ret = varVal; if( Variable_SetValue( Block, Node->UniOp.Value, value ) ) { - Object_Dereference( ret ); + SpiderScript_DereferenceValue( ret ); return ERRPTR; } - Object_Dereference( value ); + SpiderScript_DereferenceValue( value ); } break; @@ -893,7 +524,7 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) { params[i] = AST_ExecuteNode(Block, node); if( params[i] == ERRPTR ) { - while(i--) Object_Dereference(params[i]); + while(i--) SpiderScript_DereferenceValue(params[i]); ret = ERRPTR; goto _return; } @@ -917,7 +548,7 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) if( !obj || obj == ERRPTR || obj->Type != SS_DATATYPE_OBJECT ) { AST_RuntimeError(Node->FunctionCall.Object, "Type Mismatch - Required SS_DATATYPE_OBJECT for method call"); - while(i--) Object_Dereference(params[i]); + while(i--) SpiderScript_DereferenceValue(params[i]); ret = ERRPTR; break; } @@ -925,7 +556,7 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) obj->Object, Node->FunctionCall.Name, Node->FunctionCall.NumArgs, params ); - Object_Dereference(obj); + SpiderScript_DereferenceValue(obj); } else { @@ -937,7 +568,7 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) // Dereference parameters - while(i--) Object_Dereference(params[i]); + while(i--) SpiderScript_DereferenceValue(params[i]); // falls out } @@ -953,9 +584,9 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) else { tmpobj = AST_ExecuteNode(Block, Node->If.False); } - Object_Dereference(ret); + SpiderScript_DereferenceValue(ret); if( tmpobj == ERRPTR ) return ERRPTR; - Object_Dereference(tmpobj); + SpiderScript_DereferenceValue(tmpobj); ret = NULL; break; @@ -968,12 +599,12 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) // Check initial condition if( !Node->For.bCheckAfter ) { - Object_Dereference(ret); + SpiderScript_DereferenceValue(ret); ret = AST_ExecuteNode(Block, Node->For.Condition); if(ret == ERRPTR) return ERRPTR; if(!SpiderScript_IsValueTrue(ret)) { - Object_Dereference(ret); + SpiderScript_DereferenceValue(ret); ret = NULL; break; } @@ -982,12 +613,12 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) // Perform loop for( ;; ) { - Object_Dereference(ret); + SpiderScript_DereferenceValue(ret); // Code ret = AST_ExecuteNode(Block, Node->For.Code); if(ret == ERRPTR) return ERRPTR; - Object_Dereference(ret); + SpiderScript_DereferenceValue(ret); if(Block->BreakTarget) { @@ -1008,14 +639,14 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) // Increment ret = AST_ExecuteNode(Block, Node->For.Increment); if(ret == ERRPTR) return ERRPTR; - Object_Dereference(ret); + SpiderScript_DereferenceValue(ret); // Check condition ret = AST_ExecuteNode(Block, Node->For.Condition); if(ret == ERRPTR) return ERRPTR; if(!SpiderScript_IsValueTrue(ret)) break; } - Object_Dereference(ret); + SpiderScript_DereferenceValue(ret); ret = NULL; break; @@ -1046,7 +677,7 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) ret = NULL; if( Variable_Define(Block, Node->DefVar.DataType, Node->DefVar.Name, tmpobj) == ERRPTR ) ret = ERRPTR; - Object_Dereference(tmpobj); + SpiderScript_DereferenceValue(tmpobj); break; // Scope @@ -1104,7 +735,7 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) if( strcmp(Node->Scope.Name, tmpobj->Object->Type->AttributeDefs[i].Name) == 0 ) { ret = tmpobj->Object->Attributes[i]; - Object_Reference(ret); + SpiderScript_ReferenceValue(ret); break; } } @@ -1122,7 +753,7 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) tmpobj = AST_ExecuteNode(Block, Node->Cast.Value); if(tmpobj == ERRPTR) return ERRPTR; ret = SpiderScript_CastValueTo( Node->Cast.DataType, tmpobj ); - Object_Dereference(tmpobj); + SpiderScript_DereferenceValue(tmpobj); } break; @@ -1132,7 +763,7 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) if(op1 == ERRPTR) return ERRPTR; op2 = AST_ExecuteNode(Block, Node->BinOp.Right); // Offset if(op2 == ERRPTR) { - Object_Dereference(op1); + SpiderScript_DereferenceValue(op1); return ERRPTR; } @@ -1153,7 +784,7 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) if( !op2 || op2->Type != SS_DATATYPE_INTEGER ) { tmpobj = SpiderScript_CastValueTo(SS_DATATYPE_INTEGER, op2); - Object_Dereference(op2); + SpiderScript_DereferenceValue(op2); op2 = tmpobj; } @@ -1165,10 +796,10 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) } ret = op1->Array.Items[ op2->Integer ]; - Object_Reference(ret); + SpiderScript_ReferenceValue(ret); - Object_Dereference(op1); - Object_Dereference(op2); + SpiderScript_DereferenceValue(op1); + SpiderScript_DereferenceValue(op2); break; // TODO: Implement runtime constants @@ -1183,7 +814,7 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) case NODETYPE_INTEGER: case NODETYPE_REAL: ret = &Node->Constant; - Object_Reference(ret); + SpiderScript_ReferenceValue(ret); break; // --- Operations --- @@ -1192,7 +823,7 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) op1 = AST_ExecuteNode(Block, Node->UniOp.Value); if(op1 == ERRPTR) return ERRPTR; ret = SpiderScript_CreateInteger( !SpiderScript_IsValueTrue(op1) ); - Object_Dereference(op1); + SpiderScript_DereferenceValue(op1); break; case NODETYPE_LOGICALAND: // Logical AND (&&) case NODETYPE_LOGICALOR: // Logical OR (||) @@ -1201,7 +832,7 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) if(op1 == ERRPTR) return ERRPTR; op2 = AST_ExecuteNode(Block, Node->BinOp.Right); if(op2 == ERRPTR) { - Object_Dereference(op1); + SpiderScript_DereferenceValue(op1); return ERRPTR; } @@ -1220,8 +851,8 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) } // Free intermediate objects - Object_Dereference(op1); - Object_Dereference(op2); + SpiderScript_DereferenceValue(op1); + SpiderScript_DereferenceValue(op2); break; // Comparisons @@ -1234,15 +865,15 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) if(op1 == ERRPTR) return ERRPTR; op2 = AST_ExecuteNode(Block, Node->BinOp.Right); if(op2 == ERRPTR) { - Object_Dereference(op1); + SpiderScript_DereferenceValue(op1); ret = ERRPTR; break; } if( !op1 || !op2 ) { AST_RuntimeError(Node, "NULL Comparison (%p and %p)", op1, op2); - if(op1) Object_Dereference(op1); - if(op2) Object_Dereference(op2); + if(op1) SpiderScript_DereferenceValue(op1); + if(op2) SpiderScript_DereferenceValue(op2); ret = SpiderScript_CreateInteger( !op1 && !op2 ); break; } @@ -1254,9 +885,9 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) { tmpobj = op2; op2 = SpiderScript_CastValueTo(op1->Type, op2); - Object_Dereference(tmpobj); + SpiderScript_DereferenceValue(tmpobj); if(op2 == ERRPTR) { - Object_Dereference(op1); + SpiderScript_DereferenceValue(op1); return ERRPTR; } } @@ -1309,8 +940,8 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) } // Free intermediate objects - Object_Dereference(op1); - Object_Dereference(op2); + SpiderScript_DereferenceValue(op1); + SpiderScript_DereferenceValue(op2); // Error check if( ret == ERRPTR ) @@ -1337,7 +968,7 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) op1 = AST_ExecuteNode(Block, Node->UniOp.Value); if(op1 == ERRPTR) return ERRPTR; ret = AST_ExecuteNode_UniOp(Block, Node, Node->Type, op1); - Object_Dereference(op1); + SpiderScript_DereferenceValue(op1); break; // General Binary Operations @@ -1357,15 +988,15 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) if(op1 == ERRPTR) return ERRPTR; op2 = AST_ExecuteNode(Block, Node->BinOp.Right); if(op2 == ERRPTR) { - Object_Dereference(op1); + SpiderScript_DereferenceValue(op1); return ERRPTR; } ret = AST_ExecuteNode_BinOp(Block, Node, Node->Type, op1, op2); // Free intermediate objects - Object_Dereference(op1); - Object_Dereference(op2); + SpiderScript_DereferenceValue(op1); + SpiderScript_DereferenceValue(op2); break; //default: @@ -1417,7 +1048,7 @@ tSpiderValue *AST_ExecuteNode_UniOp(tAST_BlockState *Block, tAST_Node *Node, int // Integer Operations case SS_DATATYPE_INTEGER: if( Value->ReferenceCount == 1 ) - Object_Reference(ret = Value); + SpiderScript_ReferenceValue(ret = Value); else ret = SpiderScript_CreateInteger(0); switch(Operation) @@ -1426,7 +1057,7 @@ tSpiderValue *AST_ExecuteNode_UniOp(tAST_BlockState *Block, tAST_Node *Node, int case NODETYPE_BWNOT: ret->Integer = ~Value->Integer; break; default: AST_RuntimeError(Node, "SpiderScript internal error: Exec,UniOP,Integer unknown op %i", Operation); - Object_Dereference(ret); + SpiderScript_DereferenceValue(ret); ret = ERRPTR; break; } @@ -1520,7 +1151,7 @@ tSpiderValue *AST_ExecuteNode_BinOp(tAST_BlockState *Block, tAST_Node *Node, int switch(Operation) { case NODETYPE_ADD: // Concatenate - ret = Object_StringConcat(Left, Right); + ret = SpiderScript_StringConcat(Left, Right); break; // TODO: Support python style 'i = %i' % i ? // Might do it via a function call @@ -1540,7 +1171,7 @@ tSpiderValue *AST_ExecuteNode_BinOp(tAST_BlockState *Block, tAST_Node *Node, int // Integer Operations case SS_DATATYPE_INTEGER: if( Left->ReferenceCount == 1 ) - Object_Reference(ret = Left); + SpiderScript_ReferenceValue(ret = Left); else ret = SpiderScript_CreateInteger(0); switch(Operation) @@ -1560,7 +1191,7 @@ tSpiderValue *AST_ExecuteNode_BinOp(tAST_BlockState *Block, tAST_Node *Node, int break; default: AST_RuntimeError(Node, "SpiderScript internal error: Exec,BinOP,Integer unknown op %i", Operation); - Object_Dereference(ret); + SpiderScript_DereferenceValue(ret); ret = ERRPTR; break; } @@ -1569,7 +1200,7 @@ tSpiderValue *AST_ExecuteNode_BinOp(tAST_BlockState *Block, tAST_Node *Node, int // Real Numbers case SS_DATATYPE_REAL: if( Left->ReferenceCount == 1 ) - Object_Reference(ret = Left); + SpiderScript_ReferenceValue(ret = Left); else ret = SpiderScript_CreateReal(0); switch(Operation) @@ -1580,7 +1211,7 @@ tSpiderValue *AST_ExecuteNode_BinOp(tAST_BlockState *Block, tAST_Node *Node, int case NODETYPE_DIVIDE: ret->Real = Left->Real / Right->Real; break; default: AST_RuntimeError(Node, "SpiderScript internal error: Exec,BinOP,Real unknown op %i", Operation); - Object_Dereference(ret); + SpiderScript_DereferenceValue(ret); ret = ERRPTR; break; } @@ -1620,7 +1251,7 @@ tAST_Variable *Variable_Define(tAST_BlockState *Block, int Type, const char *Nam var->Next = NULL; var->Type = Type; var->Object = Value; - if(Value) Object_Reference(Value); + if(Value) SpiderScript_ReferenceValue(Value); strcpy(var->Name, Name); if(prev) prev->Next = var; @@ -1705,8 +1336,8 @@ int Variable_SetValue(tAST_BlockState *Block, tAST_Node *VarNode, tSpiderValue * } // printf("Assign %p to '%s'\n", Value, var->Name); - Object_Reference(Value); - Object_Dereference(var->Object); + SpiderScript_ReferenceValue(Value); + SpiderScript_DereferenceValue(var->Object); var->Object = Value; return 0; } @@ -1720,7 +1351,7 @@ tSpiderValue *Variable_GetValue(tAST_BlockState *Block, tAST_Node *VarNode) if( !var ) return ERRPTR; - Object_Reference(var->Object); + SpiderScript_ReferenceValue(var->Object); return var->Object; } @@ -1730,7 +1361,7 @@ tSpiderValue *Variable_GetValue(tAST_BlockState *Block, tAST_Node *VarNode) void Variable_Destroy(tAST_Variable *Variable) { // printf("Variable_Destroy: (%p'%s')\n", Variable, Variable->Name); - Object_Dereference(Variable->Object); + SpiderScript_DereferenceValue(Variable->Object); free(Variable); } diff --git a/Usermode/Libraries/libspiderscript.so_src/exec_bytecode.c b/Usermode/Libraries/libspiderscript.so_src/exec_bytecode.c new file mode 100644 index 00000000..b9384245 --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/exec_bytecode.c @@ -0,0 +1,99 @@ +/* + * SpiderScript Library + * by John Hodge (thePowersGang) + * + * bytecode_makefile.c + * - Generate a bytecode file + */ +#include +#include "bytecode_ops.h" + +typedef sBC_StackEnt tBC_StackEnt; + +enum eBC_StackEntTypes +{ + ET_NULL, // Start of the stack + ET_FUNCTION_START, // Start of the function + ET_INTEGER, // Integer / Boolean + ET_REAL, // Real number + ET_OBJECT, // Object + ET_REFERENCE // Reference to a tSpiderValue +}; + +struct sBC_StackEnt +{ + uint8_t EntType; + uint8_t _padding[3]; + union { + uint64_t Integer; + double Real; + tSpiderValue *Reference; + tSpiderObject *Object; + }; +}; + +struct sBC_Stack +{ + int EntrySpace; + int EntryCount; + tBC_StackEnt Entries[]; +}; + +// === CODE === +int Bytecode_int_StackPop(tBC_Stack *Stack, tBC_StackEnt *Dest) +{ + if( Stack->EntryCount == 0 ) return 1; + Stack->EntryCount --; + *Dest = Stack->Entries[Stack->EntryCount]; + return 0; +} + +int Bytecode_int_StackPush(tBC_Stack *Stack, tBC_StackEnt *Src) +{ + if( Stack->EntryCount == Stack->EntrySpace ) return 1; + Stack->Entries[Stack->EntryCount] = *Src; + Stack->EntryCount ++; + return 0; +} + +#define GET_STACKVAL(dst) if((ret = Bytecode_int_StackPop(Stack, &dst))) return ret; + +int Bytecode_ExecuteFunction(tBC_Function *Fcn, tBC_Stack *Stack, int ArgCount); +{ + tBC_Op *op; + tBC_StackEnt val1, val2; + tBC_StackEnt local_vars[Fcn->MaxVariableCount+Fcn->ArgumentCount]; + + // Pop off arguments + + // Mark the start + + // Execute! + op = Fcn->Operations; + while(op) + { + tBC_Op *nextop = op->Next; + switch(op->Type) + { + case BC_OP_JUMP: + nextop = Fcn->Labels[op->StringInt.Integer]; + break; + case BC_OP_JUMPIF: + GET_STACKVAL(val1); + if( Bytecode_int_IsStackEntTrue(&val1) ) + nextop = Fcn->Labels[op->StringInt.Integer]; + break; + case BC_OP_JUMPIFNOT: + GET_STACKVAL(val1); + if( !Bytecode_int_IsStackEntTrue(&val1) ) + nextop = Fcn->Labels[op->StringInt.Integer]; + break; + default: + // TODO: + break; + } + op = nextop; + } + + // Clean up +} diff --git a/Usermode/Libraries/libspiderscript.so_src/main.c b/Usermode/Libraries/libspiderscript.so_src/main.c index bfeaf509..f26a40cd 100644 --- a/Usermode/Libraries/libspiderscript.so_src/main.c +++ b/Usermode/Libraries/libspiderscript.so_src/main.c @@ -7,6 +7,7 @@ #include #include #include "ast.h" +#include "bytecode_gen.h" // === IMPORTS === extern tAST_Script *Parse_Buffer(tSpiderVariant *Variant, const char *Buffer, const char *Filename); @@ -94,6 +95,11 @@ tSpiderScript *SpiderScript_ParseFile(tSpiderVariant *Variant, const char *Filen return ret; } +int SpiderScript_SaveBytecode(tSpiderScript *Script, const char *DestFile) +{ + return Bytecode_ConvertScript(Script, DestFile); +} + /** * \brief Free a script */ diff --git a/Usermode/Libraries/libspiderscript.so_src/values.c b/Usermode/Libraries/libspiderscript.so_src/values.c new file mode 100644 index 00000000..f05360d4 --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/values.c @@ -0,0 +1,394 @@ +/* + * SpiderScript Library + * by John Hodge (thePowersGang) + * + * values.c + * - Manage tSpiderValue objects + */ +#include +#include +#include +#include "spiderscript.h" + +// === IMPORTS === +extern void AST_RuntimeError(void *Node, const char *Format, ...); + +// === PROTOTYPES === +void SpiderScript_DereferenceValue(tSpiderValue *Object); +void SpiderScript_ReferenceValue(tSpiderValue *Object); +tSpiderValue *SpiderScript_CreateInteger(uint64_t Value); +tSpiderValue *SpiderScript_CreateReal(double Value); +tSpiderValue *SpiderScript_CreateString(int Length, const char *Data); +tSpiderValue *SpiderScript_CastValueTo(int Type, tSpiderValue *Source); + int SpiderScript_IsValueTrue(tSpiderValue *Value); +void SpiderScript_FreeValue(tSpiderValue *Value); +char *SpiderScript_DumpValue(tSpiderValue *Value); + +// === CODE === +/** + * \brief Dereference a created object + */ +void SpiderScript_DereferenceValue(tSpiderValue *Object) +{ + if(!Object || Object == ERRPTR) return ; + Object->ReferenceCount --; + if( Object->ReferenceCount == 0 ) + { + switch( (enum eSpiderScript_DataTypes) Object->Type ) + { + case SS_DATATYPE_OBJECT: + Object->Object->Type->Destructor( Object->Object ); + break; + case SS_DATATYPE_OPAQUE: + Object->Opaque.Destroy( Object->Opaque.Data ); + break; + default: + break; + } + free(Object); + } +} + +/** + * \brief Reference a value + */ +void SpiderScript_ReferenceValue(tSpiderValue *Object) +{ + if(!Object || Object == ERRPTR) return ; + Object->ReferenceCount ++; +} + +/** + * \brief Allocate and initialise a SpiderScript object + */ +tSpiderObject *SpiderScript_AllocateObject(tSpiderObjectDef *Class, int ExtraBytes) +{ + int size = sizeof(tSpiderObject) + Class->NAttributes * sizeof(tSpiderValue*) + ExtraBytes; + tSpiderObject *ret = malloc(size); + + ret->Type = Class; + ret->ReferenceCount = 1; + ret->OpaqueData = &ret->Attributes[ Class->NAttributes ]; + memset( ret->Attributes, 0, Class->NAttributes * sizeof(tSpiderValue*) ); + + return ret; +} + +/** + * \brief Create an integer object + */ +tSpiderValue *SpiderScript_CreateInteger(uint64_t Value) +{ + tSpiderValue *ret = malloc( sizeof(tSpiderValue) ); + ret->Type = SS_DATATYPE_INTEGER; + ret->ReferenceCount = 1; + ret->Integer = Value; + return ret; +} + +/** + * \brief Create an real number object + */ +tSpiderValue *SpiderScript_CreateReal(double Value) +{ + tSpiderValue *ret = malloc( sizeof(tSpiderValue) ); + ret->Type = SS_DATATYPE_REAL; + ret->ReferenceCount = 1; + ret->Real = Value; + return ret; +} + +/** + * \brief Create an string object + */ +tSpiderValue *SpiderScript_CreateString(int Length, const char *Data) +{ + tSpiderValue *ret = malloc( sizeof(tSpiderValue) + Length + 1 ); + ret->Type = SS_DATATYPE_STRING; + ret->ReferenceCount = 1; + ret->String.Length = Length; + if( Data ) + memcpy(ret->String.Data, Data, Length); + else + memset(ret->String.Data, 0, Length); + ret->String.Data[Length] = '\0'; + return ret; +} + +/** + * \brief Concatenate two strings + */ +tSpiderValue *SpiderScript_StringConcat(const tSpiderValue *Str1, const tSpiderValue *Str2) +{ + int newLen = 0; + tSpiderValue *ret; + + if( Str1 && Str1->Type != SS_DATATYPE_STRING) + return NULL; + if( Str2 && Str2->Type != SS_DATATYPE_STRING) + return NULL; + + if(Str1) newLen += Str1->String.Length; + if(Str2) newLen += Str2->String.Length; + ret = malloc( sizeof(tSpiderValue) + newLen + 1 ); + ret->Type = SS_DATATYPE_STRING; + ret->ReferenceCount = 1; + ret->String.Length = newLen; + if(Str1) + memcpy(ret->String.Data, Str1->String.Data, Str1->String.Length); + if(Str2) { + if(Str1) + memcpy(ret->String.Data+Str1->String.Length, Str2->String.Data, Str2->String.Length); + else + memcpy(ret->String.Data, Str2->String.Data, Str2->String.Length); + } + ret->String.Data[ newLen ] = '\0'; + return ret; +} + +/** + * \brief Cast one object to another + * \brief Type Destination type + * \brief Source Input data + */ +tSpiderValue *SpiderScript_CastValueTo(int Type, tSpiderValue *Source) +{ + tSpiderValue *ret = ERRPTR; + int len = 0; + + if( !Source ) + { + switch(Type) + { + case SS_DATATYPE_INTEGER: return SpiderScript_CreateInteger(0); + case SS_DATATYPE_REAL: return SpiderScript_CreateReal(0); + case SS_DATATYPE_STRING: return SpiderScript_CreateString(4, "null"); + } + return NULL; + } + + // Check if anything needs to be done + if( Source->Type == Type ) { + SpiderScript_DereferenceValue(Source); + return Source; + } + + // Debug + #if 0 + { + printf("Casting %i ", Source->Type); + switch(Source->Type) + { + case SS_DATATYPE_INTEGER: printf("0x%lx", Source->Integer); break; + case SS_DATATYPE_STRING: printf("\"%s\"", Source->String.Data); break; + case SS_DATATYPE_REAL: printf("%f", Source->Real); break; + default: break; + } + printf(" to %i\n", Type); + } + #endif + + // Object casts + #if 0 + if( Source->Type == SS_DATATYPE_OBJECT ) + { + const char *name = NULL; + switch(Type) + { + case SS_DATATYPE_INTEGER: name = "cast Integer"; break; + case SS_DATATYPE_REAL: name = "cast Real"; break; + case SS_DATATYPE_STRING: name = "cast String"; break; + case SS_DATATYPE_ARRAY: name = "cast Array"; break; + default: + AST_RuntimeError(NULL, "Invalid cast to %i from Object", Type); + return ERRPTR; + } + if( fcnname ) + { + ret = Object_ExecuteMethod(Left->Object, fcnname, Right); + if( ret != ERRPTR ) + return ret; + // Fall through and try casting (which will usually fail) + } + } + #endif + + switch( (enum eSpiderScript_DataTypes)Type ) + { + case SS_DATATYPE_UNDEF: + case SS_DATATYPE_ARRAY: + case SS_DATATYPE_OPAQUE: + AST_RuntimeError(NULL, "Invalid cast to %i", Type); + return ERRPTR; + case SS_DATATYPE_OBJECT: + // TODO: + AST_RuntimeError(NULL, "Invalid cast to %i", Type); + return ERRPTR; + + case SS_DATATYPE_INTEGER: + ret = malloc(sizeof(tSpiderValue)); + ret->Type = SS_DATATYPE_INTEGER; + ret->ReferenceCount = 1; + switch(Source->Type) + { + case SS_DATATYPE_INTEGER: break; // Handled above + case SS_DATATYPE_STRING: ret->Integer = atoi(Source->String.Data); break; + case SS_DATATYPE_REAL: ret->Integer = Source->Real; break; + default: + AST_RuntimeError(NULL, "Invalid cast from %i to Integer", Source->Type); + free(ret); + ret = ERRPTR; + break; + } + break; + + case SS_DATATYPE_REAL: + ret = malloc(sizeof(tSpiderValue)); + ret->Type = SS_DATATYPE_REAL; + ret->ReferenceCount = 1; + switch(Source->Type) + { + case SS_DATATYPE_STRING: ret->Real = atof(Source->String.Data); break; + case SS_DATATYPE_INTEGER: ret->Real = Source->Integer; break; + default: + AST_RuntimeError(NULL, "Invalid cast from %i to Real", Source->Type); + free(ret); + ret = ERRPTR; + break; + } + break; + + case SS_DATATYPE_STRING: + switch(Source->Type) + { + case SS_DATATYPE_INTEGER: len = snprintf(NULL, 0, "%li", Source->Integer); break; + case SS_DATATYPE_REAL: len = snprintf(NULL, 0, "%g", Source->Real); break; + default: break; + } + ret = malloc(sizeof(tSpiderValue) + len + 1); + ret->Type = SS_DATATYPE_STRING; + ret->ReferenceCount = 1; + ret->String.Length = len; + switch(Source->Type) + { + case SS_DATATYPE_INTEGER: sprintf(ret->String.Data, "%li", Source->Integer); break; + case SS_DATATYPE_REAL: + sprintf(ret->String.Data, "%g", Source->Real); break; + default: + AST_RuntimeError(NULL, "Invalid cast from %i to String", Source->Type); + free(ret); + ret = ERRPTR; + break; + } + break; + + default: + AST_RuntimeError(NULL, "BUG - BUG REPORT: Unimplemented cast target %i", Type); + ret = ERRPTR; + break; + } + + return ret; +} + +/** + * \brief Condenses a value down to a boolean + */ +int SpiderScript_IsValueTrue(tSpiderValue *Value) +{ + if( Value == ERRPTR ) return 0; + if( Value == NULL ) return 0; + + switch( (enum eSpiderScript_DataTypes)Value->Type ) + { + case SS_DATATYPE_UNDEF: + return 0; + + case SS_DATATYPE_INTEGER: + return !!Value->Integer; + + case SS_DATATYPE_REAL: + return (-.5f < Value->Real && Value->Real < 0.5f); + + case SS_DATATYPE_STRING: + return Value->String.Length > 0; + + case SS_DATATYPE_OBJECT: + return Value->Object != NULL; + + case SS_DATATYPE_OPAQUE: + return Value->Opaque.Data != NULL; + + case SS_DATATYPE_ARRAY: + return Value->Array.Length > 0; + default: + AST_RuntimeError(NULL, "Unknown type %i in SpiderScript_IsValueTrue", Value->Type); + return 0; + } + return 0; +} + +/** + * \brief Free a value + * \note Just calls Object_Dereference + */ +void SpiderScript_FreeValue(tSpiderValue *Value) +{ + SpiderScript_DereferenceValue(Value); +} + +/** + * \brief Dump a value into a string + * \return Heap string + */ +char *SpiderScript_DumpValue(tSpiderValue *Value) +{ + char *ret; + if( Value == ERRPTR ) + return strdup("ERRPTR"); + if( Value == NULL ) + return strdup("null"); + + switch( (enum eSpiderScript_DataTypes)Value->Type ) + { + case SS_DATATYPE_UNDEF: return strdup("undefined"); + + case SS_DATATYPE_INTEGER: + ret = malloc( sizeof(Value->Integer)*2 + 3 ); + sprintf(ret, "0x%lx", Value->Integer); + return ret; + + case SS_DATATYPE_REAL: + ret = malloc( sprintf(NULL, "%f", Value->Real) + 1 ); + sprintf(ret, "%f", Value->Real); + return ret; + + case SS_DATATYPE_STRING: + ret = malloc( Value->String.Length + 3 ); + ret[0] = '"'; + strcpy(ret+1, Value->String.Data); + ret[Value->String.Length+1] = '"'; + ret[Value->String.Length+2] = '\0'; + return ret; + + case SS_DATATYPE_OBJECT: + ret = malloc( sprintf(NULL, "{%s *%p}", Value->Object->Type->Name, Value->Object) + 1 ); + sprintf(ret, "{%s *%p}", Value->Object->Type->Name, Value->Object); + return ret; + + case SS_DATATYPE_OPAQUE: + ret = malloc( sprintf(NULL, "*%p", Value->Opaque.Data) + 1 ); + sprintf(ret, "*%p", Value->Opaque.Data); + return ret; + + case SS_DATATYPE_ARRAY: + return strdup("Array"); + + default: + AST_RuntimeError(NULL, "Unknown type %i in Object_Dump", Value->Type); + return NULL; + } + +} + + diff --git a/Usermode/include/spiderscript.h b/Usermode/include/spiderscript.h index 60870ef3..c87a0e50 100644 --- a/Usermode/include/spiderscript.h +++ b/Usermode/include/spiderscript.h @@ -246,6 +246,11 @@ extern tSpiderValue *SpiderScript_ExecuteFunction(tSpiderScript *Script, int NArguments, tSpiderValue **Arguments ); +/** + * \brief Convert a script to bytecode and save to a file + */ +extern int SpiderScript_SaveBytecode(tSpiderScript *Script, const char *DestFile); + /** * \brief Free a script * \param Script Script structure to free @@ -258,9 +263,12 @@ extern tSpiderObject *SpiderScript_AllocateObject(tSpiderObjectDef *Class, int E * \name tSpiderValue Manipulation functions * \{ */ +extern void SpiderScript_DereferenceValue(tSpiderValue *Object); +extern void SpiderScript_ReferenceValue(tSpiderValue *Object); extern tSpiderValue *SpiderScript_CreateInteger(uint64_t Value); extern tSpiderValue *SpiderScript_CreateReal(double Value); extern tSpiderValue *SpiderScript_CreateString(int Length, const char *Data); +extern tSpiderValue *SpiderScript_StringConcat(const tSpiderValue *Str1, const tSpiderValue *Str2); extern tSpiderValue *SpiderScript_CastValueTo(int Type, tSpiderValue *Source); extern int SpiderScript_IsValueTrue(tSpiderValue *Value); extern void SpiderScript_FreeValue(tSpiderValue *Value);