PostScript之二-操作数栈,栈操作符和数学运算符

引言:

这是第二篇关于 PostScript 的系列文章。本文的主要目的是论述堆栈的操作。操作数栈可能是 PostScript 中最主要的部分。赋值、算术或数学运算、循环和逻辑运算都在这块特殊的存储区内进行。是的!堆栈是一块特殊的存储区,被 PostScript 用来执行几乎所有我们要求它完成的操作。堆栈以后进先出的规则保持信息。你可以把它形象化成一根一端封闭的导管。当你向它里面放入某物时,它会将管内所有 的东西都推向封闭的一端以释放出空间。因此,最后进入的元素总是离开口的那一端最近。堆栈内的元素可以是字符串、数字常量、键码、数据块……   

栈操作符

虽然堆栈内的元素已经排列有序,但同时还存在着一些允许我们重新对这些元素进行排列的栈操作符。这些操作应用于堆栈内的一个或多个元素。操作符按其定义操纵堆栈内的元素。它们是否需要参数(在 PostScript 的术语中被称为操作数)依所进行的操作而定。若需要参数,则它们首先必须被推入堆栈中。之后,操作符依据这些参数采取相应的动作。这里我们给出了这些操作符的一个列表,并有详细的说明。我们还在后面给出了一些例子以阐明更多的细节。 

pop: 此操作符删除操作数栈顶(最后进入)的元素。

exch: 此操作符对换操作数栈顶的两个元素。

dup: 此操作符复制最后进入操作数栈的元素并将其推入操作数栈,即复制栈顶元素。 

copy: 此操作符需要一个在执行前被推入操作数栈的整型操作数(即参数)。设此整型参数为n,则给出命令 n copy 。此操作完成后,位于栈顶的n个元素的拷贝作为一批最后进入的元素而被置入操作数栈中。换言之,copy 是一个批复制操作符。 

index: 此操作符需要一个在执行前被推入操作数栈的整型操作数。设此整型参数为n,则给出命令 n index 。操作完成后,栈顶第n个元素的一个拷贝作为最后进入的元素被置入操作数栈。换言之,index 操作符能选中操作数栈的一个内部元素,生成它的拷贝,并置入操作数栈内。元素的索引以栈顶元素为零开始。 

roll: 此操作符需要两个在执行前被推入操作数栈的整型参数。设它们分别为m和n,则给出命令 m n index 。这里m指明加入滚转操作(roll)的元素数,n指明滚转操作的次数。一次滚转操作定义为:操作数栈顶元素变为栈内的第m个元素,而原栈顶以下的m-1个元素依次向栈顶移动一个位置。此过程只在n为1时成立。当n为2时,将进行连续的两次滚转操作。换言之,m 2 roll 等效于 m 1 roll m 1 roll 。参数n可以为负数。这时发生的操作过程与n为正数时相反。这意味着命令 m n roll m -n roll 将不会产生任何效果,即没有改变操作数栈。元素的索引以栈顶元素为零开始。

clear: 此操作符删除操作数栈内的所有元素。 

count: 此操作符计算操作数栈内的元素个数。计算结果作为一个新元素而被推入操作数栈内。如果不想要此新元素,你可以给出组合命令 count pstack pop 。这里count操作的结果在被文件操作符pstack执行时显示后,pop操作再从操作数栈中删除此新元素。  

mark: 此操作符在操作数栈内放入一个标记符(-marktype-)。这个元素可以将操作数栈内的元素划分成子集。另外两个操作符 cleartomarkcounttomark 在执行时需要在栈内搜寻此元素。若找不到,则系统给出错误信息。 

cleartomark: 此操作符删除操作数栈内从栈顶开始到第一个标记符 -marktype- 之间的所有元素,包括 -marktype- 本身。若栈内没有 -marktype- 元素,则系统给出错误信息。 

counttomark: 此操作符从操作数栈内的栈顶元素开始计数,直到遇到 -marktype- 元素为止。计数结果是一整型值,作为最后的元素被推入操作数栈。所遇到的 -marktype- 元素不加入计数值。若栈内没有 -marktype- 元素,则 PostScript 产生错误信息,不做任何操作。 

