Though uLisp has tail-call optimization, if the C compiler doesn't, then the garbage collector needs stack in proportion to list length:
void markobject (object *obj) {
if (obj == NULL) return;
object* arg = car(obj);
if (marked(obj)) return;
int type = obj->type;
mark(obj);
if (type != SYMBOL && type != NUMBER) { // cons
markobject(arg);
markobject(cdr(obj)); // <--- TAIL CALL!
}
}
It's simple enough to obj = cdr(obj) and wrap a loop around this.