SpiderScript - Moved header to directory, ready to remove from tree
[tpg/acess2.git] / Usermode / Libraries / libspiderscript.so_src / exec.c
1 /*
2 * SpiderScript Library
3 * by John Hodge (thePowersGang)
4
5 * bytecode_makefile.c
6 * - Generate a bytecode file
7 */
8 #include <stdlib.h>
9 #include "common.h"
10 #include "ast.h"
11 #include <stdio.h>
12 #include <string.h>
13 #include <stdarg.h>
14
15 #define BC_NS_SEPARATOR '@'
16
17 // === IMPORTS ===
18 extern tSpiderFunction  *gpExports_First;
19 extern tSpiderNamespace gExportNamespaceRoot;
20 extern tSpiderValue     *AST_ExecuteFunction(tSpiderScript *Script, tScript_Function *Fcn, int NArguments, tSpiderValue **Arguments);
21 extern tSpiderValue     *Bytecode_ExecuteFunction(tSpiderScript *Script, tScript_Function *Fcn, int NArguments, tSpiderValue **Args);
22
23 // === PROTOTYPES ===
24 void    AST_RuntimeMessage(tAST_Node *Node, const char *Type, const char *Format, ...);
25 void    AST_RuntimeError(tAST_Node *Node, const char *Format, ...);
26
27 // === CODE ===
28 void *SpiderScript_int_GetNamespace(tSpiderScript *Script, tSpiderNamespace *RootNamespace,
29         const char *BasePath, const char *ItemPath,
30         const char **ItemName
31         )
32 {
33          int    len;
34         const char      *name;
35         const char      *end;
36          int    bTriedBase;
37         
38         tSpiderNamespace        *lastns, *ns;
39
40         // Prepend the base namespace
41         if( BasePath ) {
42                 name = BasePath;
43                 bTriedBase = 0;
44         }
45         else {
46                 bTriedBase = 1;
47                 name = ItemPath;
48         }
49         
50         // Scan
51         lastns = RootNamespace;
52         do {
53                 end = strchr(name, BC_NS_SEPARATOR);
54                 if(!end) {
55                         if( !bTriedBase )
56                                 len = strlen(name);
57                         else
58                                 break;
59                 }
60                 else {
61                         len = end - name;
62                 }
63
64                 // Check for this level
65                 for( ns = lastns->FirstChild; ns; ns = ns->Next )
66                 {
67 //                      printf("%p %.*s == %s\n", lastns, len, name, ns->Name);
68                         if( strncmp(name, ns->Name, len) == 0 && ns->Name[len] == 0 )
69                                 break ;
70                 }
71                 
72                 if(!ns) return NULL;
73
74                 if(!end && !bTriedBase) {
75                         end = ItemPath - 1;     // -1 to counter (name = end + 1)
76                         bTriedBase = 1;
77                 }
78
79                 lastns = ns;
80                 name = end + 1;
81         } while( end );
82
83         *ItemName = name;
84
85         return lastns;
86 }
87
88 tSpiderFunction *SpiderScript_int_GetNativeFunction(tSpiderScript *Script, tSpiderNamespace *RootNamespace,
89         const char *BasePath, const char *FunctionPath)
90 {
91         tSpiderNamespace *ns;
92         const char *name;
93         tSpiderFunction *fcn;
94
95         ns = SpiderScript_int_GetNamespace(Script, RootNamespace, BasePath, FunctionPath, &name);
96         if(!ns) return NULL;
97
98         for( fcn = ns->Functions; fcn; fcn = fcn->Next )
99         {
100                 if( strcmp(name, fcn->Name) == 0 )
101                         return fcn;
102         }
103         
104         return NULL;
105 }
106
107 tSpiderObjectDef *SpiderScript_int_GetNativeClass(tSpiderScript *Script, tSpiderNamespace *RootNamespace,
108         const char *BasePath, const char *ClassPath
109         )
110 {
111         tSpiderNamespace *ns;
112         const char *name;
113         tSpiderObjectDef *class;
114
115         ns = SpiderScript_int_GetNamespace(Script, RootNamespace, BasePath, ClassPath, &name);
116         if(!ns) return NULL;
117         
118         for( class = ns->Classes; class; class = class->Next )
119         {
120                 if( strcmp(name, class->Name) == 0 )
121                         return class;
122         }
123
124         return NULL;
125 }
126
127 /**
128  * \brief Execute a script function
129  * \param Script        Script context to execute in
130  * \param Namespace     Namespace to search for the function
131  * \param Function      Function name to execute
132  * \param NArguments    Number of arguments to pass
133  * \param Arguments     Arguments passed
134  */
135 tSpiderValue *SpiderScript_ExecuteFunction(tSpiderScript *Script,
136         const char *Function,
137         const char *DefaultNamespaces[],
138         int NArguments, tSpiderValue **Arguments,
139         void **FunctionIdent
140         )
141 {
142         tSpiderValue    *ret = ERRPTR;
143         tSpiderFunction *fcn = NULL;
144         tScript_Function        *sfcn;
145          int    i;
146
147         if( FunctionIdent && *FunctionIdent ) {
148                 if( *(intptr_t*)FunctionIdent & 1 ) {
149                         sfcn = (void*)( *(intptr_t*)FunctionIdent & ~1 );
150                         goto _exec_sfcn;
151                 }
152                 else {
153                         fcn = *FunctionIdent;
154                         goto _exec_fcn;
155                 }
156         }
157
158         // Scan list, Last item should always be NULL, so abuse that to check non-prefixed      
159         for( i = 0; i == 0 || (DefaultNamespaces && DefaultNamespaces[i-1]); i ++ )
160         {
161                 const char *ns = DefaultNamespaces ? DefaultNamespaces[i] : NULL;
162                 fcn = SpiderScript_int_GetNativeFunction(Script, &Script->Variant->RootNamespace, ns, Function);
163                 if( fcn )       break;
164
165                 fcn = SpiderScript_int_GetNativeFunction(Script, &gExportNamespaceRoot, ns, Function);
166                 if( fcn )       break;
167         
168                 // TODO: Script namespacing
169         }
170
171         // Search the variant's global exports
172         if( !fcn )
173         {
174                 for( fcn = Script->Variant->Functions; fcn; fcn = fcn->Next )
175                 {
176                         if( strcmp( fcn->Name, Function ) == 0 )
177                                 break;
178                 }
179         }
180         
181         // Fourth: Search language exports
182         if( !fcn )
183         {
184                 for( fcn = gpExports_First; fcn; fcn = fcn->Next )
185                 {
186                         if( strcmp( fcn->Name, Function ) == 0 )
187                                 break;
188                 }
189         }
190         
191         // Find the function in the script?
192         // TODO: Script namespacing
193         if( !fcn && strchr(Function, BC_NS_SEPARATOR) == NULL )
194         {
195                 for( sfcn = Script->Functions; sfcn; sfcn = sfcn->Next )
196                 {
197                         if( strcmp(sfcn->Name, Function) == 0 )
198                                 break;
199                 }
200         _exec_sfcn:
201                 // Execute!
202                 if(sfcn)
203                 {
204                         if( sfcn->BCFcn )
205                                 ret = Bytecode_ExecuteFunction(Script, sfcn, NArguments, Arguments);
206                         else
207                                 ret = AST_ExecuteFunction(Script, sfcn, NArguments, Arguments);
208
209                         if( FunctionIdent ) {
210                                 *FunctionIdent = sfcn;
211                                 // Abuses alignment requirements on almost all platforms
212                                 *(intptr_t*)FunctionIdent |= 1;
213                         }
214
215                         return ret;
216                 }
217         }
218
219 _exec_fcn:
220         if(fcn)
221         {
222                 // Execute!
223                 // TODO: Type Checking
224                 ret = fcn->Handler( Script, NArguments, Arguments );
225         
226                 if( FunctionIdent )
227                         *FunctionIdent = fcn;           
228
229                 return ret;
230         }
231         else
232         {
233                 fprintf(stderr, "Undefined reference to function '%s'\n", Function);
234                 return ERRPTR;
235         }
236 }
237
238 /**
239  * \brief Execute an object method function
240  * \param Script        Script context to execute in
241  * \param Object        Object in which to find the method
242  * \param MethodName    Name of method to call
243  * \param NArguments    Number of arguments to pass
244  * \param Arguments     Arguments passed
245  */
246 tSpiderValue *SpiderScript_ExecuteMethod(tSpiderScript *Script,
247         tSpiderObject *Object, const char *MethodName,
248         int NArguments, tSpiderValue **Arguments)
249 {
250         tSpiderFunction *fcn;
251         tSpiderValue    this;
252         tSpiderValue    *newargs[NArguments+1];
253          int    i;
254         
255         // TODO: Support program defined objects
256         
257         // Search for the function
258         for( fcn = Object->Type->Methods; fcn; fcn = fcn->Next )
259         {
260                 if( strcmp(fcn->Name, MethodName) == 0 )
261                         break;
262         }
263         // Error
264         if( !fcn )
265         {
266                 AST_RuntimeError(NULL, "Class '%s' does not have a method '%s'",
267                         Object->Type->Name, MethodName);
268                 return ERRPTR;
269         }
270         
271         // Create the "this" argument
272         this.Type = SS_DATATYPE_OBJECT;
273         this.ReferenceCount = 1;
274         this.Object = Object;
275         newargs[0] = &this;
276         memcpy(&newargs[1], Arguments, NArguments*sizeof(tSpiderValue*));
277         
278         // Check the type of the arguments
279         for( i = 0; fcn->ArgTypes[i]; i ++ )
280         {
281                 if( i >= NArguments ) {
282                         for( ; fcn->ArgTypes[i]; i ++ ) ;
283                         AST_RuntimeError(NULL, "Argument count mismatch (%i passed, %i expected)",
284                                 NArguments, i);
285                         return ERRPTR;
286                 }
287                 if( Arguments[i] && Arguments[i]->Type != fcn->ArgTypes[i] )
288                 {
289                         AST_RuntimeError(NULL, "Argument type mismatch (%i, expected %i)",
290                                 Arguments[i]->Type, fcn->ArgTypes[i]);
291                         return ERRPTR;
292                 }
293         }
294         
295         // Call handler
296         return fcn->Handler(Script, NArguments+1, newargs);
297 }
298
299 /**
300  * \brief Execute a script function
301  * \param Script        Script context to execute in
302  * \param Function      Function name to execute
303  * \param NArguments    Number of arguments to pass
304  * \param Arguments     Arguments passed
305  */
306 tSpiderValue *SpiderScript_CreateObject(tSpiderScript *Script,
307         const char *ClassPath, const char *DefaultNamespaces[],
308         int NArguments, tSpiderValue **Arguments)
309 {
310         tSpiderValue    *ret = ERRPTR;
311         tSpiderObjectDef        *class;
312          int    i;      
313
314         // Scan list, Last item should always be NULL, so abuse that to check non-prefixed      
315         for( i = 0; i == 0 || DefaultNamespaces[i-1]; i ++ )
316         {
317                 const char *ns = DefaultNamespaces[i];
318                 class = SpiderScript_int_GetNativeClass(Script, &Script->Variant->RootNamespace, ns, ClassPath);
319                 if( class )     break;
320
321                 class = SpiderScript_int_GetNativeClass(Script, &gExportNamespaceRoot, ns, ClassPath);
322                 if( class )     break;
323                 
324                 // TODO: Language defined classes
325         }
326                 
327         // First: Find the function in the script
328         // TODO: Implement script-defined classes
329         #if 0
330         {
331                 tAST_Function   *astClass;
332                 for( astClass = Script->Script->Classes; astClass; astClass = astClass->Next )
333                 {
334                         if( strcmp(astClass->Name, ClassName) == 0 )
335                                 break;
336                 }
337                 // Execute!
338                 if(astClass)
339                 {
340                         tAST_BlockState bs;
341                         tAST_Node       *arg;
342                          int    i = 0;
343                         
344                         // Build a block State
345                         bs.FirstVar = NULL;
346                         bs.RetVal = NULL;
347                         bs.Parent = NULL;
348                         bs.BaseNamespace = &Script->Variant->RootNamespace;
349                         bs.CurNamespace = NULL;
350                         bs.Script = Script;
351                         bs.Ident = giNextBlockIdent ++;
352                         
353                         for( arg = astFcn->Arguments; arg; arg = arg->NextSibling, i++ )
354                         {
355                                 if( i >= NArguments )   break;  // TODO: Return gracefully
356                                 // TODO: Type checks
357                                 Variable_Define(&bs,
358                                         arg->DefVar.DataType, arg->DefVar.Name,
359                                         Arguments[i]);
360                         }
361                         
362                         // Execute function
363                         ret = AST_ExecuteNode(&bs, astFcn->Code);
364                         if( ret != ERRPTR )
365                         {
366                                 SpiderScript_DereferenceValue(ret);     // Dereference output of last block statement
367                                 ret = bs.RetVal;        // Set to return value of block
368                         }
369                         bFound = 1;
370                         
371                         while(bs.FirstVar)
372                         {
373                                 tAST_Variable   *nextVar = bs.FirstVar->Next;
374                                 Variable_Destroy( bs.FirstVar );
375                                 bs.FirstVar = nextVar;
376                         }
377                 }
378         }
379         #endif
380         
381         // Execute!
382         if(class)
383         {
384                 tSpiderObject   *obj;
385                 // TODO: Type Checking
386                 
387                 // Call constructor
388                 obj = class->Constructor( NArguments, Arguments );
389                 if( obj == NULL || obj == ERRPTR )
390                         return (void *)obj;
391                 
392                 // Creatue return object
393                 ret = malloc( sizeof(tSpiderValue) );
394                 ret->Type = SS_DATATYPE_OBJECT;
395                 ret->ReferenceCount = 1;
396                 ret->Object = obj;
397                 
398                 return ret;
399         }
400         else    // Not found?
401         {
402                 fprintf(stderr, "Undefined reference to class '%s'\n", ClassPath);
403                 return ERRPTR;
404         }
405 }
406
407 void AST_RuntimeMessage(tAST_Node *Node, const char *Type, const char *Format, ...)
408 {
409         va_list args;
410         
411         if(Node) {
412                 fprintf(stderr, "%s:%i: ", Node->File, Node->Line);
413         }
414         fprintf(stderr, "%s: ", Type);
415         va_start(args, Format);
416         vfprintf(stderr, Format, args);
417         va_end(args);
418         fprintf(stderr, "\n");
419 }
420 void AST_RuntimeError(tAST_Node *Node, const char *Format, ...)
421 {
422         va_list args;
423         
424         if(Node) {
425                 fprintf(stderr, "%s:%i: ", Node->File, Node->Line);
426         }
427         fprintf(stderr, "error: ");
428         va_start(args, Format);
429         vfprintf(stderr, Format, args);
430         va_end(args);
431         fprintf(stderr, "\n");
432 }

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