Hm. I can't think of any way. It's an implementation detail whether it
even exists as an independent object, depending on the details of the
scanner code.
Here's my current version.
case '{': { // This is the one part that makes it a recursive-descent parser
int ret;
Xpost_Object tail;
tail = xpost_name_cons(ctx, "}");
xpost_stack_push(ctx->lo, ctx->os, mark);
while (1) {
Xpost_Object t;
ret = toke(ctx, src, next, back, &t);
//printf("grok: x?%d", xpost_object_is_exe(t));
if (ret)
return ret;
if ((xpost_object_get_type(t) == nametype)
&& (xpost_dict_compare_objects(ctx, t, tail) == 0))
break;
xpost_stack_push(ctx->lo, ctx->os, t);
}
ret = xpost_op_array_to_mark(ctx); // ie. the /] operator
if (ret)
return ret;
//return xpost_object_cvx(xpost_stack_pop(ctx->lo, ctx->os));
*retval = xpost_object_cvx(xpost_stack_pop(ctx->lo, ctx->os));
return 0;
}
which you may snip in replies in favor of this pseudocode:
object tail = name("}")
push(mark)
while(1)
object t = token() ;;< recursive call to scanner
if (t.type == nametype) and (compare(t,tail)==0)
break
push(t)
counttomark array astore cvx
This produces the correct behavior for correct constructs,
but for a bare right-curly }
it will yield the executable name `}`
which the interpreter will then execute.
If you choose not to abuse it, the only difference apparent
to the user is that ghostscript signals a syntaxerror and
xpost signals an undefined error.
Ghostscript scanning is much more complicated, but does
a superior job of dealing with the spectrum of line-termination
conventions than xpost. I fear I will have to learn more
details from gs before I'm through.
--
s/fear/hope
s/have/get