From cafb65b48663d2851ed4330b4731c20f9b2bfaae Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 3 Apr 2011 18:35:34 +0800 Subject: [PATCH] SpiderScript - Added tags to loops to allow arbitary depth breaks --- .../Libraries/libspiderscript.so_src/ast.c | 26 +++++- .../Libraries/libspiderscript.so_src/ast.h | 13 ++- .../libspiderscript.so_src/exec_ast.c | 88 +++++++++++++------ .../Libraries/libspiderscript.so_src/lex.c | 3 + .../Libraries/libspiderscript.so_src/parse.c | 86 ++++++++++++++++-- .../Libraries/libspiderscript.so_src/tokens.h | 3 + 6 files changed, 179 insertions(+), 40 deletions(-) diff --git a/Usermode/Libraries/libspiderscript.so_src/ast.c b/Usermode/Libraries/libspiderscript.so_src/ast.c index 5fbdf77d..94d697cc 100644 --- a/Usermode/Libraries/libspiderscript.so_src/ast.c +++ b/Usermode/Libraries/libspiderscript.so_src/ast.c @@ -29,6 +29,8 @@ tAST_Function *AST_AppendFunction(tAST_Script *Script, const char *Name, int Ret tAST_Function *ret; ret = malloc( sizeof(tAST_Function) + strlen(Name) + 1 ); + if(!ret) return NULL; + ret->Next = NULL; strcpy(ret->Name, Name); ret->Code = NULL; @@ -191,7 +193,7 @@ size_t AST_WriteNode(void *Buffer, size_t Offset, tAST_Node *Node) // Looping Construct (For loop node) case NODETYPE_LOOP: WRITE_8(Buffer, Offset, Node->For.bCheckAfter); - + WRITE_STR(Buffer, Offset, Node->For.Tag); Offset += AST_WriteNode(Buffer, Offset, Node->For.Init); Offset += AST_WriteNode(Buffer, Offset, Node->For.Condition); Offset += AST_WriteNode(Buffer, Offset, Node->For.Increment); @@ -263,6 +265,8 @@ size_t AST_WriteNode(void *Buffer, size_t Offset, tAST_Node *Node) break; case NODETYPE_VARIABLE: case NODETYPE_CONSTANT: + case NODETYPE_BREAK: + case NODETYPE_CONTINUE: // TODO: De-Duplicate the strings WRITE_STR(Buffer, Offset, Node->Variable.Name); break; @@ -400,6 +404,8 @@ void AST_FreeNode(tAST_Node *Node) case NODETYPE_NOP: break; case NODETYPE_VARIABLE: break; case NODETYPE_CONSTANT: break; + case NODETYPE_BREAK: break; + case NODETYPE_CONTINUE: break; case NODETYPE_STRING: case NODETYPE_INTEGER: @@ -479,14 +485,15 @@ tAST_Node *AST_NewIf(tParser *Parser, tAST_Node *Condition, tAST_Node *True, tAS return ret; } -tAST_Node *AST_NewLoop(tParser *Parser, tAST_Node *Init, int bPostCheck, tAST_Node *Condition, tAST_Node *Increment, tAST_Node *Code) +tAST_Node *AST_NewLoop(tParser *Parser, const char *Tag, tAST_Node *Init, int bPostCheck, tAST_Node *Condition, tAST_Node *Increment, tAST_Node *Code) { - tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_LOOP, 0); + tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_LOOP, strlen(Tag) + 1); ret->For.Init = Init; ret->For.bCheckAfter = !!bPostCheck; ret->For.Condition = Condition; ret->For.Increment = Increment; ret->For.Code = Code; + strcpy(ret->For.Tag, Tag); return ret; } @@ -541,6 +548,19 @@ tAST_Node *AST_NewUniOp(tParser *Parser, int Operation, tAST_Node *Value) return ret; } +tAST_Node *AST_NewBreakout(tParser *Parser, int Type, const char *DestTag) +{ + int len = (DestTag ? strlen(DestTag) : 0); + tAST_Node *ret = AST_int_AllocateNode(Parser, Type, len + 1); + + if( DestTag ) + strcpy(ret->Variable.Name, DestTag); + else + ret->Variable.Name[0] = '\0'; + + return ret; +} + tAST_Node *AST_NewNop(tParser *Parser) { tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_NOP, 0); diff --git a/Usermode/Libraries/libspiderscript.so_src/ast.h b/Usermode/Libraries/libspiderscript.so_src/ast.h index 06d33333..67f77277 100644 --- a/Usermode/Libraries/libspiderscript.so_src/ast.h +++ b/Usermode/Libraries/libspiderscript.so_src/ast.h @@ -34,6 +34,8 @@ enum eAST_NodeTypes NODETYPE_CAST, //!< Cast a value to another (Uniop) NODETYPE_RETURN, //!< Return from a function (reserved word) + NODETYPE_BREAK, //!< Break out of a loop + NODETYPE_CONTINUE, //!< Next loop iteration NODETYPE_ASSIGN, //!< Variable assignment operator NODETYPE_POSTINC, //!< Post-increment (i++) - Uniop NODETYPE_POSTDEC, //!< Post-decrement (i--) - Uniop @@ -152,11 +154,11 @@ struct sAST_Node tAST_Node *Condition; tAST_Node *Increment; tAST_Node *Code; + char Tag[]; } For; /** - * \note Used for \a NODETYPE_VARIABLE, \a NODETYPE_CONSTANT and - * \a NODETYPE_SCOPE + * \note Used for \a NODETYPE_VARIABLE and \a NODETYPE_CONSTANT */ struct { char _unused; // Shut GCC up @@ -197,7 +199,9 @@ struct sAST_BlockState tSpiderValue *RetVal; tSpiderNamespace *BaseNamespace; //!< Base namespace (for entire block) tSpiderNamespace *CurNamespace; //!< Currently selected namespace - int Ident; + int Ident; //!< ID number used for variable lookup caching + const char *BreakTarget; + int BreakType; }; struct sAST_Variable @@ -236,12 +240,13 @@ extern tAST_Node *AST_NewCodeBlock(tParser *Parser); extern void AST_AppendNode(tAST_Node *Parent, tAST_Node *Child); extern tAST_Node *AST_NewIf(tParser *Parser, tAST_Node *Condition, tAST_Node *True, tAST_Node *False); -extern tAST_Node *AST_NewLoop(tParser *Parser, tAST_Node *Init, int bPostCheck, tAST_Node *Condition, tAST_Node *Increment, tAST_Node *Code); +extern tAST_Node *AST_NewLoop(tParser *Parser, const char *Tag, tAST_Node *Init, int bPostCheck, tAST_Node *Condition, tAST_Node *Increment, tAST_Node *Code); extern tAST_Node *AST_NewAssign(tParser *Parser, int Operation, tAST_Node *Dest, tAST_Node *Value); extern tAST_Node *AST_NewCast(tParser *Parser, int Target, tAST_Node *Value); extern tAST_Node *AST_NewBinOp(tParser *Parser, int Operation, tAST_Node *Left, tAST_Node *Right); extern tAST_Node *AST_NewUniOp(tParser *Parser, int Operation, tAST_Node *Value); +extern tAST_Node *AST_NewBreakout(tParser *Parser, int Type, const char *DestTag); extern tAST_Node *AST_NewScopeDereference(tParser *Parser, const char *Name, tAST_Node *Child); extern void AST_FreeNode(tAST_Node *Node); diff --git a/Usermode/Libraries/libspiderscript.so_src/exec_ast.c b/Usermode/Libraries/libspiderscript.so_src/exec_ast.c index 480ecc19..53558386 100644 --- a/Usermode/Libraries/libspiderscript.so_src/exec_ast.c +++ b/Usermode/Libraries/libspiderscript.so_src/exec_ast.c @@ -749,10 +749,13 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) blockInfo.RetVal = NULL; blockInfo.BaseNamespace = Block->BaseNamespace; blockInfo.CurNamespace = NULL; + blockInfo.BreakTarget = NULL; blockInfo.Ident = giNextBlockIdent ++; ret = NULL; // Loop over all nodes, or until the return value is set - for(node = Node->Block.FirstChild; node && !blockInfo.RetVal; node = node->NextSibling ) + for(node = Node->Block.FirstChild; + node && !blockInfo.RetVal && !blockInfo.BreakTarget; + node = node->NextSibling ) { ret = AST_ExecuteNode(&blockInfo, node); if(ret == ERRPTR) break; // Error check @@ -771,6 +774,12 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) // Set parent's return value if needed if( blockInfo.RetVal ) Block->RetVal = blockInfo.RetVal; + if( blockInfo.BreakTarget ) { + Block->BreakTarget = blockInfo.BreakTarget; + Block->BreakType = blockInfo.BreakType; + } + + // TODO: Unset break if break type deontes a block break } break; @@ -922,38 +931,59 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) // Loop case NODETYPE_LOOP: + // Initialise ret = AST_ExecuteNode(Block, Node->For.Init); if(ret == ERRPTR) break; - if( Node->For.bCheckAfter ) + + // Check initial condition + if( !Node->For.bCheckAfter ) { - do { - Object_Dereference(ret); - ret = AST_ExecuteNode(Block, Node->For.Code); - if(ret == ERRPTR) return ERRPTR; - Object_Dereference(ret); - ret = AST_ExecuteNode(Block, Node->For.Increment); - if(ret == ERRPTR) return ERRPTR; + Object_Dereference(ret); + + ret = AST_ExecuteNode(Block, Node->For.Condition); + if(ret == ERRPTR) return ERRPTR; + if(!SpiderScript_IsValueTrue(ret)) { Object_Dereference(ret); - ret = AST_ExecuteNode(Block, Node->For.Condition); - if(ret == ERRPTR) return ERRPTR; - } while( SpiderScript_IsValueTrue(ret) ); + ret = NULL; + break; + } } - else + + // Perform loop + for( ;; ) { Object_Dereference(ret); - ret = AST_ExecuteNode(Block, Node->For.Condition); + + // Code + ret = AST_ExecuteNode(Block, Node->For.Code); if(ret == ERRPTR) return ERRPTR; - while( SpiderScript_IsValueTrue(ret) ) { - Object_Dereference(ret); - ret = AST_ExecuteNode(Block, Node->For.Code); - if(ret == ERRPTR) return ERRPTR; - Object_Dereference(ret); - ret = AST_ExecuteNode(Block, Node->For.Increment); - if(ret == ERRPTR) return ERRPTR; - Object_Dereference(ret); - ret = AST_ExecuteNode(Block, Node->For.Condition); - if(ret == ERRPTR) return ERRPTR; + Object_Dereference(ret); + + if(Block->BreakTarget) + { + if( Block->BreakTarget[0] == '\0' || strcmp(Block->BreakTarget, Node->For.Tag) == 0 ) + { + // Ours + free((void*)Block->BreakTarget); Block->BreakTarget = NULL; + if( Block->BreakType == NODETYPE_CONTINUE ) { + // Continue, just keep going + } + else + break; + } + else + break; // Break out of this loop } + + // Increment + ret = AST_ExecuteNode(Block, Node->For.Increment); + if(ret == ERRPTR) return ERRPTR; + Object_Dereference(ret); + + // Check condition + ret = AST_ExecuteNode(Block, Node->For.Condition); + if(ret == ERRPTR) return ERRPTR; + if(!SpiderScript_IsValueTrue(ret)) break; } Object_Dereference(ret); ret = NULL; @@ -967,6 +997,12 @@ tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node) ret = NULL; // the `return` statement does not return a value break; + case NODETYPE_BREAK: + case NODETYPE_CONTINUE: + Block->BreakTarget = strdup(Node->Variable.Name); + Block->BreakType = Node->Type; + break; + // Define a variable case NODETYPE_DEFVAR: if( Node->DefVar.InitialValue ) { @@ -1601,7 +1637,9 @@ tAST_Variable *Variable_Lookup(tAST_BlockState *Block, tAST_Node *VarNode, int C */ int Variable_SetValue(tAST_BlockState *Block, tAST_Node *VarNode, tSpiderValue *Value) { - tAST_Variable *var = Variable_Lookup(Block, VarNode, Value->Type); + tAST_Variable *var; + + var = Variable_Lookup(Block, VarNode, (Value ? Value->Type : SS_DATATYPE_UNDEF)); if( !var ) return -1; diff --git a/Usermode/Libraries/libspiderscript.so_src/lex.c b/Usermode/Libraries/libspiderscript.so_src/lex.c index 62d6c3f7..bb86198d 100644 --- a/Usermode/Libraries/libspiderscript.so_src/lex.c +++ b/Usermode/Libraries/libspiderscript.so_src/lex.c @@ -25,6 +25,8 @@ const struct { {TOK_RWD_FUNCTION, "function"}, {TOK_RWD_RETURN, "return"}, + {TOK_RWD_BREAK, "break"}, + {TOK_RWD_CONTINUE, "continue"}, {TOK_RWD_NEW, "new"}, {TOK_RWD_IF, "if"}, @@ -33,6 +35,7 @@ const struct { {TOK_RWD_WHILE, "while"}, {TOK_RWD_FOR, "for"}, + {TOK_RWD_NULL, "null"}, {TOK_RWD_VOID, "void"}, {TOK_RWD_OBJECT, "Object"}, {TOK_RWD_OPAQUE, "Opaque"}, diff --git a/Usermode/Libraries/libspiderscript.so_src/parse.c b/Usermode/Libraries/libspiderscript.so_src/parse.c index 12f26747..7ed079ea 100644 --- a/Usermode/Libraries/libspiderscript.so_src/parse.c +++ b/Usermode/Libraries/libspiderscript.so_src/parse.c @@ -12,6 +12,7 @@ #include "ast.h" #define DEBUG 0 +#define SUPPORT_BREAK_TAGS 1 // === PROTOTYPES === tAST_Script *Parse_Buffer(tSpiderVariant *Variant, const char *Buffer, const char *Filename); @@ -42,9 +43,10 @@ void SyntaxAssert(tParser *Parser, int Have, int Want); void SyntaxError(tParser *Parser, int bFatal, const char *Message, ...); #define SyntaxAssert(_parser, _have, _want) do { \ - if( (_have) != (_want) ) { \ + int have = (_have), want = (_want); \ + if( (have) != (want) ) { \ SyntaxError(Parser, 1, "Unexpected %s(%i), expecting %s(%i)\n", \ - csaTOKEN_NAMES[_have], _have, csaTOKEN_NAMES[_want], _want); \ + csaTOKEN_NAMES[have], have, csaTOKEN_NAMES[want], want); \ return NULL; \ } \ }while(0) @@ -281,11 +283,38 @@ tAST_Node *Parse_DoBlockLine(tParser *Parser) ret = AST_NewUniOp(Parser, NODETYPE_RETURN, Parse_DoExpr0(Parser)); break; + // Break / Continue (end a loop / go to next iteration) + case TOK_RWD_CONTINUE: + case TOK_RWD_BREAK: + { + int tok; + char *ident = NULL; + tok = GetToken(Parser); + // Get the number of nesting levels to break + if(LookAhead(Parser) == TOK_IDENT) + { + GetToken(Parser); + ident = strndup(Parser->TokenStr, Parser->TokenLen); + } + // Get the action + switch(tok) + { + case TOK_RWD_BREAK: ret = AST_NewBreakout(Parser, NODETYPE_BREAK, ident); break; + case TOK_RWD_CONTINUE: ret = AST_NewBreakout(Parser, NODETYPE_CONTINUE, ident); break; + default: + SyntaxError(Parser, 1, "BUG Unhandled break/continue (%s)", + csaTOKEN_NAMES[tok]); + return NULL; + } + } + break; + // Control Statements case TOK_RWD_IF: { tAST_Node *cond, *true, *false = NULL; GetToken(Parser); // eat the if + SyntaxAssert(Parser, GetToken(Parser), TOK_PAREN_OPEN); cond = Parse_DoExpr0(Parser); // Get condition SyntaxAssert(Parser, GetToken(Parser), TOK_PAREN_CLOSE); @@ -302,8 +331,20 @@ tAST_Node *Parse_DoBlockLine(tParser *Parser) case TOK_RWD_FOR: { + const char *tag = ""; tAST_Node *init=NULL, *cond=NULL, *inc=NULL, *code; GetToken(Parser); // Eat 'for' + + #if SUPPORT_BREAK_TAGS + if(LookAhead(Parser) == TOK_LT) + { + GetToken(Parser); + SyntaxAssert(Parser, GetToken(Parser), TOK_IDENT); + tag = strndup(Parser->TokenStr, Parser->TokenLen); + SyntaxAssert(Parser, GetToken(Parser), TOK_GT); + } + #endif + SyntaxAssert(Parser, GetToken(Parser), TOK_PAREN_OPEN); if(LookAhead(Parser) != TOK_SEMICOLON) @@ -322,31 +363,55 @@ tAST_Node *Parse_DoBlockLine(tParser *Parser) SyntaxAssert(Parser, GetToken(Parser), TOK_PAREN_CLOSE); code = Parse_DoCodeBlock(Parser); - ret = AST_NewLoop(Parser, init, 0, cond, inc, code); + ret = AST_NewLoop(Parser, tag, init, 0, cond, inc, code); } return ret; case TOK_RWD_DO: { + const char *tag = ""; tAST_Node *code, *cond; GetToken(Parser); // Eat 'do' + + #if SUPPORT_BREAK_TAGS + if(LookAhead(Parser) == TOK_LT) + { + GetToken(Parser); + SyntaxAssert(Parser, GetToken(Parser), TOK_IDENT); + tag = strndup(Parser->TokenStr, Parser->TokenLen); + SyntaxAssert(Parser, GetToken(Parser), TOK_GT); + } + #endif + code = Parse_DoCodeBlock(Parser); SyntaxAssert( Parser, GetToken(Parser), TOK_RWD_WHILE ); SyntaxAssert( Parser, GetToken(Parser), TOK_PAREN_OPEN ); cond = Parse_DoExpr0(Parser); SyntaxAssert( Parser, GetToken(Parser), TOK_PAREN_CLOSE ); - ret = AST_NewLoop(Parser, AST_NewNop(Parser), 1, cond, AST_NewNop(Parser), code); + ret = AST_NewLoop(Parser, tag, AST_NewNop(Parser), 1, cond, AST_NewNop(Parser), code); } break; case TOK_RWD_WHILE: { + const char *tag = ""; tAST_Node *code, *cond; GetToken(Parser); // Eat 'while' + + #if SUPPORT_BREAK_TAGS + if(LookAhead(Parser) == TOK_LT) + { + GetToken(Parser); + SyntaxAssert(Parser, GetToken(Parser), TOK_IDENT); + tag = strndup(Parser->TokenStr, Parser->TokenLen); + SyntaxAssert(Parser, GetToken(Parser), TOK_GT); + } + #endif + SyntaxAssert( Parser, GetToken(Parser), TOK_PAREN_OPEN ); cond = Parse_DoExpr0(Parser); SyntaxAssert( Parser, GetToken(Parser), TOK_PAREN_CLOSE ); code = Parse_DoCodeBlock(Parser); - ret = AST_NewLoop(Parser, AST_NewNop(Parser), 0, cond, AST_NewNop(Parser), code); + ret = AST_NewLoop(Parser, tag, AST_NewNop(Parser), 0, cond, AST_NewNop(Parser), code); } return ret; @@ -756,10 +821,15 @@ tAST_Node *Parse_DoValue(tParser *Parser) GetToken(Parser); return AST_NewReal( Parser, atof(Parser->TokenStr) ); - case TOK_IDENT: return Parse_GetIdent(Parser, 0); - case TOK_VARIABLE: return Parse_GetVariable(Parser); + case TOK_IDENT: + return Parse_GetIdent(Parser, 0); + case TOK_VARIABLE: + return Parse_GetVariable(Parser); + case TOK_RWD_NULL: + GetToken(Parser); + return AST_NewNop(Parser); // NODETYPE_NOP returns NULL case TOK_RWD_NEW: - GetToken(Parser); // Omnomnom + GetToken(Parser); return Parse_GetIdent(Parser, 1); default: diff --git a/Usermode/Libraries/libspiderscript.so_src/tokens.h b/Usermode/Libraries/libspiderscript.so_src/tokens.h index 4c6fae3f..44176b0b 100644 --- a/Usermode/Libraries/libspiderscript.so_src/tokens.h +++ b/Usermode/Libraries/libspiderscript.so_src/tokens.h @@ -67,6 +67,8 @@ enum eTokens TOK_RWD_DO, TOK_RWD_WHILE, TOK_RWD_FOR, + // - Value + TOK_RWD_NULL, // - Types TOK_RWD_VOID, TOK_RWD_OBJECT, @@ -155,6 +157,7 @@ const char * const csaTOKEN_NAMES[] = { "TOK_RWD_WHILE", "TOK_RWD_FOR", + "TOK_RWD_NULL", "TOK_RWD_VOID", "TOK_RWD_OBJECT", "TOK_RWD_OPAUQE", -- 2.20.1