6

在 PostScript 中,如果你有

[4 5 6]

你有以下令牌:

mark integer integer integer mark

堆栈是这样的:

| mark |
| mark | integer |
| mark | integer | integer |
| mark | integer | integer | integer |
| array |

现在我的问题 是:]-mark 运算符是文字对象还是可执行对象?

我是否正确 [-mark 是文字对象(只是数据)并且 ]-mark 是可执行对象(因为当您看到此 ]-mark 运算符时总是需要创建一个数组)?

PostScript 语言参考手册第 3.3.2 节给了我:

[ 和 ] 运算符在执行时会生成一个以封闭对象作为元素的文字数组对象。同样,<< 和 >> (LanguageLevel 2) 产生一个文字字典对象。

我不清楚这两个 [ ] 运算符是可执行的还是只有 ] 运算符。

4

2 回答 2

8

概括。

所有这些特殊标记 , , [,]都作为可执行名称从扫描器中出来。并被定义为产生一个标记类型对象(因此它们本身不是运算符,但它们是在所有运算符所在的位置定义的可执行名称)。并且被定义为过程或操作符,其执行方式与任何其他过程或操作符一样。这些使用运算符来查找左括号。但是所有这些标记都由扫描仪特殊处理,扫描仪可以识别它们而没有周围的空白,因为它们是其分隔符集的一部分。<<>>[<<systemdict]>>counttomark

细节。

这完全取决于你什么时候看。让我们追溯一下解释器对这些标记做了什么。我将用一个字符串来说明这一点,但它与文件的工作原理相同。

所以如果你有一个输入字符串

([4 5 6]) cvx exec

cvx使文字对象可执行。程序流是一个文件对象,也标记为可执行文件exec将对象推入执行堆栈,解释器在内部解释器处理循环的下一次迭代中遇到该对象。执行程序流时,可执行文件对象位于执行堆栈的最顶端。

解释器token用来调用扫描器。扫描程序会跳过初始空格,然后读取所有非空格字符直到下一个分隔符,然后尝试将字符串解释为数字,但未能将其变为可执行名称。括号是定界符集的一部分,因此被称为“自定界”。所以扫描器读取一个括号字符,停止读取,因为它是一个分隔符,发现它不能是一个数字,所以它产生一个可执行名称。

Top of Exec Stack | Operand Stack
(4 5 6]) [        | 

接下来,解释器循环执行任何可执行文件(除非它是一个数组)。执行令牌意味着从字典中加载它,然后执行定义(如果它是可执行的)。[定义为-mark-对象,与mark定义名称相同。从技术上讲,它不是操作员或程序,它只是一个定义。自动加载发生是因为名称来自扫描仪并设置了可执行标志。

(4 5 6])  | -mark-

然后扫描器产生 4、5 和 6,它们是数字并被直接推入操作数堆栈。6 由]在流上推回的 定界。

(])  | -mark- 4 5 6

解释器不执行这些数字,因为它们是不可执行的,但如果它执行了,它会是一样的。执行一个数字的动作只是将它压入堆栈。

然后,扫描仪终于遇到了右支架]。这就是魔法发生的地方。自定界,后面不需要任何空格。扫描器产生可执行文件名],解释器通过加载来执行它,它发现......

{ counttomark array astore exch pop }

或者可能是执行此操作的实际操作员。但是,是的。counttomark产生元素的数量。array创建该大小的数组。astore用堆栈中的元素填充数组。并exch pop一劳永逸地丢弃那个讨厌的标记。

对于字典,<<与 完全相同[。它留下一个标记。然后你排列一些键值对,并且>>是对...产生影响的过程

{ counttomark dup dict begin 2 idiv { def } repeat pop currentdict end }

制作字典。定义所有对。弹出标记。拿出字典。此版本的过程尝试通过将其设置为双倍大小来创建快速字典。将2 idivto 移到前面dup以制作一个字典。

因此,从哲学上讲,counttomark是您正在使用的运算符。并且它需要一个不用于其他任何东西的特殊对象类型,marktype 对象,-mark-. 其余的只是语法糖,让您访问这种堆栈计数能力来创建线性数据结构。

附录

这是一个模拟解释器循环读取的过程currentfile

{currentfile token not {exit} if dup type /arraytype ne {exec} if }loop

exec负责loading(并进一步执行)任何可执行文件名。从中可以看出,这token确实是扫描仪的名称;并且解释器循环直接遇到的过程(数组)不会被执行( type /arraytype ne {exec} if)。

然而,使用token字符串可以让你做一些很酷的事情。例如,您可以使用替换名称动态构造过程主体。这很像一个 lisp 宏。

/makeadder { % n  .  { n add }
    1 dict begin
    /n exch def
    ({//n add}) token % () {n add} true
    pop exch pop % {n add}
    end
} def

token从字符串中读取整个过程,将立即评估的名称 //n替换为其当前定义的值。请注意,扫描程序一次读取一个可执行数组,在返回之前在内部有效地执行[... ] cvx(在某些解释器中,例如我自己xpost的在单独的内存中。但是 2 级垃圾收集使这在很大程度上无关紧要)。

还有bind一个运算符通过用运算符对象本身替换运算符名称来修改过程。这些技巧可帮助您在速度关键的过程(如内部循环)中排除名称查找。

于 2013-02-11T07:31:45.260 回答
3

[ 和 ] 都是可执行标记。[ 产生一个标记对象, ] 创建一个对象数组到最后一个标记

于 2013-02-10T20:32:06.253 回答