Usermode/libspiderscript - Fixing a multitude of bugs
[tpg/acess2.git] / Usermode / Libraries / libspiderscript.so_src / exec_ast.c
1 /*
2  * SpiderScript Library
3  *
4  * AST Execution
5  */
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <stdarg.h>
9 #include <string.h>
10 #include "common.h"
11 #include "ast.h"
12
13 #define TRACE_VAR_LOOKUPS       0
14 #define TRACE_NODE_RETURNS      0
15
16 // === IMPORTS ===
17
18 // === PROTOTYPES ===
19 // - Node Execution
20 tSpiderValue    *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node);
21 tSpiderValue    *AST_ExecuteNode_BinOp(tSpiderScript *Script, tAST_Node *Node, int Operation, tSpiderValue *Left, tSpiderValue *Right);
22 tSpiderValue    *AST_ExecuteNode_UniOp(tSpiderScript *Script, tAST_Node *Node, int Operation, tSpiderValue *Value);
23 // - Variables
24 tAST_Variable *Variable_Define(tAST_BlockState *Block, int Type, const char *Name, tSpiderValue *Value);
25  int    Variable_SetValue(tAST_BlockState *Block, tAST_Node *VarNode, tSpiderValue *Value);
26 tSpiderValue    *Variable_GetValue(tAST_BlockState *Block, tAST_Node *VarNode);
27 void    Variable_Destroy(tAST_Variable *Variable);
28 // - Errors
29 void    AST_RuntimeMessage(tAST_Node *Node, const char *Type, const char *Format, ...);
30 void    AST_RuntimeError(tAST_Node *Node, const char *Format, ...);
31
32 // === GLOBALS ===
33  int    giNextBlockIdent = 1;
34
35 // === CODE ===
36 tSpiderValue *AST_ExecuteFunction(tSpiderScript *Script, tScript_Function *Fcn, int NArguments, tSpiderValue **Arguments)
37 {
38         tAST_BlockState bs;
39         tSpiderValue    *ret;
40          int    i = 0;
41         
42         // Build a block State
43         bs.FirstVar = NULL;
44         bs.RetVal = NULL;
45         bs.Parent = NULL;
46         bs.BaseNamespace = &Script->Variant->RootNamespace;
47         bs.CurNamespace = NULL;
48         bs.Script = Script;
49         bs.Ident = giNextBlockIdent ++;
50         
51         // Parse arguments
52         for( i = 0; i < Fcn->ArgumentCount; i ++ )
53         {
54                 if( i >= NArguments )   break;  // TODO: Return gracefully
55                 // TODO: Type checks
56                 Variable_Define(&bs,
57                         Fcn->Arguments[i].Type, Fcn->Arguments[i].Name,
58                         Arguments[i]);
59         }
60                         
61         // Execute function
62         ret = AST_ExecuteNode(&bs, Fcn->ASTFcn);
63         if(ret != ERRPTR)
64         {
65                 SpiderScript_DereferenceValue(ret);     // Dereference output of last block statement
66                 ret = bs.RetVal;        // Set to return value of block
67         }
68                         
69         while(bs.FirstVar)
70         {
71                 tAST_Variable   *nextVar = bs.FirstVar->Next;
72                 Variable_Destroy( bs.FirstVar );
73                 bs.FirstVar = nextVar;
74         }
75         return ret;
76 }
77
78 /**
79  * \brief Execute an AST node and return its value
80  * \param Block Execution context
81  * \param Node  Node to execute
82  */
83 tSpiderValue *AST_ExecuteNode(tAST_BlockState *Block, tAST_Node *Node)
84 {
85         tAST_Node       *node;
86         tSpiderValue    *ret = NULL, *tmpobj;
87         tSpiderValue    *op1, *op2;     // Binary operations
88          int    i;
89         
90         switch(Node->Type)
91         {
92         // No Operation
93         case NODETYPE_NOP:
94                 ret = NULL;
95                 break;
96         
97         // Code block
98         case NODETYPE_BLOCK:
99                 {
100                         tAST_BlockState blockInfo;
101                         blockInfo.Parent = Block;
102                         blockInfo.Script = Block->Script;
103                         blockInfo.FirstVar = NULL;
104                         blockInfo.RetVal = NULL;
105                         blockInfo.BaseNamespace = Block->BaseNamespace;
106                         blockInfo.CurNamespace = NULL;
107                         blockInfo.BreakTarget = NULL;
108                         blockInfo.Ident = giNextBlockIdent ++;
109                         ret = NULL;
110                         // Loop over all nodes, or until the return value is set
111                         for(node = Node->Block.FirstChild;
112                                 node && !blockInfo.RetVal && !blockInfo.BreakTarget;
113                                 node = node->NextSibling )
114                         {
115                                 ret = AST_ExecuteNode(&blockInfo, node);
116                                 if(ret == ERRPTR)       break;  // Error check
117                                 if(ret != NULL) SpiderScript_DereferenceValue(ret);     // Free unused value
118                         }
119                         // Clean up variables
120                         while(blockInfo.FirstVar)
121                         {
122                                 tAST_Variable   *nextVar = blockInfo.FirstVar->Next;
123                                 Variable_Destroy( blockInfo.FirstVar );
124                                 blockInfo.FirstVar = nextVar;
125                         }
126                         // Clear ret if not an error
127                         if(ret != ERRPTR)       ret = NULL;
128                         
129                         // Set parent's return value if needed
130                         if( blockInfo.RetVal )
131                                 Block->RetVal = blockInfo.RetVal;
132                         if( blockInfo.BreakTarget ) {
133                                 Block->BreakTarget = blockInfo.BreakTarget;
134                                 Block->BreakType = blockInfo.BreakType;
135                         }
136                         
137                         // TODO: Unset break if break type deontes a block break
138                 }
139                 
140                 break;
141         
142         // Assignment
143         case NODETYPE_ASSIGN:
144                 // TODO: Support assigning to object attributes
145                 if( Node->Assign.Dest->Type != NODETYPE_VARIABLE ) {
146                         AST_RuntimeError(Node, "LVALUE of assignment is not a variable");
147                         return ERRPTR;
148                 }
149                 ret = AST_ExecuteNode(Block, Node->Assign.Value);
150                 if(ret == ERRPTR)       return ERRPTR;
151                 
152                 // Perform assignment operation
153                 if( Node->Assign.Operation != NODETYPE_NOP )
154                 {
155                         tSpiderValue    *varVal, *value;
156
157                         varVal = Variable_GetValue(Block, Node->Assign.Dest);
158                         if(varVal == ERRPTR)    return ERRPTR;
159                         #if 0
160                         #else
161                         if(varVal && varVal->ReferenceCount == 2) {
162                                 SpiderScript_DereferenceValue(varVal);
163 //                              printf("pre: (%s) varVal->ReferenceCount = %i\n",
164 //                                      Node->Assign.Dest->Variable.Name,
165 //                                      varVal->ReferenceCount);
166                         }
167                         #endif
168                         value = AST_ExecuteNode_BinOp(Block->Script, Node, Node->Assign.Operation, varVal, ret);
169                         if(value == ERRPTR)     return ERRPTR;
170
171                         if(ret) SpiderScript_DereferenceValue(ret);
172                         #if 0
173                         if(varVal)      SpiderScript_DereferenceValue(varVal);
174                         #else
175                         if(varVal && varVal->ReferenceCount == 1) {
176                                 SpiderScript_ReferenceValue(varVal);
177 //                              printf("post: varVal->ReferenceCount = %i\n", varVal->ReferenceCount);
178                                 break;  // If varVal was non-null, it has been updated by _BinOp
179                         }
180                         #endif
181                         // Else, it was NULL, so has to be assigned
182                         ret = value;
183                 }
184                 
185                 // Set the variable value
186                 if( Variable_SetValue( Block, Node->Assign.Dest, ret ) ) {
187                         SpiderScript_DereferenceValue( ret );
188                         return ERRPTR;
189                 }
190                 break;
191         
192         // Post increment/decrement
193         case NODETYPE_POSTINC:
194         case NODETYPE_POSTDEC:
195                 {
196                         tSpiderValue    *varVal, *value;
197                         static tSpiderValue     one = {
198                                 .Type = SS_DATATYPE_INTEGER,
199                                 .ReferenceCount = 1,
200                                 {.Integer = 1}
201                                 };
202                         
203                         // TODO: Support assigning to object attributes
204                         if( Node->UniOp.Value->Type != NODETYPE_VARIABLE ) {
205                                 AST_RuntimeError(Node, "LVALUE of assignment is not a variable");
206                                 return ERRPTR;
207                         }
208                 
209                         // Get values (current variable contents and a static one)
210                         varVal = Variable_GetValue(Block, Node->UniOp.Value);
211                         
212                         if( Node->Type == NODETYPE_POSTDEC )
213                                 value = AST_ExecuteNode_BinOp(Block->Script, Node, NODETYPE_SUBTRACT, varVal, &one);
214                         else
215                                 value = AST_ExecuteNode_BinOp(Block->Script, Node, NODETYPE_ADD, varVal, &one);
216                         if( value == ERRPTR )
217                                 return ERRPTR;
218                         
219                         ret = varVal;
220                 
221                         if( Variable_SetValue( Block, Node->UniOp.Value, value ) ) {
222                                 SpiderScript_DereferenceValue( ret );
223                                 return ERRPTR;
224                         }
225                         SpiderScript_DereferenceValue( value );
226                 }
227                 break;
228         
229         // Function Call
230         case NODETYPE_METHODCALL:
231         case NODETYPE_FUNCTIONCALL:
232         case NODETYPE_CREATEOBJECT:
233                 // Logical block (used to allocate `params`)
234                 {
235                         tSpiderNamespace        *ns = Block->CurNamespace;
236                         tSpiderValue    *params[Node->FunctionCall.NumArgs];
237                         i = 0;
238                         for(node = Node->FunctionCall.FirstArg; node; node = node->NextSibling)
239                         {
240                                 params[i] = AST_ExecuteNode(Block, node);
241                                 if( params[i] == ERRPTR ) {
242                                         while(i--)      SpiderScript_DereferenceValue(params[i]);
243                                         ret = ERRPTR;
244                                         goto _return;
245                                 }
246                                 i ++;
247                         }
248                         
249                         if( !ns )       ns = Block->BaseNamespace;
250                         
251                         // Call the function
252                         if( Node->Type == NODETYPE_CREATEOBJECT )
253                         {
254                                 ret = SpiderScript_CreateObject(Block->Script,
255                                         ns,
256                                         Node->FunctionCall.Name,
257                                         Node->FunctionCall.NumArgs, params
258                                         );
259                         }
260                         else if( Node->Type == NODETYPE_METHODCALL )
261                         {
262                                 tSpiderValue *obj = AST_ExecuteNode(Block, Node->FunctionCall.Object);
263                                 if( !obj || obj == ERRPTR || obj->Type != SS_DATATYPE_OBJECT ) {
264                                         AST_RuntimeError(Node->FunctionCall.Object,
265                                                 "Type Mismatch - Required SS_DATATYPE_OBJECT for method call");
266                                         while(i--)      SpiderScript_DereferenceValue(params[i]);
267                                         ret = ERRPTR;
268                                         break;
269                                 }
270                                 ret = SpiderScript_ExecuteMethod(Block->Script,
271                                         obj->Object, Node->FunctionCall.Name,
272                                         Node->FunctionCall.NumArgs, params
273                                         );
274                                 SpiderScript_DereferenceValue(obj);
275                         }
276                         else
277                         {
278                                 ret = SpiderScript_ExecuteFunction(Block->Script,
279                                         ns, Node->FunctionCall.Name,
280                                         Node->FunctionCall.NumArgs, params
281                                         );
282                         }
283
284                         
285                         // Dereference parameters
286                         while(i--)      SpiderScript_DereferenceValue(params[i]);
287                         
288                         // falls out
289                 }
290                 break;
291         
292         // Conditional
293         case NODETYPE_IF:
294                 ret = AST_ExecuteNode(Block, Node->If.Condition);
295                 if( ret == ERRPTR )     break;
296                 if( SpiderScript_IsValueTrue(ret) ) {
297                         tmpobj = AST_ExecuteNode(Block, Node->If.True);
298                 }
299                 else {
300                         tmpobj = AST_ExecuteNode(Block, Node->If.False);
301                 }
302                 SpiderScript_DereferenceValue(ret);
303                 if( tmpobj == ERRPTR )  return ERRPTR;
304                 SpiderScript_DereferenceValue(tmpobj);
305                 ret = NULL;
306                 break;
307         
308         // Loop
309         case NODETYPE_LOOP:
310                 // Initialise
311                 ret = AST_ExecuteNode(Block, Node->For.Init);
312                 if(ret == ERRPTR)       break;
313                 
314                 // Check initial condition
315                 if( !Node->For.bCheckAfter )
316                 {
317                         SpiderScript_DereferenceValue(ret);
318                 
319                         ret = AST_ExecuteNode(Block, Node->For.Condition);
320                         if(ret == ERRPTR)       return ERRPTR;
321                         if(!SpiderScript_IsValueTrue(ret)) {
322                                 SpiderScript_DereferenceValue(ret);
323                                 ret = NULL;
324                                 break;
325                         }
326                 }
327         
328                 // Perform loop
329                 for( ;; )
330                 {
331                         SpiderScript_DereferenceValue(ret);
332                         
333                         // Code
334                         ret = AST_ExecuteNode(Block, Node->For.Code);
335                         if(ret == ERRPTR)       return ERRPTR;
336                         SpiderScript_DereferenceValue(ret);
337                         
338                         if(Block->BreakTarget)
339                         {
340                                 if( Block->BreakTarget[0] == '\0' || strcmp(Block->BreakTarget, Node->For.Tag) == 0 )
341                                 {
342                                         // Ours
343                                         free((void*)Block->BreakTarget);        Block->BreakTarget = NULL;
344                                         if( Block->BreakType == NODETYPE_CONTINUE ) {
345                                                 // Continue, just keep going
346                                         }
347                                         else
348                                                 break;
349                                 }
350                                 else
351                                         break;  // Break out of this loop
352                         }
353                         
354                         // Increment
355                         ret = AST_ExecuteNode(Block, Node->For.Increment);
356                         if(ret == ERRPTR)       return ERRPTR;
357                         SpiderScript_DereferenceValue(ret);
358                         
359                         // Check condition
360                         ret = AST_ExecuteNode(Block, Node->For.Condition);
361                         if(ret == ERRPTR)       return ERRPTR;
362                         if(!SpiderScript_IsValueTrue(ret))      break;
363                 }
364                 SpiderScript_DereferenceValue(ret);
365                 ret = NULL;
366                 break;
367         
368         // Return
369         case NODETYPE_RETURN:
370                 ret = AST_ExecuteNode(Block, Node->UniOp.Value);
371                 if(ret == ERRPTR)       break;
372                 Block->RetVal = ret;    // Return value set
373                 ret = NULL;     // the `return` statement does not return a value
374                 break;
375         
376         case NODETYPE_BREAK:
377         case NODETYPE_CONTINUE:
378                 Block->BreakTarget = strdup(Node->Variable.Name);
379                 Block->BreakType = Node->Type;
380                 break;
381         
382         // Define a variable
383         case NODETYPE_DEFVAR:
384                 if( Node->DefVar.InitialValue ) {
385                         tmpobj = AST_ExecuteNode(Block, Node->DefVar.InitialValue);
386                         if(tmpobj == ERRPTR)    return ERRPTR;
387                 }
388                 else {
389                         tmpobj = NULL;
390                 }
391                 // TODO: Handle arrays
392                 ret = NULL;
393                 if( Variable_Define(Block, Node->DefVar.DataType, Node->DefVar.Name, tmpobj) == ERRPTR )
394                         ret = ERRPTR;
395                 SpiderScript_DereferenceValue(tmpobj);
396                 break;
397         
398         // Scope
399         case NODETYPE_SCOPE:
400                 {
401                 tSpiderNamespace        *ns;
402                 
403                 // Set current namespace if unset
404                 if( !Block->CurNamespace )
405                         Block->CurNamespace = Block->BaseNamespace;
406                 
407                 // Empty string means use the root namespace
408                 if( Node->Scope.Name[0] == '\0' )
409                 {
410                         ns = &Block->Script->Variant->RootNamespace;
411                 }
412                 else
413                 {
414                         // Otherwise scan the current namespace for the element
415                         for( ns = Block->CurNamespace->FirstChild; ns; ns = ns->Next )
416                         {
417                                 if( strcmp(ns->Name, Node->Scope.Name) == 0 )
418                                         break;
419                         }
420                 }
421                 if(!ns) {
422                         AST_RuntimeError(Node, "Unknown namespace '%s'", Node->Scope.Name);
423                         ret = ERRPTR;
424                         break;
425                 }
426                 Block->CurNamespace = ns;
427                 
428                 ret = AST_ExecuteNode(Block, Node->Scope.Element);
429                 }
430                 break;
431         
432         // Variable
433         case NODETYPE_VARIABLE:
434                 ret = Variable_GetValue( Block, Node );
435                 break;
436         
437         // Element of an Object
438         case NODETYPE_ELEMENT:
439                 tmpobj = AST_ExecuteNode( Block, Node->Scope.Element );
440                 if(tmpobj == ERRPTR)    return ERRPTR;
441                 if( !tmpobj || tmpobj->Type != SS_DATATYPE_OBJECT )
442                 {
443                         AST_RuntimeError(Node->Scope.Element, "Unable to dereference a non-object");
444                         ret = ERRPTR;
445                         break ;
446                 }
447                 
448                 for( i = 0; i < tmpobj->Object->Type->NAttributes; i ++ )
449                 {
450                         if( strcmp(Node->Scope.Name, tmpobj->Object->Type->AttributeDefs[i].Name) == 0 )
451                         {
452                                 ret = tmpobj->Object->Attributes[i];
453                                 SpiderScript_ReferenceValue(ret);
454                                 break;
455                         }
456                 }
457                 if( i == tmpobj->Object->Type->NAttributes )
458                 {
459                         AST_RuntimeError(Node->Scope.Element, "Unknown attribute '%s' of class '%s'",
460                                 Node->Scope.Name, tmpobj->Object->Type->Name);
461                         ret = ERRPTR;
462                 }
463                 break;
464
465         // Cast a value to another
466         case NODETYPE_CAST:
467                 {
468                 tmpobj = AST_ExecuteNode(Block, Node->Cast.Value);
469                 if(tmpobj == ERRPTR) return ERRPTR;
470                 ret = SpiderScript_CastValueTo( Node->Cast.DataType, tmpobj );
471                 SpiderScript_DereferenceValue(tmpobj);
472                 }
473                 break;
474
475         // Index into an array
476         case NODETYPE_INDEX:
477                 op1 = AST_ExecuteNode(Block, Node->BinOp.Left); // Array
478                 if(op1 == ERRPTR)       return ERRPTR;
479                 op2 = AST_ExecuteNode(Block, Node->BinOp.Right);        // Offset
480                 if(op2 == ERRPTR) {
481                         SpiderScript_DereferenceValue(op1);
482                         return ERRPTR;
483                 }
484                 
485                 if( !op1 || op1->Type != SS_DATATYPE_ARRAY )
486                 {
487                         // TODO: Implement "operator []" on objects
488                         AST_RuntimeError(Node, "Indexing non-array");
489                         ret = ERRPTR;
490                         break;
491                 }
492                 
493                 if( (!op2 || op2->Type != SS_DATATYPE_INTEGER) && !Block->Script->Variant->bImplicitCasts ) {
494                         AST_RuntimeError(Node, "Array index is not an integer");
495                         ret = ERRPTR;
496                         break;
497                 }
498                 
499                 if( !op2 || op2->Type != SS_DATATYPE_INTEGER )
500                 {
501                         tmpobj = SpiderScript_CastValueTo(SS_DATATYPE_INTEGER, op2);
502                         SpiderScript_DereferenceValue(op2);
503                         op2 = tmpobj;
504                 }
505                 
506                 if( op2->Integer >= op1->Array.Length ) {
507                         AST_RuntimeError(Node, "Array index out of bounds %i >= %i",
508                                 op2->Integer, op1->Array.Length);
509                         ret = ERRPTR;
510                         break;
511                 }
512                 
513                 ret = op1->Array.Items[ op2->Integer ];
514                 SpiderScript_ReferenceValue(ret);
515                 
516                 SpiderScript_DereferenceValue(op1);
517                 SpiderScript_DereferenceValue(op2);
518                 break;
519
520         // TODO: Implement runtime constants
521         case NODETYPE_CONSTANT:
522                 // TODO: Scan namespace for constant name
523                 AST_RuntimeError(Node, "TODO - Runtime Constants");
524                 ret = ERRPTR;
525                 break;
526         
527         // Constant Values
528         case NODETYPE_STRING:
529         case NODETYPE_INTEGER:
530         case NODETYPE_REAL:
531                 ret = &Node->Constant;
532                 SpiderScript_ReferenceValue(ret);
533                 break;
534         
535         // --- Operations ---
536         // Boolean Operations
537         case NODETYPE_LOGICALNOT:       // Logical NOT (!)
538                 op1 = AST_ExecuteNode(Block, Node->UniOp.Value);
539                 if(op1 == ERRPTR)       return ERRPTR;
540                 ret = SpiderScript_CreateInteger( !SpiderScript_IsValueTrue(op1) );
541                 SpiderScript_DereferenceValue(op1);
542                 break;
543         case NODETYPE_LOGICALAND:       // Logical AND (&&)
544         case NODETYPE_LOGICALOR:        // Logical OR (||)
545         case NODETYPE_LOGICALXOR:       // Logical XOR (^^)
546                 op1 = AST_ExecuteNode(Block, Node->BinOp.Left);
547                 if(op1 == ERRPTR)       return ERRPTR;
548                 op2 = AST_ExecuteNode(Block, Node->BinOp.Right);
549                 if(op2 == ERRPTR) {
550                         SpiderScript_DereferenceValue(op1);
551                         return ERRPTR;
552                 }
553                 
554                 switch( Node->Type )
555                 {
556                 case NODETYPE_LOGICALAND:
557                         ret = SpiderScript_CreateInteger( SpiderScript_IsValueTrue(op1) && SpiderScript_IsValueTrue(op2) );
558                         break;
559                 case NODETYPE_LOGICALOR:
560                         ret = SpiderScript_CreateInteger( SpiderScript_IsValueTrue(op1) || SpiderScript_IsValueTrue(op2) );
561                         break;
562                 case NODETYPE_LOGICALXOR:
563                         ret = SpiderScript_CreateInteger( SpiderScript_IsValueTrue(op1) ^ SpiderScript_IsValueTrue(op2) );
564                         break;
565                 default:        break;
566                 }
567                 
568                 // Free intermediate objects
569                 SpiderScript_DereferenceValue(op1);
570                 SpiderScript_DereferenceValue(op2);
571                 break;
572         
573         // General Unary Operations
574         case NODETYPE_BWNOT:    // Bitwise NOT (~)
575         case NODETYPE_NEGATE:   // Negation (-)
576                 op1 = AST_ExecuteNode(Block, Node->UniOp.Value);
577                 if(op1 == ERRPTR)       return ERRPTR;
578                 ret = AST_ExecuteNode_UniOp(Block->Script, Node, Node->Type, op1);
579                 SpiderScript_DereferenceValue(op1);
580                 break;
581         
582         // General Binary Operations
583         case NODETYPE_ADD:
584         case NODETYPE_SUBTRACT:
585         case NODETYPE_MULTIPLY:
586         case NODETYPE_DIVIDE:
587         case NODETYPE_MODULO:
588         case NODETYPE_BWAND:
589         case NODETYPE_BWOR:
590         case NODETYPE_BWXOR:
591         case NODETYPE_BITSHIFTLEFT:
592         case NODETYPE_BITSHIFTRIGHT:
593         case NODETYPE_BITROTATELEFT:
594         case NODETYPE_EQUALS:
595         case NODETYPE_NOTEQUALS:
596         case NODETYPE_LESSTHAN:
597         case NODETYPE_GREATERTHAN:
598         case NODETYPE_LESSTHANEQUAL:
599         case NODETYPE_GREATERTHANEQUAL:
600                 // Get operands
601                 op1 = AST_ExecuteNode(Block, Node->BinOp.Left);
602                 if(op1 == ERRPTR)       return ERRPTR;
603                 op2 = AST_ExecuteNode(Block, Node->BinOp.Right);
604                 if(op2 == ERRPTR) {
605                         SpiderScript_DereferenceValue(op1);
606                         return ERRPTR;
607                 }
608                 
609                 ret = AST_ExecuteNode_BinOp(Block->Script, Node, Node->Type, op1, op2);
610                 
611                 // Free intermediate objects
612                 SpiderScript_DereferenceValue(op1);
613                 SpiderScript_DereferenceValue(op2);
614                 break;
615         
616         //default:
617         //      ret = NULL;
618         //      AST_RuntimeError(Node, "BUG - SpiderScript AST_ExecuteNode Unimplemented %i", Node->Type);
619         //      break;
620         }
621 _return:
622         // Reset namespace when no longer needed
623         if( Node->Type != NODETYPE_SCOPE )
624                 Block->CurNamespace = NULL;
625
626         #if TRACE_NODE_RETURNS
627         if(ret && ret != ERRPTR) {
628                 AST_RuntimeError(Node, "Ret type of %p %i is %i", Node, Node->Type, ret->Type);
629         }
630         else {
631                 AST_RuntimeError(Node, "Ret type of %p %i is %p", Node, Node->Type, ret);
632         }
633         #endif
634
635         return ret;
636 }
637
638 tSpiderValue *AST_ExecuteNode_UniOp(tSpiderScript *Script, tAST_Node *Node, int Operation, tSpiderValue *Value)
639 {
640         tSpiderValue    *ret;
641         #if 0
642         if( Value->Type == SS_DATATYPE_OBJECT )
643         {
644                 const char      *fcnname;
645                 switch(Operation)
646                 {
647                 case NODETYPE_NEGATE:   fcnname = "-ve";        break;
648                 case NODETYPE_BWNOT:    fcnname = "~";  break;
649                 default:        fcnname = NULL; break;
650                 }
651                 
652                 if( fcnname )
653                 {
654                         ret = Object_ExecuteMethod(Value->Object, fcnname, );
655                         if( ret != ERRPTR )
656                                 return ret;
657                 }
658         }
659         #endif
660         switch(Value->Type)
661         {
662         // Integer Operations
663         case SS_DATATYPE_INTEGER:
664                 if( Value->ReferenceCount == 1 )
665                         SpiderScript_ReferenceValue(ret = Value);
666                 else
667                         ret = SpiderScript_CreateInteger(0);
668                 switch(Operation)
669                 {
670                 case NODETYPE_NEGATE:   ret->Integer = -Value->Integer; break;
671                 case NODETYPE_BWNOT:    ret->Integer = ~Value->Integer; break;
672                 default:
673                         AST_RuntimeError(Node, "SpiderScript internal error: Exec,UniOP,Integer unknown op %i", Operation);
674                         SpiderScript_DereferenceValue(ret);
675                         ret = ERRPTR;
676                         break;
677                 }
678                 break;
679         // Real number Operations
680         case SS_DATATYPE_REAL:
681                 switch(Operation)
682                 {
683                 case NODETYPE_NEGATE:   ret = SpiderScript_CreateInteger( -Value->Real );       break;
684                 default:
685                         AST_RuntimeError(Node, "SpiderScript internal error: Exec,UniOP,Real unknown op %i", Operation);
686                         ret = ERRPTR;
687                         break;
688                 }
689                 break;
690         
691         default:
692                 AST_RuntimeError(NULL, "Invalid operation (%i) on type (%i)", Operation, Value->Type);
693                 ret = ERRPTR;
694                 break;
695         }
696         
697         return ret;
698 }
699
700 tSpiderValue *AST_ExecuteNode_BinOp(tSpiderScript *Script, tAST_Node *Node, int Operation, tSpiderValue *Left, tSpiderValue *Right)
701 {
702         tSpiderValue    *preCastValue = Right;
703         tSpiderValue    *ret;
704         
705         // Convert types
706         if( Left && Right && Left->Type != Right->Type )
707         {
708                 #if 0
709                 // Object types
710                 // - Operator overload functions
711                 if( Left->Type == SS_DATATYPE_OBJECT )
712                 {
713                         const char      *fcnname;
714                         switch(Operation)
715                         {
716                         case NODETYPE_ADD:      fcnname = "+";  break;
717                         case NODETYPE_SUBTRACT: fcnname = "-";  break;
718                         case NODETYPE_MULTIPLY: fcnname = "*";  break;
719                         case NODETYPE_DIVIDE:   fcnname = "/";  break;
720                         case NODETYPE_MODULO:   fcnname = "%";  break;
721                         case NODETYPE_BWAND:    fcnname = "&";  break;
722                         case NODETYPE_BWOR:     fcnname = "|";  break;
723                         case NODETYPE_BWXOR:    fcnname = "^";  break;
724                         case NODETYPE_BITSHIFTLEFT:     fcnname = "<<"; break;
725                         case NODETYPE_BITSHIFTRIGHT:fcnname = ">>";     break;
726                         case NODETYPE_BITROTATELEFT:fcnname = "<<<";    break;
727                         default:        fcnname = NULL; break;
728                         }
729                         
730                         if( fcnname )
731                         {
732                                 ret = Object_ExecuteMethod(Left->Object, fcnname, Right);
733                                 if( ret != ERRPTR )
734                                         return ret;
735                                 // Fall through and try casting (which will usually fail)
736                         }
737                 }
738                 #endif
739                 
740                 // If implicit casts are allowed, convert Right to Left's type
741                 if(Script->Variant->bImplicitCasts)
742                 {
743                         Right = SpiderScript_CastValueTo(Left->Type, Right);
744                         if(Right == ERRPTR)
745                                 return ERRPTR;
746                 }
747                 // If statically typed, this should never happen, but catch it anyway
748                 else {
749                         AST_RuntimeError(Node, "Implicit cast not allowed (from %i to %i)", Right->Type, Left->Type);
750                         return ERRPTR;
751                 }
752         }
753         
754         // NULL Check
755         if( Left == NULL || Right == NULL ) {
756                 if(Right && Right != preCastValue)      free(Right);
757                 return NULL;
758         }
759
760         // Catch comparisons
761         switch(Operation)
762         {
763         case NODETYPE_EQUALS:
764         case NODETYPE_NOTEQUALS:
765         case NODETYPE_LESSTHAN:
766         case NODETYPE_GREATERTHAN:
767         case NODETYPE_LESSTHANEQUAL:
768         case NODETYPE_GREATERTHANEQUAL: {
769                  int    cmp;
770                 ret = NULL;
771                 // Do operation
772                 switch(Left->Type)
773                 {
774                 // - String Compare (does a strcmp, well memcmp)
775                 case SS_DATATYPE_STRING:
776                         // Call memcmp to do most of the work
777                         cmp = memcmp(
778                                 Left->String.Data, Right->String.Data,
779                                 (Left->String.Length < Right->String.Length) ? Left->String.Length : Right->String.Length
780                                 );
781                         // Handle reaching the end of the string
782                         if( cmp == 0 ) {
783                                 if( Left->String.Length == Right->String.Length )
784                                         cmp = 0;
785                                 else if( Left->String.Length < Right->String.Length )
786                                         cmp = 1;
787                                 else
788                                         cmp = -1;
789                         }
790                         break;
791                 
792                 // - Integer Comparisons
793                 case SS_DATATYPE_INTEGER:
794                         if( Left->Integer == Right->Integer )
795                                 cmp = 0;
796                         else if( Left->Integer < Right->Integer )
797                                 cmp = -1;
798                         else
799                                 cmp = 1;
800                         break;
801                 // - Real Number Comparisons
802                 case SS_DATATYPE_REAL:
803                         cmp = (Left->Real - Right->Real) / Right->Real * 10000; // < 0.1% difference is equality
804                         break;
805                 default:
806                         AST_RuntimeError(Node, "TODO - Comparison of type %i", Left->Type);
807                         ret = ERRPTR;
808                         break;
809                 }
810                 
811                 // Error check
812                 if( ret != ERRPTR )
813                 {
814                         if(Left->ReferenceCount == 1 && Left->Type != SS_DATATYPE_STRING)
815                                 SpiderScript_ReferenceValue(ret = Left);
816                         else
817                                 ret = SpiderScript_CreateInteger(0);
818                         
819                         // Create return
820                         switch(Operation)
821                         {
822                         case NODETYPE_EQUALS:   ret->Integer = (cmp == 0);      break;
823                         case NODETYPE_NOTEQUALS:        ret->Integer = (cmp != 0);      break;
824                         case NODETYPE_LESSTHAN: ret->Integer = (cmp < 0);       break;
825                         case NODETYPE_GREATERTHAN:      ret->Integer = (cmp > 0);       break;
826                         case NODETYPE_LESSTHANEQUAL:    ret->Integer = (cmp <= 0);      break;
827                         case NODETYPE_GREATERTHANEQUAL: ret->Integer = (cmp >= 0);      break;
828                         default:
829                                 AST_RuntimeError(Node, "Exec,CmpOp unknown op %i", Operation);
830                                 SpiderScript_DereferenceValue(ret);
831                                 ret = ERRPTR;
832                                 break;
833                         }
834                 }
835                 if(Right && Right != preCastValue)      free(Right);
836                 return ret;
837                 }
838
839         // Fall through and sort by type instead
840         default:
841                 break;
842         }
843         
844         // Do operation
845         switch(Left->Type)
846         {
847         // String Concatenation
848         case SS_DATATYPE_STRING:
849                 switch(Operation)
850                 {
851                 case NODETYPE_ADD:      // Concatenate
852                         ret = SpiderScript_StringConcat(Left, Right);
853                         break;
854                 // TODO: Support python style 'i = %i' % i ?
855                 // Might do it via a function call
856                 // Implement it via % with an array, but getting past the cast will be fun
857 //              case NODETYPE_MODULUS:
858 //                      break;
859                 // TODO: Support string repititions
860 //              case NODETYPE_MULTIPLY:
861 //                      break;
862
863                 default:
864                         AST_RuntimeError(Node, "SpiderScript internal error: Exec,BinOP,String unknown op %i", Operation);
865                         ret = ERRPTR;
866                         break;
867                 }
868                 break;
869         // Integer Operations
870         case SS_DATATYPE_INTEGER:
871                 if( Left->ReferenceCount == 1 )
872                         SpiderScript_ReferenceValue(ret = Left);
873                 else
874                         ret = SpiderScript_CreateInteger(0);
875                 switch(Operation)
876                 {
877                 case NODETYPE_ADD:      ret->Integer = Left->Integer + Right->Integer;  break;
878                 case NODETYPE_SUBTRACT: ret->Integer = Left->Integer - Right->Integer;  break;
879                 case NODETYPE_MULTIPLY: ret->Integer = Left->Integer * Right->Integer;  break;
880                 case NODETYPE_DIVIDE:   ret->Integer = Left->Integer / Right->Integer;  break;
881                 case NODETYPE_MODULO:   ret->Integer = Left->Integer % Right->Integer;  break;
882                 case NODETYPE_BWAND:    ret->Integer = Left->Integer & Right->Integer;  break;
883                 case NODETYPE_BWOR:     ret->Integer = Left->Integer | Right->Integer;  break;
884                 case NODETYPE_BWXOR:    ret->Integer = Left->Integer ^ Right->Integer;  break;
885                 case NODETYPE_BITSHIFTLEFT: ret->Integer = Left->Integer << Right->Integer;     break;
886                 case NODETYPE_BITSHIFTRIGHT:ret->Integer = Left->Integer >> Right->Integer;     break;
887                 case NODETYPE_BITROTATELEFT:
888                         ret->Integer = (Left->Integer << Right->Integer) | (Left->Integer >> (64-Right->Integer));
889                         break;
890                 default:
891                         AST_RuntimeError(Node, "SpiderScript internal error: Exec,BinOP,Integer unknown op %i", Operation);
892                         SpiderScript_DereferenceValue(ret);
893                         ret = ERRPTR;
894                         break;
895                 }
896                 break;
897         
898         // Real Numbers
899         case SS_DATATYPE_REAL:
900                 if( Left->ReferenceCount == 1 )
901                         SpiderScript_ReferenceValue(ret = Left);
902                 else
903                         ret = SpiderScript_CreateReal(0);
904                 switch(Operation)
905                 {
906                 case NODETYPE_ADD:      ret->Real = Left->Real + Right->Real;   break;
907                 case NODETYPE_SUBTRACT: ret->Real = Left->Real - Right->Real;   break;
908                 case NODETYPE_MULTIPLY: ret->Real = Left->Real * Right->Real;   break;
909                 case NODETYPE_DIVIDE:   ret->Real = Left->Real / Right->Real;   break;
910                 default:
911                         AST_RuntimeError(Node, "SpiderScript internal error: Exec,BinOP,Real unknown op %i", Operation);
912                         SpiderScript_DereferenceValue(ret);
913                         ret = ERRPTR;
914                         break;
915                 }
916                 break;
917         
918         default:
919                 AST_RuntimeError(Node, "BUG - Invalid operation (%i) on type (%i)", Operation, Left->Type);
920                 ret = ERRPTR;
921                 break;
922         }
923         
924         if(Right && Right != preCastValue)      free(Right);
925         
926         return ret;
927 }
928
929 /**
930  * \brief Define a variable
931  * \param Block Current block state
932  * \param Type  Type of the variable
933  * \param Name  Name of the variable
934  * \return Boolean Failure
935  */
936 tAST_Variable *Variable_Define(tAST_BlockState *Block, int Type, const char *Name, tSpiderValue *Value)
937 {
938         tAST_Variable   *var, *prev = NULL;
939         
940         for( var = Block->FirstVar; var; prev = var, var = var->Next )
941         {
942                 if( strcmp(var->Name, Name) == 0 ) {
943                         AST_RuntimeError(NULL, "Redefinition of variable '%s'", Name);
944                         return ERRPTR;
945                 }
946         }
947         
948         var = malloc( sizeof(tAST_Variable) + strlen(Name) + 1 );
949         var->Next = NULL;
950         var->Type = Type;
951         var->Object = Value;
952         if(Value)       SpiderScript_ReferenceValue(Value);
953         strcpy(var->Name, Name);
954         
955         if(prev)        prev->Next = var;
956         else    Block->FirstVar = var;
957         
958         //printf("Defined variable %s (%i)\n", Name, Type);
959         
960         return var;
961 }
962
963 tAST_Variable *Variable_Lookup(tAST_BlockState *Block, tAST_Node *VarNode, int CreateType)
964 {       
965         tAST_Variable   *var = NULL;
966         
967         // Speed hack
968         if( VarNode->BlockState == Block && VarNode->BlockIdent == Block->Ident ) {
969                 var = VarNode->ValueCache;
970                 #if TRACE_VAR_LOOKUPS
971                 AST_RuntimeMessage(VarNode, "debug", "Fast var fetch on '%s' %p (%p:%i)",
972                         VarNode->Variable.Name, var,
973                         VarNode->BlockState, VarNode->BlockIdent
974                         );
975                 #endif
976         }
977         else
978         {
979                 tAST_BlockState *bs;
980                 for( bs = Block; bs; bs = bs->Parent )
981                 {
982                         for( var = bs->FirstVar; var; var = var->Next )
983                         {
984                                 if( strcmp(var->Name, VarNode->Variable.Name) == 0 )
985                                         break;
986                         }
987                         if(var) break;
988                 }
989                 
990                 if( !var )
991                 {
992                         if( Block->Script->Variant->bDyamicTyped && CreateType != SS_DATATYPE_UNDEF ) {
993                                 // Define variable
994                                 var = Variable_Define(Block, CreateType, VarNode->Variable.Name, NULL);
995                         }
996                         else
997                         {
998                                 AST_RuntimeError(VarNode, "Variable '%s' is undefined", VarNode->Variable.Name);
999                                 return NULL;
1000                         }
1001                 }
1002                 
1003                 #if TRACE_VAR_LOOKUPS
1004                 AST_RuntimeMessage(VarNode, "debug", "Saved variable lookup of '%s' %p (%p:%i)",
1005                         VarNode->Variable.Name, var,
1006                         Block, Block->Ident);
1007                 #endif
1008                 
1009                 VarNode->ValueCache = var;
1010                 VarNode->BlockState = Block;
1011                 VarNode->BlockIdent = Block->Ident;
1012         }
1013         
1014         return var;
1015 }
1016
1017 /**
1018  * \brief Set the value of a variable
1019  * \return Boolean Failure
1020  */
1021 int Variable_SetValue(tAST_BlockState *Block, tAST_Node *VarNode, tSpiderValue *Value)
1022 {
1023         tAST_Variable   *var;
1024         
1025         var = Variable_Lookup(Block, VarNode, (Value ? Value->Type : SS_DATATYPE_UNDEF));
1026         
1027         if( !var )      return -1;
1028         
1029         if( !Block->Script->Variant->bDyamicTyped && (Value && var->Type != Value->Type) )
1030         {
1031                 AST_RuntimeError(VarNode, "Type mismatch assigning to '%s'",
1032                         VarNode->Variable.Name);
1033                 return -2;
1034         }
1035
1036 //      printf("Assign %p to '%s'\n", Value, var->Name);
1037         SpiderScript_ReferenceValue(Value);
1038         SpiderScript_DereferenceValue(var->Object);
1039         var->Object = Value;
1040         return 0;
1041 }
1042
1043 /**
1044  * \brief Get the value of a variable
1045  */
1046 tSpiderValue *Variable_GetValue(tAST_BlockState *Block, tAST_Node *VarNode)
1047 {
1048         tAST_Variable   *var = Variable_Lookup(Block, VarNode, 0);
1049         
1050         if( !var )      return ERRPTR;
1051         
1052         SpiderScript_ReferenceValue(var->Object);
1053         return var->Object;
1054 }
1055
1056 /**
1057  * \brief Destorys a variable
1058  */
1059 void Variable_Destroy(tAST_Variable *Variable)
1060 {
1061 //      printf("Variable_Destroy: (%p'%s')\n", Variable, Variable->Name);
1062         SpiderScript_DereferenceValue(Variable->Object);
1063         free(Variable);
1064 }
1065

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