3 * - Script AST Manipulator
12 extern void SyntaxError(tParser *Parser, int bFatal, const char *Message, ...);
16 * \brief Append a function to a script
18 int AST_AppendFunction(tSpiderScript *Script, const char *Name, int ReturnType, tAST_Node *Args, tAST_Node *Code)
20 tScript_Function *fcn;
21 int arg_count = 0, arg_bytes = 0;
24 // Count and size arguments
25 for(arg = Args; arg; arg = arg->NextSibling)
28 arg_bytes += sizeof(fcn->Arguments[0]) + strlen(Args->DefVar.Name) + 1;
31 // Allocate information
32 fcn = malloc( sizeof(tScript_Function) + arg_bytes + strlen(Name) + 1 );
35 fcn->Name = (char*)&fcn->Arguments[arg_count];
36 strcpy(fcn->Name, Name);
37 fcn->ReturnType = ReturnType;
38 fcn->ArgumentCount = arg_count;
43 arg_bytes = strlen(Name) + 1; // Used as an offset into fcn->Name
45 for(arg = Args; arg; arg = arg->NextSibling)
47 fcn->Arguments[arg_count].Name = fcn->Name + arg_bytes;
48 strcpy(fcn->Arguments[arg_count].Name, arg->DefVar.Name);
49 fcn->Arguments[arg_count].Type = arg->DefVar.DataType;
50 arg_bytes += strlen(arg->DefVar.Name) + 1;
54 if(Script->LastFunction == NULL) {
55 Script->Functions = Script->LastFunction = fcn;
58 Script->LastFunction->Next = fcn;
59 Script->LastFunction = fcn;
66 * \name Node Manipulation
69 #define WRITE_N(_buffer, _offset, _len, _dataptr) do { \
70 if(_buffer) memcpy((char*)_buffer + _offset, _dataptr, _len);\
74 #define WRITE_8(_buffer, _offset, _val) do {\
76 WRITE_N(_buffer, _offset, 1, &v);\
78 #define WRITE_16(_buffer, _offset, _val) do {\
80 WRITE_N(_buffer, _offset, 2, &v);\
82 #define WRITE_32(_buffer, _offset, _val) do {\
84 WRITE_N(_buffer, _offset, 4, &v);\
86 #define WRITE_64(_buffer, _offset, _val) do {\
88 WRITE_N(_buffer, _offset, 8, &v);\
90 #define WRITE_REAL(_buffer, _offset, _val) do {\
92 WRITE_N(_buffer, _offset, sizeof(double), &v);\
95 #define WRITE_STR(_buffer, _offset, _string) do {\
96 int len = strlen(_string);\
97 WRITE_16(_buffer, _offset, len);\
98 WRITE_N(_buffer, _offset, len, _string);\
99 if((_offset & 1) == 1)WRITE_8(_buffer, _offset, 0); \
100 if((_offset & 3) == 2)WRITE_16(_buffer, _offset, 0); \
102 #define WRITE_NODELIST(_buffer, _offset, _listHead) do {\
105 for(node=(_listHead); node; node = node->NextSibling) {\
107 _offset += AST_WriteNode(_buffer, _offset, node); \
108 WRITE_32(_buffer, ptr, ptr); \
110 if(ptr != -1){ptr -= 4; WRITE_32(_buffer, ptr, 0);} \
114 * \brief Writes a script dump to a buffer
115 * \return Size of encoded data
116 * \note If \a Buffer is NULL, no write is done, but the size is still returned
118 size_t AST_WriteScript(void *Buffer, tSpiderScript *Script)
120 tScript_Function *fcn;
121 size_t ret = 0, ptr = 0;
124 for( fcn = Script->Functions; fcn; fcn = fcn->Next )
126 // printf("fcn = %p, fcn->Name = %p\n", fcn, fcn->Name);
128 WRITE_32(Buffer, ret, 0); // Next
129 WRITE_STR(Buffer, ret, fcn->Name);
130 WRITE_32(Buffer, ret, fcn->ArgumentCount);
131 for( i = 0; i < fcn->ArgumentCount; i ++ )
133 WRITE_16(Buffer, ret, fcn->Arguments[i].Type);
134 WRITE_STR(Buffer, ret, fcn->Arguments[i].Name);
136 ret += AST_WriteNode(Buffer, ret, fcn->ASTFcn);
137 WRITE_32(Buffer, ptr, ret); // Actually set `Next`
142 WRITE_32(Buffer, ptr, 0); // Clear next for final
149 * \brief Write a node to a file
151 size_t AST_WriteNode(void *Buffer, size_t Offset, tAST_Node *Node)
153 size_t baseOfs = Offset;
156 //fprintf(stderr, "Possible Bug - NULL passed to AST_WriteNode\n");
157 WRITE_32(Buffer, Offset, 0);
158 WRITE_16(Buffer, Offset, NODETYPE_NOP);
159 WRITE_16(Buffer, Offset, 0); // Line (0)
163 WRITE_32(Buffer, Offset, 0); // Next
164 WRITE_16(Buffer, Offset, Node->Type);
165 // TODO: Scan the buffer for the location of the filename (with NULL byte)
166 // else, write the string at the end of the node
167 WRITE_16(Buffer, Offset, Node->Line); // Line
168 //WRITE_32(Buffer, Offset, 0); // File
174 WRITE_NODELIST(Buffer, Offset, Node->Block.FirstChild);
178 case NODETYPE_METHODCALL:
179 Offset += AST_WriteNode(Buffer, Offset, Node->FunctionCall.Object);
180 case NODETYPE_FUNCTIONCALL:
181 case NODETYPE_CREATEOBJECT:
182 // TODO: Search for the same function name and add a pointer
183 WRITE_STR(Buffer, Offset, Node->FunctionCall.Name);
184 WRITE_NODELIST(Buffer, Offset, Node->FunctionCall.FirstArg);
189 Offset += AST_WriteNode(Buffer, Offset, Node->If.Condition);
190 Offset += AST_WriteNode(Buffer, Offset, Node->If.True);
191 Offset += AST_WriteNode(Buffer, Offset, Node->If.False);
194 // Looping Construct (For loop node)
196 WRITE_8(Buffer, Offset, Node->For.bCheckAfter);
197 // printf("Node %p, Loop Tag %p\n", Node, Node->For.Tag);
198 WRITE_STR(Buffer, Offset, Node->For.Tag);
199 Offset += AST_WriteNode(Buffer, Offset, Node->For.Init);
200 Offset += AST_WriteNode(Buffer, Offset, Node->For.Condition);
201 Offset += AST_WriteNode(Buffer, Offset, Node->For.Increment);
202 Offset += AST_WriteNode(Buffer, Offset, Node->For.Code);
206 case NODETYPE_ASSIGN:
207 WRITE_8(Buffer, Offset, Node->Assign.Operation);
208 Offset += AST_WriteNode(Buffer, Offset, Node->Assign.Dest);
209 Offset += AST_WriteNode(Buffer, Offset, Node->Assign.Value);
214 WRITE_8(Buffer, Offset, Node->Cast.DataType);
215 Offset += AST_WriteNode(Buffer, Offset, Node->Cast.Value);
219 case NODETYPE_DEFVAR:
220 WRITE_8(Buffer, Offset, Node->DefVar.DataType);
221 // TODO: Duplicate compress the strings
222 WRITE_STR(Buffer, Offset, Node->DefVar.Name);
224 WRITE_NODELIST(Buffer, Offset, Node->DefVar.LevelSizes);
225 Offset += AST_WriteNode(Buffer, Offset, Node->DefVar.InitialValue);
230 case NODETYPE_ELEMENT:
231 WRITE_STR(Buffer, Offset, Node->Scope.Name);
232 Offset += AST_WriteNode(Buffer, Offset, Node->UniOp.Value);
236 case NODETYPE_RETURN:
238 case NODETYPE_LOGICALNOT:
239 case NODETYPE_NEGATE:
240 case NODETYPE_POSTINC:
241 case NODETYPE_POSTDEC:
242 Offset += AST_WriteNode(Buffer, Offset, Node->UniOp.Value);
248 case NODETYPE_SUBTRACT:
249 case NODETYPE_MULTIPLY:
250 case NODETYPE_DIVIDE:
251 case NODETYPE_MODULO:
252 case NODETYPE_BITSHIFTLEFT:
253 case NODETYPE_BITSHIFTRIGHT:
254 case NODETYPE_BITROTATELEFT:
255 case NODETYPE_BWAND: case NODETYPE_LOGICALAND:
256 case NODETYPE_BWOR: case NODETYPE_LOGICALOR:
257 case NODETYPE_BWXOR: case NODETYPE_LOGICALXOR:
258 case NODETYPE_EQUALS:
259 case NODETYPE_LESSTHAN: case NODETYPE_LESSTHANEQUAL:
260 case NODETYPE_GREATERTHAN: case NODETYPE_GREATERTHANEQUAL:
261 Offset += AST_WriteNode(Buffer, Offset, Node->BinOp.Left);
262 Offset += AST_WriteNode(Buffer, Offset, Node->BinOp.Right);
265 // Node types with no children
268 case NODETYPE_VARIABLE:
269 case NODETYPE_CONSTANT:
271 case NODETYPE_CONTINUE:
272 // TODO: De-Duplicate the strings
273 WRITE_STR(Buffer, Offset, Node->Variable.Name);
275 case NODETYPE_STRING:
276 WRITE_32(Buffer, Offset, Node->Constant.String.Length);
277 WRITE_N(Buffer, Offset, Node->Constant.String.Length, Node->Constant.String.Data);
279 case NODETYPE_INTEGER:
280 WRITE_64(Buffer, Offset, Node->Constant.Integer);
283 WRITE_REAL(Buffer, Offset, Node->Constant.Real);
287 // fprintf(stderr, "AST_WriteNode: Unknown node type %i\n", Node->Type);
291 return Offset - baseOfs;
295 * \brief Free a node and all subnodes
297 void AST_FreeNode(tAST_Node *Node)
303 // Referenced counted file name
304 (*(int*)(Node->File - sizeof(int))) -= 1;
305 if( *(int*)(Node->File - sizeof(int)) == 0 )
306 free( (void*)(Node->File - sizeof(int)) );
312 for( node = Node->Block.FirstChild; node; )
314 tAST_Node *savedNext = node->NextSibling;
321 case NODETYPE_METHODCALL:
322 AST_FreeNode(Node->FunctionCall.Object);
323 case NODETYPE_FUNCTIONCALL:
324 case NODETYPE_CREATEOBJECT:
325 for( node = Node->FunctionCall.FirstArg; node; )
327 tAST_Node *savedNext = node->NextSibling;
335 AST_FreeNode(Node->If.Condition);
336 AST_FreeNode(Node->If.True);
337 AST_FreeNode(Node->If.False);
340 // Looping Construct (For loop node)
342 AST_FreeNode(Node->For.Init);
343 AST_FreeNode(Node->For.Condition);
344 AST_FreeNode(Node->For.Increment);
345 AST_FreeNode(Node->For.Code);
349 case NODETYPE_ASSIGN:
350 AST_FreeNode(Node->Assign.Dest);
351 AST_FreeNode(Node->Assign.Value);
356 AST_FreeNode(Node->Cast.Value);
360 case NODETYPE_ELEMENT:
361 AST_FreeNode(Node->Scope.Element);
365 case NODETYPE_DEFVAR:
366 for( node = Node->DefVar.LevelSizes; node; )
368 tAST_Node *savedNext = node->NextSibling;
372 AST_FreeNode(Node->DefVar.InitialValue);
376 case NODETYPE_RETURN:
378 case NODETYPE_LOGICALNOT:
379 case NODETYPE_NEGATE:
380 case NODETYPE_POSTINC:
381 case NODETYPE_POSTDEC:
382 AST_FreeNode(Node->UniOp.Value);
388 case NODETYPE_SUBTRACT:
389 case NODETYPE_MULTIPLY:
390 case NODETYPE_DIVIDE:
391 case NODETYPE_MODULO:
392 case NODETYPE_BITSHIFTLEFT:
393 case NODETYPE_BITSHIFTRIGHT:
394 case NODETYPE_BITROTATELEFT:
395 case NODETYPE_BWAND: case NODETYPE_LOGICALAND:
396 case NODETYPE_BWOR: case NODETYPE_LOGICALOR:
397 case NODETYPE_BWXOR: case NODETYPE_LOGICALXOR:
398 case NODETYPE_EQUALS:
399 case NODETYPE_LESSTHAN: case NODETYPE_LESSTHANEQUAL:
400 case NODETYPE_GREATERTHAN: case NODETYPE_GREATERTHANEQUAL:
401 AST_FreeNode( Node->BinOp.Left );
402 AST_FreeNode( Node->BinOp.Right );
405 // Node types with no children
406 case NODETYPE_NOP: break;
407 case NODETYPE_VARIABLE: break;
408 case NODETYPE_CONSTANT: break;
410 case NODETYPE_CONTINUE: break;
412 case NODETYPE_STRING:
413 case NODETYPE_INTEGER:
415 if( Node->ValueCache )
416 SpiderScript_DereferenceValue(Node->ValueCache);
417 Node->ValueCache = NULL;
423 tAST_Node *AST_int_AllocateNode(tParser *Parser, int Type, int ExtraSize)
425 tAST_Node *ret = malloc( sizeof(tAST_Node) + ExtraSize );
426 ret->NextSibling = NULL;
427 ret->File = Parser->Filename; *(int*)(Parser->Filename - sizeof(int)) += 1;
428 ret->Line = Parser->CurLine;
432 ret->BlockState = NULL;
434 ret->ValueCache = NULL;
439 tAST_Node *AST_NewCodeBlock(tParser *Parser)
441 tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_BLOCK, 0 );
443 ret->Block.FirstChild = NULL;
444 ret->Block.LastChild = NULL;
449 void AST_AppendNode(tAST_Node *Parent, tAST_Node *Child)
451 // Ignore NULL children
452 if( !Child ) return ;
454 Child->NextSibling = NULL;
455 switch( Parent->Type )
458 if(Parent->Block.FirstChild == NULL) {
459 Parent->Block.FirstChild = Parent->Block.LastChild = Child;
462 Parent->Block.LastChild->NextSibling = Child;
463 Parent->Block.LastChild = Child;
466 case NODETYPE_DEFVAR:
467 if(Parent->DefVar.LevelSizes == NULL) {
468 Parent->DefVar.LevelSizes = Parent->DefVar.LevelSizes_Last = Child;
471 Parent->DefVar.LevelSizes_Last->NextSibling = Child;
472 Parent->DefVar.LevelSizes_Last = Child;
476 fprintf(stderr, "BUG REPORT: AST_AppendNode on an invalid node type (%i)\n", Parent->Type);
481 tAST_Node *AST_NewIf(tParser *Parser, tAST_Node *Condition, tAST_Node *True, tAST_Node *False)
483 tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_IF, 0);
484 ret->If.Condition = Condition;
486 ret->If.False = False;
490 tAST_Node *AST_NewLoop(tParser *Parser, const char *Tag, tAST_Node *Init, int bPostCheck, tAST_Node *Condition, tAST_Node *Increment, tAST_Node *Code)
494 // NOTE: The +3) & ~3 is to align the size to 4 bytes, and shut valgrind up
495 // - GCC sometimes inlines strlen as a loop of dword reads, triggering valgrind
496 ret = AST_int_AllocateNode(Parser, NODETYPE_LOOP, (strlen(Tag) + 1 + 3) & ~3);
497 ret->For.Init = Init;
498 ret->For.bCheckAfter = !!bPostCheck;
499 ret->For.Condition = Condition;
500 ret->For.Increment = Increment;
501 ret->For.Code = Code;
502 strcpy(ret->For.Tag, Tag);
506 tAST_Node *AST_NewAssign(tParser *Parser, int Operation, tAST_Node *Dest, tAST_Node *Value)
508 tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_ASSIGN, 0);
510 if( Dest->Type != NODETYPE_VARIABLE && Dest->Type != NODETYPE_ELEMENT ) {
512 SyntaxError(Parser, 1, "Assign target is not a variable or attribute (instead %i)",
519 ret->Assign.Operation = Operation;
520 ret->Assign.Dest = Dest;
521 ret->Assign.Value = Value;
526 tAST_Node *AST_NewCast(tParser *Parser, int Target, tAST_Node *Value)
528 tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_CAST, 0);
530 ret->Cast.DataType = Target;
531 ret->Cast.Value = Value;
536 tAST_Node *AST_NewBinOp(tParser *Parser, int Operation, tAST_Node *Left, tAST_Node *Right)
538 tAST_Node *ret = AST_int_AllocateNode(Parser, Operation, 0);
540 ret->BinOp.Left = Left;
541 ret->BinOp.Right = Right;
548 tAST_Node *AST_NewUniOp(tParser *Parser, int Operation, tAST_Node *Value)
550 tAST_Node *ret = AST_int_AllocateNode(Parser, Operation, 0);
552 ret->UniOp.Value = Value;
557 tAST_Node *AST_NewBreakout(tParser *Parser, int Type, const char *DestTag)
559 int len = (DestTag ? strlen(DestTag) : 0);
560 tAST_Node *ret = AST_int_AllocateNode(Parser, Type, len + 1);
563 strcpy(ret->Variable.Name, DestTag);
565 ret->Variable.Name[0] = '\0';
570 tAST_Node *AST_NewNop(tParser *Parser)
572 tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_NOP, 0);
578 * \brief Create a new string node
580 tAST_Node *AST_NewString(tParser *Parser, const char *String, int Length)
582 tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_STRING, Length + 1);
584 ret->Constant.Type = SS_DATATYPE_STRING;
585 ret->Constant.ReferenceCount = 1;
586 ret->Constant.String.Length = Length;
587 memcpy(ret->Constant.String.Data, String, Length);
588 ret->Constant.String.Data[Length] = '\0';
594 * \brief Create a new integer node
596 tAST_Node *AST_NewInteger(tParser *Parser, int64_t Value)
598 tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_INTEGER, 0);
599 ret->Constant.Type = SS_DATATYPE_INTEGER;
600 ret->Constant.ReferenceCount = 1;
601 ret->Constant.Integer = Value;
606 * \brief Create a new real number node
608 tAST_Node *AST_NewReal(tParser *Parser, double Value)
610 tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_REAL, 0);
611 ret->Constant.Type = SS_DATATYPE_REAL;
612 ret->Constant.ReferenceCount = 1;
613 ret->Constant.Real = Value;
618 * \brief Create a new variable reference node
620 tAST_Node *AST_NewVariable(tParser *Parser, const char *Name)
622 tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_VARIABLE, strlen(Name) + 1 );
623 strcpy(ret->Variable.Name, Name);
628 * \brief Create a new variable definition node
630 tAST_Node *AST_NewDefineVar(tParser *Parser, int Type, const char *Name)
632 tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_DEFVAR, strlen(Name) + 1 );
634 ret->DefVar.DataType = Type;
635 ret->DefVar.LevelSizes = NULL;
636 ret->DefVar.LevelSizes_Last = NULL;
637 ret->DefVar.InitialValue = NULL;
638 strcpy(ret->DefVar.Name, Name);
644 * \brief Create a new runtime constant reference node
646 tAST_Node *AST_NewConstant(tParser *Parser, const char *Name)
648 tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_CONSTANT, strlen(Name) + 1 );
650 strcpy(ret->Variable.Name, Name);
656 * \brief Create a function call node
657 * \note Argument list is manipulated using AST_AppendFunctionCallArg
659 tAST_Node *AST_NewFunctionCall(tParser *Parser, const char *Name)
661 tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_FUNCTIONCALL, strlen(Name) + 1 );
663 ret->FunctionCall.Object = NULL;
664 ret->FunctionCall.FirstArg = NULL;
665 ret->FunctionCall.LastArg = NULL;
666 ret->FunctionCall.NumArgs = 0;
667 strcpy(ret->FunctionCall.Name, Name);
671 tAST_Node *AST_NewMethodCall(tParser *Parser, tAST_Node *Object, const char *Name)
673 tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_METHODCALL, strlen(Name) + 1 );
675 ret->FunctionCall.Object = Object;
676 ret->FunctionCall.FirstArg = NULL;
677 ret->FunctionCall.LastArg = NULL;
678 ret->FunctionCall.NumArgs = 0;
679 strcpy(ret->FunctionCall.Name, Name);
684 tAST_Node *AST_NewCreateObject(tParser *Parser, const char *Name)
686 tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_CREATEOBJECT, strlen(Name) + 1 );
688 ret->FunctionCall.Object = NULL;
689 ret->FunctionCall.FirstArg = NULL;
690 ret->FunctionCall.LastArg = NULL;
691 ret->FunctionCall.NumArgs = 0;
692 strcpy(ret->FunctionCall.Name, Name);
698 * \brief Append an argument to a function call
700 void AST_AppendFunctionCallArg(tAST_Node *Node, tAST_Node *Arg)
702 if( Node->Type != NODETYPE_FUNCTIONCALL
703 && Node->Type != NODETYPE_CREATEOBJECT
704 && Node->Type != NODETYPE_METHODCALL)
706 fprintf(stderr, "BUG REPORT: AST_AppendFunctionCallArg on an invalid node type (%i)\n", Node->Type);
710 if(Node->FunctionCall.LastArg) {
711 Node->FunctionCall.LastArg->NextSibling = Arg;
712 Node->FunctionCall.LastArg = Arg;
715 Node->FunctionCall.FirstArg = Arg;
716 Node->FunctionCall.LastArg = Arg;
718 Node->FunctionCall.NumArgs ++;
722 * \brief Add a scope node
724 tAST_Node *AST_NewScopeDereference(tParser *Parser, const char *Name, tAST_Node *Child)
726 tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_SCOPE, strlen(Name) + 1 );
727 ret->Scope.Element = Child;
728 strcpy(ret->Scope.Name, Name);
733 * \brief Add a scope node
735 tAST_Node *AST_NewClassElement(tParser *Parser, tAST_Node *Object, const char *Name)
737 tAST_Node *ret = AST_int_AllocateNode(Parser, NODETYPE_ELEMENT, strlen(Name) + 1 );
738 ret->Scope.Element = Object;
739 strcpy(ret->Scope.Name, Name);