]> git.mxchange.org Git - simgear.git/blob - simgear/nasal/lib.c
Melchior FRANZ:
[simgear.git] / simgear / nasal / lib.c
1 #include <math.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <stdarg.h>
5 #include <string.h>
6
7 #ifdef _MSC_VER // sigh...
8 #define vsnprintf _vsnprintf
9 #endif
10
11 #include "nasal.h"
12 #include "code.h"
13
14 #define NEWSTR(c, s, l) naStr_fromdata(naNewString(c), s, l)
15
16 static naRef size(naContext c, naRef me, int argc, naRef* args)
17 {
18     if(argc == 0) return naNil();
19     if(naIsString(args[0])) return naNum(naStr_len(args[0]));
20     if(naIsVector(args[0])) return naNum(naVec_size(args[0]));
21     if(naIsHash(args[0])) return naNum(naHash_size(args[0]));
22     naRuntimeError(c, "object has no size()");
23     return naNil();
24 }
25
26 static naRef keys(naContext c, naRef me, int argc, naRef* args)
27 {
28     naRef v, h = args[0];
29     if(!naIsHash(h)) return naNil();
30     v = naNewVector(c);
31     naHash_keys(v, h);
32     return v;
33 }
34
35 static naRef append(naContext c, naRef me, int argc, naRef* args)
36 {
37     int i;
38     if(argc < 2) return naNil();
39     if(!naIsVector(args[0])) return naNil();
40     for(i=1; i<argc; i++) naVec_append(args[0], args[i]);
41     return args[0];
42 }
43
44 static naRef pop(naContext c, naRef me, int argc, naRef* args)
45 {
46     if(argc < 1 || !naIsVector(args[0])) return naNil();
47     return naVec_removelast(args[0]);
48 }
49
50 static naRef setsize(naContext c, naRef me, int argc, naRef* args)
51 {
52     int sz;
53     if(argc < 2) return naNil();
54     sz = (int)naNumValue(args[1]).num;
55     if(!naIsVector(args[0])) return naNil();
56     naVec_setsize(args[0], sz);
57     return args[0];
58 }
59
60 static naRef subvec(naContext c, naRef me, int argc, naRef* args)
61 {
62     int i;
63     naRef nlen, result, v = args[0];
64     int len = 0, start = (int)naNumValue(args[1]).num;
65     if(argc < 2) return naNil();
66     nlen = argc > 2 ? naNumValue(args[2]) : naNil();
67     if(!naIsNil(nlen))
68         len = (int)nlen.num;
69     if(!naIsVector(v) || start < 0 || start >= naVec_size(v) || len < 0)
70         return naNil();
71     if(len == 0 || len > naVec_size(v) - start) len = naVec_size(v) - start;
72     result = naNewVector(c);
73     naVec_setsize(result, len);
74     for(i=0; i<len; i++)
75         naVec_set(result, i, naVec_get(v, start + i));
76     return result;
77 }
78
79 static naRef delete(naContext c, naRef me, int argc, naRef* args)
80 {
81     if(argc > 1 && naIsHash(args[0])) naHash_delete(args[0], args[1]);
82     return naNil();
83 }
84
85 static naRef intf(naContext c, naRef me, int argc, naRef* args)
86 {
87     if(argc > 0) {
88         naRef n = naNumValue(args[0]);
89         if(naIsNil(n)) return n;
90         if(n.num < 0) n.num = -floor(-n.num);
91         else n.num = floor(n.num);
92         return n;
93     } else return naNil();
94 }
95
96 static naRef num(naContext c, naRef me, int argc, naRef* args)
97 {
98     return argc > 0 ? naNumValue(args[0]) : naNil();
99 }
100
101 static naRef streq(naContext c, naRef me, int argc, naRef* args)
102 {
103     return argc > 1 ? naNum(naStrEqual(args[0], args[1])) : naNil();
104 }
105
106 static naRef substr(naContext c, naRef me, int argc, naRef* args)
107 {
108     naRef src = argc > 1 ? args[0] : naNil();
109     naRef startR = argc > 1 ? args[1] : naNil();
110     naRef lenR = argc > 2 ? args[2] : naNil();
111     int start, len;
112     if(!naIsString(src)) return naNil();
113     startR = naNumValue(startR);
114     if(naIsNil(startR)) return naNil();
115     start = (int)startR.num;
116     if(naIsNil(lenR)) {
117         len = naStr_len(src) - start;
118         if(len < 0) return naNil();
119     } else {
120         lenR = naNumValue(lenR);
121         if(naIsNil(lenR)) return naNil();
122         len = (int)lenR.num;
123     }
124     return naStr_substr(naNewString(c), src, start, len);
125 }
126
127 static naRef f_strc(naContext c, naRef me, int argc, naRef* args)
128 {
129     int idx;
130     struct naStr* str = args[0].ref.ptr.str;
131     naRef idr = argc > 1 ? naNumValue(args[1]) : naNum(0);
132     if(argc < 1 || IS_NIL(idr) || !IS_STR(args[0]))
133         naRuntimeError(c, "bad arguments to strc");
134     idx = (int)naNumValue(idr).num;
135     if(idx > str->len) naRuntimeError(c, "strc index out of bounds");
136     return naNum(str->data[idx]);
137 }
138
139 static naRef f_chr(naContext c, naRef me, int argc, naRef* args)
140 {
141     char chr[1];
142     naRef cr = argc ? naNumValue(args[0]) : naNil();
143     if(IS_NIL(cr)) naRuntimeError(c, "chr argument not string");
144     chr[0] = (char)cr.num;
145     return NEWSTR(c, chr, 1);
146 }
147
148 static naRef contains(naContext c, naRef me, int argc, naRef* args)
149 {
150     naRef hash = argc > 0 ? args[0] : naNil();
151     naRef key = argc > 1 ? args[1] : naNil();
152     if(naIsNil(hash) || naIsNil(key)) return naNil();
153     if(!naIsHash(hash)) return naNil();
154     return naHash_get(hash, key, &key) ? naNum(1) : naNum(0);
155 }
156
157 static naRef typeOf(naContext c, naRef me, int argc, naRef* args)
158 {
159     naRef r = argc > 0 ? args[0] : naNil();
160     char* t = "unknown";
161     if(naIsNil(r)) t = "nil";
162     else if(naIsNum(r)) t = "scalar";
163     else if(naIsString(r)) t = "scalar";
164     else if(naIsVector(r)) t = "vector";
165     else if(naIsHash(r)) t = "hash";
166     else if(naIsFunc(r)) t = "func";
167     else if(naIsGhost(r)) t = "ghost";
168     r = NEWSTR(c, t, strlen(t));
169     return r;
170 }
171
172 static naRef f_compile(naContext c, naRef me, int argc, naRef* args)
173 {
174     int errLine;
175     naRef script, code, fname;
176     script = argc > 0 ? args[0] : naNil();
177     if(!naIsString(script)) return naNil();
178     fname = NEWSTR(c, "<compile>", 9);
179     code = naParseCode(c, fname, 1,
180                        naStr_data(script), naStr_len(script), &errLine);
181     if(!naIsCode(code)) return naNil(); // FIXME: export error to caller...
182     return naBindToContext(c, code);
183 }
184
185 // Funcation metacall API.  Allows user code to generate an arg vector
186 // at runtime and/or call function references on arbitrary objects.
187 static naRef f_call(naContext c, naRef me, int argc, naRef* args)
188 {
189     naContext subc;
190     naRef callargs, callme, result;
191     callargs = argc > 1 ? args[1] : naNil();
192     callme = argc > 2 ? args[2] : naNil(); // Might be nil, that's OK
193     if(!naIsFunc(args[0])) naRuntimeError(c, "call() on non-function");
194     if(naIsNil(callargs)) callargs = naNewVector(c);
195     else if(!naIsVector(callargs)) naRuntimeError(c, "call() args not vector");
196     if(!naIsHash(callme)) callme = naNil();
197     subc = naNewContext();
198     subc->callParent = c;
199     c->callChild = subc;
200     result = naCall(subc, args[0], callargs, callme, naNil());
201     c->callChild = 0;
202     if(argc > 2 && !IS_NIL(subc->dieArg))
203         if(naIsVector(args[argc-1]))
204             naVec_append(args[argc-1], subc->dieArg);
205     naFreeContext(subc);
206     return result;
207 }
208
209 static naRef f_die(naContext c, naRef me, int argc, naRef* args)
210 {
211     c->dieArg = argc > 0 ? args[0] : naNil();
212     naRuntimeError(c, "__die__");
213     return naNil(); // never executes
214 }
215
216 // Wrapper around vsnprintf, iteratively increasing the buffer size
217 // until it fits.  Returned buffer should be freed by the caller.
218 char* dosprintf(char* f, ...)
219 {
220     char* buf;
221     va_list va;
222     int len = 16;
223     while(1) {
224         buf = naAlloc(len);
225         va_start(va, f);
226         if(vsnprintf(buf, len, f, va) < len) {
227             va_end(va);
228             return buf;
229         }
230         va_end(va);
231         naFree(buf);
232         len *= 2;
233     }
234 }
235
236 // Inspects a printf format string f, and finds the next "%..." format
237 // specifier.  Stores the start of the specifier in out, the length in
238 // len, and the type in type.  Returns a pointer to the remainder of
239 // the format string, or 0 if no format string was found.  Recognizes
240 // all of ANSI C's syntax except for the "length modifier" feature.
241 // Note: this does not validate the format character returned in
242 // "type". That is the caller's job.
243 static char* nextFormat(naContext ctx, char* f, char** out, int* len, char* type)
244 {
245     // Skip to the start of the format string
246     while(*f && *f != '%') f++;
247     if(!*f) return 0;
248     *out = f++;
249
250     while(*f && (*f=='-' || *f=='+' || *f==' ' || *f=='0' || *f=='#')) f++;
251
252     // Test for duplicate flags.  This is pure pedantry and could
253     // be removed on all known platforms, but just to be safe...
254     {   char *p1, *p2;
255         for(p1 = *out + 1; p1 < f; p1++)
256             for(p2 = p1+1; p2 < f; p2++)
257                 if(*p1 == *p2)
258                     naRuntimeError(ctx, "duplicate flag in format string"); }
259
260     while(*f && *f >= '0' && *f <= '9') f++;
261     if(*f && *f == '.') f++;
262     while(*f && *f >= '0' && *f <= '9') f++;
263     if(!*f) naRuntimeError(ctx, "invalid format string");
264
265     *type = *f++;
266     *len = f - *out;
267     return f;
268 }
269
270 #define ERR(m) naRuntimeError(ctx, m)
271 #define APPEND(r) result = naStr_concat(naNewString(ctx), result, r)
272 static naRef f_sprintf(naContext ctx, naRef me, int argc, naRef* args)
273 {
274     char t, nultmp, *fstr, *next, *fout=0, *s;
275     int flen, argn=1;
276     naRef format, arg, result = naNewString(ctx);
277
278     if(argc < 1) ERR("not enough arguments to sprintf");
279     format = naStringValue(ctx, argc > 0 ? args[0] : naNil());
280     if(naIsNil(format)) ERR("bad format string in sprintf");
281     s = naStr_data(format);
282                                
283     while((next = nextFormat(ctx, s, &fstr, &flen, &t))) {
284         APPEND(NEWSTR(ctx, s, fstr-s)); // stuff before the format string
285         if(flen == 2 && fstr[1] == '%') {
286             APPEND(NEWSTR(ctx, "%", 1));
287             s = next;
288             continue;
289         }
290         if(argn >= argc) ERR("not enough arguments to sprintf");
291         arg = args[argn++];
292         nultmp = fstr[flen]; // sneaky nul termination...
293         fstr[flen] = 0;
294         if(t == 's') {
295             arg = naStringValue(ctx, arg);
296             if(naIsNil(arg)) fout = dosprintf(fstr, "nil");
297             else             fout = dosprintf(fstr, naStr_data(arg));
298         } else {
299             arg = naNumValue(arg);
300             if(naIsNil(arg))
301                 fout = dosprintf(fstr, "nil");
302             else if(t=='d' || t=='i' || t=='c')
303                 fout = dosprintf(fstr, (int)naNumValue(arg).num);
304             else if(t=='o' || t=='u' || t=='x' || t=='X')
305                 fout = dosprintf(fstr, (unsigned int)naNumValue(arg).num);
306             else if(t=='e' || t=='E' || t=='f' || t=='F' || t=='g' || t=='G')
307                 fout = dosprintf(fstr, naNumValue(arg).num);
308             else
309                 ERR("invalid sprintf format type");
310         }
311         fstr[flen] = nultmp;
312         APPEND(NEWSTR(ctx, fout, strlen(fout)));
313         naFree(fout);
314         s = next;
315     }
316     APPEND(NEWSTR(ctx, s, strlen(s)));
317     return result;
318 }
319
320 // FIXME: handle ctx->callParent frames too!
321 static naRef f_caller(naContext ctx, naRef me, int argc, naRef* args)
322 {
323     int fidx;
324     struct Frame* frame;
325     naRef result, fr = argc ? naNumValue(args[0]) : naNum(1);
326     if(IS_NIL(fr)) naRuntimeError(ctx, "non numeric argument to caller()");
327     fidx = (int)fr.num;
328     if(fidx > ctx->fTop - 1) return naNil();
329     frame = &ctx->fStack[ctx->fTop - 1 - fidx];
330     result = naNewVector(ctx);
331     naVec_append(result, frame->locals);
332     naVec_append(result, frame->func);
333     naVec_append(result, frame->func.ref.ptr.func->code.ref.ptr.code->srcFile);
334     naVec_append(result, naNum(naGetLine(ctx, fidx)));
335     return result;
336 }
337
338 static naRef f_closure(naContext ctx, naRef me, int argc, naRef* args)
339 {
340     int i;
341     naRef func, idx;
342     struct naFunc* f;
343     func = argc > 0 ? args[0] : naNil();
344     idx = argc > 1 ? naNumValue(args[1]) : naNil();
345     if(!IS_FUNC(func) || IS_NIL(idx))
346         naRuntimeError(ctx, "bad arguments to closure()");
347     i = (int)idx.num;
348     f = func.ref.ptr.func;
349     while(i > 0 && f) { i--; f = f->next.ref.ptr.func; }
350     if(!f) return naNil();
351     return f->namespace;
352 }
353
354 static int match(char* a, char* b, int l)
355 {
356     int i;
357     for(i=0; i<l; i++) if(a[i] != b[i]) return 0;
358     return 1;
359 }
360
361 static int find(char* a, int al, char* s, int sl, int start)
362 {
363     int i;
364     if(al == 0) return 0;
365     for(i=start; i<sl-al+1; i++) if(match(a, s+i, al)) return i;
366     return -1;
367 }
368
369 static naRef f_find(naContext ctx, naRef me, int argc, naRef* args)
370 {
371     int start = 0;
372     if(argc < 2 || !IS_STR(args[0]) || !IS_STR(args[1]))
373         naRuntimeError(ctx, "bad/missing argument to split");
374     if(argc > 2) start = (int)(naNumValue(args[2]).num);
375     return naNum(find(args[0].ref.ptr.str->data, args[0].ref.ptr.str->len,
376                       args[1].ref.ptr.str->data, args[1].ref.ptr.str->len,
377                       start));
378 }
379
380 static naRef f_split(naContext ctx, naRef me, int argc, naRef* args)
381 {
382     int sl, dl, i;
383     char *s, *d, *s0;
384     naRef result;
385     if(argc < 2 || !IS_STR(args[0]) || !IS_STR(args[1]))
386         naRuntimeError(ctx, "bad/missing argument to split");
387     d = naStr_data(args[0]); dl = naStr_len(args[0]);
388     s = naStr_data(args[1]); sl = naStr_len(args[1]);
389     result = naNewVector(ctx);
390     if(dl == 0) { // special case zero-length delimiter
391         for(i=0; i<sl; i++) naVec_append(result, NEWSTR(ctx, s+i, 1));
392         return result;
393     }
394     s0 = s;
395     for(i=0; i <= sl-dl; i++) {
396         if(match(s+i, d, dl)) {
397             naVec_append(result, NEWSTR(ctx, s0, s+i-s0));
398             s0 = s + i + dl;
399             i += dl - 1;
400         }
401     }
402     if(s0 - s <= sl) naVec_append(result, NEWSTR(ctx, s0, s+sl-s0));
403     return result;
404 }
405
406 // This is a comparatively weak RNG, based on the C library's rand()
407 // function, which is usually not threadsafe and often of limited
408 // precision.  The 5x loop guarantees that we get a full double worth
409 // of precision even for 15 bit (Win32...) rand() implementations.
410 static naRef f_rand(naContext ctx, naRef me, int argc, naRef* args)
411 {
412     int i;
413     double r = 0;
414     if(argc) {
415         if(!IS_NUM(args[0])) naRuntimeError(ctx, "rand() seed not number");
416         srand((unsigned int)args[0].num);
417         return naNil();
418     }
419     for(i=0; i<5; i++) r = (r + rand()) * (1.0/(RAND_MAX+1.0));
420     return naNum(r);
421 }
422
423 static naRef f_bind(naContext ctx, naRef me, int argc, naRef* args)
424 {
425     naRef func = argc > 0 ? args[0] : naNil();
426     naRef hash = argc > 1 ? args[1] : naNewHash(ctx);
427     naRef next = argc > 2 ? args[2] : naNil();
428     if(!IS_FUNC(func) || (!IS_NIL(next) && !IS_FUNC(next)) || !IS_HASH(hash))
429         naRuntimeError(ctx, "bad argument to bind");
430     func = naNewFunc(ctx, func.ref.ptr.func->code);
431     func.ref.ptr.func->namespace = hash;
432     func.ref.ptr.func->next = next;
433     return func;
434 }
435
436 struct func { char* name; naCFunction func; };
437 static struct func funcs[] = {
438     { "size", size },
439     { "keys", keys }, 
440     { "append", append }, 
441     { "pop", pop }, 
442     { "setsize", setsize }, 
443     { "subvec", subvec }, 
444     { "delete", delete }, 
445     { "int", intf },
446     { "num", num },
447     { "streq", streq },
448     { "substr", substr },
449     { "strc", f_strc },
450     { "chr", f_chr },
451     { "contains", contains },
452     { "typeof", typeOf },
453     { "compile", f_compile },
454     { "call", f_call },
455     { "die", f_die },
456     { "sprintf", f_sprintf },
457     { "caller", f_caller },
458     { "closure", f_closure },
459     { "find", f_find },
460     { "split", f_split },
461     { "rand", f_rand },
462     { "bind", f_bind },
463 };
464
465 naRef naStdLib(naContext c)
466 {
467     naRef namespace = naNewHash(c);
468     int i, n = sizeof(funcs)/sizeof(struct func);
469     for(i=0; i<n; i++) {
470         naRef code = naNewCCode(c, funcs[i].func);
471         naRef name = NEWSTR(c, funcs[i].name, strlen(funcs[i].name));
472         name = naInternSymbol(name);
473         naHash_set(namespace, name, naNewFunc(c, code));
474     }
475     return namespace;
476 }