SpiderScript - Added tags to loops to allow arbitary depth breaks
authorJohn Hodge <[email protected]>
Sun, 3 Apr 2011 10:35:34 +0000 (18:35 +0800)
committerJohn Hodge <[email protected]>
Sun, 3 Apr 2011 10:35:34 +0000 (18:35 +0800)
Usermode/Libraries/libspiderscript.so_src/ast.c
Usermode/Libraries/libspiderscript.so_src/ast.h
Usermode/Libraries/libspiderscript.so_src/exec_ast.c
Usermode/Libraries/libspiderscript.so_src/lex.c
Usermode/Libraries/libspiderscript.so_src/parse.c
Usermode/Libraries/libspiderscript.so_src/tokens.h

index 5fbdf77..94d697c 100644 (file)
@@ -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);
index 06d3333..67f7727 100644 (file)
@@ -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);
index 480ecc1..5355838 100644 (file)
@@ -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;
        
index 62d6c3f..bb86198 100644 (file)
@@ -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"},
index 12f2674..7ed079e 100644 (file)
@@ -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:
index 4c6fae3..44176b0 100644 (file)
@@ -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",

UCC git Repository :: git.ucc.asn.au