PostScript标记令牌

时间:2013-02-10 19:03:03

标签: operators interpreter postscript

在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节给我:

  

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

如果两个[]运算符都是可执行的,或者只有]运算符,那么我并不清楚。

2 个答案:

答案 0 :(得分:6)

概要

所有这些特殊令牌[]<<>>都以扫描程序的形式出现在可执行文件名中。 [<<被定义为产生marktype对象(因此它们本身不是运算符,但它们是在systemdict中定义的可执行名称,其中所有运算符生活)。 ]>>被定义为与任何其他过程或运算符一样执行的过程或运算符。这些使用counttomark运算符来查找左括号。但所有这些令牌都由扫描仪专门处理,扫描仪识别它们而不包围空白,因为它们是分隔符集的一部分。

详情。

一切都取决于 时你看它。让我们来看看解释器对这些令牌所做的事情。我将用一个字符串来说明这一点,但它与文件的作用相同。

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

([4 5 6]) cvx exec

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

口译员使用token来呼叫扫描仪。扫描程序跳过初始空格,然后读取所有非空白字符直到下一个分隔符,然后尝试将该字符串解释为数字,并且失败它成为可执行文件名。括号是分隔符集的一部分,因此被称为“自定界”&#39;。因此扫描程序读取一个括号字符,因为它是一个分隔符而停止读取,发现它不能是一个数字,所以它产生一个可执行文件名。

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 }

制作字典。定义所有对。流行标记。产生字典。此版本的过程尝试通过使其成为双倍大小来创建 fast 字典。将2 idiv移至dup之前,以制作字典。

因此,为了获得哲学,counttomark是您正在使用的运算符。它需要一个特殊的对象类型,它不能用于其他任何东西,即marktype对象-mark-。其余的只是语法糖,让你可以访问这种堆栈计数功能来创建线性数据结构。

附录

这是一个对来自currentfile的解释器循环进行建模的过程。

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

exec负责load(并进一步执行)任何可执行文件名称。您可以从中看到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运算符,它通过将运算符名称替换为运算符对象本身来修改过程。这些技巧可以帮助您在速度关键程序(如内循环)中分解名称查找。

答案 1 :(得分:3)

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