PostScript标记令牌

juF*_*uFo 4 interpreter postscript operators

在PostScript中,如果你有

[4 5 6]
Run Code Online (Sandbox Code Playgroud)

你有以下令牌:

mark integer integer integer mark
Run Code Online (Sandbox Code Playgroud)

堆栈是这样的:

| mark |
| mark | integer |
| mark | integer | integer |
| mark | integer | integer | integer |
| array |
Run Code Online (Sandbox Code Playgroud)

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

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

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

[和]运算符在执行时会生成一个文本数组对象,其中en-closed对象为元素.同样,<<和>>(LanguageLevel 2)生成一个文字字典对象.

如果两个[]运算符都是可执行的或只有]运算符,那对我来说并不清楚.

lus*_*oog 6

摘要.

所有这些特殊标记,[,],<<,>>,走出了扫描仪的可执行文件名称.[<<定义为生成marktype对象(因此它们本身不是运算符,但它们是systemdict在所有运算符所在的位置定义的可执行名称).]并且>>被定义为与任何其他过程或操作符一样执行的过程或操作符.这些使用counttomark操作员找到开口支架.但所有这些令牌都由扫描仪专门处理,扫描仪识别它们而不包围空白,因为它们是分隔符集的一部分.

细节.

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

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

([4 5 6]) cvx exec
Run Code Online (Sandbox Code Playgroud)

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

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

Top of Exec Stack | Operand Stack
(4 5 6]) [        | 
Run Code Online (Sandbox Code Playgroud)

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

(4 5 6])  | -mark-
Run Code Online (Sandbox Code Playgroud)

然后扫描仪产生4,5和6这些数字并直接推到操作数堆栈.图6 ]的流程被推回到流上.

(])  | -mark- 4 5 6
Run Code Online (Sandbox Code Playgroud)

解释器不会执行这些数字,因为它们不可执行,但如果它可以执行则会相同.该行动的执行数是简单地将其推入堆栈.

然后,最后扫描仪遇到右括号].这就是魔术发生的地方.自定界,不需要任何空格.扫描程序产生可执行文件名称],解释程序通过加载执行它,它发现...

{ counttomark array astore exch pop }
Run Code Online (Sandbox Code Playgroud)

或者也许是一个真正的运营商.但是,是的.counttomark产生元素的数量.array创建一个大小的数组.astore使用堆栈中的元素填充数组.并且exch pop一劳永逸地抛弃那个讨厌的标记.

对于词典,<<完全相同[.它下降了一个标记.然后你排列一些键值对,并且>>是做某事的程序...

{ counttomark dup dict begin 2 idiv { def } repeat pop currentdict end }
Run Code Online (Sandbox Code Playgroud)

做一本字典.定义所有对.流行标记.产生字典.此版本的过程尝试通过使其成为双倍大小来创建快速字典.移动2 idiv到之前dup制作一个字典.

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

附录

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

{currentfile token not {exit} if dup type /arraytype ne {exec} if }loop
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

token从字符串中读取整个过程,用其当前定义的值替换立即计算的名称 //n.请注意,扫描程序一次读取一个可执行数组,在返回之前在内部有效地执行[... ] cvx(在某些解释器中,像我自己一样xpost,这允许你绕过堆栈大小限制来构建数组,因为数组是构建的在单独的内存中.但是2级垃圾收集使得这在很大程度上无关紧要).

还有一个bind操作员通过用操作符对象本身替换操作符名来修改过程.这些技巧可以帮助您在速度关键程序(如内循环)中分解名称查找.