PostScript 立即或延迟执行
PostScript immediate or deferred execution
我正在努力思考立即执行和延迟执行。
据我了解,解释器维护一个标志,知道它是否在延迟执行中。
过程的延迟执行可能是因为名称查找返回了过程。
现在我试图找出什么类型、动作或操作控制这个解释器标志。
例如,下面的这段代码在末尾有一个立即评估的名称,returns 一个过程。但是这个过程被推送了,虽然它是可执行的(xcheck):
/setdata
{
/a 1 def
/b 0 def
/foo
a 0 ne
b 0 ne
and
def
{ foo false and }
} def
//setdata
我知道有一条特殊规定:
Procedures appearing directly (either as part of a program being read
from a file or as part of some larger procedure in memory) are usually
part of a definition or of a construct, such as a conditional, that
operates on the procedure explicitly. But procedures obtained
indirectly—for example, as a result of looking up a name—are usually
intended to be executed. A PostScript program can override these
semantics when necessary.
我明白,如果你直接遇到一个程序,你必须推送它(即使它是可执行的)。 (立即评估名称returns一个程序,这是直接遇到的,所以应该推到OS。)
现在,如果我正在考虑在解释器中实现此逻辑的代码,我可以想到这样的事情:
如果我有一个 literalname 查找,请设置解释器的 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 }
在尝试理解解释器时,我有很多相同的问题和困惑。 IMO 术语 延迟执行 不是很有用。另外,我认为 immediately evaluated 这个词也不是很有用。您不需要 DeferredFlag。
这里涉及两个独立但相关的部分:解释器循环和 token
运算符。
token
处理 "deferred execution" 的部分,它将可执行数组的所有标记收集到单个对象中。因此,如果文件或字符串以过程主体开头,则对其调用 token
会产生整个过程主体。
{ execution is deferred until the closing }
它看起来像一条注释,但那是一行后记代码,即使 延迟 个单词的 none 也不会出现错误 运行, closing、the等定义。但是,如果您对其调用 exec
,或者将其定义为一个调用,那么它将执行并且最好定义内容。
解释器循环总是从 exec 堆栈中获取顶部对象,并且在语义上,可执行数组、文件和字符串的行为都相同。解释器将其视为源并获取第一个元素。名称大小写有点不同,因为它本身不是 source。 (我正在介绍我自己的这个概念,希望它 helps/works。)在 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
时才推送到执行堆栈。
对于另一个问题,立即评估的名称,我更愿意称它们为立即加载 名称。 token
运算符在返回之前对其调用 load
。如果在正确的地方完成,很容易处理。它与 "deferred-execution" 部分没有真正的交互。
编辑:
我运行你的样本通过我的debugger追踪。这显示了每个令牌执行后 op_stack 的 运行ning 图片。左边的元素是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>
我正在努力思考立即执行和延迟执行。 据我了解,解释器维护一个标志,知道它是否在延迟执行中。
过程的延迟执行可能是因为名称查找返回了过程。
现在我试图找出什么类型、动作或操作控制这个解释器标志。
例如,下面的这段代码在末尾有一个立即评估的名称,returns 一个过程。但是这个过程被推送了,虽然它是可执行的(xcheck):
/setdata
{
/a 1 def
/b 0 def
/foo
a 0 ne
b 0 ne
and
def
{ foo false and }
} def
//setdata
我知道有一条特殊规定:
Procedures appearing directly (either as part of a program being read from a file or as part of some larger procedure in memory) are usually part of a definition or of a construct, such as a conditional, that operates on the procedure explicitly. But procedures obtained indirectly—for example, as a result of looking up a name—are usually intended to be executed. A PostScript program can override these semantics when necessary.
我明白,如果你直接遇到一个程序,你必须推送它(即使它是可执行的)。 (立即评估名称returns一个程序,这是直接遇到的,所以应该推到OS。)
现在,如果我正在考虑在解释器中实现此逻辑的代码,我可以想到这样的事情:
如果我有一个 literalname 查找,请设置解释器的 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 }
在尝试理解解释器时,我有很多相同的问题和困惑。 IMO 术语 延迟执行 不是很有用。另外,我认为 immediately evaluated 这个词也不是很有用。您不需要 DeferredFlag。
这里涉及两个独立但相关的部分:解释器循环和 token
运算符。
token
处理 "deferred execution" 的部分,它将可执行数组的所有标记收集到单个对象中。因此,如果文件或字符串以过程主体开头,则对其调用 token
会产生整个过程主体。
{ execution is deferred until the closing }
它看起来像一条注释,但那是一行后记代码,即使 延迟 个单词的 none 也不会出现错误 运行, closing、the等定义。但是,如果您对其调用 exec
,或者将其定义为一个调用,那么它将执行并且最好定义内容。
解释器循环总是从 exec 堆栈中获取顶部对象,并且在语义上,可执行数组、文件和字符串的行为都相同。解释器将其视为源并获取第一个元素。名称大小写有点不同,因为它本身不是 source。 (我正在介绍我自己的这个概念,希望它 helps/works。)在 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
时才推送到执行堆栈。
对于另一个问题,立即评估的名称,我更愿意称它们为立即加载 名称。 token
运算符在返回之前对其调用 load
。如果在正确的地方完成,很容易处理。它与 "deferred-execution" 部分没有真正的交互。
编辑:
我运行你的样本通过我的debugger追踪。这显示了每个令牌执行后 op_stack 的 运行ning 图片。左边的元素是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>