SpiderScript - Moved header to directory, ready to remove from tree
[tpg/acess2.git] / Usermode / Libraries / libspiderscript.so_src / parse.c
index 12f2674..45eafb2 100644 (file)
 #define WANT_TOKEN_STRINGS     1
 #include "tokens.h"
 #include "ast.h"
+#include "common.h"
 
 #define DEBUG  0
+#define        SUPPORT_BREAK_TAGS      1
 
 // === PROTOTYPES ===
-tAST_Script    *Parse_Buffer(tSpiderVariant *Variant, const char *Buffer, const char *Filename);
-void   *Parse_FunctionDefinition(tAST_Script *Script, tSpiderVariant *Variant, tParser *Parser, int Type);
-tAST_Node      *Parse_DoCodeBlock(tParser *Parser);
-tAST_Node      *Parse_DoBlockLine(tParser *Parser);
+ int   Parse_Buffer(tSpiderScript *Script, const char *Buffer, const char *Filename);
+void   *Parse_FunctionDefinition(tSpiderScript *Script, tParser *Parser, int Type);
+tAST_Node      *Parse_DoCodeBlock(tParser *Parser, tAST_Node *CodeNode);
+tAST_Node      *Parse_DoBlockLine(tParser *Parser, tAST_Node *CodeNode);
+tAST_Node      *Parse_VarDefList(tParser *Parser, tAST_Node *CodeNode, int Type);
 tAST_Node      *Parse_GetVarDef(tParser *Parser, int Type);
 
 tAST_Node      *Parse_DoExpr0(tParser *Parser);        // Assignment
@@ -41,13 +44,17 @@ tAST_Node   *Parse_GetIdent(tParser *Parser, int bObjectCreate);
 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) ) { \
+#if 0
+#define SyntaxAssert(_parser, _have, _want)    SyntaxAssertV(_parser, _have, _want, NULL)
+#define SyntaxAssertV(_parser, _have, _want, _rv) do { \
+       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); \
-               return NULL; \
+                       csaTOKEN_NAMES[have], have, csaTOKEN_NAMES[want], want); \
+               return _rv; \
        } \
 }while(0)
+#endif
 
 #define TODO(Parser, message...) do {\
        fprintf(stderr, "TODO: "message);\
@@ -58,14 +65,13 @@ void        SyntaxError(tParser *Parser, int bFatal, const char *Message, ...);
 /**
  * \brief Parse a buffer into a syntax tree
  */
