我试图绕过即时和延期执行。 据我所知,解释器维护一个标志,知道它是否在延迟执行中。
延迟执行过程可能是因为名称查找返回了一个过程。
现在我试图找出控制此解释器标志的类型,操作或操作。
例如,下面的这段代码在结尾处有一个立即计算的名称,它返回一个过程。但是这个程序被推送,而它是可执行的(xcheck):
/setdata
{
/a 1 def
/b 0 def
/foo
a 0 ne
b 0 ne
and
def
{ foo false and }
} def
//setdata
我知道有一条特殊规则:
直接出现的程序(作为正在阅读的程序的一部分 通常是从文件中或作为内存中某些较大过程的一部分) 定义或构造的一部分,例如条件, 明确地对程序进行操作。但程序获得 间接 - 例如,通常是查找姓名的结果 打算执行。 PostScript程序可以覆盖这些 必要时的语义。
据我所知,如果你直接遇到一个程序就必须推它(即使它是可执行的)。 (立即计算的名称返回一个直接遇到的过程,因此应将其推送到操作系统。)
现在如果我在代码中思考在解释器中实现这个逻辑,我可以想到这样的事情:
如果我有文字名查找,请设置解释器的DeferredFlag = true; 现在我怎么知道延迟执行何时结束?如果我遇到" def"我可以硬编码名字,但可能还有其他人。
(+如果程序嵌套在正在执行的程序中,等等......)
我无法找到一种方法来控制解释器中的DeferredFlag以了解当前的执行模式。
希望问题很明确。
更新
我试图调试一些额外的代码示例但没有成功。
代码1:
/foo { 2 3 add } def
foo
% result: 5
代码2:
/foo { 2 3 add } def
//foo
% result: { 2 3 add }
代码3:
/foo { 2 3 add } def
/bar { foo } def
bar
% result: 5
代码4:
/foo { 2 3 add } def
/bar { //foo } def
bar
% result: { 2 3 add }
答案 0 :(得分:3)
在尝试理解翻译时,我遇到了许多相同的问题和困惑。 IMO术语延迟执行并不是很有用。另外,我认为术语立即评估也不是很有用。你不需要DeferredFlag。
这里涉及两个独立但相关的部分:解释器循环和token
运算符。
token
处理"延迟执行"它将可执行数组的所有标记收集到一个对象中。因此,如果文件或字符串以过程体开始,则在其上调用token
会产生整个过程体。
{ execution is deferred until the closing }
它看起来像一个评论,但这是一行后记代码,即使没有任何单词延迟,关闭, 等定义。但是,如果你在它上面调用exec
,或者将其定义为一个名称,那么它将被执行并且最好定义内容。
解释器循环始终从exec堆栈中获取top对象,并且在语义上,可执行数组,文件和字符串的行为都相同。解释器将其视为源并获取第一个元素。名称情况略有不同,因为它本身不是源。 (我正在介绍我自己的这个概念,希望它能帮助/工作。)在C-ish伪代码中:
main_loop(){
while( ! quit ){
eval();
}
}
eval(){
object = pop( exec_stack );
if( !executable_flag( object ) ) push( op_stack, object );
else switch( type_of( object ) ){
case array: array_handler( object ); break;
case string: string_handler( object ); break;
case file: file_handler( object ); break;
case name: name_handler( object ); break;
default: push( op_stack, object );
}
}
在名称的情况下,查找名称并执行可执行文件。
name_handler( object ) {
object = load( object );
push( executable_flag( object ) ? exec_stack : op_stack, object );
}
在其他三个中,您还必须检查它是否是数组。
array_handler( object ){
switch( length( object ){
default:
push( exec_stack, getinterval( object, 1, length( object ) - 1 ) );
/* fall-thru */
case 1:
object = get( object, 0 );
push( executable_flag( object ) && type_of( object ) != array ?
exec_stack : op_stack, object );
case 0:
/* do nothing */
}
}
仅当executable_flag( object ) && type_of( object ) != array
然后你推送到exec堆栈。
对于另一个问题,立即评估的名称,我更喜欢称之为立即加载的名称。 token
运算符在返回之前调用load
。如果在正确的地方完成,易于处理。它与"延迟执行"没有真正的互动。一部分。
编辑:
我通过我的debugger跟踪您的示例。这显示了每个令牌执行后op_stack的运行图。左侧的元素是token
返回的对象。请注意,token
已经消耗了所有//
个。
$ cat test.ps
(db5.ps) run currentfile cvx traceon debug
/foo { 2 3 add } def
foo
% result: 5
/foo { 2 3 add } def
//foo
% result: { 2 3 add }
/foo { 2 3 add } def
/bar { foo } def
bar
% result: 5
/foo { 2 3 add } def
/bar { //foo } def
bar
% result: { 2 3 add }
$ gsnd -DNOSAFER test.ps
GPL Ghostscript 9.19 (2016-03-23)
Copyright (C) 2016 Artifex Software, Inc. All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
%|-
/foo %|- /foo
{2 3 add} %|- /foo {2 3 add}
def %|-
foo %|- 5
/foo %|- 5 /foo
{2 3 add} %|- 5 /foo {2 3 add}
def %|- 5
{2 3 add} %|- 5 {2 3 add}
/foo %|- 5 {2 3 add} /foo
{2 3 add} %|- 5 {2 3 add} /foo {2 3 add}
def %|- 5 {2 3 add}
/bar %|- 5 {2 3 add} /bar
{foo} %|- 5 {2 3 add} /bar {foo}
def %|- 5 {2 3 add}
bar %|- 5 {2 3 add} 5
/foo %|- 5 {2 3 add} 5 /foo
{2 3 add} %|- 5 {2 3 add} 5 /foo {2 3 add}
def %|- 5 {2 3 add} 5
/bar %|- 5 {2 3 add} 5 /bar
{{2 3 add}} %|- 5 {2 3 add} 5 /bar {{2 3 add}}
def %|- 5 {2 3 add} 5
bar GS<4>
GS<4>pstack
{2 3 add}
5
{2 3 add}
5
GS<4>