+ APPEND(NEWSTR(c, s, strlen(s)));
+ return result;
+}
+
+// FIXME: needs to honor subcontext list
+static naRef f_caller(naContext c, naRef me, int argc, naRef* args)
+{
+ int fidx;
+ struct Frame* frame;
+ naRef result, fr = argc ? naNumValue(args[0]) : naNum(1);
+ if(IS_NIL(fr)) ARGERR();
+ fidx = (int)fr.num;
+ if(fidx > c->fTop - 1) return naNil();
+ frame = &c->fStack[c->fTop - 1 - fidx];
+ result = naNewVector(c);
+ naVec_append(result, frame->locals);
+ naVec_append(result, frame->func);
+ naVec_append(result, PTR(PTR(frame->func).func->code).code->srcFile);
+ naVec_append(result, naNum(naGetLine(c, fidx)));
+ return result;
+}
+
+static naRef f_closure(naContext c, naRef me, int argc, naRef* args)
+{
+ int i;
+ struct naFunc* f;
+ naRef func = argc > 0 ? args[0] : naNil();
+ naRef idx = argc > 1 ? naNumValue(args[1]) : naNum(0);
+ if(!IS_FUNC(func) || IS_NIL(idx)) ARGERR();
+ i = (int)idx.num;
+ f = PTR(func).func;
+ while(i > 0 && f) { i--; f = PTR(f->next).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<l; i++) if(a[i] != b[i]) return 0;
+ return 1;
+}
+
+static int find(unsigned char* a, int al, unsigned char* s, int sl, int start)
+{
+ int i;
+ if(al == 0) return 0;
+ for(i=start; i<sl-al+1; i++) if(match(a, s+i, al)) return i;
+ return -1;
+}
+
+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((void*)naStr_data(args[0]), naStr_len(args[0]),
+ (void*)naStr_data(args[1]), naStr_len(args[1]),
+ start));
+}
+
+static naRef f_split(naContext c, 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])) ARGERR();
+ d = naStr_data(args[0]); dl = naStr_len(args[0]);
+ s = naStr_data(args[1]); sl = naStr_len(args[1]);
+ result = naNewVector(c);
+ if(dl == 0) { // special case zero-length delimiter
+ for(i=0; i<sl; i++) naVec_append(result, NEWSTR(c, s+i, 1));
+ return result;
+ }
+ s0 = s;
+ for(i=0; i <= sl-dl; i++) {
+ if(match((unsigned char*)(s+i), (unsigned char*)d, dl)) {
+ naVec_append(result, NEWSTR(c, s0, s+i-s0));
+ s0 = s + i + dl;
+ i += dl - 1;
+ }
+ }
+ if(s0 - s <= sl) naVec_append(result, NEWSTR(c, s0, s+sl-s0));
+ return result;
+}
+
+// This is a comparatively weak RNG, based on the C library's rand()
+// function, which is usually not threadsafe and often of limited
+// precision. The 5x loop guarantees that we get a full double worth
+// of precision even for 15 bit (Win32...) rand() implementations.
+static naRef f_rand(naContext c, naRef me, int argc, naRef* args)
+{
+ int i;
+ double r = 0;
+ if(argc) {
+ if(!IS_NUM(args[0])) naRuntimeError(c, "rand() seed not number");
+ srand((unsigned int)args[0].num);
+ return naNil();
+ }
+ for(i=0; i<5; i++) r = (r + rand()) * (1.0/(RAND_MAX+1.0));
+ return naNum(r);
+}
+
+static naRef f_bind(naContext c, naRef me, int argc, naRef* args)
+{
+ naRef func = argc > 0 ? args[0] : naNil();
+ naRef hash = argc > 1 ? args[1] : naNewHash(c);
+ naRef next = argc > 2 ? args[2] : naNil();
+ if(!IS_FUNC(func) || (!IS_NIL(next) && !IS_FUNC(next)) || !IS_HASH(hash))
+ ARGERR();
+ func = naNewFunc(c, PTR(func).func->code);
+ PTR(func).func->namespace = hash;
+ PTR(func).func->next = next;
+ 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 },
+ { "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 },
+ { "chr", f_chr },
+ { "contains", f_contains },
+ { "typeof", f_typeof },
+ { "ghosttype", f_ghosttype },
+ { "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 },
+ { "sort", f_sort },
+ { "id", f_id },
+ { 0 }
+};
+
+naRef naInit_std(naContext c)
+{
+ return naGenLib(c, funcs);