在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)产生一个 文字字典对象。
如果两个[]运算符都是可执行的,或者只有]运算符,那么我并不清楚。
答案 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)
[和]都是可执行的令牌。 [生成标记对象],创建一个对象数组到最后一个标记