#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
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);\
/**
* \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);
*(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
{
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!
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",
// 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;
// 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;
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;
/**
* \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;
{
// New block
case TOK_BRACE_OPEN:
- return Parse_DoCodeBlock(Parser);
+ return Parse_DoCodeBlock(Parser, CodeNode);
// Empty statement
case TOK_SEMICOLON:
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);
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)
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;
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;
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
*/
{
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) {
return NULL;
}
}
+ else if( ret->DefVar.InitialValue )
+ {
+ AST_AppendFunctionCallArg(ret->DefVar.InitialValue, AST_NewInteger(Parser, ret->DefVar.DataType));
+ }
return ret;
}
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;
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:
}
}
+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);
+ }
+}