现在让我们讨论一下堆栈。如果你想看到以上操作符的执行过程,必须首先启动 PostScript 解释器。正如在第一篇文章中所提到的,LINUX 世界中使用一种公共解释器软件-- ghostscript 。在命令行中给出适当的参数,gostsript 能以不同的方式启动。通常的做法只要在 X窗口环境下键入 gs 。由于X窗口的设置问题,有时会启动不了。这时会出现一个关于无法创建简便图形控制窗口的错误信息。如果不能解决此问题,就要迫使 gostscript 使用x11驱动设备。为此,你必须键入 gs -sDEVICE=x11 。不管是带参数的还是不带参数的简单命令 gs(如果能启动的话),都会创建一个在会话期间作于显示的具有白色背景的空白窗口。由于显示方面与本文无关,我们不需要此显示窗口,可以把它去掉。为此目的,我们通过在命令行 gsgs -sDEVICE=x11 后加入参数 -dNODISPLAY 使 gostscript 解释器启动后不带显示窗口。如果这样做了,则在显示完一段版权声明之后,便会在新一行的开始出现 gostscript 的提示符GS>。这时,gostscript 可以处理用户键入的命令了。此时操作数栈是空的。 

为查看操作数栈的内容,你可以使用文件操作符-- pstack 。它之所以被称为文件操作符,是因为它将操作数栈的信息送往确省是屏幕的标准输出文件。如果在提示符后键入此命令,但没有显示任何东西,而在新一行的开始出现了提示符GS>,这正好说明操作数栈是空的。

为元素送往操作数栈,你可以在提示符后键入此元素。例如,你想送入元素1,则在提示符后键入1就可以了。之后,下一行的开始会出现新的提示符。但此时,提示符不是GS>,而是GS<1>。这种新的提示符表示出了操作数栈内元素的个数。因此,如果在你的 gostscript 会话期间出现了提示符 GS<123> ,则意味着在操作数栈内有123个元素。 

你可以在一次操作内往操作数栈输入多个元素。为此,你必须连续地键入所有的元素,并用空格分开。例如,如果你在提示符后键入1 2 3 4 5 6 ,则元素1,2,3,4,5,6都将被分别推入操作数栈。如果在这之后执行pstack操作,元素会以垂直顺序显示,并且最后进入的元素最先显示。以上两 个命令行的对话过程显示如下:

GS>1 2 3 4 5 6 
GS<6>pstack
6
5
4
3
2
1
GS<6>
在一次单步操作内实现元素的输入并显示堆栈内容也是可能的。所有需要做的只是在将被输入的元素后面加上pstack命令,即:
GS>1 2 3 4 5 6 pstack
6
5
4
3
2
1
GS<6>
到现在为止,我们输入的元素都是数字。数字不是所唯一允许的元素类型,可以输入其它类型的元素如变量或键码、字符串、数据块等。我们会在后面详细论及。然而,现在我们必须说明:如果你想输入如单个字符a或字符串abc时,会出现错误信息。这是因为 PostScript 无法理解这些东西。如果你想输入字符或字符串,必须将它们用()包围起来。这里我们提及一个被称为 marktype 的特殊类型的元素。为此我们给出下面的一个对话例子:
GS>1 2 3 mark 4 5 6 pstack
6
5
4
-marktype-
3
2
1
GS<7>
现在我们来看一些用于操纵操作数栈的操作符。我将给出一个对话例子来说明这些操作符是如何作用的,并结束这一小节,而不准备作更进一步的解释说明。
GS>1 2 3 mark 4 5 6 pstack
6
5
4
-marktype-
3
2
1
GS<7>pop pstack
5
4
-marktype

2
1
GS<6>exch pstack
4
5
-marktype
3
2
1
GS<6>dup pstack
4
4
5
-marktype-
3
2
1
GS<7>2 copy pstack
4
4
4
4
5
-marktype
3
2
1
GS<9>5 index pstack
-marktype-
4
4
4
4
5
-marktype
3
2
1
GS<10>cleartomark cleartomark pstack
3
2
1
GS<3>3 1 roll pstack
2
1
3
GS<3>count pstack
3
2
1
3
GS<4>mark 7 8 pstack
8
7
-marktype-
3
2
1
3
GS<7>counttomark pstack
2
8
7
-marktype-
3
2
1
3
GS<8>clear pstack
GS>
 

