#define NEWCSTR(c, s) NEWSTR(c, s, strlen(s))
// Generic argument error, assumes that the symbol "c" is a naContext,
-// and that the __func__ string is of the form "f_NASALSYMBOL".
+// and that the __FUNCTION__ string is of the form "f_NASALSYMBOL".
#define ARGERR() \
- naRuntimeError(c, "bad/missing argument to %s()", (__func__ + 2))
+ naRuntimeError(c, "bad/missing argument to %s()", (__FUNCTION__ + 2))
static naRef f_size(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);
}
// 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;
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)))
+ 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)
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));
}
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(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 },
{ "split", f_split },
{ "rand", f_rand },
{ "bind", f_bind },
+ { "sort", f_sort },
+ { "id", f_id },
{ 0 }
};