X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fnasal%2Flib.c;h=504a33c28bd67e180f7e5b463af1ad11fd3b5e61;hb=c12162407c59ff1ef111d2b2c82f6fd68dbfafe2;hp=7597742cf8e0cf1b5acb92c7eaeac883f1ef1a0e;hpb=c9e53d5a30a353ead78dda3cd7e625f2ffee867e;p=simgear.git diff --git a/simgear/nasal/lib.c b/simgear/nasal/lib.c index 7597742c..504a33c2 100644 --- a/simgear/nasal/lib.c +++ b/simgear/nasal/lib.c @@ -1,68 +1,72 @@ +#include +#include +#include +#include +#include + +#ifdef _MSC_VER // sigh... +#define vsnprintf _vsnprintf +#endif + #include "nasal.h" +#include "code.h" -// No need to include just for this: -// It needs a funny name because MSVC wants to treat "strlen" as a -// special symbol. Ugh... -static int StrLen(char* s) -{ - char* s0 = s; - while(*s) s++; - return s - s0; -} +#define NEWSTR(c, s, l) naStr_fromdata(naNewString(c), s, l) +#define NEWCSTR(c, s) NEWSTR(c, s, strlen(s)) -static naRef size(naContext c, naRef args) +static naRef size(naContext c, naRef me, int argc, naRef* args) { - naRef r; - if(naVec_size(args) == 0) return naNil(); - r = naVec_get(args, 0); - if(naIsString(r)) return naNum(naStr_len(r)); - if(naIsVector(r)) return naNum(naVec_size(r)); - if(naIsHash(r)) return naNum(naHash_size(r)); + if(argc == 0) return naNil(); + if(naIsString(args[0])) return naNum(naStr_len(args[0])); + if(naIsVector(args[0])) return naNum(naVec_size(args[0])); + if(naIsHash(args[0])) return naNum(naHash_size(args[0])); + naRuntimeError(c, "object has no size()"); return naNil(); } -static naRef keys(naContext c, naRef args) +static naRef keys(naContext c, naRef me, int argc, naRef* args) { - naRef v, h = naVec_get(args, 0); + naRef v, h = args[0]; if(!naIsHash(h)) return naNil(); v = naNewVector(c); naHash_keys(v, h); return v; } -static naRef append(naContext c, naRef args) +static naRef append(naContext c, naRef me, int argc, naRef* args) { - naRef v = naVec_get(args, 0); - naRef e = naVec_get(args, 1); - if(!naIsVector(v)) return naNil(); - naVec_append(v, e); - return v; + int i; + if(argc < 2) return naNil(); + if(!naIsVector(args[0])) return naNil(); + for(i=1; i 2 ? naNumValue(args[2]) : naNil(); if(!naIsNil(nlen)) - len = (int)naNumValue(naVec_get(args, 2)).num; + len = (int)nlen.num; if(!naIsVector(v) || start < 0 || start >= naVec_size(v) || len < 0) return naNil(); if(len == 0 || len > naVec_size(v) - start) len = naVec_size(v) - start; @@ -73,51 +77,64 @@ static naRef subvec(naContext c, naRef args) return result; } -static naRef delete(naContext c, naRef args) +static naRef delete(naContext c, naRef me, int argc, naRef* args) { - naRef h = naVec_get(args, 0); - naRef k = naVec_get(args, 1); - if(naIsHash(h)) naHash_delete(h, k); + if(argc > 1 && naIsHash(args[0])) naHash_delete(args[0], args[1]); return naNil(); } -static naRef intf(naContext c, naRef args) +static naRef intf(naContext c, naRef me, int argc, naRef* args) { - naRef n = naNumValue(naVec_get(args, 0)); - if(!naIsNil(n)) n.num = (int)n.num; - return n; + if(argc > 0) { + naRef n = naNumValue(args[0]); + if(naIsNil(n)) return n; + if(n.num < 0) n.num = -floor(-n.num); + else n.num = floor(n.num); + return n; + } else return naNil(); } -static naRef num(naContext c, naRef args) +static naRef num(naContext c, naRef me, int argc, naRef* args) { - return naNumValue(naVec_get(args, 0)); + return argc > 0 ? naNumValue(args[0]) : naNil(); } -static naRef streq(naContext c, naRef args) +static naRef streq(naContext c, naRef me, int argc, naRef* args) { - int i; - naRef a = naVec_get(args, 0); - naRef b = naVec_get(args, 1); - if(!naIsString(a) || !naIsString(b)) return naNil(); - if(naStr_len(a) != naStr_len(b)) return naNum(0); - for(i=0; i 1 ? naNum(naStrEqual(args[0], args[1])) : naNil(); +} + +static naRef f_cmp(naContext c, naRef me, int argc, naRef* args) +{ + char *a, *b; + int i, len; + if(argc < 2 || !naIsString(args[0]) || !naIsString(args[1])) + naRuntimeError(c, "bad argument to cmp"); + a = naStr_data(args[0]); + b = naStr_data(args[1]); + len = naStr_len(args[0]); + if(naStr_len(args[1]) < len) + len = naStr_len(args[1]); + for(i=0; i 0) return naNum(1); + } + return naNum(0); } -static naRef substr(naContext c, naRef args) +static naRef substr(naContext c, naRef me, int argc, naRef* args) { - naRef src = naVec_get(args, 0); - naRef startR = naVec_get(args, 1); - naRef lenR = naVec_get(args, 2); + naRef src = argc > 1 ? args[0] : naNil(); + naRef startR = argc > 1 ? naNumValue(args[1]) : naNil(); + naRef lenR = argc > 2 ? naNumValue(args[2]) : naNil(); int start, len; if(!naIsString(src)) return naNil(); - startR = naNumValue(startR); if(naIsNil(startR)) return naNil(); start = (int)startR.num; if(naIsNil(lenR)) { len = naStr_len(src) - start; + if(len < 0) return naNil(); } else { lenR = naNumValue(lenR); if(naIsNil(lenR)) return naNil(); @@ -126,18 +143,27 @@ static naRef substr(naContext c, naRef args) return naStr_substr(naNewString(c), src, start, len); } -static naRef contains(naContext c, naRef args) +static naRef f_chr(naContext c, naRef me, int argc, naRef* args) +{ + char chr[1]; + naRef cr = argc ? naNumValue(args[0]) : naNil(); + if(IS_NIL(cr)) naRuntimeError(c, "chr argument not string"); + chr[0] = (char)cr.num; + return NEWSTR(c, chr, 1); +} + +static naRef contains(naContext c, naRef me, int argc, naRef* args) { - naRef hash = naVec_get(args, 0); - naRef key = naVec_get(args, 1); + naRef hash = argc > 0 ? args[0] : naNil(); + naRef key = argc > 1 ? args[1] : naNil(); if(naIsNil(hash) || naIsNil(key)) return naNil(); if(!naIsHash(hash)) return naNil(); return naHash_get(hash, key, &key) ? naNum(1) : naNum(0); } -static naRef typeOf(naContext c, naRef args) +static naRef typeOf(naContext c, naRef me, int argc, naRef* args) { - naRef r = naVec_get(args, 0); + naRef r = argc > 0 ? args[0] : naNil(); char* t = "unknown"; if(naIsNil(r)) t = "nil"; else if(naIsNum(r)) t = "scalar"; @@ -146,10 +172,278 @@ static naRef typeOf(naContext c, naRef args) else if(naIsHash(r)) t = "hash"; else if(naIsFunc(r)) t = "func"; else if(naIsGhost(r)) t = "ghost"; - r = naStr_fromdata(naNewString(c), t, StrLen(t)); + r = NEWCSTR(c, t); return r; } +static naRef f_compile(naContext c, naRef me, int argc, naRef* args) +{ + int errLine; + naRef script, code, fname; + script = argc > 0 ? args[0] : naNil(); + if(!naIsString(script)) return naNil(); + fname = NEWCSTR(c, ""); + code = naParseCode(c, fname, 1, + naStr_data(script), naStr_len(script), &errLine); + if(!naIsCode(code)) return naNil(); // FIXME: export error to caller... + return naBindToContext(c, code); +} + +static naRef f_call(naContext c, naRef me, int argc, naRef* args) +{ + naContext subc; + naRef callargs, callme, callns, result; + struct VecRec* vr; + callargs = argc > 1 ? args[1] : naNil(); + callme = argc > 2 ? args[2] : naNil(); // Might be nil, that's OK + callns = argc > 3 ? args[3] : naNil(); // ditto + if(!IS_HASH(callme)) callme = naNil(); + if(!IS_HASH(callns)) callns = naNil(); + if(!IS_FUNC(args[0]) || (!IS_NIL(callargs) && !IS_VEC(callargs))) + naRuntimeError(c, "bad argument to call()"); + subc = naNewContext(); + subc->callParent = c; + c->callChild = subc; + vr = IS_NIL(callargs) ? 0 : callargs.ref.ptr.vec->rec; + result = naCall(subc, args[0], vr ? vr->size : 0, vr ? vr->array : 0, + callme, callns); + c->callChild = 0; + if(argc > 2 && IS_VEC(args[argc-1])) { + if(!IS_NIL(subc->dieArg)) naVec_append(args[argc-1], subc->dieArg); + else if(naGetError(subc)) + naVec_append(args[argc-1], NEWCSTR(subc, naGetError(subc))); + } + naFreeContext(subc); + return result; +} + +static naRef f_die(naContext c, naRef me, int argc, naRef* args) +{ + c->dieArg = argc > 0 ? args[0] : naNil(); + naRuntimeError(c, "__die__"); + return naNil(); // never executes +} + +// Wrapper around vsnprintf, iteratively increasing the buffer size +// until it fits. Returned buffer should be freed by the caller. +char* dosprintf(char* f, ...) +{ + char* buf; + va_list va; + int len = 16; + while(1) { + buf = naAlloc(len); + va_start(va, f); + if(vsnprintf(buf, len, f, va) < len) { + va_end(va); + return buf; + } + va_end(va); + naFree(buf); + len *= 2; + } +} + +// Inspects a printf format string f, and finds the next "%..." format +// specifier. Stores the start of the specifier in out, the length in +// len, and the type in type. Returns a pointer to the remainder of +// the format string, or 0 if no format string was found. Recognizes +// all of ANSI C's syntax except for the "length modifier" feature. +// Note: this does not validate the format character returned in +// "type". That is the caller's job. +static char* nextFormat(naContext ctx, char* f, char** out, int* len, char* type) +{ + // Skip to the start of the format string + while(*f && *f != '%') f++; + if(!*f) return 0; + *out = f++; + + while(*f && (*f=='-' || *f=='+' || *f==' ' || *f=='0' || *f=='#')) f++; + + // Test for duplicate flags. This is pure pedantry and could + // be removed on all known platforms, but just to be safe... + { char *p1, *p2; + for(p1 = *out + 1; p1 < f; p1++) + for(p2 = p1+1; p2 < f; p2++) + if(*p1 == *p2) + naRuntimeError(ctx, "duplicate flag in format string"); } + + while(*f && *f >= '0' && *f <= '9') f++; + if(*f && *f == '.') f++; + while(*f && *f >= '0' && *f <= '9') f++; + if(!*f) naRuntimeError(ctx, "invalid format string"); + + *type = *f++; + *len = f - *out; + return f; +} + +#define ERR(m) naRuntimeError(ctx, m) +#define APPEND(r) result = naStr_concat(naNewString(ctx), result, r) +static naRef f_sprintf(naContext ctx, naRef me, int argc, naRef* args) +{ + char t, nultmp, *fstr, *next, *fout=0, *s; + int flen, argn=1; + naRef format, arg, result = naNewString(ctx); + + if(argc < 1) ERR("not enough arguments to sprintf"); + format = naStringValue(ctx, argc > 0 ? args[0] : naNil()); + if(naIsNil(format)) ERR("bad format string in sprintf"); + s = naStr_data(format); + + while((next = nextFormat(ctx, s, &fstr, &flen, &t))) { + APPEND(NEWSTR(ctx, s, fstr-s)); // stuff before the format string + if(flen == 2 && fstr[1] == '%') { + APPEND(NEWSTR(ctx, "%", 1)); + s = next; + continue; + } + if(argn >= argc) ERR("not enough arguments to sprintf"); + arg = args[argn++]; + nultmp = fstr[flen]; // sneaky nul termination... + fstr[flen] = 0; + if(t == 's') { + arg = naStringValue(ctx, arg); + if(naIsNil(arg)) fout = dosprintf(fstr, "nil"); + else fout = dosprintf(fstr, naStr_data(arg)); + } else { + arg = naNumValue(arg); + if(naIsNil(arg)) + fout = dosprintf(fstr, "nil"); + else if(t=='d' || t=='i' || t=='c') + fout = dosprintf(fstr, (int)naNumValue(arg).num); + else if(t=='o' || t=='u' || t=='x' || t=='X') + fout = dosprintf(fstr, (unsigned int)naNumValue(arg).num); + else if(t=='e' || t=='E' || t=='f' || t=='F' || t=='g' || t=='G') + fout = dosprintf(fstr, naNumValue(arg).num); + else + ERR("invalid sprintf format type"); + } + fstr[flen] = nultmp; + APPEND(NEWSTR(ctx, fout, strlen(fout))); + naFree(fout); + s = next; + } + APPEND(NEWSTR(ctx, s, strlen(s))); + return result; +} + +// FIXME: handle ctx->callParent frames too! +static naRef f_caller(naContext ctx, naRef me, int argc, naRef* args) +{ + int fidx; + struct Frame* frame; + naRef result, fr = argc ? naNumValue(args[0]) : naNum(1); + if(IS_NIL(fr)) naRuntimeError(ctx, "non numeric argument to caller()"); + fidx = (int)fr.num; + if(fidx > ctx->fTop - 1) return naNil(); + frame = &ctx->fStack[ctx->fTop - 1 - fidx]; + result = naNewVector(ctx); + naVec_append(result, frame->locals); + naVec_append(result, frame->func); + naVec_append(result, frame->func.ref.ptr.func->code.ref.ptr.code->srcFile); + naVec_append(result, naNum(naGetLine(ctx, fidx))); + return result; +} + +static naRef f_closure(naContext ctx, naRef me, int argc, naRef* args) +{ + int i; + naRef func, idx; + struct naFunc* f; + func = argc > 0 ? args[0] : naNil(); + idx = argc > 1 ? naNumValue(args[1]) : naNil(); + if(!IS_FUNC(func) || IS_NIL(idx)) + naRuntimeError(ctx, "bad arguments to closure()"); + i = (int)idx.num; + f = func.ref.ptr.func; + while(i > 0 && f) { i--; f = f->next.ref.ptr.func; } + if(!f) return naNil(); + return f->namespace; +} + +static int match(unsigned char* a, unsigned char* b, int l) +{ + int i; + for(i=0; i 2) start = (int)(naNumValue(args[2]).num); + return naNum(find(args[0].ref.ptr.str->data, args[0].ref.ptr.str->len, + args[1].ref.ptr.str->data, args[1].ref.ptr.str->len, + start)); +} + +static naRef f_split(naContext ctx, naRef me, int argc, naRef* args) +{ + int sl, dl, i; + char *s, *d, *s0; + naRef result; + if(argc < 2 || !IS_STR(args[0]) || !IS_STR(args[1])) + naRuntimeError(ctx, "bad/missing argument to split"); + d = naStr_data(args[0]); dl = naStr_len(args[0]); + s = naStr_data(args[1]); sl = naStr_len(args[1]); + result = naNewVector(ctx); + if(dl == 0) { // special case zero-length delimiter + for(i=0; i 0 ? args[0] : naNil(); + naRef hash = argc > 1 ? args[1] : naNewHash(ctx); + naRef next = argc > 2 ? args[2] : naNil(); + if(!IS_FUNC(func) || (!IS_NIL(next) && !IS_FUNC(next)) || !IS_HASH(hash)) + naRuntimeError(ctx, "bad argument to bind"); + func = naNewFunc(ctx, func.ref.ptr.func->code); + func.ref.ptr.func->namespace = hash; + func.ref.ptr.func->next = next; + return func; +} + struct func { char* name; naCFunction func; }; static struct func funcs[] = { { "size", size }, @@ -162,9 +456,21 @@ static struct func funcs[] = { { "int", intf }, { "num", num }, { "streq", streq }, + { "cmp", f_cmp }, { "substr", substr }, + { "chr", f_chr }, { "contains", contains }, { "typeof", typeOf }, + { "compile", f_compile }, + { "call", f_call }, + { "die", f_die }, + { "sprintf", f_sprintf }, + { "caller", f_caller }, + { "closure", f_closure }, + { "find", f_find }, + { "split", f_split }, + { "rand", f_rand }, + { "bind", f_bind }, }; naRef naStdLib(naContext c) @@ -173,8 +479,8 @@ naRef naStdLib(naContext c) int i, n = sizeof(funcs)/sizeof(struct func); for(i=0; i