数学运算符

除了用于操纵 PostScript 操作数栈的操作符外,还有一些算术和数学运算符。下面我们给出这些运算符,但是没有给出例子。有了以上的对话例子,现在读者应该可以自己将这些例子举出来。  

add: 此命令需要两个参与加法运算的数值型参数。设这两个值为m和n,则命令表示为 m n add 。执行时,m和n先后进入操作数栈,最后一步是将操作数栈顶的两个元素相加。该操作产生一个值为m和n之和的新元素。操作结束后,m和n没有被保留在操作数栈中,相加的结果成了操作数栈的栈顶元素。

div: 此命令需要两个参与除法运算的数值型参数。设这两个值为m和n,则命令表示为 m n div 。其执行机制与add相同。除法操作在浮点数的算术基础上进行。操作结束后,结果作为新元素留在操作数栈内,不保留m和n。

idiv: 此命令需要两个参与整数除法运算的数值型参数。设这两个值为m和n,则命令表示为 m n idiv 。除了该除法操作的算术运算基础之外,其它执行机制都与 div 相同。这是一种整数算术除法运算。如果参数不是整数,它也会执行下去。

mod: 此命令需要两个整型参数。它计算出第一个参数除以第二个参数后所得的余数。若参数中有一个不是整数,则执行失败。执行结束后,所得的结果作为新元素留在操作数栈内。

mul: 同于 add ,div 。它是一个需要两个数值型参数的二进制操作符。所得结果是参数的积,并且作为一个新元素被保留在操作数栈中。

sub: 同于 add ,div ,mul 。唯一不同之处在于操作类型。它从第一个参数中减去第二个参数的值。参数和结果都是数值型的。执行完后,结果被保留在操作数栈内。 

exp: 这是一个二元数学运算符,需要两个参数。第一个是基数,第二个是指数。它执行指数运算。参数的值必须在幂函数运算所允许的范围内。所得结果是一浮点数,作为新元素被保留在操作数栈内。 

atan: 这是另一个用于求角度值的二元数学运算符。所得角度值在0度和360度之间。它需要两个参数。第一个参数和第二 个参数之比代表所求角度的正切值。任何一个参数都可以为零,但不能都为零。参数的符号决定了所得结果所在象限。第一个参数值为正代表正y轴象限,相应地, 第二个参数为正代表正x轴象限。 

abs: 这是一个一元数学运算符,只需要一个参数,所得结果是它的绝对值。同上,此结果作为一个新元素被保留在操作数栈中。

neg: 改变它仅有的参数的符号。这是一个一元算术运算符。 

ceiling: 这是一个一元运算符,对参数向上取整。

floor: 这是一个一元运算符,对参数向下取整。

round: 这是一个一元运算符,取离参数最近的整数。

truncate: 这是一个一元运算符,去掉参数的小数部分。 

sqrt: 这是一个一元运算符,计算参数的平方根。

cos: 这是一个一元运算符,计算参数的余弦值。参数以角度值的形式给出。

sin: 这是一个一元运算符,计算参数的正弦值。参数以角度值的形式给出。

ln: 这是一个一元运算符,计算参数的自然对数值。

log: 这是一个一元运算符,计算参数的以10为底的对数值。 

在结束本文之前,有几点需要指出。虽然我们在以上已有所提及,可能是间接的,即命令的参数(在 PostScript 的术语中被称为操作数)可能会产生一些令人麻烦的问题。命令(在 PostScript 的术语中被称为操作符)在操作数栈内寻求它的参数。如果找到,它们被命令所用,并且被从操作数栈中删除。因此,有意或无意地给出一个在操作数栈中不存在它 所需要的参数值的命令,轻则产生关于栈顶元素类型与所需参数类型不匹配的错误信息,重则会删掉栈顶的一些元素。用户必须注意到这一点。 

