]> git.mxchange.org Git - simgear.git/blobdiff - simgear/nasal/lib.c
cppbind.Ghost: register _get called on retrieving unset member.
[simgear.git] / simgear / nasal / lib.c
index fd8451be7b264b9e8bce13e14be1782866c01bd6..796c3ca3c7008e2e9cc03f583328c87eb94e68f2 100644 (file)
@@ -56,7 +56,7 @@ static naRef f_pop(naContext c, naRef me, int argc, naRef* args)
 static naRef f_setsize(naContext c, naRef me, int argc, naRef* args)
 {
     if(argc < 2 || !naIsVector(args[0])) ARGERR();
-    naVec_setsize(args[0], (int)naNumValue(args[1]).num);
+    naVec_setsize(c, args[0], (int)naNumValue(args[1]).num);
     return args[0];
 }
 
@@ -74,7 +74,7 @@ static naRef f_subvec(naContext c, naRef me, int argc, naRef* args)
     if(naIsNil(nlen) || len > naVec_size(v) - start)
         len = naVec_size(v) - start;
     result = naNewVector(c);
-    naVec_setsize(result, len);
+    naVec_setsize(c, result, len);
     for(i=0; i<len; i++)
         naVec_set(result, i, naVec_get(v, start + i));
     return result;
@@ -128,17 +128,50 @@ static naRef f_cmp(naContext c, naRef me, int argc, naRef* args)
 
 static naRef f_substr(naContext c, naRef me, int argc, naRef* args)
 {
-    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) || naIsNil(startR)) ARGERR();
-    start = (int)startR.num;
-    len = naIsNil(lenR) ? (naStr_len(src) - start) : (int)lenR.num;
-    if(len < 0) ARGERR();
+    int start, len, srclen;
+    naRef src = argc > 0 ? args[0] : naNil();
+    naRef startr = argc > 1 ? naNumValue(args[1]) : naNil();
+    naRef lenr = argc > 2 ? naNumValue(args[2]) : naNil();
+    if(!naIsString(src)) ARGERR();
+    if(naIsNil(startr) || !naIsNum(startr)) ARGERR();
+    if(!naIsNil(lenr) && !naIsNum(lenr)) ARGERR();
+    srclen = naStr_len(src);
+    start = (int)startr.num;
+    len = naIsNum(lenr) ? (int)lenr.num : (srclen - start);
+    if(start < 0) start += srclen;
+    if(start < 0) start = len = 0;
+    if(start >= srclen) start = len = 0;
+    if(len < 0) len = 0;
+    if(len > srclen - start) len = srclen - start;
     return naStr_substr(naNewString(c), src, start, len);
 }
 
