+/* 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(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 },
+ { "append", f_append },
+ { "pop", f_pop },
+ { "setsize", f_setsize },
+ { "subvec", f_subvec },
+ { "delete", f_delete },
+ { "int", f_int },
+ { "num", f_num },
+ { "streq", f_streq },
+ { "cmp", f_cmp },
+ { "substr", f_substr },