在结束之前,我们建议想要使用 PostScript 的用户去写一些更加复杂和易懂的程序。在本系列以后的篇章中,将介绍更多的关于 PostScript 语言的细节。我们乐于接受关于本文的任何问题和建议。

<think>嗯,用户想了解如何在PostScript中进行单步调试。我对PostScript的了解不算很深,但知道它主要是一种页面描述语言,常用于打印图形处理。不过,用户的问题是关于调试的,特别是单步调试。我需要先确认PostScript是否有内置的调试工具或方法。 首先,PostScript作为一门编程语言,通常是在解释器中运行的,比如Ghostscript。所以可能需要依赖解释器提供的调试功能。Ghostscript是一个常用的开源解释器,我应该检查它是否支持单步调试。 接下来,用户可能需要具体的步骤,比如如何启动调试模式,如何设置断点,如何逐行执行代码,查看变量状态等。但PostScript本身可能没有像现代IDE那样的调试工具,所以可能需要通过命令行参数或者特定的命令来启用调试功能。 另外,考虑到PostScript的堆语言特性,调试时可能需要观察操作数的变化。这时候,插入打印语句(比如==或=)可能是一个常见的方法,用户可以在代码中添加这些来输出变量的值,帮助定位问题。 然后,我需要思考是否有其他工具或方法,比如使用专门的调试器,或者通过日志记录。可能Ghostscript的-dDEBUG参数可以启用调试输出,但不确定这是否适用于单步执行。 还需要注意,用户可能没有使用Ghostscript,而是其他PostScript解释器,所以可能需要提到不同解释器的差异,但大多数情况下Ghostscript是主流选择。 另外,单步调试可能涉及交互式执行,比如逐行解释代码。Ghostscript是否支持交互模式?比如在命令行中运行gs -dNOPAUSE -dBATCH,或者有其他参数允许逐步执行。 总结一下,可能的步骤是:1. 使用Ghostscript的调试参数启动;2. 在代码中添加打印语句;3. 使用交互式模式逐步执行;4. 结合堆查看命令。需要将这些步骤详细说明,并给出具体示例,比如如何插入==,如何使用Ghostscript的命令行选项。 还要注意用户可能不熟悉Ghostscript的调试命令,所以需要具体说明如何操作,比如在代码中插入调试语句的位置,或者如何在交互模式下输入指令。 最后,确保回答结构清晰,分步骤说明,并强调PostScript调试的局限性,可能需要结合多种方法。同时,提醒用户注意语法错误状态,因为堆的错误积累可能导致后续问题。</think>PostScript 的单步调试需要结合解释器工具代码调试技巧,以下是分步说明: 1. **选择支持调试的解释器** - 推荐使用 Ghostscript(开源 PostScript 解释器) - 启动调试模式命令: ```shell gs -dDEBUG ``` 2. **插入堆打印语句** - 在代码关键位置插入 `pstack` 命令: ```postscript /x 5 def x == % 打印变量值 (当前坐标: ) print currentpoint == % 打印坐标 ``` 3. **分阶段执行代码** - 使用 `-dNOPAUSE -dBATCH` 参数分页执行: ```shell gs -dNOPAUSE -dBATCH file.ps ``` 4. **交互式调试方法** - 启动交互模式: ```shell gs -dNODISPLAY ``` - 逐行输入代码并观察堆变化: ```postscript GS> 3 4 add % 输入后立即执行并显示堆 ``` 5. **断点模拟技巧** - 插入条件终止语句: ```postscript currentfile 100 string readline (debug) eq { stopped } if ``` 6. **图形状态调试** - 输出当前图形状态: ```postscript currentfont == % 显示字体信息 matrix currentmatrix == % 显示变换矩阵 ``` **调试注意事项**: -溢出需及时用 `clear` 清理 - 路径绘制建议使用 `stroke` 后接 `initclip` 重置 - 使用 `where` 命令追踪运算符定义位置 通过以上方法结合使用,可有效定位坐标计算错误、字体加载失败等典型问题。由于 PostScript 的堆特性,建议每次调试后记录堆状态,格式示例: $$ \text{操作前堆} \rightarrow \text{操作符} \rightarrow \text{操作后堆} $$
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值