interp()函数用了大量的宏。IREF_NEXT只是其中一个宏,但是出现的频率很高。
但是透彻的理解这个宏将为理解interp()函数提供便利。
它的定义形式如下:
这句看似简单的代码实际上不那么容易理解,而后面一些列的宏要么与此类似,要么间接的调用它。
而ref的定义是下面这个样子:
struct tas_s tas;
union v { /* name the union to keep gdb happy */
int intval;
ushort boolval;
float realval;
ulong saveid;
byte *bytes;
const byte *const_bytes;
ref *refs;
const ref *const_refs;
name *pname;
const name *const_pname;
dict *pdict;
const dict *const_pdict;
/*
* packed is the normal variant for referring to packed arrays,
* but we need a writable variant for memory management and for
* storing into packed dictionary key arrays.
*/
const ref_packed *packed;
ref_packed *writable_packed;
op_proc_t opproc;
struct stream_s *pfile;
struct gx_device_s *pdevice;
obj_header_t *pstruct;
} value;
};
要使(const ref*)rp +1 变得有有意义,关键在于解析器是怎么处理的。
我们以"{}"为例,来解释它。
识别过程,是在scan_token()函数中办到的,但是具体是怎样办到的?
看下面截取的部分代码片段:
if_debug4('S', "[S}]d=%d, s=%d->%d, c=%d/n",
pdepth, pstack,
(pstack == pdepth ? 0 :
ref_stack_index(&o_stack, size)->value.intval),
size + pstack);
myref = (pstack == pdepth ? pref : &arr);/*上面已经解释了,肯定不等。*/
if (check_only) {
make_empty_array(myref, 0);
ref_stack_pop(&o_stack, size);
} else if (ref_array_packing.value.boolval) {
retcode = make_packed_array(myref, &o_stack, size,
idmemory, "scanner(packed)");
if (retcode < 0) { /* must be VMerror */
osp++;
scan_putback();
scan_type = scanning_none;
goto pause_ret;
}
r_set_attrs(myref, a_executable);
} else {/*讨论一种易于理解的方式*/
/* 为myref分配内存空间 */
retcode = ialloc_ref_array(myref,
a_executable + a_all, size,
"scanner(proc)");
if (retcode < 0) { /* must be VMerror */
osp++;
scan_putback();
scan_type = scanning_none;
goto pause_ret;
}//ref_stack_store 这步非常重要:把o_stack中size个对象拷贝到myref中。
retcode = ref_stack_store(&o_stack, myref, size, 0, 1,
false, idmemory, "scanner");
if (retcode < 0) {
ifree_ref_array(myref, "scanner(proc)");
sreturn(retcode);
}
/*因为全部拷贝到myref中了,所以把所有的操作符栈中关于过程的对象全部pop*/
ref_stack_pop(&o_stack, size);
}
if (pstack == pdepth) { /* This was the top-level procedure. */
spop1();
pstack = 0;
} else {
if (osp < osbot)
ref_stack_pop_block(&o_stack);
pstack = osp->value.intval;
*osp = arr;
goto snext;
}
}
break;
.............
}
现在到了一个很关键的函数——ref_stack_store(const ref_stack_t *pstack, ref *parray, uint count,
uint skip, int age, bool check, gs_dual_memory_t *idmemory,
client_name_t cname);
这个函数能够说明为什么IREF_NEXT能以那样的形式出现是正确的。
这个函数的源代码如下:
int
ref_stack_store(const ref_stack_t *pstack, ref *parray, uint count,
uint skip, int age, bool check, gs_dual_memory_t *idmemory,
client_name_t cname)
{
uint left, pass;
ref *to;
ref_stack_enum_t rsenum;
if (count > ref_stack_count(pstack) || count > r_size(parray))
return_error(e_rangecheck);
if (check) {
int code = ref_stack_store_check(pstack, parray, count, skip);
if (code < 0)
return code;
}
to = parray->value.refs + count;
left = count, pass = skip;
ref_stack_enum_begin(&rsenum, pstack);
do {
ref *from = rsenum.ptr;
uint size = rsenum.size;
if (size <= pass)
pass -= size;
else {
if (pass != 0)
size -= pass, pass = 0;
from += size;
if (size > left)
size = left;
left -= size;
switch (age) {
case -1: /* not an array */
while (size--) {
from--, to--;
ref_assign(to, from);
}
break;
case 0: /* old array */
while (size--) {
from--, to--;
ref_assign_old(parray, to, from, cname);
}
break;
case 1: /* new array */
while (size--) {
from--, to--;
ref_assign_new(to, from);
}
break;
}
if (left == 0)
break;
}
} while (ref_stack_enum_next(&rsenum));
r_set_size(parray, count);
return 0;
}
由于传入的age参数是1,所以进入case 1:这个阶段。
加粗的黑体字代码表明了宏所运用的方式是正确的。
这点可以通过运行调试来斧正。