PUSH(ctx, evalAndOr(ctx, op, a, b));
break;
case OP_CAT:
- a = stringify(ctx, POP(ctx)); b = stringify(ctx, POP(ctx));
+ // stringify can call the GC, so don't take stuff of the stack!
+ if(ctx->opTop <= 1) ERR(ctx, "BUG: stack underflow");
+ a = stringify(ctx, ctx->opStack[ctx->opTop-1]);
+ b = stringify(ctx, ctx->opStack[ctx->opTop-2]);
c = naStr_concat(naNewString(ctx), b, a);
+ ctx->opTop -= 2;
PUSH(ctx, c);
break;
case OP_NEG:
{
// Return early if an error occurred. It will be visible to the
// caller via naGetError().
+ ctx->error = 0;
if(setjmp(ctx->jumpHandle))
return naNil();
// What actually gets executed at runtime is a bound FUNC object,
// which combines the raw code with a pointer to a CLOSURE chain of
// namespaces.
-enum { T_STR, T_VEC, T_HASH, T_CODE, T_CLOSURE, T_FUNC, T_CCODE,
+enum { T_STR, T_VEC, T_HASH, T_CODE, T_CLOSURE, T_FUNC, T_CCODE, T_GHOST,
NUM_NASAL_TYPES }; // V. important that this come last!
#define IS_REF(r) ((r).ref.reftag == NASAL_REFTAG)
#define IS_FUNC(r) (IS_OBJ((r)) && (r).ref.ptr.obj->type == T_FUNC)
#define IS_CLOSURE(r) (IS_OBJ((r)) && (r).ref.ptr.obj->type == T_CLOSURE)
#define IS_CCODE(r) (IS_OBJ((r)) && (r).ref.ptr.obj->type == T_CCODE)
+#define IS_GHOST(r) (IS_OBJ((r)) && (r).ref.ptr.obj->type == T_GHOST)
#define IS_CONTAINER(r) (IS_VEC(r)||IS_HASH(r))
#define IS_SCALAR(r) (IS_NUM((r)) || IS_STR((r)))
naCFunction fptr;
};
+struct naGhost {
+ GC_HEADER;
+ naGhostType* gtype;
+ void* ptr;
+};
+
struct naPool {
int type;
int elemsz;
naFree(o->constants); o->constants = 0;
}
+static void naGhost_gcclean(struct naGhost* g)
+{
+ if(g->ptr) g->gtype->destroy(g->ptr);
+ g->ptr = 0;
+}
+
static void freeelem(struct naPool* p, struct naObj* o)
{
// Mark the object as "freed" for debugging purposes
case T_CODE:
naCode_gcclean((struct naCode*)o);
break;
+ case T_GHOST:
+ naGhost_gcclean((struct naGhost*)o);
+ break;
}
// And add it to the free list
{
struct naHash* h = hash.ref.ptr.hash;
int i;
- if(!IS_HASH(hash)) return;
+ if(!IS_HASH(hash) || !h->table) return;
for(i=0; i<(1<<h->lgalloced); i++) {
struct HashNode* hn = h->table[i];
while(hn) {
static int hexc(char c, struct Parser* p, int index)
{
if(c >= '0' && c <= '9') return c - '0';
- if(c >= 'A' && c <= 'F') return c - 'a' + 10;
+ if(c >= 'A' && c <= 'F') return c - 'A' + 10;
if(c >= 'a' && c <= 'f') return c - 'a' + 10;
error(p, "bad hex constant", index);
return 0;
if(len < 4) error(p, "unterminated string", index);
*cOut = (char)((hexc(buf[2], p, index)<<4) | hexc(buf[3], p, index));
*eatenOut = 4;
+ break;
default:
// Unhandled, put the backslash back
*cOut = '\\';
{
int i = start;
while((i < p->len) &&
- ((p->buf[i] >= 'A' && p->buf[i] <= 'Z') ||
+ ((p->buf[i] == '_') ||
+ (p->buf[i] >= 'A' && p->buf[i] <= 'Z') ||
(p->buf[i] >= 'a' && p->buf[i] <= 'z') ||
(p->buf[i] >= '0' && p->buf[i] <= '9')))
{ i++; }
if(!handled) {
int symlen=0, lexlen=0, lexeme;
lexlen = tryLexemes(p, i, &lexeme);
- if((c>='A' && c<='Z') || (c>='a' && c<='z'))
+ if((c>='A' && c<='Z') || (c>='a' && c<='z') || (c=='_'))
symlen = trySymbol(p, i);
if(lexlen && lexlen >= symlen) {
newToken(p, i, LEXEMES[lexeme].tok, 0, 0, 0);
else if(naIsVector(r)) t = "vector";
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));
return r;
}
return closure;
}
+naRef naNewGhost(naContext c, naGhostType* type, void* ptr)
+{
+ naRef ghost = naNew(c, T_GHOST);
+ ghost.ref.ptr.ghost->gtype = type;
+ ghost.ref.ptr.ghost->ptr = ptr;
+ return ghost;
+}
+
+naGhostType* naGhost_type(naRef ghost)
+{
+ if(!IS_GHOST(ghost)) return 0;
+ return ghost.ref.ptr.ghost->gtype;
+}
+
+void* naGhost_ptr(naRef ghost)
+{
+ if(!IS_GHOST(ghost)) return 0;
+ return ghost.ref.ptr.ghost->ptr;
+}
+
naRef naNil()
{
naRef r;
case T_FUNC: return sizeof(struct naFunc);
case T_CLOSURE: return sizeof(struct naClosure);
case T_CCODE: return sizeof(struct naCCode);
+ case T_GHOST: return sizeof(struct naGhost);
};
return 0x7fffffff; // Make sure the answer is nonsense :)
}
return IS_CCODE(r);
}
+int naIsGhost(naRef r)
+{
+ return IS_GHOST(r);
+}
struct naFunc* func;
struct naClosure* closure;
struct naCCode* ccode;
+ struct naGhost* ghost;
} ptr;
#ifndef NASAL_BIG_ENDIAN_32_BIT
int reftag; // Little-endian and 64 bit systems need this here!
void naHash_delete(naRef hash, naRef key);
void naHash_keys(naRef dst, naRef hash);
+// Ghost utilities:
+typedef struct naGhostType {
+ void (*destroy)(void* ghost);
+} naGhostType;
+naRef naNewGhost(naContext c, naGhostType* t, void* ghost);
+naGhostType* naGhost_type(naRef ghost);
+void* naGhost_ptr(naRef ghost);
+int naIsGhost(naRef r);
+
#ifdef __cplusplus
} // extern "C"
#endif