From 1529dadb6c2170bf9899fbde46d06a3d9a392b52 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Tue, 17 Aug 2010 09:40:31 +0800 Subject: [PATCH] SpiderScript! (with a sample script) --- Usermode/Libraries/libc.so_src/string.c | 13 + .../Libraries/libspiderscript.so_src/Makefile | 2 +- .../libspiderscript.so_src/Scripts/sample.isc | 36 ++ .../Libraries/libspiderscript.so_src/ast.c | 97 ++++- .../Libraries/libspiderscript.so_src/ast.h | 85 +++- .../libspiderscript.so_src/exec_ast.c | 368 +++++++++++++++--- .../libspiderscript.so_src/exports.c | 48 +++ .../Libraries/libspiderscript.so_src/lex.c | 116 +++++- .../Libraries/libspiderscript.so_src/main.c | 123 +++++- .../Libraries/libspiderscript.so_src/parse.c | 256 ++++++++++-- .../Libraries/libspiderscript.so_src/tokens.h | 77 ++++ 11 files changed, 1069 insertions(+), 152 deletions(-) create mode 100644 Usermode/Libraries/libspiderscript.so_src/Scripts/sample.isc create mode 100644 Usermode/Libraries/libspiderscript.so_src/exports.c diff --git a/Usermode/Libraries/libc.so_src/string.c b/Usermode/Libraries/libc.so_src/string.c index e85356d5..4e8dfe2e 100644 --- a/Usermode/Libraries/libc.so_src/string.c +++ b/Usermode/Libraries/libc.so_src/string.c @@ -19,6 +19,19 @@ EXPORT int strcmp(const char *s1, const char *s2) return (int)*s1 - (int)*s2; } +/** + * \fn EXPORT int strncmp(const char *s1, const char *s2) + * \brief Compare two strings + */ +EXPORT int strncmp(const char *s1, const char *s2, size_t n) +{ + if( n == 0 ) return 0; + while(n -- && *s1 == *s2 && *s1 != '\0' && *s2 != '\0') { + s1++; s2++; + } + return (int)*s1 - (int)*s2; +} + /** * \fn EXPORT char *strcpy(char *dst, const char *src) * \brief Copy a string to another diff --git a/Usermode/Libraries/libspiderscript.so_src/Makefile b/Usermode/Libraries/libspiderscript.so_src/Makefile index fd42a31d..954b2a70 100644 --- a/Usermode/Libraries/libspiderscript.so_src/Makefile +++ b/Usermode/Libraries/libspiderscript.so_src/Makefile @@ -7,7 +7,7 @@ CPPFLAGS += CFLAGS += -Wall LDFLAGS += -lc -lgcc -soname libspiderscript.so --no-allow-shlib-undefined -OBJ = main.o lex.o parse.o ast.o exec_ast.o +OBJ = main.o lex.o parse.o ast.o exec_ast.o exports.o BIN = ../libspiderscript.so include ../Makefile.tpl diff --git a/Usermode/Libraries/libspiderscript.so_src/Scripts/sample.isc b/Usermode/Libraries/libspiderscript.so_src/Scripts/sample.isc new file mode 100644 index 00000000..7e9a866f --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/Scripts/sample.isc @@ -0,0 +1,36 @@ + +# Should I use brackets in this language? +# Well, considering that all whitespace is ignored, it might be an idea + +# Well, it would make VVV a little simpler +# Just define a funciton with the name 'Sys.IO.Open' +# Not a namespace Sys, with a child Sys + +$fp = Sys.IO.Open( "/Devices/ipstack" ); +$ifname = Sys.IO.IOCtl( $fp, 4, "/Devices/ne2k/0" ); +Sys.IO.Close($fp); + +# Let's see: +# b - Signed 8-bit integer, B - unsigned +# w - 16 bit +# l - 32 bit +# q - 64 bit +# f - 32-bit float +# d - 64-bit double +# Fields can be prefixed by a size for arrays (taking only one argument) +# * indicates a variable size array +# E.g. +# Sys.Mem.MakeStruct( "L*B", $len, $len, $str ); +# Hmm.. that would mean I will need arrays... fuck it, do them later + +function SetIPv4($ifaceName, $addr) +{ + $fp = Sys.IO.Open( "/Devices/ipstack/$ifaceName" ); + $data = Lang.MakeStruct( "l", 4 ); + Sys.IO.IOCtl( $fp, 4, $data ); + $data = Lang.Struct( "BBBB", $addr[0], $addr[1], $addr[2], $addr[3] ); + Sys.IO.IOCtl( $fp, 6, $data ); + Sys.IO.Close( $fp ); +} + +SetIPv4( $ifname, Lang.Array(10, 0, 2, 55) ); diff --git a/Usermode/Libraries/libspiderscript.so_src/ast.c b/Usermode/Libraries/libspiderscript.so_src/ast.c index e0e1e37a..45b8176a 100644 --- a/Usermode/Libraries/libspiderscript.so_src/ast.c +++ b/Usermode/Libraries/libspiderscript.so_src/ast.c @@ -2,6 +2,7 @@ * Acess2 Init * - Script AST Manipulator */ +#include #include #include #include "ast.h" @@ -24,9 +25,9 @@ tAST_Function *AST_AppendFunction(tAST_Script *Script, const char *Name) { tAST_Function *ret; - ret = malloc( sizeof(tAST_Function) ); + ret = malloc( sizeof(tAST_Function) + strlen(Name) + 1 ); ret->Next = NULL; - ret->Name = strdup(Name); + strcpy(ret->Name, Name); ret->Code = NULL; ret->Arguments = NULL; @@ -41,6 +42,20 @@ tAST_Function *AST_AppendFunction(tAST_Script *Script, const char *Name) return ret; } +void AST_AppendFunctionArg(tAST_Function *Function, tAST_Node *Node) +{ + if( !Function->Arguments ) { + Function->Arguments_Last = Function->Arguments = Node; + } + else { + Function->Arguments_Last->NextSibling = Node; + Function->Arguments_Last = Node; + } +} + +/** + * \brief Set the code for a function + */ void AST_SetFunctionCode(tAST_Function *Function, tAST_Node *Root) { Function->Code = Root; @@ -50,6 +65,9 @@ void AST_SetFunctionCode(tAST_Function *Function, tAST_Node *Root) * \name Node Manipulation * \{ */ +/** + * \brief Free a node and all subnodes + */ void AST_FreeNode(tAST_Node *Node) { tAST_Node *node; @@ -81,12 +99,28 @@ void AST_FreeNode(tAST_Node *Node) AST_FreeNode(Node->Assign.Value); break; + // Casting + case NODETYPE_CAST: + AST_FreeNode(Node->Cast.Value); + break; + + // Define a variable + case NODETYPE_DEFVAR: + for( node = Node->DefVar.LevelSizes; node; ) + { + tAST_Node *savedNext = node->NextSibling; + AST_FreeNode(node); + node = savedNext; + } + break; + // Unary Operations case NODETYPE_RETURN: AST_FreeNode(Node->UniOp.Value); break; // Binary Operations + case NODETYPE_INDEX: case NODETYPE_ADD: case NODETYPE_SUBTRACT: case NODETYPE_MULTIPLY: @@ -130,14 +164,30 @@ tAST_Node *AST_NewCodeBlock(void) void AST_AppendNode(tAST_Node *Parent, tAST_Node *Child) { - if(Parent->Type != NODETYPE_BLOCK) return ; - - if(Parent->Block.FirstChild == NULL) { - Parent->Block.FirstChild = Parent->Block.LastChild = Child; - } - else { - Parent->Block.LastChild->NextSibling = Child; - Parent->Block.LastChild = Child; + Child->NextSibling = NULL; + switch( Parent->Type ) + { + case NODETYPE_BLOCK: + if(Parent->Block.FirstChild == NULL) { + Parent->Block.FirstChild = Parent->Block.LastChild = Child; + } + else { + Parent->Block.LastChild->NextSibling = Child; + Parent->Block.LastChild = Child; + } + break; + case NODETYPE_DEFVAR: + if(Parent->DefVar.LevelSizes == NULL) { + Parent->DefVar.LevelSizes = Parent->DefVar.LevelSizes_Last = Child; + } + else { + Parent->DefVar.LevelSizes_Last->NextSibling = Child; + Parent->DefVar.LevelSizes_Last = Child; + } + break; + default: + fprintf(stderr, "BUG REPORT: AST_AppendNode on an invalid node type (%i)\n", Parent->Type); + break; } } @@ -166,6 +216,19 @@ tAST_Node *AST_NewBinOp(int Operation, tAST_Node *Left, tAST_Node *Right) return ret; } +/** + */ +tAST_Node *AST_NewUniOp(int Operation, tAST_Node *Value) +{ + tAST_Node *ret = malloc( sizeof(tAST_Node) ); + + ret->NextSibling = NULL; + ret->Type = Operation; + ret->UniOp.Value = Value; + + return ret; +} + /** * \brief Create a new string node */ @@ -206,6 +269,20 @@ tAST_Node *AST_NewVariable(const char *Name) return ret; } +/** + * \brief Create a new variable definition node + */ +tAST_Node *AST_NewDefineVar(int Type, const char *Name) +{ + tAST_Node *ret = malloc( sizeof(tAST_Node) + strlen(Name) + 1 ); + ret->NextSibling = NULL; + ret->Type = NODETYPE_DEFVAR; + ret->DefVar.DataType = Type; + ret->DefVar.LevelSizes = NULL; + strcpy(ret->DefVar.Name, Name); + return ret; +} + /** * \brief Create a new runtime constant reference node */ diff --git a/Usermode/Libraries/libspiderscript.so_src/ast.h b/Usermode/Libraries/libspiderscript.so_src/ast.h index 820167bd..91795636 100644 --- a/Usermode/Libraries/libspiderscript.so_src/ast.h +++ b/Usermode/Libraries/libspiderscript.so_src/ast.h @@ -5,10 +5,12 @@ #include +typedef enum eAST_NodeTypes tAST_NodeType; typedef struct sAST_Script tAST_Script; typedef struct sAST_Function tAST_Function; typedef struct sAST_Node tAST_Node; -typedef enum eAST_NodeTypes tAST_NodeType; +typedef struct sAST_BlockState tAST_BlockState; +typedef struct sAST_Variable tAST_Variable; /** * \brief Node Types @@ -25,10 +27,15 @@ enum eAST_NodeTypes NODETYPE_INTEGER, //!< Integer Constant NODETYPE_REAL, //!< Real Constant + NODETYPE_DEFVAR, //!< Define a variable (Variable) + NODETYPE_CAST, //!< Cast a value to another (Uniop) + NODETYPE_RETURN, //!< Return from a function (reserved word) NODETYPE_ASSIGN, //!< Variable assignment operator NODETYPE_FUNCTIONCALL, //!< Call a function + NODETYPE_INDEX, //!< Index into an array + NODETYPE_LOGICALAND, //!< Logical AND operator NODETYPE_LOGICALOR, //!< Logical OR operator NODETYPE_LOGICALXOR, //!< Logical XOR operator @@ -56,6 +63,7 @@ struct sSpiderScript { tSpiderVariant *Variant; tAST_Script *Script; + char *CurNamespace; //!< Current namespace prefix (NULL = Root) - No trailing . }; struct sAST_Script @@ -66,10 +74,11 @@ struct sAST_Script struct sAST_Function { - tAST_Function *Next; - char *Name; - tAST_Node *Code; - tAST_Node *Arguments; // HACKJOB (Only NODETYPE_VARIABLE is allowed) + tAST_Function *Next; //!< Next function in list + tAST_Node *Code; //!< Function Code + tAST_Node *Arguments; // HACKJOB (Only NODETYPE_DEFVAR is allowed) + tAST_Node *Arguments_Last; + char Name[]; //!< Function Name }; struct sAST_Node @@ -118,32 +127,66 @@ struct sAST_Node char Name[]; } Variable; + struct { + int DataType; + int Depth; + tAST_Node *LevelSizes; + tAST_Node *LevelSizes_Last; + char Name[]; + } DefVar; + + struct { + int DataType; + tAST_Node *Value; + } Cast; + uint64_t Integer; double Real; }; }; +/** + * \brief Code Block state (stores local variables) + */ +struct sAST_BlockState +{ + tAST_BlockState *Parent; + tSpiderScript *Script; //!< Script + tAST_Variable *FirstVar; //!< First variable in the list +}; + +struct sAST_Variable +{ + tAST_Variable *Next; + int Type; // Only used for static typing + tSpiderObject *Object; + char Name[]; +}; + // === FUNCTIONS === -tAST_Script *AST_NewScript(void); +extern tAST_Script *AST_NewScript(void); -tAST_Function *AST_AppendFunction(tAST_Script *Script, const char *Name); -void AST_AppendFunctionArg(tAST_Function *Function, int Type, tAST_Node *Arg); -void AST_SetFunctionCode(tAST_Function *Function, tAST_Node *Root); +extern tAST_Function *AST_AppendFunction(tAST_Script *Script, const char *Name); +extern void AST_AppendFunctionArg(tAST_Function *Function, tAST_Node *Arg); +extern void AST_SetFunctionCode(tAST_Function *Function, tAST_Node *Root); -tAST_Node *AST_NewString(const char *String, int Length); -tAST_Node *AST_NewInteger(uint64_t Value); -tAST_Node *AST_NewVariable(const char *Name); -tAST_Node *AST_NewConstant(const char *Name); -tAST_Node *AST_NewFunctionCall(const char *Name); -void AST_AppendFunctionCallArg(tAST_Node *Node, tAST_Node *Arg); +extern tAST_Node *AST_NewString(const char *String, int Length); +extern tAST_Node *AST_NewInteger(uint64_t Value); +extern tAST_Node *AST_NewVariable(const char *Name); +extern tAST_Node *AST_NewDefineVar(int Type, const char *Name); +extern tAST_Node *AST_NewConstant(const char *Name); +extern tAST_Node *AST_NewFunctionCall(const char *Name); +extern void AST_AppendFunctionCallArg(tAST_Node *Node, tAST_Node *Arg); -tAST_Node *AST_NewCodeBlock(void); -void AST_AppendNode(tAST_Node *Parent, tAST_Node *Child); -tAST_Node *AST_NewAssign(int Operation, tAST_Node *Dest, tAST_Node *Value); -tAST_Node *AST_NewBinOp(int Operation, tAST_Node *Left, tAST_Node *Right); +extern tAST_Node *AST_NewCodeBlock(void); +extern void AST_AppendNode(tAST_Node *Parent, tAST_Node *Child); +extern tAST_Node *AST_NewAssign(int Operation, tAST_Node *Dest, tAST_Node *Value); +extern tAST_Node *AST_NewBinOp(int Operation, tAST_Node *Left, tAST_Node *Right); +extern tAST_Node *AST_NewUniOp(int Operation, tAST_Node *Value); -void AST_FreeNode(tAST_Node *Node); +extern void AST_FreeNode(tAST_Node *Node); -tSpiderVariable *AST_ExecuteNode(tSpiderScript *Script, tAST_Node *Node); +// exec_ast.h +extern tSpiderObject *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node); #endif diff --git a/Usermode/Libraries/libspiderscript.so_src/exec_ast.c b/Usermode/Libraries/libspiderscript.so_src/exec_ast.c index 90045bca..cf7416a4 100644 --- a/Usermode/Libraries/libspiderscript.so_src/exec_ast.c +++ b/Usermode/Libraries/libspiderscript.so_src/exec_ast.c @@ -5,41 +5,45 @@ #include #include "ast.h" -#define ERRPTR ((void*)((intptr_t)0-1)) - // === PROTOTYPES === -void Object_Dereference(tSpiderVariable *Object); -void Object_Reference(tSpiderVariable *Object); -tSpiderVariable *Object_CreateInteger(uint64_t Value); -tSpiderVariable *Object_CreateReal(double Value); -tSpiderVariable *Object_CreateString(int Length, const char *Data); -tSpiderVariable *Object_CastTo(int Type, tSpiderVariable *Source); - int Object_IsTrue(tSpiderVariable *Value); +void Object_Dereference(tSpiderObject *Object); +void Object_Reference(tSpiderObject *Object); +tSpiderObject *Object_CreateInteger(uint64_t Value); +tSpiderObject *Object_CreateReal(double Value); +tSpiderObject *Object_CreateString(int Length, const char *Data); +tSpiderObject *Object_CastTo(int Type, tSpiderObject *Source); + int Object_IsTrue(tSpiderObject *Value); + +tSpiderObject *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node); -void Variable_SetValue(tSpiderScript *Script, const char *Name, tSpiderVariable *Value); -tSpiderVariable *Variable_GetValue(tSpiderScript *Script, const char *Name); +tAST_Variable *Variable_Define(tAST_BlockState *Block, int Type, const char *Name); +void Variable_SetValue(tAST_BlockState *Block, const char *Name, tSpiderObject *Value); +tSpiderObject *Variable_GetValue(tAST_BlockState *Block, const char *Name); +void Variable_Destroy(tAST_Variable *Variable); // === CODE === /** * \brief Dereference a created object */ -void Object_Dereference(tSpiderVariable *Object) +void Object_Dereference(tSpiderObject *Object) { + if(!Object) return ; Object->ReferenceCount --; if( Object->ReferenceCount == 0 ) free(Object); } -void Object_Reference(tSpiderVariable *Object) +void Object_Reference(tSpiderObject *Object) { + if(!Object) return ; Object->ReferenceCount ++; } /** * \brief Create an integer object */ -tSpiderVariable *Object_CreateInteger(uint64_t Value) +tSpiderObject *Object_CreateInteger(uint64_t Value) { - tSpiderVariable *ret = malloc( sizeof(tSpiderVariable) ); + tSpiderObject *ret = malloc( sizeof(tSpiderObject) ); ret->Type = SS_DATATYPE_INTEGER; ret->ReferenceCount = 1; ret->Integer = Value; @@ -49,9 +53,9 @@ tSpiderVariable *Object_CreateInteger(uint64_t Value) /** * \brief Create an real number object */ -tSpiderVariable *Object_CreateReal(double Value) +tSpiderObject *Object_CreateReal(double Value) { - tSpiderVariable *ret = malloc( sizeof(tSpiderVariable) ); + tSpiderObject *ret = malloc( sizeof(tSpiderObject) ); ret->Type = SS_DATATYPE_REAL; ret->ReferenceCount = 1; ret->Real = Value; @@ -61,9 +65,9 @@ tSpiderVariable *Object_CreateReal(double Value) /** * \brief Create an string object */ -tSpiderVariable *Object_CreateString(int Length, const char *Data) +tSpiderObject *Object_CreateString(int Length, const char *Data) { - tSpiderVariable *ret = malloc( sizeof(tSpiderVariable) + Length + 1 ); + tSpiderObject *ret = malloc( sizeof(tSpiderObject) + Length + 1 ); ret->Type = SS_DATATYPE_STRING; ret->ReferenceCount = 1; ret->String.Length = Length; @@ -73,10 +77,36 @@ tSpiderVariable *Object_CreateString(int Length, const char *Data) } /** + * \brief Concatenate two strings */ -tSpiderVariable *Object_CastTo(int Type, tSpiderVariable *Source) +tSpiderObject *Object_StringConcat(tSpiderObject *Str1, tSpiderObject *Str2) { - tSpiderVariable *ret; + int newLen = 0; + tSpiderObject *ret; + if(Str1) newLen += Str1->String.Length; + if(Str2) newLen += Str2->String.Length; + ret = malloc( sizeof(tSpiderObject) + 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 + */ +tSpiderObject *Object_CastTo(int Type, tSpiderObject *Source) +{ + tSpiderObject *ret = ERRPTR; // Check if anything needs to be done if( Source->Type == Type ) { Object_Reference(Source); @@ -92,7 +122,7 @@ tSpiderVariable *Object_CastTo(int Type, tSpiderVariable *Source) return ERRPTR; case SS_DATATYPE_INTEGER: - ret = malloc(sizeof(tSpiderVariable)); + ret = malloc(sizeof(tSpiderObject)); ret->Type = SS_DATATYPE_INTEGER; ret->ReferenceCount = 1; switch(Source->Type) @@ -102,9 +132,14 @@ tSpiderVariable *Object_CastTo(int Type, tSpiderVariable *Source) case SS_DATATYPE_REAL: ret->Integer = Source->Real; break; default: fprintf(stderr, "Object_CastTo - Invalid cast from %i\n", Source->Type); + free(ret); + ret = ERRPTR; break; } break; + default: + fprintf(stderr, "BUG REPORT: Unimplemented cast target\n"); + break; } return ret; @@ -113,7 +148,7 @@ tSpiderVariable *Object_CastTo(int Type, tSpiderVariable *Source) /** * \brief Condenses a value down to a boolean */ -int Object_IsTrue(tSpiderVariable *Value) +int Object_IsTrue(tSpiderObject *Value) { switch(Value->Type) { @@ -139,33 +174,53 @@ int Object_IsTrue(tSpiderVariable *Value) return 0; } -tSpiderVariable *AST_ExecuteNode(tSpiderScript *Script, tAST_Node *Node) +/** + * \brief Execute an AST node and return its value + */ +tSpiderObject *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) { tAST_Node *node; - tSpiderVariable *ret, *tmpvar; - tSpiderVariable *op1, *op2; // Binary operations + tSpiderObject *ret, *tmpobj; + tSpiderObject *op1, *op2; // Binary operations int cmp; // Used in comparisons switch(Node->Type) { // No Operation - case NODETYPE_NOP: ret = NULL; break ; + case NODETYPE_NOP: ret = NULL; break; // Code block case NODETYPE_BLOCK: - ret = NULL; - for(node = Node->Block.FirstChild; node; node = node->NextSibling ) { - if(node->Type == NODETYPE_RETURN) { - ret = AST_ExecuteNode(Script, node); - break ; + tAST_BlockState blockInfo; + blockInfo.FirstVar = NULL; + blockInfo.Parent = Block; + blockInfo.Script = Block->Script; + ret = NULL; + for(node = Node->Block.FirstChild; node; node = node->NextSibling ) + { + if(node->Type == NODETYPE_RETURN) { + ret = AST_ExecuteNode(&blockInfo, node); + break ; + } + else { + tmpobj = AST_ExecuteNode(&blockInfo, node); + if(tmpobj == ERRPTR) { // Error check + ret = ERRPTR; + break ; + } + if(tmpobj) Object_Dereference(tmpobj); // Free unused value + } } - else { - tmpvar = AST_ExecuteNode(Script, node); - if(tmpvar == ERRPTR) return ERRPTR; // Error check - if(tmpvar) Object_Dereference(tmpvar); // Free unused value + // Clean up variables + while(blockInfo.FirstVar) + { + tAST_Variable *nextVar = blockInfo.FirstVar->Next; + Variable_Destroy( blockInfo.FirstVar ); + blockInfo.FirstVar = nextVar; } } + break; // Assignment @@ -174,28 +229,80 @@ tSpiderVariable *AST_ExecuteNode(tSpiderScript *Script, tAST_Node *Node) fprintf(stderr, "Syntax error: LVALUE of assignment is not a variable\n"); return ERRPTR; } - ret = AST_ExecuteNode(Script, Node->Assign.Value); - // TODO: Apply operation - Variable_SetValue( Script, Node->Assign.Dest->Variable.Name, ret ); + ret = AST_ExecuteNode(Block, Node->Assign.Value); + if(ret != ERRPTR) + Variable_SetValue( Block, Node->Assign.Dest->Variable.Name, ret ); break; + // Function Call case NODETYPE_FUNCTIONCALL: - // TODO: Find a function from the export list in variant - //SpiderScript_ExecuteMethod(Script, Node->FunctionCall.Name - ret = ERRPTR; - fprintf(stderr, "TODO: Implement function calls\n"); + { + int nParams = 0; + for(node = Node->FunctionCall.FirstArg; node; node = node->NextSibling) { + nParams ++; + } + // Logical block (used to allocate `params`) + { + tSpiderObject *params[nParams]; + int i=0; + for(node = Node->FunctionCall.FirstArg; node; node = node->NextSibling) { + params[i] = AST_ExecuteNode(Block, node); + if( params[i] == ERRPTR ) { + while(i--) Object_Dereference(params[i]); + ret = ERRPTR; + goto _return; + } + i ++; + } + + // Call the function (SpiderScript_ExecuteMethod does the + // required namespace handling) + ret = SpiderScript_ExecuteMethod(Block->Script, Node->FunctionCall.Name, nParams, params); + + // Dereference parameters + while(i--) Object_Dereference(params[i]); + + // falls out + } + } break; // Return's special handling happens elsewhere case NODETYPE_RETURN: - ret = AST_ExecuteNode(Script, Node->UniOp.Value); + ret = AST_ExecuteNode(Block, Node->UniOp.Value); break; - // Variable - case NODETYPE_VARIABLE: ret = Variable_GetValue( Script, Node->Variable.Name ); break; + // Define a variable + case NODETYPE_DEFVAR: + ret = NULL; + if( Variable_Define(Block, Node->DefVar.DataType, Node->DefVar.Name) == ERRPTR ) + ret = ERRPTR; + break; + // Variable + case NODETYPE_VARIABLE: + ret = Variable_GetValue( Block, Node->Variable.Name ); + break; + + // Cast a value to another + case NODETYPE_CAST: + ret = Object_CastTo( + Node->Cast.DataType, + AST_ExecuteNode(Block, Node->Cast.Value) + ); + break; + + // Index into an array + case NODETYPE_INDEX: + fprintf(stderr, "TODO: Array indexing\n"); + ret = ERRPTR; + break; + // TODO: Implement runtime constants - case NODETYPE_CONSTANT: ret = ERRPTR; break; + case NODETYPE_CONSTANT: + fprintf(stderr, "TODO: Runtime Constants\n"); + ret = ERRPTR; + break; // Constant Values case NODETYPE_STRING: ret = Object_CreateString( Node->String.Length, Node->String.Data ); break; case NODETYPE_INTEGER: ret = Object_CreateInteger( Node->Integer ); break; @@ -206,8 +313,14 @@ tSpiderVariable *AST_ExecuteNode(tSpiderScript *Script, tAST_Node *Node) case NODETYPE_LOGICALAND: // Logical AND (&&) case NODETYPE_LOGICALOR: // Logical OR (||) case NODETYPE_LOGICALXOR: // Logical XOR (^^) - op1 = AST_ExecuteNode(Script, Node->BinOp.Left); - op2 = AST_ExecuteNode(Script, Node->BinOp.Right); + op1 = AST_ExecuteNode(Block, Node->BinOp.Left); + if(op1 == ERRPTR) return ERRPTR; + op2 = AST_ExecuteNode(Block, Node->BinOp.Right); + if(op2 == ERRPTR) { + Object_Dereference(op1); + return ERRPTR; + } + switch( Node->Type ) { case NODETYPE_LOGICALAND: @@ -221,14 +334,23 @@ tSpiderVariable *AST_ExecuteNode(tSpiderScript *Script, tAST_Node *Node) break; default: break; } + + // Free intermediate objects + Object_Dereference(op1); + Object_Dereference(op2); break; // Comparisons case NODETYPE_EQUALS: case NODETYPE_LESSTHAN: case NODETYPE_GREATERTHAN: - op1 = AST_ExecuteNode(Script, Node->BinOp.Left); - op2 = AST_ExecuteNode(Script, Node->BinOp.Right); + op1 = AST_ExecuteNode(Block, Node->BinOp.Left); + if(op1 == ERRPTR) return ERRPTR; + op2 = AST_ExecuteNode(Block, Node->BinOp.Right); + if(op2 == ERRPTR) { + Object_Dereference(op1); + return ERRPTR; + } // No conversion done for NULL // TODO: Determine if this will ever be needed @@ -242,14 +364,19 @@ tSpiderVariable *AST_ExecuteNode(tSpiderScript *Script, tAST_Node *Node) // Convert types if( op1->Type != op2->Type ) { // If dynamically typed, convert op2 to op1's type - if(Script->Variant->bDyamicTyped) + if(Block->Script->Variant->bDyamicTyped) { - tmpvar = op2; + tmpobj = op2; op2 = Object_CastTo(op1->Type, op2); - Object_Dereference(tmpvar); + Object_Dereference(tmpobj); + if(op2 == ERRPTR) { + Object_Dereference(op1); + return ERRPTR; + } } // If statically typed, this should never happen, but catch it anyway else { + fprintf(stderr, "PARSER ERROR: Statically typed implicit cast\n"); ret = ERRPTR; break; } @@ -308,20 +435,30 @@ tSpiderVariable *AST_ExecuteNode(tSpiderScript *Script, tAST_Node *Node) case NODETYPE_BITSHIFTRIGHT: case NODETYPE_BITROTATELEFT: // Get operands - op1 = AST_ExecuteNode(Script, Node->BinOp.Left); - op2 = AST_ExecuteNode(Script, Node->BinOp.Right); + op1 = AST_ExecuteNode(Block, Node->BinOp.Left); + if(op1 == ERRPTR) return ERRPTR; + op2 = AST_ExecuteNode(Block, Node->BinOp.Right); + if(op2 == ERRPTR) { + Object_Dereference(op1); + return ERRPTR; + } // Convert types - if( op1->Type != op2->Type ) { + if( op1 && op2 && op1->Type != op2->Type ) { // If dynamically typed, convert op2 to op1's type - if(Script->Variant->bDyamicTyped) + if(Block->Script->Variant->bDyamicTyped) { - tmpvar = op2; + tmpobj = op2; op2 = Object_CastTo(op1->Type, op2); - Object_Dereference(tmpvar); + Object_Dereference(tmpobj); + if(op2 == ERRPTR) { + Object_Dereference(op1); + return ERRPTR; + } } // If statically typed, this should never happen, but catch it anyway else { + fprintf(stderr, "PARSER ERROR: Statically typed implicit cast\n"); ret = ERRPTR; break; } @@ -335,8 +472,11 @@ tSpiderVariable *AST_ExecuteNode(tSpiderScript *Script, tAST_Node *Node) case SS_DATATYPE_STRING: switch(Node->Type) { + case NODETYPE_ADD: // Concatenate + ret = Object_StringConcat(op1, op2); + break; default: - fprintf(stderr, "SpiderScript internal error: Exec,BinOP,String unknown op %i", Node->Type); + fprintf(stderr, "SpiderScript internal error: Exec,BinOP,String unknown op %i\n", Node->Type); ret = ERRPTR; break; } @@ -375,5 +515,111 @@ tSpiderVariable *AST_ExecuteNode(tSpiderScript *Script, tAST_Node *Node) // fprintf(stderr, "ERROR: SpiderScript AST_ExecuteNode Unimplemented %i\n", Node->Type); // break; } +_return: 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 + */ +tAST_Variable *Variable_Define(tAST_BlockState *Block, int Type, const char *Name) +{ + tAST_Variable *var, *prev = NULL; + + for( var = Block->FirstVar; var; prev = var, var = var->Next ) + { + if( strcmp(var->Name, Name) == 0 ) { + fprintf(stderr, "ERROR: Redefinition of variable '%s'\n", Name); + return ERRPTR; + } + } + + var = malloc( sizeof(tAST_Variable) + strlen(Name) + 1 ); + var->Next = NULL; + var->Type = Type; + var->Object = NULL; + strcpy(var->Name, Name); + + if(prev) prev->Next = var; + else Block->FirstVar = var; + + //printf("Defined variable %s (%i)\n", Name, Type); + + return var; +} + +/** + * \brief Set the value of a variable + */ +void Variable_SetValue(tAST_BlockState *Block, const char *Name, tSpiderObject *Value) +{ + tAST_Variable *var; + tAST_BlockState *bs; + + for( bs = Block; bs; bs = bs->Parent ) + { + for( var = bs->FirstVar; var; var = var->Next ) + { + if( strcmp(var->Name, Name) == 0 ) { + if( !Block->Script->Variant->bDyamicTyped + && (Value && var->Type != Value->Type) ) { + fprintf(stderr, "ERROR: Type mismatch assigning to '%s'\n", Name); + return ; + } + Object_Reference(Value); + Object_Dereference(var->Object); + var->Object = Value; + return ; + } + } + } + + if( Block->Script->Variant->bDyamicTyped ) + { + // Define variable + var = Variable_Define(Block, Value->Type, Name); + Object_Reference(Value); + var->Object = Value; + } + else + { + fprintf(stderr, "ERROR: Variable '%s' set while undefined\n", Name); + } +} + +/** + * \brief Get the value of a variable + */ +tSpiderObject *Variable_GetValue(tAST_BlockState *Block, const char *Name) +{ + tAST_Variable *var; + tAST_BlockState *bs; + + for( bs = Block; bs; bs = bs->Parent ) + { + for( var = bs->FirstVar; var; var = var->Next ) + { + if( strcmp(var->Name, Name) == 0 ) { + Object_Reference(var->Object); + return var->Object; + } + } + } + + fprintf(stderr, "ERROR: Variable '%s' used undefined\n", Name); + + return ERRPTR; +} + +/** + * \brief Destorys a variable + */ +void Variable_Destroy(tAST_Variable *Variable) +{ + Object_Dereference(Variable->Object); + free(Variable); +} diff --git a/Usermode/Libraries/libspiderscript.so_src/exports.c b/Usermode/Libraries/libspiderscript.so_src/exports.c new file mode 100644 index 00000000..bce3e79f --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/exports.c @@ -0,0 +1,48 @@ +/* + * Acess2 - SpiderScript + * - Script Exports (Lang. Namespace) + */ +#include +#include +#include +#include + +// === PROTOTYPES === +tSpiderObject *Exports_Lang_Struct(tSpiderScript *Script, int NArgs, tSpiderObject **Args); + +// === GLOBALS === + int gaExports_Lang_Struct_Args[] = {SS_DATATYPE_STRING,-1}; + +tSpiderFunction gaSpiderScript_Exports[] = { + {"Lang.Struct", Exports_Lang_Struct, gaExports_Lang_Struct_Args} +}; +const int giSpiderScript_NumExports = sizeof(gaSpiderScript_Exports)/sizeof(tSpiderFunction); + +// === CODE === +tSpiderObject *Exports_Lang_Struct(tSpiderScript *Script, int NArgs, tSpiderObject **Args) +{ + int i; + printf("Exports_Lang_Struct: (Script=%p, NArgs=%i, Args=%p)\n", Script, NArgs, Args); + + for( i = 0; i < NArgs; i ++ ) + { + printf(" Args[%i] = {Type: %i, ", i, Args[i]->Type); + switch(Args[i]->Type) + { + case SS_DATATYPE_INTEGER: + printf(" Integer: 0x%lx", Args[i]->Integer); + break; + case SS_DATATYPE_REAL: + printf(" Real: %f", Args[i]->Real); + break; + case SS_DATATYPE_STRING: + printf(" Length: %i, Data = '%s'", Args[i]->String.Length, Args[i]->String.Data); + break; + default: + break; + } + printf("}\n"); + } + + return NULL; +} diff --git a/Usermode/Libraries/libspiderscript.so_src/lex.c b/Usermode/Libraries/libspiderscript.so_src/lex.c index 6badd45b..25a7aa7e 100644 --- a/Usermode/Libraries/libspiderscript.so_src/lex.c +++ b/Usermode/Libraries/libspiderscript.so_src/lex.c @@ -1,14 +1,22 @@ /* - * Acess2 init + * SpiderScript * - Script Lexer */ #include "tokens.h" #include +#include +#include + +#define USE_SCOPE_CHAR 0 + +#define DEBUG 0 + +#define ARRAY_SIZE(x) ((sizeof(x))/(sizeof((x)[0]))) // === PROTOTYPES === int is_ident(char ch); - int isdigit(char ch); - int isspace(char ch); + int isdigit(int ch); + int isspace(int ch); int GetToken(tParser *File); // === CONSTANTS === @@ -17,8 +25,12 @@ const struct { const char *Name; } csaReservedWords[] = { {TOK_RWD_FUNCTION, "function"}, - {TOK_RWD_INTEGER, "integer"}, - {TOK_RWD_REAL, "string"} + {TOK_RWD_RETURN, "return"}, + {TOK_RWD_VOID, "void"}, + {TOK_RWD_OBJECT, "Object"}, + {TOK_RWD_INTEGER, "Integer"}, + {TOK_RWD_REAL, "Real"}, + {TOK_RWD_STRING, "String"} }; // === CODE === @@ -31,19 +43,43 @@ int GetToken(tParser *File) int ret; if( File->NextToken != -1 ) { + // Save Last + File->LastToken = File->Token; + File->LastTokenStr = File->TokenStr; + File->LastTokenLen = File->TokenLen; + File->LastLine = File->CurLine; + // Restore Next File->Token = File->NextToken; File->TokenStr = File->NextTokenStr; File->TokenLen = File->NextTokenLen; + File->CurLine = File->NextLine; + // Set State + File->CurPos = File->TokenStr + File->TokenLen; File->NextToken = -1; + { + char buf[ File->TokenLen + 1]; + memcpy(buf, File->TokenStr, File->TokenLen); + buf[File->TokenLen] = 0; + #if DEBUG + printf(" GetToken: FAST Return %i (%i long) (%s)\n", File->Token, File->TokenLen, buf); + #endif + } return File->Token; } + //printf(" GetToken: File=%p, File->CurPos = %p\n", File, File->CurPos); + // Clear whitespace (including comments) for( ;; ) { // Whitespace while( isspace( *File->CurPos ) ) + { + //printf("whitespace 0x%x, line = %i\n", *File->CurPos, File->CurLine); + if( *File->CurPos == '\n' ) + File->CurLine ++; File->CurPos ++; + } // # Line Comments if( *File->CurPos == '#' ) { @@ -61,9 +97,13 @@ int GetToken(tParser *File) // C-Style Block Comments if( *File->CurPos == '/' && File->CurPos[1] == '*' ) { - File->CurPos += 2; + File->CurPos += 2; // Eat the '/*' while( *File->CurPos && !(File->CurPos[-1] == '*' && *File->CurPos == '/') ) + { + if( *File->CurPos == '\n' ) File->CurLine ++; File->CurPos ++; + } + File->CurPos ++; // Eat the '/' continue ; } @@ -75,11 +115,14 @@ int GetToken(tParser *File) File->LastToken = File->Token; File->LastTokenStr = File->TokenStr; File->LastTokenLen = File->TokenLen; + File->LastLine = File->CurLine; // Read token File->TokenStr = File->CurPos; switch( *File->CurPos++ ) { + case '\0': ret = TOK_EOF; break; + // Operations case '/': ret = TOK_DIV; break; case '*': ret = TOK_MUL; break; @@ -95,9 +138,9 @@ int GetToken(tParser *File) // Strings case '"': - File->TokenStr ++; while( *File->CurPos && !(*File->CurPos == '"' && *File->CurPos != '\\') ) File->CurPos ++; + File->CurPos ++; ret = TOK_STR; break; @@ -111,7 +154,10 @@ int GetToken(tParser *File) // Core symbols case ';': ret = TOK_SEMICOLON; break; + case ',': ret = TOK_COMMA; break; + #if USE_SCOPE_CHAR case '.': ret = TOK_SCOPE; break; + #endif // Equals case '=': @@ -128,7 +174,6 @@ int GetToken(tParser *File) // Variables // \$[0-9]+ or \$[_a-zA-Z][_a-zA-Z0-9]* case '$': - File->TokenStr ++; // Numeric Variable if( isdigit( *File->CurPos ) ) { while( isdigit(*File->CurPos) ) @@ -136,7 +181,7 @@ int GetToken(tParser *File) } // Ident Variable else { - while( is_ident(*File->CurPos) ) + while( is_ident(*File->CurPos) || isdigit(*File->CurPos) ) File->CurPos ++; } ret = TOK_VARIABLE; @@ -144,6 +189,7 @@ int GetToken(tParser *File) // Default (Numbers and Identifiers) default: + File->CurPos --; // Numbers if( isdigit(*File->CurPos) ) { @@ -160,16 +206,49 @@ int GetToken(tParser *File) while( is_ident(*File->CurPos) || isdigit(*File->CurPos) ) File->CurPos ++; + // This is set later too, but we use it below + File->TokenLen = File->CurPos - File->TokenStr; ret = TOK_IDENT; + + // Check if it's a reserved word + { + char buf[File->TokenLen + 1]; + int i; + memcpy(buf, File->TokenStr, File->TokenLen); + buf[File->TokenLen] = 0; + for( i = 0; i < ARRAY_SIZE(csaReservedWords); i ++ ) + { + if(strcmp(csaReservedWords[i].Name, buf) == 0) { + ret = csaReservedWords[i].Value; + break ; + } + } + } + // If there's no match, just keep ret as TOK_IDENT + break; } // Syntax Error - ret = 0; + ret = TOK_INVAL; + + fprintf(stderr, "Syntax Error: Unknown symbol '%c'\n", *File->CurPos); + longjmp(File->JmpTarget, 1); + break; } // Return File->Token = ret; File->TokenLen = File->CurPos - File->TokenStr; + + #if DEBUG + { + char buf[ File->TokenLen + 1]; + memcpy(buf, File->TokenStr, File->TokenLen); + buf[File->TokenLen] = 0; + //printf(" GetToken: File->CurPos = %p\n", File->CurPos); + printf(" GetToken: Return %i (%i long) (%s)\n", ret, File->TokenLen, buf); + } + #endif return ret; } @@ -177,13 +256,20 @@ void PutBack(tParser *File) { if( File->LastToken == -1 ) { // ERROR: + fprintf(stderr, "INTERNAL ERROR: Putback when LastToken==-1\n"); + longjmp( File->JmpTarget, -1 ); return ; } + #if DEBUG + printf(" PutBack: Was on %i\n", File->Token); + #endif // Save + File->NextLine = File->CurLine; File->NextToken = File->Token; File->NextTokenStr = File->TokenStr; File->NextTokenLen = File->TokenLen; // Restore + File->CurLine = File->LastLine; File->Token = File->LastToken; File->TokenStr = File->LastTokenStr; File->TokenLen = File->LastTokenLen; @@ -194,6 +280,7 @@ void PutBack(tParser *File) int LookAhead(tParser *File) { + // TODO: Should I save the entire state here? int ret = GetToken(File); PutBack(File); return ret; @@ -207,19 +294,22 @@ int LookAhead(tParser *File) int is_ident(char ch) { if('a' <= ch && ch <= 'z') return 1; - if('Z' <= ch && ch <= 'Z') return 1; + if('A' <= ch && ch <= 'Z') return 1; if(ch == '_') return 1; + #if !USE_SCOPE_CHAR + if(ch == '.') return 1; + #endif if(ch < 0) return 1; return 0; } -int isdigit(char ch) +int isdigit(int ch) { if('0' <= ch && ch <= '9') return 1; return 0; } -int isspace(char ch) +int isspace(int ch) { if(' ' == ch) return 1; if('\t' == ch) return 1; diff --git a/Usermode/Libraries/libspiderscript.so_src/main.c b/Usermode/Libraries/libspiderscript.so_src/main.c index ce39c080..e5c225ce 100644 --- a/Usermode/Libraries/libspiderscript.so_src/main.c +++ b/Usermode/Libraries/libspiderscript.so_src/main.c @@ -10,6 +10,10 @@ // === IMPORTS === extern tAST_Script *Parse_Buffer(tSpiderVariant *Variant, char *Buffer); +extern const int giSpiderScript_NumExports; +extern tSpiderFunction gaSpiderScript_Exports[]; +extern tAST_Variable *Variable_Define(tAST_BlockState *Block, int Type, const char *Name); +extern void Variable_SetValue(tAST_BlockState *Block, const char *Name, tSpiderObject *Value); // === CODE === /** @@ -42,13 +46,21 @@ tSpiderScript *SpiderScript_ParseFile(tSpiderVariant *Variant, const char *Filen fLen = ftell(fp); fseek(fp, 0, SEEK_SET); - data = malloc(fLen); + // Allocate and read data + data = malloc(fLen + 1); if(!data) return NULL; fread(data, fLen, 1, fp); + data[fLen] = '\0'; fclose(fp); + ret->CurNamespace = NULL; ret->Script = Parse_Buffer(Variant, data); + if( ret->Script == NULL ) { + free(data); + free(ret); + return NULL; + } free(data); @@ -57,21 +69,105 @@ tSpiderScript *SpiderScript_ParseFile(tSpiderVariant *Variant, const char *Filen /** * \brief Execute a script function - * \todo Arguments? + * \param Script Script context to execute in + * \param Function Function name to execute + * \param NArguments Number of arguments to pass + * \param Arguments Arguments passed */ -tSpiderVariable *SpiderScript_ExecuteMethod(tSpiderScript *Script, const char *Function) +tSpiderObject *SpiderScript_ExecuteMethod(tSpiderScript *Script, + const char *Function, int NArguments, tSpiderObject **Arguments) { - tAST_Function *fcn = Script->Script->Functions; + char *trueName = NULL; + int i; + int bFound = 0; // Used to keep nesting levels down + tSpiderObject *ret = ERRPTR; + + // Handle namespaces + if( Function[0] == '.' ) { + trueName = (char*)&Function[1]; + } + else if( !Script->CurNamespace ) { + trueName = (char*)Function; + } + else { + int len = strlen(Script->CurNamespace) + 1 + strlen(Function); + trueName = malloc( len + 1 ); + strcpy(trueName, Script->CurNamespace); + strcat(trueName, "."); + strcat(trueName, Function); + } + + // First: Find the function in the script + { + tAST_Function *fcn = Script->Script->Functions; + for( ; fcn; fcn = fcn->Next ) { + if( strcmp(fcn->Name, trueName) == 0 ) + break; + } + // Execute! + if(fcn) { + tAST_BlockState bs; + bs.FirstVar = NULL; //< TODO: Parameters + bs.Parent = NULL; + bs.Script = Script; + { + tAST_Node *arg; + int i = 0; + for( arg = fcn->Arguments; arg; arg = arg->NextSibling, i++ ) + { + // TODO: Type checks + Variable_Define(&bs, arg->DefVar.DataType, arg->DefVar.Name); + if( i >= NArguments ) break; // TODO: Return gracefully + Variable_SetValue(&bs, arg->DefVar.Name, Arguments[i]); + } + } + ret = AST_ExecuteNode(&bs, fcn->Code); + bFound = 1; + } + } + + // Didn't find it in script? + if(!bFound) + { + // Second: Search the variant's exports + for( i = 0; i < Script->Variant->NFunctions; i ++ ) + { + if( strcmp( Script->Variant->Functions[i].Name, trueName) == 0 ) + break; + } + // Execute! + if(i < Script->Variant->NFunctions) { + ret = Script->Variant->Functions[i].Handler( Script, NArguments, Arguments ); + bFound = 1; + } + } - // Find the function - for( ; fcn; fcn = fcn->Next ) { - if( strcmp(fcn->Name, Function) == 0 ) - break; + // Not in variant exports? Search the language internal ones + if(!bFound) + { + for( i = 0; i < giSpiderScript_NumExports; i ++ ) + { + if( strcmp( gaSpiderScript_Exports[i].Name, trueName ) == 0 ) + break; + } + // Execute! + if(i < giSpiderScript_NumExports) { + ret = gaSpiderScript_Exports[i].Handler( Script, NArguments, Arguments ); + bFound = 1; + } } - if(!fcn) return NULL; - // Execute! - return AST_ExecuteNode(Script, fcn->Code); + // Not found? + if(!bFound) + { + fprintf(stderr, "Undefined reference to '%s'\n", trueName); + } + + if( trueName != Function && trueName != &Function[1] ) + free(trueName); + + return ret; + } /** @@ -99,4 +195,9 @@ void SpiderScript_Free(tSpiderScript *Script) free( fcn ); fcn = nextFcn; } + + // TODO: Pass this off to AST for a proper cleanup + free(Script->Script); + + free(Script); } diff --git a/Usermode/Libraries/libspiderscript.so_src/parse.c b/Usermode/Libraries/libspiderscript.so_src/parse.c index 2ccf5d87..31dabade 100644 --- a/Usermode/Libraries/libspiderscript.so_src/parse.c +++ b/Usermode/Libraries/libspiderscript.so_src/parse.c @@ -6,13 +6,17 @@ #include #include #include +#define WANT_TOKEN_STRINGS 1 #include "tokens.h" #include "ast.h" +#define DEBUG 0 + // === PROTOTYPES === tAST_Script *Parse_Buffer(tSpiderVariant *Variant, char *Buffer); tAST_Node *Parse_DoCodeBlock(tParser *Parser); -tAST_Node *Parse_DoExpr(tParser *Parser); +tAST_Node *Parse_DoBlockLine(tParser *Parser); +tAST_Node *Parse_GetVarDef(tParser *Parser, int Type); tAST_Node *Parse_DoExpr0(tParser *Parser); // Assignment tAST_Node *Parse_DoExpr1(tParser *Parser); // Boolean Operators @@ -30,7 +34,7 @@ tAST_Node *Parse_GetNumeric(tParser *Parser); tAST_Node *Parse_GetVariable(tParser *Parser); tAST_Node *Parse_GetIdent(tParser *Parser); -void SyntaxAssert(int Have, int Want); +void SyntaxAssert(tParser *Parser, int Have, int Want); // === CODE === /** @@ -44,35 +48,99 @@ tAST_Script *Parse_Buffer(tSpiderVariant *Variant, char *Buffer) tAST_Node *mainCode; char *name; tAST_Function *fcn; + int type; + + #if DEBUG >= 2 + printf("Parse_Buffer: (Variant=%p, Buffer=%p)\n", Variant, Buffer); + #endif // Initialise parser + parser.LastToken = -1; + parser.NextToken = -1; + parser.CurLine = 1; parser.BufStart = Buffer; parser.CurPos = Buffer; ret = AST_NewScript(); mainCode = AST_NewCodeBlock(); - for(;;) + if( setjmp( parser.JmpTarget ) != 0 ) + { + AST_FreeNode( mainCode ); + return NULL; + } + + while(Parser->Token != TOK_EOF) { switch( GetToken(Parser) ) { - case TOK_RWD_FUNCTION: - // Define a function - SyntaxAssert( GetToken(Parser), TOK_IDENT ); + case TOK_EOF: + break; + + // Typed variables/functions + case TOKEN_GROUP_TYPES: + { + int tok, type; + TOKEN_GET_DATATYPE(type, Parser->Token); + + tok = GetToken(Parser); + // Define a function (pass on to the other function definition code) + if( tok == TOK_IDENT ) { + goto defFcn; + } + // Define a variable + else if( tok == TOK_VARIABLE ) { + AST_AppendNode( mainCode, Parse_GetVarDef(Parser, type) ); + SyntaxAssert(Parser, GetToken(Parser), TOK_SEMICOLON); + } + else { + fprintf(stderr, "ERROR: Unexpected %s, expected TOK_IDENT or TOK_VARIABLE\n", + csaTOKEN_NAMES[tok]); + } + } + break; + + // Define a function + case TOK_RWD_FUNCTION: + if( !Variant->bDyamicTyped ) { + fprintf(stderr, "ERROR: Attempt to create a dynamic function\n"); + longjmp(Parser->JmpTarget, -1); + } + type = SS_DATATYPE_DYNAMIC; + SyntaxAssert(Parser, GetToken(Parser), TOK_IDENT ); + defFcn: name = strndup( Parser->TokenStr, Parser->TokenLen ); fcn = AST_AppendFunction( ret, name ); + #if DEBUG + printf("DefFCN %s\n", name); + #endif free(name); - SyntaxAssert( GetToken(Parser), TOK_PAREN_OPEN ); - // TODO: Arguments - SyntaxAssert( GetToken(Parser), TOK_PAREN_CLOSE ); + // Get arguments + SyntaxAssert(Parser, GetToken(Parser), TOK_PAREN_OPEN ); + if( LookAhead(Parser) != TOK_PAREN_CLOSE ) + { + do { + type = SS_DATATYPE_DYNAMIC; + GetToken(Parser); + // Non dynamic typed variants must use data types + if( !Variant->bDyamicTyped ) { + TOKEN_GET_DATATYPE(type, Parser->Token); + GetToken(Parser); + } + AST_AppendFunctionArg(fcn, Parse_GetVarDef(Parser, type)); + } while(GetToken(Parser) == TOK_COMMA); + } + else + GetToken(Parser); + SyntaxAssert(Parser, Parser->Token, TOK_PAREN_CLOSE ); AST_SetFunctionCode( fcn, Parse_DoCodeBlock(Parser) ); break; default: - PutBack(&parser); - AST_AppendNode( mainCode, Parse_DoExpr(Parser) ); + PutBack(Parser); + AST_AppendNode( mainCode, Parse_DoBlockLine(Parser) ); break; } } @@ -80,6 +148,8 @@ tAST_Script *Parse_Buffer(tSpiderVariant *Variant, char *Buffer) fcn = AST_AppendFunction( ret, "" ); AST_SetFunctionCode( fcn, mainCode ); + printf("---- %p parsed as SpiderScript ----\n", Buffer); + return ret; } @@ -89,24 +159,93 @@ tAST_Script *Parse_Buffer(tSpiderVariant *Variant, char *Buffer) tAST_Node *Parse_DoCodeBlock(tParser *Parser) { tAST_Node *ret; - SyntaxAssert( GetToken(Parser), TOK_BRACE_OPEN ); + SyntaxAssert(Parser, GetToken(Parser), TOK_BRACE_OPEN ); ret = AST_NewCodeBlock(); - while( GetToken(Parser) != TOK_BRACE_CLOSE ) + while( LookAhead(Parser) != TOK_BRACE_CLOSE ) + { + AST_AppendNode( ret, Parse_DoBlockLine(Parser) ); + } + GetToken(Parser); // Omnomnom + return ret; +} + +/** + * \brief Parse a line in a block + */ +tAST_Node *Parse_DoBlockLine(tParser *Parser) +{ + tAST_Node *ret; + + //printf("Parse_DoBlockLine: Line %i\n", Parser->CurLine); + + switch(LookAhead(Parser)) { - AST_AppendNode( ret, Parse_DoExpr(Parser) ); - SyntaxAssert( GetToken(Parser), TOK_SEMICOLON ); + + // Return from a method + case TOK_RWD_RETURN: + //printf("return\n"); + GetToken(Parser); + ret = AST_NewUniOp(NODETYPE_RETURN, Parse_DoExpr0(Parser)); + break; + + // Define Variables + case TOK_RWD_OBJECT: + case TOK_RWD_STRING: + case TOK_RWD_REAL: + case TOK_RWD_INTEGER: + { + int type; + + switch(GetToken(Parser)) + { + case TOK_RWD_INTEGER: type = SS_DATATYPE_INTEGER; break; + case TOK_RWD_OBJECT: type = SS_DATATYPE_OBJECT; break; + case TOK_RWD_REAL: type = SS_DATATYPE_REAL; break; + case TOK_RWD_STRING: type = SS_DATATYPE_STRING; break; + } + + SyntaxAssert(Parser, GetToken(Parser), TOK_VARIABLE); + + ret = Parse_GetVarDef(Parser, type); + } + break; + + // Default + default: + //printf("exp0\n"); + ret = Parse_DoExpr0(Parser); + break; } + + SyntaxAssert(Parser, GetToken(Parser), TOK_SEMICOLON ); return ret; } /** - * \brief Parse an expression + * \brief Get a variable definition */ -tAST_Node *Parse_DoExpr(tParser *Parser) +tAST_Node *Parse_GetVarDef(tParser *Parser, int Type) { - return Parse_DoExpr0(Parser); + char name[Parser->TokenLen]; + tAST_Node *ret; + + SyntaxAssert(Parser, Parser->Token, TOK_VARIABLE); + + // copy the name (trimming the $) + memcpy(name, Parser->TokenStr+1, Parser->TokenLen-1); + name[Parser->TokenLen-1] = 0; + // Define the variable + ret = AST_NewDefineVar(Type, name); + // Handle arrays + while( LookAhead(Parser) == TOK_SQUARE_OPEN ) + { + GetToken(Parser); + AST_AppendNode(ret, Parse_DoExpr0(Parser)); + SyntaxAssert(Parser, GetToken(Parser), TOK_SQUARE_CLOSE); + } + return ret; } /** @@ -126,14 +265,18 @@ tAST_Node *Parse_DoExpr0(tParser *Parser) #if 0 case TOK_DIV_EQU: GetToken(Parser); // Eat Token - ret = AST_NewAssignOp(ret, NODETYPE_DIVIDE, DoExpr0(Parser)); + ret = AST_NewAssign(NODETYPE_DIVIDE, ret, Parse_DoExpr0(Parser)); break; case TOK_MULT_EQU: GetToken(Parser); // Eat Token - ret = AST_NewAssignOp(ret, NODETYPE_MULTIPLY, DoExpr0(Parser)); + ret = AST_NewAssign(NODETYPE_MULTIPLY, ret, Parse_DoExpr0(Parser)); break; #endif - default: break; + default: + #if DEBUG >= 2 + printf("Parse_DoExpr0: Parser->Token = %i\n", Parser->Token); + #endif + break; } return ret; } @@ -290,12 +433,18 @@ tAST_Node *Parse_DoExpr6(tParser *Parser) // -------------------- tAST_Node *Parse_DoParen(tParser *Parser) { + #if DEBUG >= 2 + printf("Parse_DoParen: (Parser=%p)\n", Parser); + #endif if(LookAhead(Parser) == TOK_PAREN_OPEN) { tAST_Node *ret; GetToken(Parser); + + // TODO: Handle casts here + ret = Parse_DoExpr0(Parser); - SyntaxAssert(GetToken(Parser), TOK_PAREN_CLOSE); + SyntaxAssert(Parser, GetToken(Parser), TOK_PAREN_CLOSE); return ret; } else @@ -309,6 +458,10 @@ tAST_Node *Parse_DoValue(tParser *Parser) { int tok = LookAhead(Parser); + #if DEBUG >= 2 + printf("Parse_DoValue: tok = %i\n", tok); + #endif + switch(tok) { case TOK_STR: return Parse_GetString(Parser); @@ -317,8 +470,9 @@ tAST_Node *Parse_DoValue(tParser *Parser) case TOK_VARIABLE: return Parse_GetVariable(Parser); default: - //ParseError2( tok, TOK_T_VALUE ); - return NULL; + fprintf(stderr, "Syntax Error: Unexpected %s on line %i, Expected TOK_T_VALUE\n", + csaTOKEN_NAMES[tok], Parser->CurLine); + longjmp( Parser->JmpTarget, -1 ); } } @@ -350,12 +504,24 @@ tAST_Node *Parse_GetNumeric(tParser *Parser) */ tAST_Node *Parse_GetVariable(tParser *Parser) { - char *name; tAST_Node *ret; - SyntaxAssert( GetToken(Parser), TOK_VARIABLE ); - name = strndup( Parser->TokenStr+1, Parser->TokenLen-1 ); - ret = AST_NewVariable( name ); - free(name); + SyntaxAssert( Parser, GetToken(Parser), TOK_VARIABLE ); + { + char name[Parser->TokenLen]; + memcpy(name, Parser->TokenStr+1, Parser->TokenLen-1); + name[Parser->TokenLen-1] = 0; + ret = AST_NewVariable( name ); + #if DEBUG >= 2 + printf("Parse_GetVariable: name = '%s'\n", name); + #endif + } + // Handle array references + while( LookAhead(Parser) == TOK_SQUARE_OPEN ) + { + GetToken(Parser); + ret = AST_NewBinOp(NODETYPE_INDEX, ret, Parse_DoExpr0(Parser)); + SyntaxAssert(Parser, GetToken(Parser), TOK_SQUARE_CLOSE); + } return ret; } @@ -366,11 +532,22 @@ tAST_Node *Parse_GetIdent(tParser *Parser) { tAST_Node *ret; char *name; - SyntaxAssert( GetToken(Parser), TOK_IDENT ); + SyntaxAssert(Parser, GetToken(Parser), TOK_IDENT ); name = strndup( Parser->TokenStr, Parser->TokenLen ); + #if 0 + while( GetToken(Parser) == TOK_SCOPE ) + { + ret = AST_New + } + PutBack(Parser); + #endif + if( GetToken(Parser) == TOK_PAREN_OPEN ) { + #if DEBUG >= 2 + printf("Parse_GetIdent: Calling '%s'\n", name); + #endif // Function Call ret = AST_NewFunctionCall( name ); // Read arguments @@ -378,13 +555,22 @@ tAST_Node *Parse_GetIdent(tParser *Parser) { PutBack(Parser); do { + #if DEBUG >= 2 + printf(" Parse_GetIdent: Argument\n"); + #endif AST_AppendFunctionCallArg( ret, Parse_DoExpr0(Parser) ); } while(GetToken(Parser) == TOK_COMMA); - SyntaxAssert( Parser->Token, TOK_PAREN_CLOSE ); + SyntaxAssert( Parser, Parser->Token, TOK_PAREN_CLOSE ); + #if DEBUG >= 2 + printf(" Parse_GetIdent: All arguments parsed\n"); + #endif } } else { // Runtime Constant + #if DEBUG >= 2 + printf("Parse_GetIdent: Referencing '%s'\n", name); + #endif PutBack(Parser); ret = AST_NewConstant( name ); } @@ -396,12 +582,12 @@ tAST_Node *Parse_GetIdent(tParser *Parser) /** * \brief Check for an error */ -void SyntaxAssert(int Have, int Want) +void SyntaxAssert(tParser *Parser, int Have, int Want) { if(Have != Want) { - fprintf(stderr, "ERROR: Expected %i, got %i\n", Want, Have); - //longjmp(jmpTarget, 1); - return; + fprintf(stderr, "ERROR: SyntaxAssert Failed, Expected %s(%i), got %s(%i) on line %i\n", + csaTOKEN_NAMES[Want], Want, csaTOKEN_NAMES[Have], Have, Parser->CurLine); + longjmp(Parser->JmpTarget, -1); } } diff --git a/Usermode/Libraries/libspiderscript.so_src/tokens.h b/Usermode/Libraries/libspiderscript.so_src/tokens.h index 44c3a81f..9ecb720f 100644 --- a/Usermode/Libraries/libspiderscript.so_src/tokens.h +++ b/Usermode/Libraries/libspiderscript.so_src/tokens.h @@ -3,6 +3,8 @@ #ifndef _TOKENS_H_ #define _TOKENS_H_ +#include + // === TYPES === typedef struct { @@ -10,14 +12,19 @@ typedef struct char *BufStart; char *CurPos; + int LastLine; int LastToken, LastTokenLen; char *LastTokenStr; + int NextLine; int NextToken, NextTokenLen; char *NextTokenStr; + int CurLine; int Token, TokenLen; char *TokenStr; + + jmp_buf JmpTarget; } tParser; // === FUNCTIONS === @@ -37,6 +44,11 @@ enum eTokens TOK_IDENT, TOK_RWD_FUNCTION, + TOK_RWD_NAMESPACE, + TOK_RWD_RETURN, + + TOK_RWD_VOID, + TOK_RWD_OBJECT, TOK_RWD_STRING, TOK_RWD_INTEGER, TOK_RWD_REAL, @@ -67,4 +79,69 @@ enum eTokens TOK_LAST }; +#define TOKEN_GROUP_TYPES TOK_RWD_VOID:\ + case TOK_RWD_OBJECT:\ + case TOK_RWD_INTEGER:\ + case TOK_RWD_STRING:\ + case TOK_RWD_REAL +#define TOKEN_GROUP_TYPES_STR "TOK_RWD_VOID, TOK_RWD_OBJECT, TOK_RWD_INTEGER, TOK_RWD_STRING or TOK_RWD_REAL" + +#define TOKEN_GET_DATATYPE(_type, _tok) do { switch(_tok) {\ + case TOK_RWD_VOID: _type = SS_DATATYPE_UNDEF; break;\ + case TOK_RWD_INTEGER:_type = SS_DATATYPE_INTEGER; break;\ + case TOK_RWD_OBJECT: _type = SS_DATATYPE_OBJECT; break;\ + case TOK_RWD_REAL: _type = SS_DATATYPE_REAL; break;\ + case TOK_RWD_STRING: _type = SS_DATATYPE_STRING; break;\ + default:fprintf(stderr,\ + "ERROR: Unexpected %s, expected "TOKEN_GROUP_TYPES_STR"\n",csaTOKEN_NAMES[Parser->Token]);\ + break;\ + } } while(0) + +# if WANT_TOKEN_STRINGS +const char * const csaTOKEN_NAMES[] = { + "TOK_INVAL", + "TOK_EOF", + + "TOK_STR", + "TOK_INTEGER", + "TOK_VARIABLE", + "TOK_IDENT", + + "TOK_RWD_FUNCTION", + "TOK_RWD_NAMESPACE", + "TOK_RWD_RETURN", + + "TOK_RWD_VOID", + "TOK_RWD_OBJECT", + "TOK_RWD_STRING", + "TOK_RWD_INTEGER", + "TOK_RWD_REAL", + + "TOK_ASSIGN", + "TOK_SEMICOLON", + "TOK_COMMA", + "TOK_SCOPE", + "TOK_ELEMENT", + + "TOK_EQUALS", + "TOK_LT", "TOK_LTE", + "TOK_GT", "TOK_GTE", + + "TOK_DIV", "TOK_MUL", + "TOK_PLUS", "TOK_MINUS", + "TOK_SHL", "TOK_SHR", + "TOK_LOGICAND", "TOK_LOGICOR", "TOK_LOGICXOR", + "TOK_AND", "TOK_OR", "TOK_XOR", + + "TOK_PAREN_OPEN", + "TOK_PAREN_CLOSE", + "TOK_BRACE_OPEN", + "TOK_BRACE_CLOSE", + "TOK_SQUARE_OPEN", + "TOK_SQUARE_CLOSE", + + "TOK_LAST" +}; +# endif + #endif -- 2.20.1