From efa38e0d56b1b620b6f4e5c4f91abc483a3065e2 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 7 Aug 2010 08:45:12 +0800 Subject: [PATCH] Fixed behavior of VTerm when driver is set at runtime Writing a script parser for use with init (SpiderScript) NOTE: Init does not compile atm, please go to an old commit or remove the references to spiderscript in it to compile --- Kernel/arch/x86_64/Makefile | 6 +- Kernel/arch/x86_64/include/mm_virt.h | 2 +- Kernel/drv/vga.c | 1 - Kernel/drv/vterm.c | 26 +- Kernel/include/modules.h | 7 + Kernel/lib.c | 3 + Kernel/modules.c | 32 +- Usermode/Applications/init_src/Makefile | 2 +- Usermode/Applications/init_src/main.c | 60 +++ Usermode/Libraries/Makefile.tpl | 11 +- Usermode/Libraries/libc.so_src/string.c | 18 + .../Libraries/libspiderscript.so_src/Makefile | 13 + .../Libraries/libspiderscript.so_src/ast.c | 256 +++++++++++ .../Libraries/libspiderscript.so_src/ast.h | 149 +++++++ .../libspiderscript.so_src/bytecode.h | 29 ++ .../libspiderscript.so_src/exec_ast.c | 379 ++++++++++++++++ .../Libraries/libspiderscript.so_src/lex.c | 230 ++++++++++ .../Libraries/libspiderscript.so_src/main.c | 102 +++++ .../Libraries/libspiderscript.so_src/parse.c | 407 ++++++++++++++++++ .../Libraries/libspiderscript.so_src/tokens.h | 70 +++ Usermode/include/string.h | 1 + 21 files changed, 1787 insertions(+), 17 deletions(-) create mode 100644 Usermode/Libraries/libspiderscript.so_src/Makefile create mode 100644 Usermode/Libraries/libspiderscript.so_src/ast.c create mode 100644 Usermode/Libraries/libspiderscript.so_src/ast.h create mode 100644 Usermode/Libraries/libspiderscript.so_src/bytecode.h create mode 100644 Usermode/Libraries/libspiderscript.so_src/exec_ast.c create mode 100644 Usermode/Libraries/libspiderscript.so_src/lex.c create mode 100644 Usermode/Libraries/libspiderscript.so_src/main.c create mode 100644 Usermode/Libraries/libspiderscript.so_src/parse.c create mode 100644 Usermode/Libraries/libspiderscript.so_src/tokens.h diff --git a/Kernel/arch/x86_64/Makefile b/Kernel/arch/x86_64/Makefile index 647a49af..22ad8c93 100644 --- a/Kernel/arch/x86_64/Makefile +++ b/Kernel/arch/x86_64/Makefile @@ -14,9 +14,9 @@ ifeq ($(ARCH),amd64) ASFLAGS += -D AMD64=1 CPPFLAGS += -DAMD64=1 else - ifeq ($(ARCH),ia64) - ASFLAGS += -D AMD64=0 -D IA64=1 - CPPFLAGS += -DAMD64=0 -DIA64=1 + ifeq ($(ARCH),x86_64) + ASFLAGS += -D AMD64=0 -D X86_64=1 + CPPFLAGS += -DAMD64=0 -DX86_64=1 endif endif diff --git a/Kernel/arch/x86_64/include/mm_virt.h b/Kernel/arch/x86_64/include/mm_virt.h index f1e0794b..ca508556 100644 --- a/Kernel/arch/x86_64/include/mm_virt.h +++ b/Kernel/arch/x86_64/include/mm_virt.h @@ -28,7 +28,7 @@ * C000 00000000 - D000 00000000 44 16 TiB Hardware Mappings * D000 00000000 - D080 00000000 39 512 GiB Per-Process Data * D080 00000000 - D100 00000000 39 512 GiB Kernel Supplied User Code - * ---- GAP ---- 9 TiB + * ---- GAP ---- 15 TiB * E000 00000000 - E400 00000000 42 4 TiB Physical Page Reference Counts (2**40 = 2**52 bytes) * E400 00000000 - E480 00000000 39 512 GiB Physical Page Bitmap (1 page per bit) * E480 00000000 - E500 00000000 39 512 GiB Physical Page DblAlloc Bitmap (1 page per bit) diff --git a/Kernel/drv/vga.c b/Kernel/drv/vga.c index ff83e539..ac00c78b 100644 --- a/Kernel/drv/vga.c +++ b/Kernel/drv/vga.c @@ -123,7 +123,6 @@ Uint8 VGA_int_GetColourNibble(Uint16 col) col = ((col>>2)&3) | ((col>>4)&0xC) | ((col>>6)&0x30); bright = ( (col & 2 ? 1 : 0) + (col & 8 ? 1 : 0) + (col & 32 ? 1 : 0) ) / 2; - switch(col) { // Black diff --git a/Kernel/drv/vterm.c b/Kernel/drv/vterm.c index b940afa8..094903cd 100644 --- a/Kernel/drv/vterm.c +++ b/Kernel/drv/vterm.c @@ -148,12 +148,10 @@ int VT_Install(char **Arguments) val = arg + strpos(arg, '='); *val++ = '\0'; if( strcmp(opt, "Video") == 0 ) { - if(gsVT_OutputDevice) free(gsVT_OutputDevice); - gsVT_OutputDevice = strdup(val); + gsVT_OutputDevice = val; } else if( strcmp(opt, "Input") == 0 ) { - if(gsVT_InputDevice) free(gsVT_InputDevice); - gsVT_InputDevice = strdup(val); + gsVT_InputDevice = val; } else if( strcmp(opt, "Width") == 0 ) { giVT_RealWidth = atoi( val ); @@ -167,9 +165,25 @@ int VT_Install(char **Arguments) } } + if(gsVT_OutputDevice) Modules_InitialiseBuiltin( gsVT_OutputDevice ); + if(gsVT_InputDevice) Modules_InitialiseBuiltin( gsVT_InputDevice ); + // Apply Defaults - if(!gsVT_OutputDevice) gsVT_OutputDevice = "/Devices/"DEFAULT_OUTPUT; - if(!gsVT_InputDevice) gsVT_InputDevice = "/Devices/"DEFAULT_INPUT; + if(!gsVT_OutputDevice) gsVT_OutputDevice = DEFAULT_OUTPUT; + if(!gsVT_InputDevice) gsVT_InputDevice = DEFAULT_INPUT; + + // Create paths + { + char *tmp; + tmp = malloc( 9 + strlen(gsVT_OutputDevice) + 1 ); + strcpy(tmp, "/Devices/"); + strcpy(&tmp[9], gsVT_OutputDevice); + gsVT_OutputDevice = tmp; + tmp = malloc( 9 + strlen(gsVT_InputDevice) + 1 ); + strcpy(tmp, "/Devices/"); + strcpy(&tmp[9], gsVT_InputDevice); + gsVT_InputDevice = tmp; + } Log_Log("VTerm", "Using '%s' as output", gsVT_OutputDevice); Log_Log("VTerm", "Using '%s' as input", gsVT_InputDevice); diff --git a/Kernel/include/modules.h b/Kernel/include/modules.h index fb737bee..db3f91ad 100644 --- a/Kernel/include/modules.h +++ b/Kernel/include/modules.h @@ -113,4 +113,11 @@ typedef struct sModuleLoader */ extern int Module_RegisterLoader(tModuleLoader *Loader); +/** + * \brief Initialise a named builtin module + * \param Name Module name to initialise + * \return -1 on not existing, 0 if the module initialised (or if it was already initialised) + */ +extern int Modules_InitialiseBuiltin(const char *Name); + #endif diff --git a/Kernel/lib.c b/Kernel/lib.c index 586a2e8f..88ab3677 100644 --- a/Kernel/lib.c +++ b/Kernel/lib.c @@ -180,6 +180,9 @@ void itoa(char *buf, Uint num, int base, int minLength, char pad) if(pos==__maxlen){return pos;}\ if(__s){__s[pos++]=ch;}else{pos++;}\ }while(0) +/** + * \brief VArg String Number Print Formatted + */ int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args) { char c, pad = ' '; diff --git a/Kernel/modules.c b/Kernel/modules.c index 009ade37..0409711d 100644 --- a/Kernel/modules.c +++ b/Kernel/modules.c @@ -17,7 +17,7 @@ void Modules_SetBuiltinParams(char *Name, char *ArgString); int Module_LoadMem(void *Buffer, Uint Length, char *ArgString); int Module_LoadFile(char *Path, char *ArgString); int Module_int_ResolveDeps(tModule *Info); - int Module_IsLoaded(char *Name); + int Module_IsLoaded(const char *Name); // === EXPORTS === EXPORT(Module_RegisterLoader); @@ -252,6 +252,32 @@ void Modules_LoadBuiltins() free(gasBuiltinModuleArgs); } +/** + * \brief Initialise a builtin module given it's name + * \example Used by VTerm to load an alternate video driver at runtime + */ +int Modules_InitialiseBuiltin(const char *Name) +{ + int i; + + // Check if it's loaded + if( Module_IsLoaded(Name) ) + return 0; + + if( !gapBuiltinModules ) + Modules_int_GetBuiltinArray(); + + for( i = 0; i < giNumBuiltinModules; i++ ) + { + if( strcmp(gapBuiltinModules[i]->Name, Name) == 0 ) { + return Module_int_Initialise(gapBuiltinModules[i], + (gasBuiltinModuleArgs ? gasBuiltinModuleArgs[i] : NULL) + ); + } + } + return -1; +} + /** * \brief Sets the parameters for a builtin module */ @@ -383,11 +409,11 @@ int Module_int_ResolveDeps(tModule *Info) } /** - * \fn int Module_IsLoaded(char *Name) + * \fn int Module_IsLoaded(const char *Name) * \brief Checks if a module is loaded * \param Name Name of module to find */ -int Module_IsLoaded(char *Name) +int Module_IsLoaded(const char *Name) { tModule *mod = gLoadedModules; diff --git a/Usermode/Applications/init_src/Makefile b/Usermode/Applications/init_src/Makefile index f60ea49a..641503d2 100644 --- a/Usermode/Applications/init_src/Makefile +++ b/Usermode/Applications/init_src/Makefile @@ -4,7 +4,7 @@ CPPFLAGS += CFLAGS += -Wall -Werror -O3 -LDFLAGS += +LDFLAGS += -lspiderscript BIN = ../init OBJ = main.o diff --git a/Usermode/Applications/init_src/main.c b/Usermode/Applications/init_src/main.c index d9a3dad5..eb2e9318 100644 --- a/Usermode/Applications/init_src/main.c +++ b/Usermode/Applications/init_src/main.c @@ -2,12 +2,49 @@ * Acess2 System Init Task */ #include +#include +#include +#include +//#include "common.h" // === CONSTANTS === #define NULL ((void*)0) #define NUM_TERMS 4 #define DEFAULT_TERMINAL "/Devices/VTerm/0" #define DEFAULT_SHELL "/Acess/SBin/login" +#define DEFAULT_SCRIPT "/Acess/Conf/BootConf.isc" + +#define ARRAY_SIZE(x) ((sizeof(x))/(sizeof((x)[0]))) + +// === PROTOTYPES === +tSpiderVariable *Script_System_IO_Open(tSpiderScript *, int, tSpiderVariable *); + +// === GLOBALS === +tSpiderFunction gaScriptNS_IO_Fcns[] = { + {"Open", Script_System_IO_Open} +}; +tSpiderNamespace gaScriptNS_System[] = { + { + "IO", + 0, NULL, + ARRAY_SIZE(gaScriptNS_IO_Fcns), gaScriptNS_IO_Fcns, + 0, NULL + } +}; + +tSpiderNamespace gaScriptNamespaces[] = { + { + "System", + ARRAY_SIZE(gaScriptNS_System), gaScriptNS_System, + 0, NULL, + 0, NULL + } +}; + +tSpiderVariant gScriptVariant = { + "init", 0, + ARRAY_SIZE(gaScriptNamespaces), gaScriptNamespaces +}; // === CODE === /** @@ -21,6 +58,9 @@ int main(int argc, char *argv[]) char termpath[sizeof(DEFAULT_TERMINAL)] = DEFAULT_TERMINAL; char *child_argv[2] = {DEFAULT_SHELL, 0}; + // - Parse init script + + // - Start virtual terminals for( i = 0; i < NUM_TERMS; i++ ) { tid = clone(CLONE_VM, 0); @@ -41,3 +81,23 @@ int main(int argc, char *argv[]) return 42; } + +/** + * \brief Reads and parses the boot configuration script + * \param Filename File to parse and execute + */ +void ExecuteScript(const char *Filename) +{ + tSpiderScript *script; + script = SpiderScript_ParseFile(&gScriptVariant, Filename); + SpiderScript_ExecuteMethod(script, ""); + SpiderScript_Free(script); +} + +/** + * \brief Open a file + */ +tSpiderVariable *Script_System_IO_Open(tSpiderScript *Script, int NArgs, tSpiderVariable *Args) +{ + return NULL; +} diff --git a/Usermode/Libraries/Makefile.tpl b/Usermode/Libraries/Makefile.tpl index b501b7d2..eedd9d33 100644 --- a/Usermode/Libraries/Makefile.tpl +++ b/Usermode/Libraries/Makefile.tpl @@ -1,6 +1,8 @@ # Acess 2 SQLite 3 Library # +DEPFILES := $(OBJ:%.o=%.d) + .PHONY: all clean install all: $(BIN) @@ -12,8 +14,13 @@ install: $(BIN) $(xCP) $(BIN) $(DISTROOT)/Libs/ $(BIN): $(OBJ) - $(LD) $(LDFLAGS) -o $(BIN) $(OBJ) + @echo [LD] -o $(BIN) $(OBJ) + @$(LD) $(LDFLAGS) -o $(BIN) $(OBJ) @$(OBJDUMP) -d -S $(BIN) > $(BIN).dsm %.o: %.c - $(CC) $(CFLAGS) -o $@ -c $< + @echo [CC] -o $@ + @$(CC) $(CFLAGS) -o $@ -c $< + @$(CC) -M -MT $@ $(CPPFLAGS) $< -o $*.d + +-include $(DEPFILES) diff --git a/Usermode/Libraries/libc.so_src/string.c b/Usermode/Libraries/libc.so_src/string.c index 98692b21..e85356d5 100644 --- a/Usermode/Libraries/libc.so_src/string.c +++ b/Usermode/Libraries/libc.so_src/string.c @@ -101,6 +101,24 @@ EXPORT char *strdup(const char *str) return ret; } +/** + * \fn EXPORT char *strndup(const char *str, size_t maxlen) + * \brief Duplicate a string into the heap with a maximum length + * \param str Input string buffer + * \param maxlen Maximum valid size of the \a str buffer + * \return Heap string with the same value of \a str + */ +EXPORT char *strndup(const char *str, size_t maxlen) +{ + size_t len; + char *ret; + for( len = 0; len < maxlen && str[len]; len ++) ; + ret = malloc( len + 1); + memcpy( ret, str, len ); + ret[len] = '\0'; + return ret; +} + /** * \fn EXPORT char *strchr(char *str, int character) * \brief Locate a character in a string diff --git a/Usermode/Libraries/libspiderscript.so_src/Makefile b/Usermode/Libraries/libspiderscript.so_src/Makefile new file mode 100644 index 00000000..fd42a31d --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/Makefile @@ -0,0 +1,13 @@ +# Acess 2 +# + +include ../Makefile.cfg + +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 +BIN = ../libspiderscript.so + +include ../Makefile.tpl diff --git a/Usermode/Libraries/libspiderscript.so_src/ast.c b/Usermode/Libraries/libspiderscript.so_src/ast.c new file mode 100644 index 00000000..e0e1e37a --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/ast.c @@ -0,0 +1,256 @@ +/* + * Acess2 Init + * - Script AST Manipulator + */ +#include +#include +#include "ast.h" + +// === CODE === +tAST_Script *AST_NewScript(void) +{ + tAST_Script *ret = malloc( sizeof(tAST_Script) ); + + ret->Functions = NULL; + ret->LastFunction = NULL; + + return ret; +} + +/** + * \brief Append a function to a script + */ +tAST_Function *AST_AppendFunction(tAST_Script *Script, const char *Name) +{ + tAST_Function *ret; + + ret = malloc( sizeof(tAST_Function) ); + ret->Next = NULL; + ret->Name = strdup(Name); + ret->Code = NULL; + ret->Arguments = NULL; + + if(Script->LastFunction == NULL) { + Script->Functions = Script->LastFunction = ret; + } + else { + Script->LastFunction->Next = ret; + Script->LastFunction = ret; + } + + return ret; +} + +void AST_SetFunctionCode(tAST_Function *Function, tAST_Node *Root) +{ + Function->Code = Root; +} + +/** + * \name Node Manipulation + * \{ + */ +void AST_FreeNode(tAST_Node *Node) +{ + tAST_Node *node; + switch(Node->Type) + { + // Block of code + case NODETYPE_BLOCK: + for( node = Node->Block.FirstChild; node; ) + { + tAST_Node *savedNext = node->NextSibling; + AST_FreeNode(node); + node = savedNext; + } + break; + + // Function Call + case NODETYPE_FUNCTIONCALL: + for( node = Node->FunctionCall.FirstArg; node; ) + { + tAST_Node *savedNext = node->NextSibling; + AST_FreeNode(node); + node = savedNext; + } + break; + + // Asignment + case NODETYPE_ASSIGN: + AST_FreeNode(Node->Assign.Dest); + AST_FreeNode(Node->Assign.Value); + break; + + // Unary Operations + case NODETYPE_RETURN: + AST_FreeNode(Node->UniOp.Value); + break; + + // Binary Operations + case NODETYPE_ADD: + case NODETYPE_SUBTRACT: + case NODETYPE_MULTIPLY: + case NODETYPE_DIVIDE: + case NODETYPE_MODULO: + case NODETYPE_BITSHIFTLEFT: + case NODETYPE_BITSHIFTRIGHT: + case NODETYPE_BITROTATELEFT: + case NODETYPE_BWAND: case NODETYPE_LOGICALAND: + case NODETYPE_BWOR: case NODETYPE_LOGICALOR: + case NODETYPE_BWXOR: case NODETYPE_LOGICALXOR: + case NODETYPE_EQUALS: + case NODETYPE_LESSTHAN: + case NODETYPE_GREATERTHAN: + AST_FreeNode( Node->BinOp.Left ); + AST_FreeNode( Node->BinOp.Right ); + break; + + // Node types with no children + case NODETYPE_NOP: break; + case NODETYPE_VARIABLE: break; + case NODETYPE_CONSTANT: break; + case NODETYPE_STRING: break; + case NODETYPE_INTEGER: break; + case NODETYPE_REAL: break; + } + free( Node ); +} + +tAST_Node *AST_NewCodeBlock(void) +{ + tAST_Node *ret = malloc( sizeof(tAST_Node) ); + + ret->NextSibling = NULL; + ret->Type = NODETYPE_BLOCK; + ret->Block.FirstChild = NULL; + ret->Block.LastChild = NULL; + + return ret; +} + +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; + } +} + +tAST_Node *AST_NewAssign(int Operation, tAST_Node *Dest, tAST_Node *Value) +{ + tAST_Node *ret = malloc( sizeof(tAST_Node) ); + + ret->NextSibling = NULL; + ret->Type = NODETYPE_ASSIGN; + ret->Assign.Operation = Operation; + ret->Assign.Dest = Dest; + ret->Assign.Value = Value; + + return ret; +} + +tAST_Node *AST_NewBinOp(int Operation, tAST_Node *Left, tAST_Node *Right) +{ + tAST_Node *ret = malloc( sizeof(tAST_Node) ); + + ret->NextSibling = NULL; + ret->Type = Operation; + ret->BinOp.Left = Left; + ret->BinOp.Right = Right; + + return ret; +} + +/** + * \brief Create a new string node + */ +tAST_Node *AST_NewString(const char *String, int Length) +{ + tAST_Node *ret = malloc( sizeof(tAST_Node) + Length + 1 ); + + ret->NextSibling = NULL; + ret->Type = NODETYPE_STRING; + ret->String.Length = Length; + memcpy(ret->String.Data, String, Length); + ret->String.Data[Length] = '\0'; + + return ret; +} + +/** + * \brief Create a new integer node + */ +tAST_Node *AST_NewInteger(uint64_t Value) +{ + tAST_Node *ret = malloc( sizeof(tAST_Node) ); + ret->NextSibling = NULL; + ret->Type = NODETYPE_INTEGER; + ret->Integer = Value; + return ret; +} + +/** + * \brief Create a new variable reference node + */ +tAST_Node *AST_NewVariable(const char *Name) +{ + tAST_Node *ret = malloc( sizeof(tAST_Node) + strlen(Name) + 1 ); + ret->NextSibling = NULL; + ret->Type = NODETYPE_VARIABLE; + strcpy(ret->Variable.Name, Name); + return ret; +} + +/** + * \brief Create a new runtime constant reference node + */ +tAST_Node *AST_NewConstant(const char *Name) +{ + tAST_Node *ret = malloc( sizeof(tAST_Node) + strlen(Name) + 1 ); + ret->NextSibling = NULL; + ret->Type = NODETYPE_CONSTANT; + strcpy(ret->Variable.Name, Name); + return ret; +} + +/** + * \brief Create a function call node + * \note Argument list is manipulated using AST_AppendFunctionCallArg + */ +tAST_Node *AST_NewFunctionCall(const char *Name) +{ + tAST_Node *ret = malloc( sizeof(tAST_Node) + strlen(Name) + 1 ); + + ret->NextSibling = NULL; + ret->Type = NODETYPE_FUNCTIONCALL; + ret->FunctionCall.FirstArg = NULL; + ret->FunctionCall.LastArg = NULL; + strcpy(ret->FunctionCall.Name, Name); + return ret; +} + +/** + * \brief Append an argument to a function call + */ +void AST_AppendFunctionCallArg(tAST_Node *Node, tAST_Node *Arg) +{ + if( Node->Type != NODETYPE_FUNCTIONCALL ) return ; + + if(Node->FunctionCall.LastArg) { + Node->FunctionCall.LastArg->NextSibling = Arg; + Node->FunctionCall.LastArg = Arg; + } + else { + Node->FunctionCall.FirstArg = Arg; + Node->FunctionCall.LastArg = Arg; + } +} + +/** + * \} + */ diff --git a/Usermode/Libraries/libspiderscript.so_src/ast.h b/Usermode/Libraries/libspiderscript.so_src/ast.h new file mode 100644 index 00000000..820167bd --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/ast.h @@ -0,0 +1,149 @@ +/* + */ +#ifndef _AST_H_ +#define _AST_H_ + +#include + +typedef struct sAST_Script tAST_Script; +typedef struct sAST_Function tAST_Function; +typedef struct sAST_Node tAST_Node; +typedef enum eAST_NodeTypes tAST_NodeType; + +/** + * \brief Node Types + */ +enum eAST_NodeTypes +{ + NODETYPE_NOP, + + NODETYPE_BLOCK, //!< Node Block + + NODETYPE_VARIABLE, //!< Variable + NODETYPE_CONSTANT, //!< Runtime Constant + NODETYPE_STRING, //!< String Constant + NODETYPE_INTEGER, //!< Integer Constant + NODETYPE_REAL, //!< Real Constant + + NODETYPE_RETURN, //!< Return from a function (reserved word) + NODETYPE_ASSIGN, //!< Variable assignment operator + NODETYPE_FUNCTIONCALL, //!< Call a function + + NODETYPE_LOGICALAND, //!< Logical AND operator + NODETYPE_LOGICALOR, //!< Logical OR operator + NODETYPE_LOGICALXOR, //!< Logical XOR operator + + NODETYPE_EQUALS, //!< Comparison Equals + NODETYPE_LESSTHAN, //!< Comparison Less Than + NODETYPE_GREATERTHAN, //!< Comparison Greater Than + + NODETYPE_BWAND, //!< Bitwise AND + NODETYPE_BWOR, //!< Bitwise OR + NODETYPE_BWXOR, //!< Bitwise XOR + + NODETYPE_BITSHIFTLEFT, //!< Bitwise Shift Left (Grow) + NODETYPE_BITSHIFTRIGHT, //!< Bitwise Shift Right (Shrink) + NODETYPE_BITROTATELEFT, //!< Bitwise Rotate Left (Grow) + + NODETYPE_ADD, //!< Add + NODETYPE_SUBTRACT, //!< Subtract + NODETYPE_MULTIPLY, //!< Multiply + NODETYPE_DIVIDE, //!< Divide + NODETYPE_MODULO, //!< Modulus +}; + +struct sSpiderScript +{ + tSpiderVariant *Variant; + tAST_Script *Script; +}; + +struct sAST_Script +{ + tAST_Function *Functions; + tAST_Function *LastFunction; +}; + +struct sAST_Function +{ + tAST_Function *Next; + char *Name; + tAST_Node *Code; + tAST_Node *Arguments; // HACKJOB (Only NODETYPE_VARIABLE is allowed) +}; + +struct sAST_Node +{ + tAST_Node *NextSibling; + tAST_NodeType Type; + + union + { + struct { + tAST_Node *FirstChild; + tAST_Node *LastChild; + } Block; + + struct { + int Operation; + tAST_Node *Dest; + tAST_Node *Value; + } Assign; + + struct { + tAST_Node *Value; + } UniOp; + + struct { + tAST_Node *Left; + tAST_Node *Right; + } BinOp; + + struct { + int Length; + char Data[]; + } String; + + struct { + tAST_Node *FirstArg; + tAST_Node *LastArg; + char Name[]; + } FunctionCall; + + /** + * \note Used for \a NODETYPE_VARIABLE and \a NODETYPE_CONSTANT + */ + struct { + char _unused; // Shut GCC up + char Name[]; + } Variable; + + uint64_t Integer; + double Real; + }; +}; + +// === FUNCTIONS === +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); + +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); + +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); + +void AST_FreeNode(tAST_Node *Node); + +tSpiderVariable *AST_ExecuteNode(tSpiderScript *Script, tAST_Node *Node); + +#endif diff --git a/Usermode/Libraries/libspiderscript.so_src/bytecode.h b/Usermode/Libraries/libspiderscript.so_src/bytecode.h new file mode 100644 index 00000000..bff4caec --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/bytecode.h @@ -0,0 +1,29 @@ +/* + * SpiderScript + * - Bytecode definitions + */ +#ifndef _BYTECODE_H_ +#define _BYTECODE_H_ + +struct sBytecodeHeader +{ + uint32_t Magic; //!< Magic Value (identifier) "\x8FSS\r" + uint32_t NFunctions; //!< Number of functions + struct { + uint32_t NameOffset; //!< Offset to the name + uint32_t CodeOffset; //!< Offset to the code + } Functions[]; +}; + +enum eBytecodeOperations +{ + BCOP_UNDEF, + BCOP_NOP, + + BCOP_DEFVAR, + BCOP_RETURN, + + NUM_BCOP +} + +#endif diff --git a/Usermode/Libraries/libspiderscript.so_src/exec_ast.c b/Usermode/Libraries/libspiderscript.so_src/exec_ast.c new file mode 100644 index 00000000..90045bca --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/exec_ast.c @@ -0,0 +1,379 @@ +/* + */ +#include +#include +#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 Variable_SetValue(tSpiderScript *Script, const char *Name, tSpiderVariable *Value); +tSpiderVariable *Variable_GetValue(tSpiderScript *Script, const char *Name); + +// === CODE === +/** + * \brief Dereference a created object + */ +void Object_Dereference(tSpiderVariable *Object) +{ + Object->ReferenceCount --; + if( Object->ReferenceCount == 0 ) free(Object); +} + +void Object_Reference(tSpiderVariable *Object) +{ + Object->ReferenceCount ++; +} + +/** + * \brief Create an integer object + */ +tSpiderVariable *Object_CreateInteger(uint64_t Value) +{ + tSpiderVariable *ret = malloc( sizeof(tSpiderVariable) ); + ret->Type = SS_DATATYPE_INTEGER; + ret->ReferenceCount = 1; + ret->Integer = Value; + return ret; +} + +/** + * \brief Create an real number object + */ +tSpiderVariable *Object_CreateReal(double Value) +{ + tSpiderVariable *ret = malloc( sizeof(tSpiderVariable) ); + ret->Type = SS_DATATYPE_REAL; + ret->ReferenceCount = 1; + ret->Real = Value; + return ret; +} + +/** + * \brief Create an string object + */ +tSpiderVariable *Object_CreateString(int Length, const char *Data) +{ + tSpiderVariable *ret = malloc( sizeof(tSpiderVariable) + Length + 1 ); + ret->Type = SS_DATATYPE_STRING; + ret->ReferenceCount = 1; + ret->String.Length = Length; + memcpy(ret->String.Data, Data, Length); + ret->String.Data[Length] = '\0'; + return ret; +} + +/** + */ +tSpiderVariable *Object_CastTo(int Type, tSpiderVariable *Source) +{ + tSpiderVariable *ret; + // Check if anything needs to be done + if( Source->Type == Type ) { + Object_Reference(Source); + return Source; + } + + switch(Type) + { + case SS_DATATYPE_UNDEF: + case SS_DATATYPE_NULL: + case SS_DATATYPE_ARRAY: + fprintf(stderr, "Object_CastTo - Invalid cast to %i\n", Type); + return ERRPTR; + + case SS_DATATYPE_INTEGER: + ret = malloc(sizeof(tSpiderVariable)); + 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: + fprintf(stderr, "Object_CastTo - Invalid cast from %i\n", Source->Type); + break; + } + break; + } + + return ret; +} + +/** + * \brief Condenses a value down to a boolean + */ +int Object_IsTrue(tSpiderVariable *Value) +{ + switch(Value->Type) + { + case SS_DATATYPE_UNDEF: + case SS_DATATYPE_NULL: + 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_ARRAY: + return Value->Array.Length > 0; + } + return 0; +} + +tSpiderVariable *AST_ExecuteNode(tSpiderScript *Script, tAST_Node *Node) +{ + tAST_Node *node; + tSpiderVariable *ret, *tmpvar; + tSpiderVariable *op1, *op2; // Binary operations + int cmp; // Used in comparisons + + switch(Node->Type) + { + // No Operation + 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 ; + } + else { + tmpvar = AST_ExecuteNode(Script, node); + if(tmpvar == ERRPTR) return ERRPTR; // Error check + if(tmpvar) Object_Dereference(tmpvar); // Free unused value + } + } + break; + + // Assignment + case NODETYPE_ASSIGN: + if( Node->Assign.Dest->Type != NODETYPE_VARIABLE ) { + 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 ); + break; + + 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"); + break; + + // Return's special handling happens elsewhere + case NODETYPE_RETURN: + ret = AST_ExecuteNode(Script, Node->UniOp.Value); + break; + + // Variable + case NODETYPE_VARIABLE: ret = Variable_GetValue( Script, Node->Variable.Name ); break; + + // TODO: Implement runtime constants + case NODETYPE_CONSTANT: 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; + case NODETYPE_REAL: ret = Object_CreateReal( Node->Real ); break; + + // --- Operations --- + // Boolean Operations + 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); + switch( Node->Type ) + { + case NODETYPE_LOGICALAND: + ret = Object_CreateInteger( Object_IsTrue(op1) && Object_IsTrue(op2) ); + break; + case NODETYPE_LOGICALOR: + ret = Object_CreateInteger( Object_IsTrue(op1) || Object_IsTrue(op2) ); + break; + case NODETYPE_LOGICALXOR: + ret = Object_CreateInteger( Object_IsTrue(op1) ^ Object_IsTrue(op2) ); + break; + default: break; + } + 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); + + // No conversion done for NULL + // TODO: Determine if this will ever be needed + if( op1->Type == SS_DATATYPE_NULL ) + { + // NULLs always typecheck + ret = Object_CreateInteger(op2->Type == SS_DATATYPE_NULL); + break; + } + + // Convert types + if( op1->Type != op2->Type ) { + // If dynamically typed, convert op2 to op1's type + if(Script->Variant->bDyamicTyped) + { + tmpvar = op2; + op2 = Object_CastTo(op1->Type, op2); + Object_Dereference(tmpvar); + } + // If statically typed, this should never happen, but catch it anyway + else { + ret = ERRPTR; + break; + } + } + // Do operation + switch(op1->Type) + { + // - NULL + case SS_DATATYPE_NULL: break; + // - String Compare (does a strcmp, well memcmp) + case SS_DATATYPE_STRING: + // Call memcmp to do most of the work + cmp = memcmp( + op1->String.Data, op2->String.Data, + (op1->String.Length < op2->String.Length) ? op1->String.Length : op2->String.Length + ); + // Handle reaching the end of the string + if( cmp == 0 ) { + if( op1->String.Length == op2->String.Length ) + cmp = 0; + else if( op1->String.Length < op2->String.Length ) + cmp = 1; + else + cmp = -1; + } + break; + } + + // Free intermediate objects + Object_Dereference(op1); + Object_Dereference(op2); + + // Create return + switch(Node->Type) + { + case NODETYPE_EQUALS: ret = Object_CreateInteger(cmp == 0); break; + case NODETYPE_LESSTHAN: ret = Object_CreateInteger(cmp < 0); break; + case NODETYPE_GREATERTHAN: ret = Object_CreateInteger(cmp > 0); break; + default: + fprintf(stderr, "SpiderScript internal error: Exec,CmpOp unknown op %i", Node->Type); + ret = ERRPTR; + break; + } + break; + + // General Binary Operations + case NODETYPE_ADD: + case NODETYPE_SUBTRACT: + case NODETYPE_MULTIPLY: + case NODETYPE_DIVIDE: + case NODETYPE_MODULO: + case NODETYPE_BWAND: + case NODETYPE_BWOR: + case NODETYPE_BWXOR: + case NODETYPE_BITSHIFTLEFT: + case NODETYPE_BITSHIFTRIGHT: + case NODETYPE_BITROTATELEFT: + // Get operands + op1 = AST_ExecuteNode(Script, Node->BinOp.Left); + op2 = AST_ExecuteNode(Script, Node->BinOp.Right); + + // Convert types + if( op1->Type != op2->Type ) { + // If dynamically typed, convert op2 to op1's type + if(Script->Variant->bDyamicTyped) + { + tmpvar = op2; + op2 = Object_CastTo(op1->Type, op2); + Object_Dereference(tmpvar); + } + // If statically typed, this should never happen, but catch it anyway + else { + ret = ERRPTR; + break; + } + } + + // Do operation + switch(op1->Type) + { + case SS_DATATYPE_NULL: break; + // String Concatenation + case SS_DATATYPE_STRING: + switch(Node->Type) + { + default: + fprintf(stderr, "SpiderScript internal error: Exec,BinOP,String unknown op %i", Node->Type); + ret = ERRPTR; + break; + } + break; + case SS_DATATYPE_INTEGER: + switch(Node->Type) + { + case NODETYPE_ADD: ret = Object_CreateInteger( op1->Integer + op2->Integer ); break; + case NODETYPE_SUBTRACT: ret = Object_CreateInteger( op1->Integer - op2->Integer ); break; + case NODETYPE_MULTIPLY: ret = Object_CreateInteger( op1->Integer * op2->Integer ); break; + case NODETYPE_DIVIDE: ret = Object_CreateInteger( op1->Integer / op2->Integer ); break; + case NODETYPE_MODULO: ret = Object_CreateInteger( op1->Integer % op2->Integer ); break; + case NODETYPE_BWAND: ret = Object_CreateInteger( op1->Integer & op2->Integer ); break; + case NODETYPE_BWOR: ret = Object_CreateInteger( op1->Integer | op2->Integer ); break; + case NODETYPE_BWXOR: ret = Object_CreateInteger( op1->Integer ^ op2->Integer ); break; + case NODETYPE_BITSHIFTLEFT: ret = Object_CreateInteger( op1->Integer << op2->Integer ); break; + case NODETYPE_BITSHIFTRIGHT:ret = Object_CreateInteger( op1->Integer >> op2->Integer ); break; + case NODETYPE_BITROTATELEFT: + ret = Object_CreateInteger( (op1->Integer << op2->Integer) | (op1->Integer >> (64-op2->Integer)) ); + break; + default: + fprintf(stderr, "SpiderScript internal error: Exec,BinOP,Integer unknown op %i", Node->Type); + ret = ERRPTR; + break; + } + break; + } + + // Free intermediate objects + Object_Dereference(op1); + Object_Dereference(op2); + break; + + //default: + // ret = NULL; + // fprintf(stderr, "ERROR: SpiderScript AST_ExecuteNode Unimplemented %i\n", Node->Type); + // break; + } + return ret; +} diff --git a/Usermode/Libraries/libspiderscript.so_src/lex.c b/Usermode/Libraries/libspiderscript.so_src/lex.c new file mode 100644 index 00000000..6badd45b --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/lex.c @@ -0,0 +1,230 @@ +/* + * Acess2 init + * - Script Lexer + */ +#include "tokens.h" +#include + +// === PROTOTYPES === + int is_ident(char ch); + int isdigit(char ch); + int isspace(char ch); + int GetToken(tParser *File); + +// === CONSTANTS === +const struct { + const int Value; + const char *Name; +} csaReservedWords[] = { + {TOK_RWD_FUNCTION, "function"}, + {TOK_RWD_INTEGER, "integer"}, + {TOK_RWD_REAL, "string"} +}; + +// === CODE === +/** + * \brief Read a token from a buffer + * \param File Parser state + */ +int GetToken(tParser *File) +{ + int ret; + + if( File->NextToken != -1 ) { + File->Token = File->NextToken; + File->TokenStr = File->NextTokenStr; + File->TokenLen = File->NextTokenLen; + File->NextToken = -1; + return File->Token; + } + + // Clear whitespace (including comments) + for( ;; ) + { + // Whitespace + while( isspace( *File->CurPos ) ) + File->CurPos ++; + + // # Line Comments + if( *File->CurPos == '#' ) { + while( *File->CurPos && *File->CurPos != '\n' ) + File->CurPos ++; + continue ; + } + + // C-Style Line Comments + if( *File->CurPos == '/' && File->CurPos[1] == '/' ) { + while( *File->CurPos && *File->CurPos != '\n' ) + File->CurPos ++; + continue ; + } + + // C-Style Block Comments + if( *File->CurPos == '/' && File->CurPos[1] == '*' ) { + File->CurPos += 2; + while( *File->CurPos && !(File->CurPos[-1] == '*' && *File->CurPos == '/') ) + File->CurPos ++; + continue ; + } + + // No more "whitespace" + break; + } + + // Save previous tokens (speeds up PutBack and LookAhead) + File->LastToken = File->Token; + File->LastTokenStr = File->TokenStr; + File->LastTokenLen = File->TokenLen; + + // Read token + File->TokenStr = File->CurPos; + switch( *File->CurPos++ ) + { + // Operations + case '/': ret = TOK_DIV; break; + case '*': ret = TOK_MUL; break; + case '+': ret = TOK_PLUS; break; + case '-': + if( *File->CurPos == '>' ) { + File->CurPos ++; + ret = TOK_ELEMENT; + } + else + ret = TOK_MINUS; + break; + + // Strings + case '"': + File->TokenStr ++; + while( *File->CurPos && !(*File->CurPos == '"' && *File->CurPos != '\\') ) + File->CurPos ++; + ret = TOK_STR; + break; + + // Brackets + case '(': ret = TOK_PAREN_OPEN; break; + case ')': ret = TOK_PAREN_CLOSE; break; + case '{': ret = TOK_BRACE_OPEN; break; + case '}': ret = TOK_BRACE_CLOSE; break; + case '[': ret = TOK_SQUARE_OPEN; break; + case ']': ret = TOK_SQUARE_CLOSE; break; + + // Core symbols + case ';': ret = TOK_SEMICOLON; break; + case '.': ret = TOK_SCOPE; break; + + // Equals + case '=': + // Comparison Equals + if( *File->CurPos == '=' ) { + File->CurPos ++; + ret = TOK_EQUALS; + break; + } + // Assignment Equals + ret = TOK_ASSIGN; + break; + + // Variables + // \$[0-9]+ or \$[_a-zA-Z][_a-zA-Z0-9]* + case '$': + File->TokenStr ++; + // Numeric Variable + if( isdigit( *File->CurPos ) ) { + while( isdigit(*File->CurPos) ) + File->CurPos ++; + } + // Ident Variable + else { + while( is_ident(*File->CurPos) ) + File->CurPos ++; + } + ret = TOK_VARIABLE; + break; + + // Default (Numbers and Identifiers) + default: + // Numbers + if( isdigit(*File->CurPos) ) + { + while( isdigit(*File->CurPos) ) + File->CurPos ++; + ret = TOK_INTEGER; + break; + } + + // Identifier + if( is_ident(*File->CurPos) ) + { + // Identifier + while( is_ident(*File->CurPos) || isdigit(*File->CurPos) ) + File->CurPos ++; + + ret = TOK_IDENT; + break; + } + // Syntax Error + ret = 0; + break; + } + // Return + File->Token = ret; + File->TokenLen = File->CurPos - File->TokenStr; + return ret; +} + +void PutBack(tParser *File) +{ + if( File->LastToken == -1 ) { + // ERROR: + return ; + } + // Save + File->NextToken = File->Token; + File->NextTokenStr = File->TokenStr; + File->NextTokenLen = File->TokenLen; + // Restore + File->Token = File->LastToken; + File->TokenStr = File->LastTokenStr; + File->TokenLen = File->LastTokenLen; + File->CurPos = File->NextTokenStr; + // Invalidate + File->LastToken = -1; +} + +int LookAhead(tParser *File) +{ + int ret = GetToken(File); + PutBack(File); + return ret; +} + +// --- Helpers --- +/** + * \brief Check for ident characters + * \note Matches Regex [a-zA-Z_] + */ +int is_ident(char ch) +{ + if('a' <= ch && ch <= 'z') return 1; + if('Z' <= ch && ch <= 'Z') return 1; + if(ch == '_') return 1; + if(ch < 0) return 1; + return 0; +} + +int isdigit(char ch) +{ + if('0' <= ch && ch <= '9') return 1; + return 0; +} + +int isspace(char ch) +{ + if(' ' == ch) return 1; + if('\t' == ch) return 1; + if('\b' == ch) return 1; + if('\n' == ch) return 1; + if('\r' == ch) return 1; + return 0; +} diff --git a/Usermode/Libraries/libspiderscript.so_src/main.c b/Usermode/Libraries/libspiderscript.so_src/main.c new file mode 100644 index 00000000..ce39c080 --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/main.c @@ -0,0 +1,102 @@ +/* + * Acess2 - SpiderScript + * Interpreter Library + */ +#include +#include +#include +#include +#include "ast.h" + +// === IMPORTS === +extern tAST_Script *Parse_Buffer(tSpiderVariant *Variant, char *Buffer); + +// === CODE === +/** + * \brief Library Entry Point + */ +int SoMain() +{ + return 0; +} + +/** + * \brief Parse a script + */ +tSpiderScript *SpiderScript_ParseFile(tSpiderVariant *Variant, const char *Filename) +{ + char *data; + int fLen; + FILE *fp; + tSpiderScript *ret; + + fp = fopen(Filename, "r"); + if( !fp ) { + return NULL; + } + + ret = malloc(sizeof(tSpiderScript)); + ret->Variant = Variant; + + fseek(fp, 0, SEEK_END); + fLen = ftell(fp); + fseek(fp, 0, SEEK_SET); + + data = malloc(fLen); + if(!data) return NULL; + fread(data, fLen, 1, fp); + + fclose(fp); + + ret->Script = Parse_Buffer(Variant, data); + + free(data); + + return ret; +} + +/** + * \brief Execute a script function + * \todo Arguments? + */ +tSpiderVariable *SpiderScript_ExecuteMethod(tSpiderScript *Script, const char *Function) +{ + tAST_Function *fcn = Script->Script->Functions; + + // Find the function + for( ; fcn; fcn = fcn->Next ) { + if( strcmp(fcn->Name, Function) == 0 ) + break; + } + if(!fcn) return NULL; + + // Execute! + return AST_ExecuteNode(Script, fcn->Code); +} + +/** + * \brief Free a script + */ +void SpiderScript_Free(tSpiderScript *Script) +{ + tAST_Function *fcn = Script->Script->Functions; + tAST_Function *nextFcn; + tAST_Node *var, *nextVar; + + // Free functions + while(fcn) { + AST_FreeNode( fcn->Code ); + + var = fcn->Arguments; + while(var) + { + nextVar = var->NextSibling; + AST_FreeNode( var ); + var = nextVar; + } + + nextFcn = fcn->Next; + free( fcn ); + fcn = nextFcn; + } +} diff --git a/Usermode/Libraries/libspiderscript.so_src/parse.c b/Usermode/Libraries/libspiderscript.so_src/parse.c new file mode 100644 index 00000000..2ccf5d87 --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/parse.c @@ -0,0 +1,407 @@ +/* + * Acess2 - SpiderScript + * - Parser + */ +#include +#include +#include +#include +#include "tokens.h" +#include "ast.h" + +// === PROTOTYPES === +tAST_Script *Parse_Buffer(tSpiderVariant *Variant, char *Buffer); +tAST_Node *Parse_DoCodeBlock(tParser *Parser); +tAST_Node *Parse_DoExpr(tParser *Parser); + +tAST_Node *Parse_DoExpr0(tParser *Parser); // Assignment +tAST_Node *Parse_DoExpr1(tParser *Parser); // Boolean Operators +tAST_Node *Parse_DoExpr2(tParser *Parser); // Comparison Operators +tAST_Node *Parse_DoExpr3(tParser *Parser); // Bitwise Operators +tAST_Node *Parse_DoExpr4(tParser *Parser); // Bit Shifts +tAST_Node *Parse_DoExpr5(tParser *Parser); // Arithmatic +tAST_Node *Parse_DoExpr6(tParser *Parser); // Mult & Div + +tAST_Node *Parse_DoParen(tParser *Parser); // Parenthesis (Always Last) +tAST_Node *Parse_DoValue(tParser *Parser); // Values + +tAST_Node *Parse_GetString(tParser *Parser); +tAST_Node *Parse_GetNumeric(tParser *Parser); +tAST_Node *Parse_GetVariable(tParser *Parser); +tAST_Node *Parse_GetIdent(tParser *Parser); + +void SyntaxAssert(int Have, int Want); + +// === CODE === +/** + * \brief Parse a buffer into a syntax tree + */ +tAST_Script *Parse_Buffer(tSpiderVariant *Variant, char *Buffer) +{ + tParser parser = {0}; + tParser *Parser = &parser; //< Keeps code consitent + tAST_Script *ret; + tAST_Node *mainCode; + char *name; + tAST_Function *fcn; + + // Initialise parser + parser.BufStart = Buffer; + parser.CurPos = Buffer; + + ret = AST_NewScript(); + mainCode = AST_NewCodeBlock(); + + for(;;) + { + switch( GetToken(Parser) ) + { + case TOK_RWD_FUNCTION: + // Define a function + SyntaxAssert( GetToken(Parser), TOK_IDENT ); + name = strndup( Parser->TokenStr, Parser->TokenLen ); + fcn = AST_AppendFunction( ret, name ); + free(name); + + SyntaxAssert( GetToken(Parser), TOK_PAREN_OPEN ); + // TODO: Arguments + SyntaxAssert( GetToken(Parser), TOK_PAREN_CLOSE ); + + AST_SetFunctionCode( fcn, Parse_DoCodeBlock(Parser) ); + break; + + default: + PutBack(&parser); + AST_AppendNode( mainCode, Parse_DoExpr(Parser) ); + break; + } + } + + fcn = AST_AppendFunction( ret, "" ); + AST_SetFunctionCode( fcn, mainCode ); + + return ret; +} + +/** + * \brief Parse a block of code surrounded by { } + */ +tAST_Node *Parse_DoCodeBlock(tParser *Parser) +{ + tAST_Node *ret; + SyntaxAssert( GetToken(Parser), TOK_BRACE_OPEN ); + + ret = AST_NewCodeBlock(); + + while( GetToken(Parser) != TOK_BRACE_CLOSE ) + { + AST_AppendNode( ret, Parse_DoExpr(Parser) ); + SyntaxAssert( GetToken(Parser), TOK_SEMICOLON ); + } + return ret; +} + +/** + * \brief Parse an expression + */ +tAST_Node *Parse_DoExpr(tParser *Parser) +{ + return Parse_DoExpr0(Parser); +} + +/** + * \brief Assignment Operations + */ +tAST_Node *Parse_DoExpr0(tParser *Parser) +{ + tAST_Node *ret = Parse_DoExpr1(Parser); + + // Check Assignment + switch(LookAhead(Parser)) + { + case TOK_ASSIGN: + GetToken(Parser); // Eat Token + ret = AST_NewAssign(NODETYPE_NOP, ret, Parse_DoExpr0(Parser)); + break; + #if 0 + case TOK_DIV_EQU: + GetToken(Parser); // Eat Token + ret = AST_NewAssignOp(ret, NODETYPE_DIVIDE, DoExpr0(Parser)); + break; + case TOK_MULT_EQU: + GetToken(Parser); // Eat Token + ret = AST_NewAssignOp(ret, NODETYPE_MULTIPLY, DoExpr0(Parser)); + break; + #endif + default: break; + } + return ret; +} + +/** + * \brief Logical/Boolean Operators + */ +tAST_Node *Parse_DoExpr1(tParser *Parser) +{ + tAST_Node *ret = Parse_DoExpr2(Parser); + + switch(GetToken(Parser)) + { + case TOK_LOGICAND: + ret = AST_NewBinOp(NODETYPE_LOGICALAND, ret, Parse_DoExpr1(Parser)); + break; + case TOK_LOGICOR: + ret = AST_NewBinOp(NODETYPE_LOGICALOR, ret, Parse_DoExpr1(Parser)); + break; + case TOK_LOGICXOR: + ret = AST_NewBinOp(NODETYPE_LOGICALXOR, ret, Parse_DoExpr1(Parser)); + break; + default: + PutBack(Parser); + break; + } + return ret; +} + +// -------------------- +// Expression 2 - Comparison Operators +// -------------------- +tAST_Node *Parse_DoExpr2(tParser *Parser) +{ + tAST_Node *ret = Parse_DoExpr3(Parser); + + // Check token + switch(GetToken(Parser)) + { + case TOK_EQUALS: + ret = AST_NewBinOp(NODETYPE_EQUALS, ret, Parse_DoExpr2(Parser)); + break; + case TOK_LT: + ret = AST_NewBinOp(NODETYPE_LESSTHAN, ret, Parse_DoExpr2(Parser)); + break; + case TOK_GT: + ret = AST_NewBinOp(NODETYPE_GREATERTHAN, ret, Parse_DoExpr2(Parser)); + break; + default: + PutBack(Parser); + break; + } + return ret; +} + +/** + * \brief Bitwise Operations + */ +tAST_Node *Parse_DoExpr3(tParser *Parser) +{ + tAST_Node *ret = Parse_DoExpr4(Parser); + + // Check Token + switch(GetToken(Parser)) + { + case TOK_OR: + ret = AST_NewBinOp(NODETYPE_BWOR, ret, Parse_DoExpr3(Parser)); + break; + case TOK_AND: + ret = AST_NewBinOp(NODETYPE_BWAND, ret, Parse_DoExpr3(Parser)); + break; + case TOK_XOR: + ret = AST_NewBinOp(NODETYPE_BWXOR, ret, Parse_DoExpr3(Parser)); + break; + default: + PutBack(Parser); + break; + } + return ret; +} + +// -------------------- +// Expression 4 - Shifts +// -------------------- +tAST_Node *Parse_DoExpr4(tParser *Parser) +{ + tAST_Node *ret = Parse_DoExpr5(Parser); + + switch(GetToken(Parser)) + { + case TOK_SHL: + ret = AST_NewBinOp(NODETYPE_BITSHIFTLEFT, ret, Parse_DoExpr5(Parser)); + break; + case TOK_SHR: + ret = AST_NewBinOp(NODETYPE_BITSHIFTRIGHT, ret, Parse_DoExpr5(Parser)); + break; + default: + PutBack(Parser); + break; + } + + return ret; +} + +// -------------------- +// Expression 5 - Arithmatic +// -------------------- +tAST_Node *Parse_DoExpr5(tParser *Parser) +{ + tAST_Node *ret = Parse_DoExpr6(Parser); + + switch(GetToken(Parser)) + { + case TOK_PLUS: + ret = AST_NewBinOp(NODETYPE_ADD, ret, Parse_DoExpr5(Parser)); + break; + case TOK_MINUS: + ret = AST_NewBinOp(NODETYPE_SUBTRACT, ret, Parse_DoExpr5(Parser)); + break; + default: + PutBack(Parser); + break; + } + + return ret; +} + +// -------------------- +// Expression 6 - Multiplcation & Division +// -------------------- +tAST_Node *Parse_DoExpr6(tParser *Parser) +{ + tAST_Node *ret = Parse_DoParen(Parser); + + switch(GetToken(Parser)) + { + case TOK_MUL: + ret = AST_NewBinOp(NODETYPE_MULTIPLY, ret, Parse_DoExpr6(Parser)); + break; + case TOK_DIV: + ret = AST_NewBinOp(NODETYPE_DIVIDE, ret, Parse_DoExpr6(Parser)); + break; + default: + PutBack(Parser); + break; + } + + return ret; +} + + +// -------------------- +// 2nd Last Expression - Parens +// -------------------- +tAST_Node *Parse_DoParen(tParser *Parser) +{ + if(LookAhead(Parser) == TOK_PAREN_OPEN) + { + tAST_Node *ret; + GetToken(Parser); + ret = Parse_DoExpr0(Parser); + SyntaxAssert(GetToken(Parser), TOK_PAREN_CLOSE); + return ret; + } + else + return Parse_DoValue(Parser); +} + +// -------------------- +// Last Expression - Value +// -------------------- +tAST_Node *Parse_DoValue(tParser *Parser) +{ + int tok = LookAhead(Parser); + + switch(tok) + { + case TOK_STR: return Parse_GetString(Parser); + case TOK_INTEGER: return Parse_GetNumeric(Parser); + case TOK_IDENT: return Parse_GetIdent(Parser); + case TOK_VARIABLE: return Parse_GetVariable(Parser); + + default: + //ParseError2( tok, TOK_T_VALUE ); + return NULL; + } +} + +/** + * \brief Get a string + */ +tAST_Node *Parse_GetString(tParser *Parser) +{ + tAST_Node *ret; + GetToken( Parser ); + // TODO: Parse Escape Codes + ret = AST_NewString( Parser->TokenStr+1, Parser->TokenLen-2 ); + return ret; +} + +/** + * \brief Get a numeric value + */ +tAST_Node *Parse_GetNumeric(tParser *Parser) +{ + uint64_t value; + GetToken( Parser ); + value = atoi( Parser->TokenStr ); + return AST_NewInteger( value ); +} + +/** + * \brief Get a variable + */ +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); + return ret; +} + +/** + * \brief Get an identifier (constand or function call) + */ +tAST_Node *Parse_GetIdent(tParser *Parser) +{ + tAST_Node *ret; + char *name; + SyntaxAssert( GetToken(Parser), TOK_IDENT ); + name = strndup( Parser->TokenStr, Parser->TokenLen ); + + if( GetToken(Parser) == TOK_PAREN_OPEN ) + { + // Function Call + ret = AST_NewFunctionCall( name ); + // Read arguments + if( GetToken(Parser) != TOK_PAREN_CLOSE ) + { + PutBack(Parser); + do { + AST_AppendFunctionCallArg( ret, Parse_DoExpr0(Parser) ); + } while(GetToken(Parser) == TOK_COMMA); + SyntaxAssert( Parser->Token, TOK_PAREN_CLOSE ); + } + } + else { + // Runtime Constant + PutBack(Parser); + ret = AST_NewConstant( name ); + } + + free(name); + return ret; +} + +/** + * \brief Check for an error + */ +void SyntaxAssert(int Have, int Want) +{ + if(Have != Want) { + fprintf(stderr, "ERROR: Expected %i, got %i\n", Want, Have); + //longjmp(jmpTarget, 1); + return; + } +} + diff --git a/Usermode/Libraries/libspiderscript.so_src/tokens.h b/Usermode/Libraries/libspiderscript.so_src/tokens.h new file mode 100644 index 00000000..44c3a81f --- /dev/null +++ b/Usermode/Libraries/libspiderscript.so_src/tokens.h @@ -0,0 +1,70 @@ +/* + */ +#ifndef _TOKENS_H_ +#define _TOKENS_H_ + +// === TYPES === +typedef struct +{ + // Lexer State + char *BufStart; + char *CurPos; + + int LastToken, LastTokenLen; + char *LastTokenStr; + + int NextToken, NextTokenLen; + char *NextTokenStr; + + int Token, TokenLen; + char *TokenStr; +} tParser; + +// === FUNCTIONS === + int GetToken(tParser *File); +void PutBack(tParser *File); + int LookAhead(tParser *File); + +// === CONSTANTS === +enum eTokens +{ + TOK_INVAL, + TOK_EOF, + + TOK_STR, + TOK_INTEGER, + TOK_VARIABLE, + TOK_IDENT, + + TOK_RWD_FUNCTION, + 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 diff --git a/Usermode/include/string.h b/Usermode/include/string.h index 1e721974..a3c4d31b 100644 --- a/Usermode/include/string.h +++ b/Usermode/include/string.h @@ -15,6 +15,7 @@ extern char *strcpy(char *dst, const char *src); extern char *strncpy(char *dst, const char *src, size_t num); extern char *strcat(char *dst, const char *src); extern char *strdup(const char *src); +extern char *strndup(const char *src, int length); extern char *strchr(char *str, int character); extern char *strrchr(char *str, int character); extern char *strstr(char *str1, const char *str2); -- 2.20.1