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

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