]> git.mxchange.org Git - simgear.git/blob - simgear/nasal/lib.c
Add left/right Nasal library functions.
[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 snprintf _snprintf
9 #define vsnprintf _vsnprintf
10 #endif
11
12 #include "nasal.h"
13 #include "code.h"
14
15 #define NEWSTR(c, s, l) naStr_fromdata(naNewString(c), s, l)
16 #define NEWCSTR(c, s) NEWSTR(c, s, strlen(s))
17
18 // Generic argument error, assumes that the symbol "c" is a naContext,
19 // and that the __FUNCTION__ string is of the form "f_NASALSYMBOL".
20 #define ARGERR() \
21     naRuntimeError(c, "bad/missing argument to %s()", (__FUNCTION__ + 2))
22
23 static naRef f_size(naContext c, naRef me, int argc, naRef* args)
24 {
25     if(argc == 0) ARGERR();
26     if(naIsString(args[0])) return naNum(naStr_len(args[0]));
27     if(naIsVector(args[0])) return naNum(naVec_size(args[0]));
28     if(naIsHash(args[0])) return naNum(naHash_size(args[0]));
29     naRuntimeError(c, "object has no size()");
30     return naNil();
31 }
32
33 static naRef f_keys(naContext c, naRef me, int argc, naRef* args)
34 {
35     naRef v, h = argc > 0 ? args[0] : naNil();
36     if(!naIsHash(h)) ARGERR();
37     v = naNewVector(c);
38     naHash_keys(v, h);
39     return v;
40 }
41
42 static naRef f_append(naContext c, naRef me, int argc, naRef* args)
43 {
44     int i;
45     if(argc < 2 || !naIsVector(args[0])) ARGERR();
46     for(i=1; i<argc; i++) naVec_append(args[0], args[i]);
47     return args[0];
48 }
49
50 static naRef f_pop(naContext c, naRef me, int argc, naRef* args)
51 {
52     if(argc < 1 || !naIsVector(args[0])) ARGERR();
53     return naVec_removelast(args[0]);
54 }
55
56 static naRef f_setsize(naContext c, naRef me, int argc, naRef* args)
57 {
58     if(argc < 2 || !naIsVector(args[0])) ARGERR();
59     naVec_setsize(c, args[0], (int)naNumValue(args[1]).num);
60     return args[0];
61 }
62
63 static naRef f_subvec(naContext c, naRef me, int argc, naRef* args)
64 {
65     int i;
66     naRef nlen, result, v = args[0];
67     int len = 0, start = (int)naNumValue(args[1]).num;
68     if(argc < 2) return naNil();
69     nlen = argc > 2 ? naNumValue(args[2]) : naNil();
70     if(!naIsNil(nlen))
71         len = (int)nlen.num;
72     if(!naIsVector(v) || start < 0 || start > naVec_size(v) || len < 0)
73         ARGERR();
74     if(naIsNil(nlen) || len > naVec_size(v) - start)
75         len = naVec_size(v) - start;
76     result = naNewVector(c);
77     naVec_setsize(c, result, len);
78     for(i=0; i<len; i++)
79         naVec_set(result, i, naVec_get(v, start + i));
80     return result;
81 }
82
83 static naRef f_delete(naContext c, naRef me, int argc, naRef* args)
84 {
85     if(argc < 2 || !naIsHash(args[0])) ARGERR();
86     naHash_delete(args[0], args[1]);
87     return args[0];
88 }
89
90 static naRef f_int(naContext c, naRef me, int argc, naRef* args)
91 {
92     if(argc > 0) {
93         naRef n = naNumValue(args[0]);
94         if(naIsNil(n)) return n;
95         if(n.num < 0) n.num = -floor(-n.num);
96         else n.num = floor(n.num);
97         return n;
98     } else ARGERR();
99     return naNil();
100 }
101
102 static naRef f_num(naContext c, naRef me, int argc, naRef* args)
103 {
104     return argc > 0 ? naNumValue(args[0]) : naNil();
105 }
106
107 static naRef f_streq(naContext c, naRef me, int argc, naRef* args)
108 {
109     return argc > 1 ? naNum(naStrEqual(args[0], args[1])) : naNil();
110 }
111
112 static naRef f_cmp(naContext c, naRef me, int argc, naRef* args)
113 {
114     char *a, *b;
115     int i, alen, blen;
116     if(argc < 2 || !naIsString(args[0]) || !naIsString(args[1]))
117         ARGERR();
118     a = naStr_data(args[0]);
119     alen = naStr_len(args[0]);
120     b = naStr_data(args[1]);
121     blen = naStr_len(args[1]);
122     for(i=0; i<alen && i<blen; i++) {
123         int diff = a[i] - b[i];
124         if(diff) return naNum(diff < 0 ? -1 : 1);
125     }
126     return naNum(alen == blen ? 0 : (alen < blen ? -1 : 1));
127 }
128
129 static naRef f_substr(naContext c, naRef me, int argc, naRef* args)
130 {
131     int start, len, srclen;
132     naRef src = argc > 0 ? args[0] : naNil();
133     naRef startr = argc > 1 ? naNumValue(args[1]) : naNil();
134     naRef lenr = argc > 2 ? naNumValue(args[2]) : naNil();
135     if(!naIsString(src)) ARGERR();
136     if(naIsNil(startr) || !naIsNum(startr)) ARGERR();
137     if(!naIsNil(lenr) && !naIsNum(lenr)) ARGERR();
138     srclen = naStr_len(src);
139     start = (int)startr.num;
140     len = naIsNum(lenr) ? (int)lenr.num : (srclen - start);
141     if(start < 0) start += srclen;
142     if(start < 0) start = len = 0;
143     if(start >= srclen) start = len = 0;
144     if(len < 0) len = 0;
145     if(len > srclen - start) len = srclen - start;
146     return naStr_substr(naNewString(c), src, start, len);
147 }
148
149 static naRef f_left(naContext c, naRef me, int argc, naRef* args)
150 {
151     int len;
152     naRef src = argc > 0 ? args[0] : naNil();
153     naRef lenr = argc > 1 ? naNumValue(args[1]) : naNil();
154     if(!naIsString(src)) ARGERR();
155     if(!naIsNum(lenr)) ARGERR();
156     len = (int)lenr.num;
157     if(len < 0) len = 0;
158     return naStr_substr(naNewString(c), src, 0, len);
159 }
160
161 static naRef f_right(naContext c, naRef me, int argc, naRef* args)
162 {
163     int len, srclen;
164     naRef src = argc > 0 ? args[0] : naNil();
165     naRef lenr = argc > 1 ? naNumValue(args[1]) : naNil();
166     if(!naIsString(src)) ARGERR();
167     if(!naIsNum(lenr)) ARGERR();
168     srclen = naStr_len(src);
169     len = (int)lenr.num;
170     if (len > srclen) len = srclen;
171     if(len < 0) len = 0;
172     return naStr_substr(naNewString(c), src, srclen - len, len);
173 }
174
175 static naRef f_chr(naContext c, naRef me, int argc, naRef* args)
176 {
177     char chr[1];
178     naRef cr = argc > 0 ? naNumValue(args[0]) : naNil();
179     if(IS_NIL(cr)) ARGERR();
180     chr[0] = (char)cr.num;
181     return NEWSTR(c, chr, 1);
182 }
183
184 static naRef f_contains(naContext c, naRef me, int argc, naRef* args)
185 {
186     naRef hash = argc > 0 ? args[0] : naNil();
187     naRef key = argc > 1 ? args[1] : naNil();
188     if(naIsNil(hash) || naIsNil(key)) ARGERR();
189     if(!naIsHash(hash)) return naNil();
190     return naHash_get(hash, key, &key) ? naNum(1) : naNum(0);
191 }
192
193 static naRef f_typeof(naContext c, naRef me, int argc, naRef* args)
194 {
195     naRef r = argc > 0 ? args[0] : naNil();
196     char* t = "unknown";
197     if(naIsNil(r)) t = "nil";
198     else if(naIsNum(r)) t = "scalar";
199     else if(naIsString(r)) t = "scalar";
200     else if(naIsVector(r)) t = "vector";
201     else if(naIsHash(r)) t = "hash";
202     else if(naIsFunc(r)) t = "func";
203     else if(naIsGhost(r)) t = "ghost";
204     return NEWCSTR(c, t);
205 }
206
207 static naRef f_ghosttype(naContext c, naRef me, int argc, naRef* args)
208 {
209     naRef g = argc > 0 ? args[0] : naNil();
210     if(!naIsGhost(g)) return naNil();
211     if(naGhost_type(g)->name) {
212         return NEWCSTR(c, (char*)naGhost_type(g)->name);
213     } else {
214         char buf[32];
215         sprintf(buf, "%p", naGhost_type(g));
216         return NEWCSTR(c, buf);
217     }
218 }
219
220 static naRef f_compile(naContext c, naRef me, int argc, naRef* args)
221 {
222     int errLine;
223     naRef script, code, fname;
224     script = argc > 0 ? args[0] : naNil();
225     fname = argc > 1 ? args[1] : NEWCSTR(c, "<compile>");
226     if(!naIsString(script) || !naIsString(fname)) return naNil();
227     code = naParseCode(c, fname, 1,
228                        naStr_data(script), naStr_len(script), &errLine);
229     if(naIsNil(code)) {
230         char buf[256];
231         snprintf(buf, sizeof(buf), "Parse error: %s at line %d",
232                  naGetError(c), errLine);
233         c->dieArg = NEWCSTR(c, buf);
234         naRuntimeError(c, "__die__");
235     }
236     return naBindToContext(c, code);
237 }
238
239 // FIXME: need a place to save the current IP when we get an error so
240 // that it can be reset if we get a die()/naRethrowError() situation
241 // later.  Right now, the IP on the stack trace is the line of the
242 // die() call, when it should be this one...
243 //
244 // FIXME: don't use naCall at all here, we don't need it.  Fix up the
245 // context stack to tail call the function directly.  There's no need
246 // for f_call() to live on the C stack at all.
247 static naRef f_call(naContext c, naRef me, int argc, naRef* args)
248 {
249     naContext subc;
250     naRef callargs, callme, callns, result;
251     struct VecRec* vr;
252     callargs = argc > 1 ? args[1] : naNil();
253     callme = argc > 2 ? args[2] : naNil(); // Might be nil, that's OK
254     callns = argc > 3 ? args[3] : naNil(); // ditto
255     if(!IS_HASH(callme)) callme = naNil();
256     if(!IS_HASH(callns)) callns = naNil();
257     if(argc==0 || !IS_FUNC(args[0]) || (!IS_NIL(callargs) && !IS_VEC(callargs)))
258         ARGERR();
259
260     subc = naSubContext(c);
261     vr = IS_NIL(callargs) ? 0 : PTR(callargs).vec->rec;
262     result = naCall(subc, args[0], vr ? vr->size : 0, vr ? vr->array : 0,
263                     callme, callns);
264     if(!naGetError(subc)) {
265         naFreeContext(subc);
266         return result;
267     }
268
269     // Error handling. Note that we don't free the subcontext after an
270     // error, in case the user re-throws the same error or calls
271     // naContinue()
272     if(argc <= 2 || !IS_VEC(args[argc-1])) {
273         naRethrowError(subc);
274     } else {
275         int i, sd;
276         naRef errv = args[argc-1];
277         if(!IS_NIL(subc->dieArg)) naVec_append(errv, subc->dieArg);
278         else naVec_append(errv, NEWCSTR(subc, naGetError(subc)));
279         sd = naStackDepth(subc);
280         for(i=0; i<sd; i++) {
281             naVec_append(errv, naGetSourceFile(subc, i));
282             naVec_append(errv, naNum(naGetLine(subc, i)));
283         }
284     }
285     return naNil();
286 }
287
288 static naRef f_die(naContext c, naRef me, int argc, naRef* args)
289 {
290     naRef darg = argc > 0 ? args[0] : naNil();
291     if(!naIsNil(darg) && c->callChild && IDENTICAL(c->callChild->dieArg, darg))
292         naRethrowError(c->callChild);
293     c->dieArg = darg;
294     naRuntimeError(c, "__die__");
295     return naNil(); // never executes
296 }
297
298 // Wrapper around vsnprintf, iteratively increasing the buffer size
299 // until it fits.  Returned buffer should be freed by the caller.
300 static char* dosprintf(char* f, ...)
301 {
302     char* buf;
303     va_list va;
304     int olen, len = 16;
305     while(1) {
306         buf = naAlloc(len);
307         va_start(va, f);
308         olen = vsnprintf(buf, len, f, va);
309         if(olen >= 0 && olen < len) {
310             va_end(va);
311             return buf;
312         }
313         va_end(va);
314         naFree(buf);
315         len *= 2;
316     }
317 }
318
319 // Inspects a printf format string f, and finds the next "%..." format
320 // specifier.  Stores the start of the specifier in out, the length in
321 // len, and the type in type.  Returns a pointer to the remainder of
322 // the format string, or 0 if no format string was found.  Recognizes
323 // all of ANSI C's syntax except for the "length modifier" feature.
324 // Note: this does not validate the format character returned in
325 // "type". That is the caller's job.
326 static char* nextFormat(naContext c, char* f, char** out, int* len, char* type)
327 {
328     // Skip to the start of the format string
329     while(*f && *f != '%') f++;
330     if(!*f) return 0;
331     *out = f++;
332
333     while(*f && (*f=='-' || *f=='+' || *f==' ' || *f=='0' || *f=='#')) f++;
334
335     // Test for duplicate flags.  This is pure pedantry and could
336     // be removed on all known platforms, but just to be safe...
337     {   char *p1, *p2;
338         for(p1 = *out + 1; p1 < f; p1++)
339             for(p2 = p1+1; p2 < f; p2++)
340                 if(*p1 == *p2)
341                     naRuntimeError(c, "duplicate flag in format string"); }
342
343     while(*f && *f >= '0' && *f <= '9') f++;
344     if(*f && *f == '.') f++;
345     while(*f && *f >= '0' && *f <= '9') f++;
346     if(!*f) naRuntimeError(c, "invalid format string");
347
348     *type = *f++;
349     *len = f - *out;
350     return f;
351 }
352
353 #define ERR(m) naRuntimeError(c, m)
354 #define APPEND(r) result = naStr_concat(naNewString(c), result, r)
355 static naRef f_sprintf(naContext c, naRef me, int argc, naRef* args)
356 {
357     char t, nultmp, *fstr, *next, *fout=0, *s;
358     int flen, argn=1;
359     naRef format, arg, result = naNewString(c);
360
361     if(argc < 1) ERR("not enough arguments to sprintf()");
362     format = naStringValue(c, argc > 0 ? args[0] : naNil());
363     if(naIsNil(format)) ERR("bad format string in sprintf()");
364     s = naStr_data(format);
365                                
366     while((next = nextFormat(c, s, &fstr, &flen, &t))) {
367         APPEND(NEWSTR(c, s, fstr-s)); // stuff before the format string
368         if(flen == 2 && fstr[1] == '%') {
369             APPEND(NEWSTR(c, "%", 1));
370             s = next;
371             continue;
372         }
373         if(argn >= argc) ERR("not enough arguments to sprintf()");
374         arg = args[argn++];
375         nultmp = fstr[flen]; // sneaky nul termination...
376         fstr[flen] = 0;
377         if(t == 's') {
378             arg = naStringValue(c, arg);
379             if(naIsNil(arg)) fout = dosprintf(fstr, "nil");
380             else             fout = dosprintf(fstr, naStr_data(arg));
381         } else {
382             arg = naNumValue(arg);
383             if(naIsNil(arg))
384                 fout = dosprintf(fstr, "nil");
385             else if(t=='d' || t=='i' || t=='c')
386                 fout = dosprintf(fstr, (int)naNumValue(arg).num);
387             else if(t=='o' || t=='u' || t=='x' || t=='X')
388                 fout = dosprintf(fstr, (unsigned int)naNumValue(arg).num);
389             else if(t=='e' || t=='E' || t=='f' || t=='F' || t=='g' || t=='G')
390                 fout = dosprintf(fstr, naNumValue(arg).num);
391             else
392                 ERR("invalid sprintf format type");
393         }
394         fstr[flen] = nultmp;
395         APPEND(NEWSTR(c, fout, strlen(fout)));
396         naFree(fout);
397         s = next;
398     }
399     APPEND(NEWSTR(c, s, strlen(s)));
400     return result;
401 }
402
403 // FIXME: needs to honor subcontext list
404 static naRef f_caller(naContext c, naRef me, int argc, naRef* args)
405 {
406     int fidx;
407     struct Frame* frame;
408     naRef result, fr = argc ? naNumValue(args[0]) : naNum(1);
409     if(IS_NIL(fr)) ARGERR();
410     fidx = (int)fr.num;
411     if(fidx > c->fTop - 1) return naNil();
412     frame = &c->fStack[c->fTop - 1 - fidx];
413     result = naNewVector(c);
414     naVec_append(result, frame->locals);
415     naVec_append(result, frame->func);
416     naVec_append(result, PTR(PTR(frame->func).func->code).code->srcFile);
417     naVec_append(result, naNum(naGetLine(c, fidx)));
418     return result;
419 }
420
421 static naRef f_closure(naContext c, naRef me, int argc, naRef* args)
422 {
423     int i;
424     struct naFunc* f;
425     naRef func = argc > 0 ? args[0] : naNil();
426     naRef idx = argc > 1 ? naNumValue(args[1]) : naNum(0);
427     if(!IS_FUNC(func) || IS_NIL(idx)) ARGERR();
428     i = (int)idx.num;
429     f = PTR(func).func;
430     while(i > 0 && f) { i--; f = PTR(f->next).func; }
431     if(!f) return naNil();
432     return f->namespace;
433 }
434
435 static int match(unsigned char* a, unsigned char* b, int l)
436 {
437     int i;
438     for(i=0; i<l; i++) if(a[i] != b[i]) return 0;
439     return 1;
440 }
441
442 static int find(unsigned char* a, int al, unsigned char* s, int sl, int start)
443 {
444     int i;
445     if(al == 0) return 0;
446     for(i=start; i<sl-al+1; i++) if(match(a, s+i, al)) return i;
447     return -1;
448 }
449
450 static naRef f_find(naContext c, naRef me, int argc, naRef* args)
451 {
452     int start = 0;
453     if(argc < 2 || !IS_STR(args[0]) || !IS_STR(args[1])) ARGERR();
454     if(argc > 2) start = (int)(naNumValue(args[2]).num);
455     return naNum(find((void*)naStr_data(args[0]), naStr_len(args[0]),
456                       (void*)naStr_data(args[1]), naStr_len(args[1]),
457                       start));
458 }
459
460 static naRef f_split(naContext c, naRef me, int argc, naRef* args)
461 {
462     int sl, dl, i;
463     char *s, *d, *s0;
464     naRef result;
465     if(argc < 2 || !IS_STR(args[0]) || !IS_STR(args[1])) ARGERR();
466     d = naStr_data(args[0]); dl = naStr_len(args[0]);
467     s = naStr_data(args[1]); sl = naStr_len(args[1]);
468     result = naNewVector(c);
469     if(dl == 0) { // special case zero-length delimiter
470         for(i=0; i<sl; i++) naVec_append(result, NEWSTR(c, s+i, 1));
471         return result;
472     }
473     s0 = s;
474     for(i=0; i <= sl-dl; i++) {
475         if(match((unsigned char*)(s+i), (unsigned char*)d, dl)) {
476             naVec_append(result, NEWSTR(c, s0, s+i-s0));
477             s0 = s + i + dl;
478             i += dl - 1;
479         }
480     }
481     if(s0 - s <= sl) naVec_append(result, NEWSTR(c, s0, s+sl-s0));
482     return result;
483 }
484
485 // This is a comparatively weak RNG, based on the C library's rand()
486 // function, which is usually not threadsafe and often of limited
487 // precision.  The 5x loop guarantees that we get a full double worth
488 // of precision even for 15 bit (Win32...) rand() implementations.
489 static naRef f_rand(naContext c, naRef me, int argc, naRef* args)
490 {
491     int i;
492     double r = 0;
493     if(argc) {
494         if(!IS_NUM(args[0])) naRuntimeError(c, "rand() seed not number");
495         srand((unsigned int)args[0].num);
496         return naNil();
497     }
498     for(i=0; i<5; i++) r = (r + rand()) * (1.0/(RAND_MAX+1.0));
499     return naNum(r);
500 }
501
502 static naRef f_bind(naContext c, naRef me, int argc, naRef* args)
503 {
504     naRef func = argc > 0 ? args[0] : naNil();
505     naRef hash = argc > 1 ? args[1] : naNewHash(c);
506     naRef next = argc > 2 ? args[2] : naNil();
507     if(!IS_FUNC(func) || (!IS_NIL(next) && !IS_FUNC(next)) || !IS_HASH(hash))
508         ARGERR();
509     func = naNewFunc(c, PTR(func).func->code);
510     PTR(func).func->namespace = hash;
511     PTR(func).func->next = next;
512     return func;
513 }
514
515 /* We use the "SortRec" gadget for two reasons: first, because ANSI
516  * qsort() doesn't give us a mechanism for passing a "context" pointer
517  * to the comparison routine we have to store one in every sorted
518  * record.  Second, using an index into the original vector here
519  * allows us to make the sort stable in the event of a zero returned
520  * from the Nasal comparison function. */
521 struct SortData { naContext ctx, subc; struct SortRec* recs;
522                   naRef* elems; int n; naRef fn; };
523 struct SortRec { struct SortData* sd; int i; };
524
525 static int sortcmp(struct SortRec* a, struct SortRec* b)
526 {
527     struct SortData* sd = a->sd;
528     naRef args[2], d;
529     args[0] = sd->elems[a->i];
530     args[1] = sd->elems[b->i];
531     d = naCall(sd->subc, sd->fn, 2, args, naNil(), naNil());
532     if(naGetError(sd->subc)) {
533         naFree(sd->recs);
534         naRethrowError(sd->subc);
535     } else if(!naIsNum(d = naNumValue(d))) {
536         naFree(sd->recs);
537         naRuntimeError(sd->ctx, "sort() comparison returned non-number");
538     }
539     return (d.num > 0) ? 1 : ((d.num < 0) ? -1 : (a->i - b->i));
540 }
541
542 static naRef f_sort(naContext c, naRef me, int argc, naRef* args)
543 {
544     int i;
545     struct SortData sd;
546     naRef out;
547     if(argc != 2 || !naIsVector(args[0]) || !naIsFunc(args[1]))
548         naRuntimeError(c, "bad/missing argument to sort()");
549     sd.subc = naSubContext(c);
550     if(!PTR(args[0]).vec->rec) return naNewVector(c);
551     sd.elems = PTR(args[0]).vec->rec->array;
552     sd.n = PTR(args[0]).vec->rec->size;
553     sd.fn = args[1];
554     sd.recs = naAlloc(sizeof(struct SortRec) * sd.n);
555     for(i=0; i<sd.n; i++) {
556         sd.recs[i].sd = &sd;
557         sd.recs[i].i = i;
558     }
559     qsort(sd.recs, sd.n, sizeof(sd.recs[0]),
560           (int(*)(const void*,const void*))sortcmp);
561     out = naNewVector(c);
562     naVec_setsize(c, out, sd.n);
563     for(i=0; i<sd.n; i++)
564         PTR(out).vec->rec->array[i] = sd.elems[sd.recs[i].i];
565     naFree(sd.recs);
566     naFreeContext(sd.subc);
567     return out;
568 }
569
570 static naRef f_id(naContext c, naRef me, int argc, naRef* args)
571 {
572     char *t = "unk", buf[64];
573     if(argc != 1 || !IS_REF(args[0]))
574         naRuntimeError(c, "bad/missing argument to id()");
575     if     (IS_STR(args[0]))   t = "str";
576     else if(IS_VEC(args[0]))   t = "vec";
577     else if(IS_HASH(args[0]))  t = "hash";
578     else if(IS_CODE(args[0]))  t = "code";
579     else if(IS_FUNC(args[0]))  t = "func";
580     else if(IS_CCODE(args[0])) t = "ccode";
581     else if(IS_GHOST(args[0])) {
582         naGhostType *gt = PTR(args[0]).ghost->gtype;
583         t = gt->name ? (char*)gt->name : "ghost";
584     }
585     sprintf(buf, "%s:%p", (char*)t, (void*)PTR(args[0]).obj);
586     return NEWCSTR(c, buf);
587 }
588
589 static naCFuncItem funcs[] = {
590     { "size", f_size },
591     { "keys", f_keys }, 
592     { "append", f_append }, 
593     { "pop", f_pop }, 
594     { "setsize", f_setsize }, 
595     { "subvec", f_subvec }, 
596     { "delete", f_delete }, 
597     { "int", f_int },
598     { "num", f_num },
599     { "streq", f_streq },
600     { "cmp", f_cmp },
601     { "substr", f_substr },
602     { "left", f_left },
603     { "right", f_right },
604     { "chr", f_chr },
605     { "contains", f_contains },
606     { "typeof", f_typeof },
607     { "ghosttype", f_ghosttype },
608     { "compile", f_compile },
609     { "call", f_call },
610     { "die", f_die },
611     { "sprintf", f_sprintf },
612     { "caller", f_caller },
613     { "closure", f_closure },
614     { "find", f_find },
615     { "split", f_split },
616     { "rand", f_rand },
617     { "bind", f_bind },
618     { "sort", f_sort },
619     { "id", f_id },
620     { 0 }
621 };
622
623 naRef naInit_std(naContext c)
624 {
625     return naGenLib(c, funcs);
626 }