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

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