c7fed03517ade1cd1db311a4a14132cc4666f1c7
[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          int    i;
145
146         // Scan list, Last item should always be NULL, so abuse that to check non-prefixed      
147         for( i = 0; i == 0 || (DefaultNamespaces && DefaultNamespaces[i-1]); i ++ )
148         {
149                 const char *ns = DefaultNamespaces ? DefaultNamespaces[i] : NULL;
150                 fcn = SpiderScript_int_GetNativeFunction(Script, &Script->Variant->RootNamespace, ns, Function);
151                 if( fcn )       break;
152
153                 fcn = SpiderScript_int_GetNativeFunction(Script, &gExportNamespaceRoot, ns, Function);
154                 if( fcn )       break;
155         
156                 // TODO: Script namespacing
157         }
158
159         // Search the variant's global exports
160         if( !fcn )
161         {
162                 for( fcn = Script->Variant->Functions; fcn; fcn = fcn->Next )
163                 {
164                         if( strcmp( fcn->Name, Function ) == 0 )
165                                 break;
166                 }
167         }
168         
169         // Fourth: Search language exports
170         if( !fcn )
171         {
172                 for( fcn = gpExports_First; fcn; fcn = fcn->Next )
173                 {
174                         if( strcmp( fcn->Name, Function ) == 0 )
175                                 break;
176                 }
177         }
178         
179         // Find the function in the script?
180         // TODO: Script namespacing
181         if( !fcn && strchr(Function, BC_NS_SEPARATOR) == NULL )
182         {
183                 tScript_Function        *sfcn;
184                 for( sfcn = Script->Functions; sfcn; sfcn = sfcn->Next )
185                 {
186                         if( strcmp(sfcn->Name, Function) == 0 )
187                                 break;
188                 }
189                 // Execute!
190                 if(sfcn)
191                 {
192                         if( sfcn->BCFcn )
193                                 ret = Bytecode_ExecuteFunction(Script, sfcn, NArguments, Arguments);
194                         else
195                                 ret = AST_ExecuteFunction(Script, sfcn, NArguments, Arguments);
196
197                         if( FunctionIdent ) {
198                                 *FunctionIdent = sfcn;
199                                 // Abuses alignment requirements on almost all platforms
200                                 *(intptr_t*)FunctionIdent |= 1;
201                         }
202
203                         return ret;
204                 }
205         }
206         
207         if(fcn)
208         {
209                 // Execute!
210                 // TODO: Type Checking
211                 ret = fcn->Handler( Script, NArguments, Arguments );
212         
213                 if( FunctionIdent )
214                         *FunctionIdent = fcn;           
215
216                 return ret;
217         }
218         else
219         {
220                 fprintf(stderr, "Undefined reference to function '%s'\n", Function);
221                 return ERRPTR;
222         }
223 }
224
225 /**
226  * \brief Execute an object method function
227  * \param Script        Script context to execute in
228  * \param Object        Object in which to find the method
229  * \param MethodName    Name of method to call
230  * \param NArguments    Number of arguments to pass
231  * \param Arguments     Arguments passed
232  */
233 tSpiderValue *SpiderScript_ExecuteMethod(tSpiderScript *Script,
234         tSpiderObject *Object, const char *MethodName,
235         int NArguments, tSpiderValue **Arguments)
236 {
237         tSpiderFunction *fcn;
238         tSpiderValue    this;
239         tSpiderValue    *newargs[NArguments+1];
240          int    i;
241         
242         // TODO: Support program defined objects
243         
244         // Search for the function
245         for( fcn = Object->Type->Methods; fcn; fcn = fcn->Next )
246         {
247                 if( strcmp(fcn->Name, MethodName) == 0 )
248                         break;
249         }
250         // Error
251         if( !fcn )
252         {
253                 AST_RuntimeError(NULL, "Class '%s' does not have a method '%s'",
254                         Object->Type->Name, MethodName);
255                 return ERRPTR;
256         }
257         
258         // Create the "this" argument
259         this.Type = SS_DATATYPE_OBJECT;
260         this.ReferenceCount = 1;
261         this.Object = Object;
262         newargs[0] = &this;
263         memcpy(&newargs[1], Arguments, NArguments*sizeof(tSpiderValue*));
264         
265         // Check the type of the arguments
266         for( i = 0; fcn->ArgTypes[i]; i ++ )
267         {
268                 if( i >= NArguments ) {
269                         for( ; fcn->ArgTypes[i]; i ++ ) ;
270                         AST_RuntimeError(NULL, "Argument count mismatch (%i passed, %i expected)",
271                                 NArguments, i);
272                         return ERRPTR;
273                 }
274                 if( Arguments[i] && Arguments[i]->Type != fcn->ArgTypes[i] )
275                 {
276                         AST_RuntimeError(NULL, "Argument type mismatch (%i, expected %i)",
277                                 Arguments[i]->Type, fcn->ArgTypes[i]);
278                         return ERRPTR;
279                 }
280         }
281         
282         // Call handler
283         return fcn->Handler(Script, NArguments+1, newargs);
284 }
285
286 /**
287  * \brief Execute a script function
288  * \param Script        Script context to execute in
289  * \param Function      Function name to execute
290  * \param NArguments    Number of arguments to pass
291  * \param Arguments     Arguments passed
292  */
293 tSpiderValue *SpiderScript_CreateObject(tSpiderScript *Script,
294         const char *ClassPath, const char *DefaultNamespaces[],
295         int NArguments, tSpiderValue **Arguments)
296 {
297         tSpiderValue    *ret = ERRPTR;
298         tSpiderObjectDef        *class;
299          int    i;      
300
301         // Scan list, Last item should always be NULL, so abuse that to check non-prefixed      
302         for( i = 0; i == 0 || DefaultNamespaces[i-1]; i ++ )
303         {
304                 const char *ns = DefaultNamespaces[i];
305                 class = SpiderScript_int_GetNativeClass(Script, &Script->Variant->RootNamespace, ns, ClassPath);
306                 if( class )     break;
307
308                 class = SpiderScript_int_GetNativeClass(Script, &gExportNamespaceRoot, ns, ClassPath);
309                 if( class )     break;
310                 
311                 // TODO: Language defined classes
312         }
313                 
314         // First: Find the function in the script
315         // TODO: Implement script-defined classes
316         #if 0
317         {
318                 tAST_Function   *astClass;
319                 for( astClass = Script->Script->Classes; astClass; astClass = astClass->Next )
320                 {
321                         if( strcmp(astClass->Name, ClassName) == 0 )
322                                 break;
323                 }
324                 // Execute!
325                 if(astClass)
326                 {
327                         tAST_BlockState bs;
328                         tAST_Node       *arg;
329                          int    i = 0;
330                         
331                         // Build a block State
332                         bs.FirstVar = NULL;
333                         bs.RetVal = NULL;
334                         bs.Parent = NULL;
335                         bs.BaseNamespace = &Script->Variant->RootNamespace;
336                         bs.CurNamespace = NULL;
337                         bs.Script = Script;
338                         bs.Ident = giNextBlockIdent ++;
339                         
340                         for( arg = astFcn->Arguments; arg; arg = arg->NextSibling, i++ )
341                         {
342                                 if( i >= NArguments )   break;  // TODO: Return gracefully
343                                 // TODO: Type checks
344                                 Variable_Define(&bs,
345                                         arg->DefVar.DataType, arg->DefVar.Name,
346                                         Arguments[i]);
347                         }
348                         
349                         // Execute function
350                         ret = AST_ExecuteNode(&bs, astFcn->Code);
351                         if( ret != ERRPTR )
352                         {
353                                 SpiderScript_DereferenceValue(ret);     // Dereference output of last block statement
354                                 ret = bs.RetVal;        // Set to return value of block
355                         }
356                         bFound = 1;
357                         
358                         while(bs.FirstVar)
359                         {
360                                 tAST_Variable   *nextVar = bs.FirstVar->Next;
361                                 Variable_Destroy( bs.FirstVar );
362                                 bs.FirstVar = nextVar;
363                         }
364                 }
365         }
366         #endif
367         
368         // Execute!
369         if(class)
370         {
371                 tSpiderObject   *obj;
372                 // TODO: Type Checking
373                 
374                 // Call constructor
375                 obj = class->Constructor( NArguments, Arguments );
376                 if( obj == NULL || obj == ERRPTR )
377                         return (void *)obj;
378                 
379                 // Creatue return object
380                 ret = malloc( sizeof(tSpiderValue) );
381                 ret->Type = SS_DATATYPE_OBJECT;
382                 ret->ReferenceCount = 1;
383                 ret->Object = obj;
384                 
385                 return ret;
386         }
387         else    // Not found?
388         {
389                 fprintf(stderr, "Undefined reference to class '%s'\n", ClassPath);
390                 return ERRPTR;
391         }
392 }
393
394 void AST_RuntimeMessage(tAST_Node *Node, const char *Type, const char *Format, ...)
395 {
396         va_list args;
397         
398         if(Node) {
399                 fprintf(stderr, "%s:%i: ", Node->File, Node->Line);
400         }
401         fprintf(stderr, "%s: ", Type);
402         va_start(args, Format);
403         vfprintf(stderr, Format, args);
404         va_end(args);
405         fprintf(stderr, "\n");
406 }
407 void AST_RuntimeError(tAST_Node *Node, 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, "error: ");
415         va_start(args, Format);
416         vfprintf(stderr, Format, args);
417         va_end(args);
418         fprintf(stderr, "\n");
419 }

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