+static naRef f_left(naContext c, naRef me, int argc, naRef* args)
+{
+    int len;
+    naRef src = argc > 0 ? args[0] : naNil();
+    naRef lenr = argc > 1 ? naNumValue(args[1]) : naNil();
+    if(!naIsString(src)) ARGERR();
+    if(!naIsNum(lenr)) ARGERR();
+    len = (int)lenr.num;
+    if(len < 0) len = 0;
+    return naStr_substr(naNewString(c), src, 0, len);
+}
+
+static naRef f_right(naContext c, naRef me, int argc, naRef* args)
+{
+    int len, srclen;
+    naRef src = argc > 0 ? args[0] : naNil();
+    naRef lenr = argc > 1 ? naNumValue(args[1]) : naNil();
+    if(!naIsString(src)) ARGERR();
+    if(!naIsNum(lenr)) ARGERR();
+    srclen = naStr_len(src);
+    len = (int)lenr.num;
+    if (len > srclen) len = srclen;
+    if(len < 0) len = 0;
+    return naStr_substr(naNewString(c), src, srclen - len, len);
+}
+
 static naRef f_chr(naContext c, naRef me, int argc, naRef* args)
 {
     char chr[1];
@@ -207,6 +240,10 @@ static naRef f_compile(naContext c, naRef me, int argc, naRef* args)
 // that it can be reset if we get a die()/naRethrowError() situation
 // later.  Right now, the IP on the stack trace is the line of the
 // die() call, when it should be this one...
+//
+// FIXME: don't use naCall at all here, we don't need it.  Fix up the
+// context stack to tail call the function directly.  There's no need
+// for f_call() to live on the C stack at all.
 static naRef f_call(naContext c, naRef me, int argc, naRef* args)
 {
     naContext subc;
@@ -215,34 +252,37 @@ static naRef f_call(naContext c, naRef me, int argc, naRef* args)
     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(callme) && !IS_GHOST(callme)) callme = naNil();
     if(!IS_HASH(callns)) callns = naNil();
-    if(!IS_FUNC(args[0]) || (!IS_NIL(callargs) && !IS_VEC(callargs)))
+    if(argc==0 || !IS_FUNC(args[0]) || (!IS_NIL(callargs) && !IS_VEC(callargs)))
         ARGERR();
 
-    // Note that we don't free the subcontext, in case the user
-    // re-throws the same error.  That happens at the next OP_RETURN
-    // or naSubContext().
     subc = naSubContext(c);
     vr = IS_NIL(callargs) ? 0 : PTR(callargs).vec->rec;
     result = naCall(subc, args[0], vr ? vr->size : 0, vr ? vr->array : 0,
                     callme, callns);
-    if(naGetError(subc)) {
-        if(argc <= 2 || !IS_VEC(args[argc-1])) {
-            naRethrowError(subc);
-        } else {
-            int i, sd;
-            naRef errv = args[argc-1];
-            if(!IS_NIL(subc->dieArg)) naVec_append(errv, subc->dieArg);
-            else naVec_append(errv, NEWCSTR(subc, naGetError(subc)));
-            sd = naStackDepth(subc);
-            for(i=0; i<sd; i++) {
-                naVec_append(errv, naGetSourceFile(subc, i));
-                naVec_append(errv, naNum(naGetLine(subc, i)));
-            }
+    if(!naGetError(subc)) {
+        naFreeContext(subc);
+        return result;
+    }
+
+    // Error handling. Note that we don't free the subcontext after an
+    // error, in case the user re-throws the same error or calls
+    // naContinue()
+    if(argc <= 2 || !IS_VEC(args[argc-1])) {
+        naRethrowError(subc);
+    } else {
+        int i, sd;
+        naRef errv = args[argc-1];
+        if(!IS_NIL(subc->dieArg)) naVec_append(errv, subc->dieArg);
+        else naVec_append(errv, NEWCSTR(subc, naGetError(subc)));
+        sd = naStackDepth(subc);
+        for(i=0; i<sd; i++) {
+            naVec_append(errv, naGetSourceFile(subc, i));
+            naVec_append(errv, naNum(naGetLine(subc, i)));
         }
     }
-    return result;
+    return naNil();
 }
 
 static naRef f_die(naContext c, naRef me, int argc, naRef* args)
@@ -412,8 +452,8 @@ static naRef f_find(naContext c, naRef me, int argc, naRef* args)
     int start = 0;
     if(argc < 2 || !IS_STR(args[0]) || !IS_STR(args[1])) ARGERR();
     if(argc > 2) start = (int)(naNumValue(args[2]).num);
-    return naNum(find(PTR(args[0]).str->data, PTR(args[0]).str->len,
-                      PTR(args[1]).str->data, PTR(args[1]).str->len,
+    return naNum(find((void*)naStr_data(args[0]), naStr_len(args[0]),
+                      (void*)naStr_data(args[1]), naStr_len(args[1]),
                       start));
 }
 
@@ -472,6 +512,80 @@ static naRef f_bind(naContext c, naRef me, int argc, naRef* args)
     return func;
 }
 
+/* We use the "SortRec" gadget for two reasons: first, because ANSI
+ * qsort() doesn't give us a mechanism for passing a "context" pointer
+ * to the comparison routine we have to store one in every sorted
+ * record.  Second, using an index into the original vector here
+ * allows us to make the sort stable in the event of a zero returned
+ * from the Nasal comparison function. */
+struct SortData { naContext ctx, subc; struct SortRec* recs;
+                  naRef* elems; int n; naRef fn; };
+struct SortRec { struct SortData* sd; int i; };
+
+static int sortcmp(struct SortRec* a, struct SortRec* b)
+{
+    struct SortData* sd = a->sd;
+    naRef args[2], d;
+    args[0] = sd->elems[a->i];
+    args[1] = sd->elems[b->i];
+    d = naCall(sd->subc, sd->fn, 2, args, naNil(), naNil());
+    if(naGetError(sd->subc)) {
+        naFree(sd->recs);
+        naRethrowError(sd->subc);
+    } else if(!naIsNum(d = naNumValue(d))) {
+        naFree(sd->recs);
+        naRuntimeError(sd->ctx, "sort() comparison returned non-number");
+    }
+    return (d.num > 0) ? 1 : ((d.num < 0) ? -1 : (a->i - b->i));
+}
+
+static naRef f_sort(naContext c, naRef me, int argc, naRef* args)
+{
+    int i;
+    struct SortData sd;
+    naRef out;
+    if(argc != 2 || !naIsVector(args[0]) || !naIsFunc(args[1]))
+        naRuntimeError(c, "bad/missing argument to sort()");
+    sd.subc = naSubContext(c);
+    if(!PTR(args[0]).vec->rec) return naNewVector(c);
+    sd.elems = PTR(args[0]).vec->rec->array;
+    sd.n = PTR(args[0]).vec->rec->size;
+    sd.fn = args[1];
+    sd.recs = naAlloc(sizeof(struct SortRec) * sd.n);
+    for(i=0; i<sd.n; i++) {
+        sd.recs[i].sd = &sd;
+        sd.recs[i].i = i;
+    }
+    qsort(sd.recs, sd.n, sizeof(sd.recs[0]),
+          (int(*)(const void*,const void*))sortcmp);
+    out = naNewVector(c);
+    naVec_setsize(c, out, sd.n);
+    for(i=0; i<sd.n; i++)
+        PTR(out).vec->rec->array[i] = sd.elems[sd.recs[i].i];
+    naFree(sd.recs);
+    naFreeContext(sd.subc);
+    return out;
+}
+
+static naRef f_id(naContext c, naRef me, int argc, naRef* args)
+{
+    char *t = "unk", buf[64];
+    if(argc != 1 || !IS_REF(args[0]))
+        naRuntimeError(c, "bad/missing argument to id()");
+    if     (IS_STR(args[0]))   t = "str";
+    else if(IS_VEC(args[0]))   t = "vec";
+    else if(IS_HASH(args[0]))  t = "hash";
+    else if(IS_CODE(args[0]))  t = "code";
+    else if(IS_FUNC(args[0]))  t = "func";
+    else if(IS_CCODE(args[0])) t = "ccode";
+    else if(IS_GHOST(args[0])) {
+        naGhostType *gt = PTR(args[0]).ghost->gtype;
+        t = gt->name ? (char*)gt->name : "ghost";
+    }
+    sprintf(buf, "%s:%p", (char*)t, (void*)PTR(args[0]).obj);
+    return NEWCSTR(c, buf);
+}
+
 static naCFuncItem funcs[] = {
     { "size", f_size },
     { "keys", f_keys }, 
@@ -485,6 +599,8 @@ static naCFuncItem funcs[] = {
     { "streq", f_streq },
     { "cmp", f_cmp },
     { "substr", f_substr },
+    { "left", f_left },
+    { "right", f_right },
     { "chr", f_chr },
     { "contains", f_contains },
     { "typeof", f_typeof },
@@ -499,6 +615,8 @@ static naCFuncItem funcs[] = {
     { "split", f_split },
     { "rand", f_rand },
     { "bind", f_bind },
+    { "sort", f_sort },
+    { "id", f_id },
     { 0 }
 };