lib.c

00001 #include <math.h>
00002 #include <stdio.h>
00003 #include <stdlib.h>
00004 #include <stdarg.h>
00005 #include <string.h>
00006 
00007 #ifdef _MSC_VER // sigh...
00008 #define snprintf _snprintf
00009 #define vsnprintf _vsnprintf
00010 #endif
00011 
00012 #include "nasal.h"
00013 #include "code.h"
00014 
00015 #define NEWSTR(c, s, l) naStr_fromdata(naNewString(c), s, l)
00016 #define NEWCSTR(c, s) NEWSTR(c, s, strlen(s))
00017 
00018 // Generic argument error, assumes that the symbol "c" is a naContext,
00019 // and that the __FUNCTION__ string is of the form "f_NASALSYMBOL".
00020 #define ARGERR() \
00021     naRuntimeError(c, "bad/missing argument to %s()", (__FUNCTION__ + 2))
00022 
00023 static naRef f_size(naContext c, naRef me, int argc, naRef* args)
00024 {
00025     if(argc == 0) ARGERR();
00026     if(naIsString(args[0])) return naNum(naStr_len(args[0]));
00027     if(naIsVector(args[0])) return naNum(naVec_size(args[0]));
00028     if(naIsHash(args[0])) return naNum(naHash_size(args[0]));
00029     naRuntimeError(c, "object has no size()");
00030     return naNil();
00031 }
00032 
00033 static naRef f_keys(naContext c, naRef me, int argc, naRef* args)
00034 {
00035     naRef v, h = argc > 0 ? args[0] : naNil();
00036     if(!naIsHash(h)) ARGERR();
00037     v = naNewVector(c);
00038     naHash_keys(v, h);
00039     return v;
00040 }
00041 
00042 static naRef f_append(naContext c, naRef me, int argc, naRef* args)
00043 {
00044     int i;
00045     if(argc < 2 || !naIsVector(args[0])) ARGERR();
00046     for(i=1; i<argc; i++) naVec_append(args[0], args[i]);
00047     return args[0];
00048 }
00049 
00050 static naRef f_pop(naContext c, naRef me, int argc, naRef* args)
00051 {
00052     if(argc < 1 || !naIsVector(args[0])) ARGERR();
00053     return naVec_removelast(args[0]);
00054 }
00055 
00056 static naRef f_setsize(naContext c, naRef me, int argc, naRef* args)
00057 {
00058     if(argc < 2 || !naIsVector(args[0])) ARGERR();
00059     naVec_setsize(args[0], (int)naNumValue(args[1]).num);
00060     return args[0];
00061 }
00062 
00063 static naRef f_subvec(naContext c, naRef me, int argc, naRef* args)
00064 {
00065     int i;
00066     naRef nlen, result, v = args[0];
00067     int len = 0, start = (int)naNumValue(args[1]).num;
00068     if(argc < 2) return naNil();
00069     nlen = argc > 2 ? naNumValue(args[2]) : naNil();
00070     if(!naIsNil(nlen))
00071         len = (int)nlen.num;
00072     if(!naIsVector(v) || start < 0 || start > naVec_size(v) || len < 0)
00073         ARGERR();
00074     if(naIsNil(nlen) || len > naVec_size(v) - start)
00075         len = naVec_size(v) - start;
00076     result = naNewVector(c);
00077     naVec_setsize(result, len);
00078     for(i=0; i<len; i++)
00079         naVec_set(result, i, naVec_get(v, start + i));
00080     return result;
00081 }
00082 
00083 static naRef f_delete(naContext c, naRef me, int argc, naRef* args)
00084 {
00085     if(argc < 2 || !naIsHash(args[0])) ARGERR();
00086     naHash_delete(args[0], args[1]);
00087     return args[0];
00088 }
00089 
00090 static naRef f_int(naContext c, naRef me, int argc, naRef* args)
00091 {
00092     if(argc > 0) {
00093         naRef n = naNumValue(args[0]);
00094         if(naIsNil(n)) return n;
00095         if(n.num < 0) n.num = -floor(-n.num);
00096         else n.num = floor(n.num);
00097         return n;
00098     } else ARGERR();
00099     return naNil();
00100 }
00101 
00102 static naRef f_num(naContext c, naRef me, int argc, naRef* args)
00103 {
00104     return argc > 0 ? naNumValue(args[0]) : naNil();
00105 }
00106 
00107 static naRef f_streq(naContext c, naRef me, int argc, naRef* args)
00108 {
00109     return argc > 1 ? naNum(naStrEqual(args[0], args[1])) : naNil();
00110 }
00111 
00112 static naRef f_cmp(naContext c, naRef me, int argc, naRef* args)
00113 {
00114     char *a, *b;
00115     int i, alen, blen;
00116     if(argc < 2 || !naIsString(args[0]) || !naIsString(args[1]))
00117         ARGERR();
00118     a = naStr_data(args[0]);
00119     alen = naStr_len(args[0]);
00120     b = naStr_data(args[1]);
00121     blen = naStr_len(args[1]);
00122     for(i=0; i<alen && i<blen; i++) {
00123         int diff = a[i] - b[i];
00124         if(diff) return naNum(diff < 0 ? -1 : 1);
00125     }
00126     return naNum(alen == blen ? 0 : (alen < blen ? -1 : 1));
00127 }
00128 
00129 static naRef f_substr(naContext c, naRef me, int argc, naRef* args)
00130 {
00131     int start, len, srclen;
00132     naRef src = argc > 0 ? args[0] : naNil();
00133     naRef startr = argc > 1 ? naNumValue(args[1]) : naNil();
00134     naRef lenr = argc > 2 ? naNumValue(args[2]) : naNil();
00135     if(!naIsString(src)) ARGERR();
00136     if(naIsNil(startr) || !naIsNum(startr)) ARGERR();
00137     if(!naIsNil(lenr) && !naIsNum(lenr)) ARGERR();
00138     srclen = naStr_len(src);
00139     start = (int)startr.num;
00140     len = naIsNum(lenr) ? (int)lenr.num : (srclen - start);
00141     if(start < 0) start += srclen;
00142     if(start < 0) start = len = 0;
00143     if(start >= srclen) start = len = 0;
00144     if(len < 0) len = 0;
00145     if(len > srclen - start) len = srclen - start;
00146     return naStr_substr(naNewString(c), src, start, len);
00147 }
00148 
00149 static naRef f_chr(naContext c, naRef me, int argc, naRef* args)
00150 {
00151     char chr[1];
00152     naRef cr = argc > 0 ? naNumValue(args[0]) : naNil();
00153     if(IS_NIL(cr)) ARGERR();
00154     chr[0] = (char)cr.num;
00155     return NEWSTR(c, chr, 1);
00156 }
00157 
00158 static naRef f_contains(naContext c, naRef me, int argc, naRef* args)
00159 {
00160     naRef hash = argc > 0 ? args[0] : naNil();
00161     naRef key = argc > 1 ? args[1] : naNil();
00162     if(naIsNil(hash) || naIsNil(key)) ARGERR();
00163     if(!naIsHash(hash)) return naNil();
00164     return naHash_get(hash, key, &key) ? naNum(1) : naNum(0);
00165 }
00166 
00167 static naRef f_typeof(naContext c, naRef me, int argc, naRef* args)
00168 {
00169     naRef r = argc > 0 ? args[0] : naNil();
00170     char* t = "unknown";
00171     if(naIsNil(r)) t = "nil";
00172     else if(naIsNum(r)) t = "scalar";
00173     else if(naIsString(r)) t = "scalar";
00174     else if(naIsVector(r)) t = "vector";
00175     else if(naIsHash(r)) t = "hash";
00176     else if(naIsFunc(r)) t = "func";
00177     else if(naIsGhost(r)) t = "ghost";
00178     return NEWCSTR(c, t);
00179 }
00180 
00181 static naRef f_ghosttype(naContext c, naRef me, int argc, naRef* args)
00182 {
00183     naRef g = argc > 0 ? args[0] : naNil();
00184     if(!naIsGhost(g)) return naNil();
00185     if(naGhost_type(g)->name) {
00186         return NEWCSTR(c, (char*)naGhost_type(g)->name);
00187     } else {
00188         char buf[32];
00189         sprintf(buf, "%p", naGhost_type(g));
00190         return NEWCSTR(c, buf);
00191     }
00192 }
00193 
00194 static naRef f_compile(naContext c, naRef me, int argc, naRef* args)
00195 {
00196     int errLine;
00197     naRef script, code, fname;
00198     script = argc > 0 ? args[0] : naNil();
00199     fname = argc > 1 ? args[1] : NEWCSTR(c, "<compile>");
00200     if(!naIsString(script) || !naIsString(fname)) return naNil();
00201     code = naParseCode(c, fname, 1,
00202                        naStr_data(script), naStr_len(script), &errLine);
00203     if(naIsNil(code)) {
00204         char buf[256];
00205         snprintf(buf, sizeof(buf), "Parse error: %s at line %d",
00206                  naGetError(c), errLine);
00207         c->dieArg = NEWCSTR(c, buf);
00208         naRuntimeError(c, "__die__");
00209     }
00210     return naBindToContext(c, code);
00211 }
00212 
00213 // FIXME: need a place to save the current IP when we get an error so
00214 // that it can be reset if we get a die()/naRethrowError() situation
00215 // later.  Right now, the IP on the stack trace is the line of the
00216 // die() call, when it should be this one...
00217 static naRef f_call(naContext c, naRef me, int argc, naRef* args)
00218 {
00219     naContext subc;
00220     naRef callargs, callme, callns, result;
00221     struct VecRec* vr;
00222     callargs = argc > 1 ? args[1] : naNil();
00223     callme = argc > 2 ? args[2] : naNil(); // Might be nil, that's OK
00224     callns = argc > 3 ? args[3] : naNil(); // ditto
00225     if(!IS_HASH(callme)) callme = naNil();
00226     if(!IS_HASH(callns)) callns = naNil();
00227     if(argc==0 || !IS_FUNC(args[0]) || (!IS_NIL(callargs) && !IS_VEC(callargs)))
00228         ARGERR();
00229 
00230     subc = naSubContext(c);
00231     vr = IS_NIL(callargs) ? 0 : PTR(callargs).vec->rec;
00232     result = naCall(subc, args[0], vr ? vr->size : 0, vr ? vr->array : 0,
00233                     callme, callns);
00234     if(!naGetError(subc)) {
00235         naFreeContext(subc);
00236         return result;
00237     }
00238 
00239     // Error handling. Note that we don't free the subcontext after an
00240     // error, in case the user re-throws the same error or calls
00241     // naContinue()
00242     if(argc <= 2 || !IS_VEC(args[argc-1])) {
00243         naRethrowError(subc);
00244     } else {
00245         int i, sd;
00246         naRef errv = args[argc-1];
00247         if(!IS_NIL(subc->dieArg)) naVec_append(errv, subc->dieArg);
00248         else naVec_append(errv, NEWCSTR(subc, naGetError(subc)));
00249         sd = naStackDepth(subc);
00250         for(i=0; i<sd; i++) {
00251             naVec_append(errv, naGetSourceFile(subc, i));
00252             naVec_append(errv, naNum(naGetLine(subc, i)));
00253         }
00254     }
00255     return naNil();
00256 }
00257 
00258 static naRef f_die(naContext c, naRef me, int argc, naRef* args)
00259 {
00260     naRef darg = argc > 0 ? args[0] : naNil();
00261     if(!naIsNil(darg) && c->callChild && IDENTICAL(c->callChild->dieArg, darg))
00262         naRethrowError(c->callChild);
00263     c->dieArg = darg;
00264     naRuntimeError(c, "__die__");
00265     return naNil(); // never executes
00266 }
00267 
00268 // Wrapper around vsnprintf, iteratively increasing the buffer size
00269 // until it fits.  Returned buffer should be freed by the caller.
00270 static char* dosprintf(char* f, ...)
00271 {
00272     char* buf;
00273     va_list va;
00274     int olen, len = 16;
00275     while(1) {
00276         buf = naAlloc(len);
00277         va_start(va, f);
00278         olen = vsnprintf(buf, len, f, va);
00279         if(olen >= 0 && olen < len) {
00280             va_end(va);
00281             return buf;
00282         }
00283         va_end(va);
00284         naFree(buf);
00285         len *= 2;
00286     }
00287 }
00288 
00289 // Inspects a printf format string f, and finds the next "%..." format
00290 // specifier.  Stores the start of the specifier in out, the length in
00291 // len, and the type in type.  Returns a pointer to the remainder of
00292 // the format string, or 0 if no format string was found.  Recognizes
00293 // all of ANSI C's syntax except for the "length modifier" feature.
00294 // Note: this does not validate the format character returned in
00295 // "type". That is the caller's job.
00296 static char* nextFormat(naContext c, char* f, char** out, int* len, char* type)
00297 {
00298     // Skip to the start of the format string
00299     while(*f && *f != '%') f++;
00300     if(!*f) return 0;
00301     *out = f++;
00302 
00303     while(*f && (*f=='-' || *f=='+' || *f==' ' || *f=='0' || *f=='#')) f++;
00304 
00305     // Test for duplicate flags.  This is pure pedantry and could
00306     // be removed on all known platforms, but just to be safe...
00307     {   char *p1, *p2;
00308         for(p1 = *out + 1; p1 < f; p1++)
00309             for(p2 = p1+1; p2 < f; p2++)
00310                 if(*p1 == *p2)
00311                     naRuntimeError(c, "duplicate flag in format string"); }
00312 
00313     while(*f && *f >= '0' && *f <= '9') f++;
00314     if(*f && *f == '.') f++;
00315     while(*f && *f >= '0' && *f <= '9') f++;
00316     if(!*f) naRuntimeError(c, "invalid format string");
00317 
00318     *type = *f++;
00319     *len = f - *out;
00320     return f;
00321 }
00322 
00323 #define ERR(m) naRuntimeError(c, m)
00324 #define APPEND(r) result = naStr_concat(naNewString(c), result, r)
00325 static naRef f_sprintf(naContext c, naRef me, int argc, naRef* args)
00326 {
00327     char t, nultmp, *fstr, *next, *fout=0, *s;
00328     int flen, argn=1;
00329     naRef format, arg, result = naNewString(c);
00330 
00331     if(argc < 1) ERR("not enough arguments to sprintf()");
00332     format = naStringValue(c, argc > 0 ? args[0] : naNil());
00333     if(naIsNil(format)) ERR("bad format string in sprintf()");
00334     s = naStr_data(format);
00335                                
00336     while((next = nextFormat(c, s, &fstr, &flen, &t))) {
00337         APPEND(NEWSTR(c, s, fstr-s)); // stuff before the format string
00338         if(flen == 2 && fstr[1] == '%') {
00339             APPEND(NEWSTR(c, "%", 1));
00340             s = next;
00341             continue;
00342         }
00343         if(argn >= argc) ERR("not enough arguments to sprintf()");
00344         arg = args[argn++];
00345         nultmp = fstr[flen]; // sneaky nul termination...
00346         fstr[flen] = 0;
00347         if(t == 's') {
00348             arg = naStringValue(c, arg);
00349             if(naIsNil(arg)) fout = dosprintf(fstr, "nil");
00350             else             fout = dosprintf(fstr, naStr_data(arg));
00351         } else {
00352             arg = naNumValue(arg);
00353             if(naIsNil(arg))
00354                 fout = dosprintf(fstr, "nil");
00355             else if(t=='d' || t=='i' || t=='c')
00356                 fout = dosprintf(fstr, (int)naNumValue(arg).num);
00357             else if(t=='o' || t=='u' || t=='x' || t=='X')
00358                 fout = dosprintf(fstr, (unsigned int)naNumValue(arg).num);
00359             else if(t=='e' || t=='E' || t=='f' || t=='F' || t=='g' || t=='G')
00360                 fout = dosprintf(fstr, naNumValue(arg).num);
00361             else
00362                 ERR("invalid sprintf format type");
00363         }
00364         fstr[flen] = nultmp;
00365         APPEND(NEWSTR(c, fout, strlen(fout)));
00366         naFree(fout);
00367         s = next;
00368     }
00369     APPEND(NEWSTR(c, s, strlen(s)));
00370     return result;
00371 }
00372 
00373 // FIXME: needs to honor subcontext list
00374 static naRef f_caller(naContext c, naRef me, int argc, naRef* args)
00375 {
00376     int fidx;
00377     struct Frame* frame;
00378     naRef result, fr = argc ? naNumValue(args[0]) : naNum(1);
00379     if(IS_NIL(fr)) ARGERR();
00380     fidx = (int)fr.num;
00381     if(fidx > c->fTop - 1) return naNil();
00382     frame = &c->fStack[c->fTop - 1 - fidx];
00383     result = naNewVector(c);
00384     naVec_append(result, frame->locals);
00385     naVec_append(result, frame->func);
00386     naVec_append(result, PTR(PTR(frame->func).func->code).code->srcFile);
00387     naVec_append(result, naNum(naGetLine(c, fidx)));
00388     return result;
00389 }
00390 
00391 static naRef f_closure(naContext c, naRef me, int argc, naRef* args)
00392 {
00393     int i;
00394     struct naFunc* f;
00395     naRef func = argc > 0 ? args[0] : naNil();
00396     naRef idx = argc > 1 ? naNumValue(args[1]) : naNum(0);
00397     if(!IS_FUNC(func) || IS_NIL(idx)) ARGERR();
00398     i = (int)idx.num;
00399     f = PTR(func).func;
00400     while(i > 0 && f) { i--; f = PTR(f->next).func; }
00401     if(!f) return naNil();
00402     return f->namespace;
00403 }
00404 
00405 static int match(unsigned char* a, unsigned char* b, int l)
00406 {
00407     int i;
00408     for(i=0; i<l; i++) if(a[i] != b[i]) return 0;
00409     return 1;
00410 }
00411 
00412 static int find(unsigned char* a, int al, unsigned char* s, int sl, int start)
00413 {
00414     int i;
00415     if(al == 0) return 0;
00416     for(i=start; i<sl-al+1; i++) if(match(a, s+i, al)) return i;
00417     return -1;
00418 }
00419 
00420 static naRef f_find(naContext c, naRef me, int argc, naRef* args)
00421 {
00422     int start = 0;
00423     if(argc < 2 || !IS_STR(args[0]) || !IS_STR(args[1])) ARGERR();
00424     if(argc > 2) start = (int)(naNumValue(args[2]).num);
00425     return naNum(find(PTR(args[0]).str->data, PTR(args[0]).str->len,
00426                       PTR(args[1]).str->data, PTR(args[1]).str->len,
00427                       start));
00428 }
00429 
00430 static naRef f_split(naContext c, naRef me, int argc, naRef* args)
00431 {
00432     int sl, dl, i;
00433     char *s, *d, *s0;
00434     naRef result;
00435     if(argc < 2 || !IS_STR(args[0]) || !IS_STR(args[1])) ARGERR();
00436     d = naStr_data(args[0]); dl = naStr_len(args[0]);
00437     s = naStr_data(args[1]); sl = naStr_len(args[1]);
00438     result = naNewVector(c);
00439     if(dl == 0) { // special case zero-length delimiter
00440         for(i=0; i<sl; i++) naVec_append(result, NEWSTR(c, s+i, 1));
00441         return result;
00442     }
00443     s0 = s;
00444     for(i=0; i <= sl-dl; i++) {
00445         if(match((unsigned char*)(s+i), (unsigned char*)d, dl)) {
00446             naVec_append(result, NEWSTR(c, s0, s+i-s0));
00447             s0 = s + i + dl;
00448             i += dl - 1;
00449         }
00450     }
00451     if(s0 - s <= sl) naVec_append(result, NEWSTR(c, s0, s+sl-s0));
00452     return result;
00453 }
00454 
00455 // This is a comparatively weak RNG, based on the C library's rand()
00456 // function, which is usually not threadsafe and often of limited
00457 // precision.  The 5x loop guarantees that we get a full double worth
00458 // of precision even for 15 bit (Win32...) rand() implementations.
00459 static naRef f_rand(naContext c, naRef me, int argc, naRef* args)
00460 {
00461     int i;
00462     double r = 0;
00463     if(argc) {
00464         if(!IS_NUM(args[0])) naRuntimeError(c, "rand() seed not number");
00465         srand((unsigned int)args[0].num);
00466         return naNil();
00467     }
00468     for(i=0; i<5; i++) r = (r + rand()) * (1.0/(RAND_MAX+1.0));
00469     return naNum(r);
00470 }
00471 
00472 static naRef f_bind(naContext c, naRef me, int argc, naRef* args)
00473 {
00474     naRef func = argc > 0 ? args[0] : naNil();
00475     naRef hash = argc > 1 ? args[1] : naNewHash(c);
00476     naRef next = argc > 2 ? args[2] : naNil();
00477     if(!IS_FUNC(func) || (!IS_NIL(next) && !IS_FUNC(next)) || !IS_HASH(hash))
00478         ARGERR();
00479     func = naNewFunc(c, PTR(func).func->code);
00480     PTR(func).func->namespace = hash;
00481     PTR(func).func->next = next;
00482     return func;
00483 }
00484 
00485 /* We use the "SortRec" gadget for two reasons: first, because ANSI
00486  * qsort() doesn't give us a mechanism for passing a "context" pointer
00487  * to the comparison routine we have to store one in every sorted
00488  * record.  Second, using an index into the original vector here
00489  * allows us to make the sort stable in the event of a zero returned
00490  * from the Nasal comparison function. */
00491 struct SortData { naContext ctx, subc; struct SortRec* recs;
00492                   naRef* elems; int n; naRef fn; };
00493 struct SortRec { struct SortData* sd; int i; };
00494 
00495 static int sortcmp(struct SortRec* a, struct SortRec* b)
00496 {
00497     struct SortData* sd = a->sd;
00498     naRef args[2], d;
00499     args[0] = sd->elems[a->i];
00500     args[1] = sd->elems[b->i];
00501     d = naCall(sd->subc, sd->fn, 2, args, naNil(), naNil());
00502     if(naGetError(sd->subc)) {
00503         naFree(sd->recs);
00504         naRethrowError(sd->subc);
00505     } else if(!naIsNum(d = naNumValue(d))) {
00506         naFree(sd->recs);
00507         naRuntimeError(sd->ctx, "sort() comparison returned non-number");
00508     }
00509     return (d.num > 0) ? 1 : ((d.num < 0) ? -1 : (a->i - b->i));
00510 }
00511 
00512 static naRef f_sort(naContext c, naRef me, int argc, naRef* args)
00513 {
00514     int i;
00515     struct SortData sd;
00516     naRef out;
00517     if(argc != 2 || !naIsVector(args[0]) || !naIsFunc(args[1]))
00518         naRuntimeError(c, "bad/missing argument to sort()");
00519     sd.subc = naSubContext(c);
00520     if(!PTR(args[0]).vec->rec) return naNewVector(c);
00521     sd.elems = PTR(args[0]).vec->rec->array;
00522     sd.n = PTR(args[0]).vec->rec->size;
00523     sd.fn = args[1];
00524     sd.recs = naAlloc(sizeof(struct SortRec) * sd.n);
00525     for(i=0; i<sd.n; i++) {
00526         sd.recs[i].sd = &sd;
00527         sd.recs[i].i = i;
00528     }
00529     qsort(sd.recs, sd.n, sizeof(sd.recs[0]),
00530           (int(*)(const void*,const void*))sortcmp);
00531     out = naNewVector(c);
00532     naVec_setsize(out, sd.n);
00533     for(i=0; i<sd.n; i++)
00534         PTR(out).vec->rec->array[i] = sd.elems[sd.recs[i].i];
00535     naFree(sd.recs);
00536     naFreeContext(sd.subc);
00537     return out;
00538 }
00539 
00540 static naCFuncItem funcs[] = {
00541     { "size", f_size },
00542     { "keys", f_keys }, 
00543     { "append", f_append }, 
00544     { "pop", f_pop }, 
00545     { "setsize", f_setsize }, 
00546     { "subvec", f_subvec }, 
00547     { "delete", f_delete }, 
00548     { "int", f_int },
00549     { "num", f_num },
00550     { "streq", f_streq },
00551     { "cmp", f_cmp },
00552     { "substr", f_substr },
00553     { "chr", f_chr },
00554     { "contains", f_contains },
00555     { "typeof", f_typeof },
00556     { "ghosttype", f_ghosttype },
00557     { "compile", f_compile },
00558     { "call", f_call },
00559     { "die", f_die },
00560     { "sprintf", f_sprintf },
00561     { "caller", f_caller },
00562     { "closure", f_closure },
00563     { "find", f_find },
00564     { "split", f_split },
00565     { "rand", f_rand },
00566     { "bind", f_bind },
00567     { "sort", f_sort },
00568     { 0 }
00569 };
00570 
00571 naRef naInit_std(naContext c)
00572 {
00573     return naGenLib(c, funcs);
00574 }

Generated on Mon Dec 17 09:30:54 2007 for SimGear by  doxygen 1.5.1