PostScript立即执行或延迟执行

4

我正在努力理解立即执行和延迟执行。据我所知,解释器会维护一个标志,以知道它是否处于延迟执行状态。

过程的延迟执行可能是由于名称查找返回了一个过程。

现在我正在尝试找出什么类型、操作或操作控制这个解释器标志。

例如,下面这段代码在结尾处有一个立即求值的名称,它返回一个过程。但是这个过程被推入,同时它也是可执行的(xcheck):

/setdata
{
  /a 1 def
  /b 0 def

 /foo
   a 0 ne
   b 0 ne
   and
 def

 { foo false and }
} def

//setdata

我知道有一个特殊规则:
块直接出现的过程(无论是从文件中读取的程序的一部分,还是作为内存中某个更大程序的一部分)通常是定义或构造的一部分,例如条件语句,其在 过程上显式操作。但是,间接获取的过程-例如通过查找名称获得的过程-通常用于执行。PostScript程序可以在必要时覆盖这些语义。
我理解如果你直接遇到一个过程,你必须将它推入栈中(即使它是可执行的)。(立即计算的名称返回一个过程,直接遇到它,所以它应该被推送到操作系统。)
现在,如果我想在解释器中实现此逻辑的代码,我可以考虑以下内容:
如果我有一个literalname查找,则将解释器的DeferredFlag = true;设置为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 }
1个回答

6
我在理解解释器时遇到了很多相同的问题和困惑。我认为 "deferred execution" 这个术语并不是非常有用。此外,我认为 "immediately evaluated" 这个术语也不是非常有用。你不需要一个 DeferredFlag。
这里涉及到两个独立但相关的部分:解释器循环和 token 操作符。
token 处理 "延迟执行" 的一部分,它将可执行数组的所有令牌收集到一个单独的对象中。因此,如果一个文件或字符串以一个过程体开头,则对其调用 token 将产生整个过程体。
{ execution is deferred until the closing }

这看起来像是一条注释,但它实际上是一行后置代码。即使没有定义deferredclosingthe等单词,它也可以无错误地运行。但是如果您在其上调用exec或将其定义为名称并调用该名称,则它将执行并且内容必须被定义。

解释器循环始终从执行堆栈中获取顶部对象,并且在语义上可执行的数组、文件和字符串都是相同的。解释器将其视为源并获取第一个元素。名称情况有所不同,因为它本质上不是一个。(我正在引入这个概念,希望它能起到帮助/作用。)以C风格的伪代码为例:

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。如果在正确的位置处理,很容易处理。它与“延迟执行”部分没有真正的交互。

编辑:

我通过我的调试器对你的示例进行了跟踪。这显示了每个令牌执行后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>

关于立即计算名称。例如,在应返回过程的情况下(作为即时评估名称的结果)。 这是否会返回1个过程对象(例如{2 3 add}),还是会返回5个代表过程的标记:标记{、标记2、标记3、标记add、标记}? - juFo
它返回1个过程对象。有两种思考方式。def总是采用单个对象作为值,并且立即评估的名称始终从先前的def生成值,因此立即评估的名称只能返回单个对象。另一方面,所有程序结构都通过token运算符进入解释器,并且token始终消耗到}并返回单个对象。如果没有右括号,则token会发出/syntaxerror信号。因此,在后置脚本中不存在“token {”这样的东西。它永远不能单独存在。 - luser droog

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接