-tAST_Script    *Parse_Buffer(tSpiderVariant *Variant, const char *Buffer, const char *Filename)
+int Parse_Buffer(tSpiderScript *Script, const char *Buffer, const char *Filename)
 {
        tParser parser = {0};
        tParser *Parser = &parser;      //< Keeps code consistent
-       tAST_Script     *ret;
        tAST_Node       *mainCode, *node;
         int    type;
-       tAST_Function   *fcn;
+       tScript_Function        *fcn;
        
        #if DEBUG >= 2
        printf("Parse_Buffer: (Variant=%p, Buffer=%p)\n", Variant, Buffer);
@@ -83,8 +89,8 @@ tAST_Script   *Parse_Buffer(tSpiderVariant *Variant, const char *Buffer, const cha
        *(int*)(parser.Filename) = 0;   // Set reference count
        parser.Filename += sizeof(int); // Move filename
        parser.ErrorHit = 0;
+       parser.Variant = Script->Variant;
        
-       ret = AST_NewScript();
        mainCode = AST_NewCodeBlock(&parser);
        
        // Give us an error fallback
@@ -92,24 +98,17 @@ tAST_Script *Parse_Buffer(tSpiderVariant *Variant, const char *Buffer, const cha
        {
                AST_FreeNode( mainCode );
                
-               for(fcn = ret->Functions; fcn; )
+               for(fcn = Script->Functions; fcn; )
                {
-                       tAST_Node       *var;
-                       tAST_Function   *nextFcn;
-                       AST_FreeNode( fcn->Code );
-                       for(var = fcn->Arguments; var;)
-                       {
-                               tAST_Node       *nextVar = var->NextSibling;
-                               AST_FreeNode( var );
-                               var = nextVar;
-                       }
+                       tScript_Function        *nextFcn;
+                       
+                       AST_FreeNode( fcn->ASTFcn );
                        
                        nextFcn = fcn->Next;
                        free( fcn );
                        fcn = nextFcn;
                }
-               free(ret);
-               return NULL;
+               return -1;
        }
        
        // Parse the file!
@@ -124,26 +123,18 @@ tAST_Script       *Parse_Buffer(tSpiderVariant *Variant, const char *Buffer, const cha
                case TOKEN_GROUP_TYPES:
                        TOKEN_GET_DATATYPE(type, Parser->Token);
                        
-                       switch(GetToken(Parser))
+                       switch(LookAhead(Parser))
                        {
                        // Define a function (pass on to the other function definition code)
                        case TOK_IDENT:
-                               PutBack(Parser);
-                               if( Parse_FunctionDefinition(ret, Variant, Parser, type) == NULL )
+                               if( Parse_FunctionDefinition(Script, Parser, type) == NULL )
                                        longjmp(Parser->JmpTarget, -1);
                                break ;
-                       // Define a variable
+                       // Define a variable (pass back to _DoBlockLine)
                        case TOK_VARIABLE:
-                               node = Parse_GetVarDef(Parser, type);
-                               if(!node)       longjmp(Parser->JmpTarget, -1);
-                               
-                               AST_AppendNode( mainCode, node );
-                               // Can't use SyntaxAssert because that returns
-                               if(GetToken(Parser) != TOK_SEMICOLON) {
-                                       SyntaxError(Parser, 1, "Unexpected %s, expected TOK_SEMICOLON",
-                                               csaTOKEN_NAMES[Parser->Token]);
-                                       longjmp(Parser->JmpTarget, -1);
-                               }
+                               node = Parse_VarDefList(Parser, mainCode, type);
+                               AST_AppendNode(mainCode, node);
+                               SyntaxAssert(Parser, GetToken(Parser), TOK_SEMICOLON);
                                break;
                        default:
                                SyntaxError(Parser, 1, "Unexpected %s, expected TOK_IDENT or TOK_VARIABLE\n",
@@ -154,14 +145,14 @@ tAST_Script       *Parse_Buffer(tSpiderVariant *Variant, const char *Buffer, const cha
                
                // Define a function
                case TOK_RWD_FUNCTION:
-                       if( !Variant->bDyamicTyped ) {
+                       if( !Script->Variant->bDyamicTyped ) {
                                SyntaxError(Parser, 1, "Dynamic functions are invalid in static mode");
                                longjmp(Parser->JmpTarget, -1);
                        }
                        
                        type = SS_DATATYPE_DYNAMIC;
                
-                       if( Parse_FunctionDefinition(ret, Variant, Parser, SS_DATATYPE_DYNAMIC) == NULL )
+                       if( Parse_FunctionDefinition(Script, Parser, SS_DATATYPE_DYNAMIC) == NULL )
                                longjmp(Parser->JmpTarget, -1);
                
                        break;
@@ -169,7 +160,7 @@ tAST_Script *Parse_Buffer(tSpiderVariant *Variant, const char *Buffer, const cha
                // Ordinary Statement
                default:
                        PutBack(Parser);
-                       node = Parse_DoBlockLine(Parser);
+                       node = Parse_DoBlockLine(Parser, mainCode);
                        if(!node)       longjmp(Parser->JmpTarget, -1);
                        AST_AppendNode( mainCode, node );
                        break;
@@ -180,71 +171,86 @@ tAST_Script       *Parse_Buffer(tSpiderVariant *Variant, const char *Buffer, const cha
                        longjmp(Parser->JmpTarget, -1);
        }
        
-       fcn = AST_AppendFunction( ret, "", SS_DATATYPE_INTEGER );
-       AST_SetFunctionCode( fcn, mainCode );
+       AST_AppendFunction( Script, "", SS_DATATYPE_INTEGER, NULL, mainCode );
        
        //printf("---- %p parsed as SpiderScript ----\n", Buffer);
        
-       return ret;
+       return 0;
 }
 
-void *Parse_FunctionDefinition(tAST_Script *Script, tSpiderVariant *Variant, tParser *Parser, int Type)
+void *Parse_FunctionDefinition(tSpiderScript *Script, tParser *Parser, int Type)
 {
-       tAST_Function   *fcn;
        char    *name;
-        int    type;
+        int    rv;
+       tAST_Node       *first_arg, *last_arg, *code;
+       
+       last_arg = (void*)&first_arg;   // HACK
        
        SyntaxAssert(Parser, GetToken(Parser), TOK_IDENT );
        
        name = strndup( Parser->TokenStr, Parser->TokenLen );
-       fcn = AST_AppendFunction( Script, name, Type );
        #if DEBUG
        printf("DefFCN %s\n", name);
        #endif
-       free(name);
        
        // Get arguments
        SyntaxAssert(Parser, GetToken(Parser), TOK_PAREN_OPEN );
        if( LookAhead(Parser) != TOK_PAREN_CLOSE )
        {
                do {
-                       type = SS_DATATYPE_DYNAMIC;
+                        int    type = SS_DATATYPE_DYNAMIC;
                        GetToken(Parser);
                        // Non dynamic typed variants must use data types
-                       if( !Variant->bDyamicTyped ) {
+                       if( !Script->Variant->bDyamicTyped ) {
                                TOKEN_GET_DATATYPE(type, Parser->Token);
                                GetToken(Parser);
                        }
-                       AST_AppendFunctionArg(fcn, Parse_GetVarDef(Parser, type)); 
+                       last_arg->NextSibling = Parse_GetVarDef(Parser, type);
+                       last_arg = last_arg->NextSibling;
+                       last_arg->NextSibling = NULL;
                }       while(GetToken(Parser) == TOK_COMMA);
        }
        else
                GetToken(Parser);
        SyntaxAssert(Parser, Parser->Token, TOK_PAREN_CLOSE );
+
+       code = Parse_DoCodeBlock(Parser, NULL);
+
+       rv = AST_AppendFunction( Script, name, Type, first_arg, code );
+
+       // Clean up argument definition nodes
+       {
+               tAST_Node       *nextarg;
+               for( ; first_arg; first_arg = nextarg )
+               {
+                       nextarg = first_arg->NextSibling;
+                       AST_FreeNode(first_arg);
+               }
+       }
+
+       free(name);
        
-       AST_SetFunctionCode( fcn, Parse_DoCodeBlock(Parser) );
-       
-       return fcn;
+       return rv == 0 ? (void*)1 : NULL;
 }
 
 /**
  * \brief Parse a block of code surrounded by { }
  */
-tAST_Node *Parse_DoCodeBlock(tParser *Parser)
+tAST_Node *Parse_DoCodeBlock(tParser *Parser, tAST_Node *CodeNode)
 {
        tAST_Node       *ret;
        
        // Check if we are being called for a one-liner
        if( GetToken(Parser) != TOK_BRACE_OPEN ) {
                PutBack(Parser);
-               return Parse_DoBlockLine(Parser);
+               return Parse_DoBlockLine(Parser, CodeNode);
        }
        
        ret = AST_NewCodeBlock(Parser);
        
        while( LookAhead(Parser) != TOK_BRACE_CLOSE )
        {
-               tAST_Node       *node = Parse_DoBlockLine(Parser);
+               tAST_Node       *node = Parse_DoBlockLine(Parser, ret);
                if(!node) {
                        AST_FreeNode(ret);
                        return NULL;
@@ -258,7 +264,7 @@ tAST_Node *Parse_DoCodeBlock(tParser *Parser)
 /**
  * \brief Parse a line in a block
  */
-tAST_Node *Parse_DoBlockLine(tParser *Parser)
+tAST_Node *Parse_DoBlockLine(tParser *Parser, tAST_Node *CodeNode)
 {
        tAST_Node       *ret;
        
@@ -268,7 +274,7 @@ tAST_Node *Parse_DoBlockLine(tParser *Parser)
        {
        // New block
        case TOK_BRACE_OPEN:
-               return Parse_DoCodeBlock(Parser);
+               return Parse_DoCodeBlock(Parser, CodeNode);
        
        // Empty statement
        case TOK_SEMICOLON:
@@ -281,18 +287,46 @@ 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;
+               }
+               if(ident)       free(ident);
+               }
+               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);
-               true = Parse_DoCodeBlock(Parser);
+               true = Parse_DoCodeBlock(Parser, CodeNode);
                if( LookAhead(Parser) == TOK_RWD_ELSE ) {
                        GetToken(Parser);
-                       false = Parse_DoCodeBlock(Parser);
+                       false = Parse_DoCodeBlock(Parser, CodeNode);
                }
                else
                        false = AST_NewNop(Parser);
@@ -302,8 +336,20 @@ tAST_Node *Parse_DoBlockLine(tParser *Parser)
        
        case TOK_RWD_FOR:
                {
+               char    *tag = NULL;
                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)
@@ -321,32 +367,57 @@ 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);
+               code = Parse_DoCodeBlock(Parser, CodeNode);
+               ret = AST_NewLoop(Parser, tag, init, 0, cond, inc, code);
+               if(tag) free(tag);
                }
                return ret;
        
        case TOK_RWD_DO:
                {
+               const char      *tag = "";
                tAST_Node       *code, *cond;
                GetToken(Parser);       // Eat 'do'
-               code = Parse_DoCodeBlock(Parser);
+               
+               #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, CodeNode);
                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);
+               code = Parse_DoCodeBlock(Parser, CodeNode);
+               ret = AST_NewLoop(Parser, tag, AST_NewNop(Parser), 0, cond, AST_NewNop(Parser), code);
                }
                return ret;
        
@@ -356,10 +427,7 @@ tAST_Node *Parse_DoBlockLine(tParser *Parser)
                         int    type;
                        GetToken(Parser);
                        TOKEN_GET_DATATYPE(type, Parser->Token);
-                       
-                       SyntaxAssert(Parser, GetToken(Parser), TOK_VARIABLE);
-                       
-                       ret = Parse_GetVarDef(Parser, type);
+                       ret = Parse_VarDefList(Parser, CodeNode, type);
                }
                break;
        
@@ -374,6 +442,23 @@ tAST_Node *Parse_DoBlockLine(tParser *Parser)
        return ret;
 }
 
+tAST_Node *Parse_VarDefList(tParser *Parser, tAST_Node *CodeNode, int Type)
+{
+       tAST_Node       *ret;
+       
+       ret = NULL;             
+       do {
+               if(ret) AST_AppendNode( CodeNode, ret );
+               SyntaxAssert(Parser, GetToken(Parser), TOK_VARIABLE);
+               
+               ret = Parse_GetVarDef(Parser, Type);
+               if(!ret)        longjmp(Parser->JmpTarget, -1);
+       } while(GetToken(Parser) == TOK_COMMA);
+       PutBack(Parser);        // Semicolon is checked by caller
+       
+       return ret;
+}
+
 /**
  * \brief Get a variable definition
  */
@@ -381,30 +466,52 @@ tAST_Node *Parse_GetVarDef(tParser *Parser, int Type)
 {
        char    name[Parser->TokenLen];
        tAST_Node       *ret;
+        int    level;
        
        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(Parser, Type, name);
        // Handle arrays
-       while( LookAhead(Parser) == TOK_SQUARE_OPEN )
+       level = 0;
+       if( LookAhead(Parser) == TOK_SQUARE_OPEN )
        {
-               tAST_Node *node;
                GetToken(Parser);
-               node = Parse_DoExpr0(Parser);
-               if(!node) {
-                       AST_FreeNode(ret);
-                       return NULL;
+               if( LookAhead(Parser) != TOK_SQUARE_CLOSE )
+               {
+                       ret->DefVar.InitialValue = AST_NewFunctionCall(Parser, "array");
+                       AST_AppendFunctionCallArg(ret->DefVar.InitialValue, Parse_DoExpr0(Parser));
                }
-               AST_AppendNode(ret, node);
                SyntaxAssert(Parser, GetToken(Parser), TOK_SQUARE_CLOSE);
+       
+               level ++;
+       }
+       while( LookAhead(Parser) == TOK_SQUARE_OPEN )
+       {
+               GetToken(Parser);
+               SyntaxAssert(Parser, GetToken(Parser), TOK_SQUARE_CLOSE);
+               level ++;
        }
        
+       // Maul the type to denote the dereference level
+       if( Parser->Variant->bDyamicTyped ) {
+               ret->DefVar.DataType = SS_DATATYPE_ARRAY;
+       }
+       else {
+               ret->DefVar.DataType |= (level << 16);
+       }
+
+       // Initial value
        if( LookAhead(Parser) == TOK_ASSIGN )
        {
+               if( ret->DefVar.InitialValue )
+               {
+                       SyntaxError(Parser, 1, "Cannot assign and set array size at the same time");
+               }
                GetToken(Parser);
                ret->DefVar.InitialValue = Parse_DoExpr0(Parser);
                if(!ret->DefVar.InitialValue) {
@@ -412,6 +519,10 @@ tAST_Node *Parse_GetVarDef(tParser *Parser, int Type)
                        return NULL;
                }
        }
+       else if( ret->DefVar.InitialValue )
+       {
+               AST_AppendFunctionCallArg(ret->DefVar.InitialValue, AST_NewInteger(Parser, ret->DefVar.DataType));
+       }
        
        return ret;
 }
@@ -507,6 +618,9 @@ tAST_Node *Parse_DoExpr2(tParser *Parser)
                case TOK_EQUALS:
                        ret = AST_NewBinOp(Parser, NODETYPE_EQUALS, ret, _next(Parser));
                        break;
+               case TOK_NOTEQUALS:
+                       ret = AST_NewBinOp(Parser, NODETYPE_NOTEQUALS, ret, _next(Parser));
+                       break;
                case TOK_LT:
                        ret = AST_NewBinOp(Parser, NODETYPE_LESSTHAN, ret, _next(Parser));
                        break;
@@ -756,10 +870,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_NewNull(Parser);     // nODETYPE_NOP returns NULL
        case TOK_RWD_NEW:
-               GetToken(Parser);       // Omnomnom
+               GetToken(Parser);
                return Parse_GetIdent(Parser, 1);
 
        default:
@@ -1001,3 +1120,11 @@ void SyntaxError(tParser *Parser, int bFatal, const char *Message, ...)
        }
 }
 
+void SyntaxAssert(tParser *Parser, int Have, int Want)
+{
+       if( Have != Want )
+       {
+               SyntaxError(Parser, 1, "Unexpected %s(%i), expecting %s(%i)\n",
+                       csaTOKEN_NAMES[Have], Have, csaTOKEN_NAMES[Want], Want);
+       }
+}

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