特定类型(数组)的 PostScript 执行
PostScript execution of specific types (Array)
我很难执行数组。
PostScript 语言参考手册第 50 页:
An executable array or executable packed array (procedure) object is
pushed on the operand stack if it is encountered directly by the
interpreter. If it is invoked indirectly as a result of executing some
other object (a name or an operator), it is called instead. The
interpreter calls a procedure by pushing it on the execution stack and
then executing the array elements in turn. When the interpreter
reaches the end of the procedure, it pops the procedure object off the
execution stack. (Actually, it pops the procedure object when there is
one element remaining and then pushes that element; this permits
unlimited depth of “tail recursion” without overflowing the execution
stack.)
绿皮书第 33 页:
The PostScript interpreter executes array objects one element at a
time, leaving the unused part of the array on the execution stack. The
interpreter always checks the remainder of the procedure body before
it executes the first element. If the procedure body is empty, the
interpreter discards it. This permits infinitely deep tail recursion,
since the empty procedure bodies will not accumulate on the execution
stack. (Tail recursion means that the recursive procedure call is the
very last element (the tail) of the procedure body.)
当我阅读两者时,我看到了细微的差别:
Actually, it pops the procedure object when there is one element
remaining and then pushes that element;
对比
The interpreter always checks the remainder of the procedure body
before it executes the first element. If the procedure body is empty,
the interpreter discards it.
PLRM:在执行最后一个元素之前 你从执行堆栈中弹出数组主体并将最后一个元素推入执行(?)堆栈。
绿皮书:PostScript解释器执行数组对象一个元素
一次,将数组中未使用的部分留在执行中
堆栈。解释器 总是 在执行 first 元素之前检查过程主体的其余部分。
对于此代码:
%!PS-Abode-2.0
/F {
findfont exch scalefont setfont
} bind def
24 /Helvetica F
0 0 moveto
(Red Book) show
showpage
当 F 标记被扫描和解释时,您查找过程主体并将其放在执行堆栈中:
{ findfont --exch-- --scalefont-- --setfont--}
-file-
示例 1: 是否像这样工作 (PLRM):
执行堆栈:
{ findfont --exch-- --scalefont-- --setfont--}
-file-
执行 scalefont 后,我们在最后一个元素,因此弹出最后一个元素,弹出过程并将最后一个元素推回:
--setfont--
-file-
示例 2: 还是像这样(绿皮书):
执行堆栈:
{ findfont --exch-- --scalefont-- --setfont--}
-file-
执行堆栈:
{ --exch-- --scalefont-- --setfont--}
-file-
执行堆栈:
{ --scalefont-- --setfont--}
-file-
执行堆栈:
{ --setfont--}
-file-
执行堆栈:(检查余数,如果为空则弹出最后一个元素,弹出数组并压入最后一个元素:
--setfont--
-file-
我不清楚,因为两者的解释不同。绿皮书建议每次修改(收缩)数组体
更新:一些伪代码
示例 1:
array[1, 2, 3, 4]
for (int i = 0; i < array.Length; i++)
{
if (i == array.Length -1)
{
exectionstack.Pop() // pop the array
}
Execute(array[i]); // execute element
}
示例 2:
//array[1, 2, 3, 4]
do {
var obj = executionstack.Pop();
if (obj is ArrayObject) {
var arr = executionstack.Pop();
var item = arr[0]; // first element
if (arr.Length > 1)
{
var newArray = new Array(arr.Length - 1); // create new array size -1
arr.CopyTo(newArray, 1, arr.Length - 1); // copy remaining elements
executionstack.Push(newArray); // push the remaining array body
}
executionstack.Push(item); // push the item
}
else
{
Execute(obj); // not an array, execute it
}
} while (executionstack.Count > 0)
除非您使用的是 1 级红皮书 (PLRM),否则请记住,绿皮书描述的是与 2 级或 3 级 PLRM 不同的解释器化身。
如有疑问,请选择 PLRM。
ISTM,您的伪代码没有抓住问题的关键。恕我直言,... Postscript 的执行就像一种低级语言,就像一种汇编语言。让我使用您的示例过程并详细介绍。
首先,"when the F token is scanned and interpreted"是什么意思?这意味着执行堆栈上有一个文件对象。
op-stack>
exec-stack> --file--
执行循环通过执行以下操作来处理可执行文件:
--file-- exec % push back on exec stack
--file-- token {
xcheck 1 index type /arraytype ne and {
exec
} if
}{
pop
} ifelse
token
returns 可执行文件名 F,因为它是可执行文件而不是数组,所以它被推回可执行堆栈。这使得堆栈像这样:
op-stack>
exec-stack> --file-- F
执行循环通过执行以下操作来处理可执行文件名称:
--name-- load
xcheck {
exec
} if
查找名称。如果该值是可执行的,则执行它。这使得堆栈像这样:
op-stack>
exec-stack> --file-- { findfont --exch-- --scalefont-- --setfont--}
执行循环通过执行以下操作来处理可执行数组:
--array--
dup length 1 gt {
dup 1 1 index 2 sub getinterval exec % push back remainder
} if
dup length 0 gt {
0 get
dup xcheck 1 index type /arraytype ne {
exec
} if
}{
% leave on stack
} ifelse
它使用 getinterval
的等价物将过程剩余部分的子数组推回 exec 堆栈。但是,如果有 1 个或更少的元素,则不要压入余数,但它会浪费堆栈 space 来保存长度为 0 的数组。这使得堆栈像这样:
op-stack>
exec-stack> --file-- { --exch-- --scalefont-- --setfont--} findfont
这种情况由可执行文件名大小写处理。
每个处理程序只做尽可能少的工作,并将事情推送到 exec 堆栈上以供稍后完成。
为了提高效率,可以非常便宜地制造子阵列是非常重要的。不要复制数组,而是设计数组类型,使两个不同的指针可以具有不同的长度和不同的起始偏移量。许多运算符都使用此功能,例如 search
其中 returns 参数字符串的子字符串。对于 Xpost,我将所有对象打包成一个 8 字节的表示形式。数组对象有 4 个 16 位字段:
tag
length
VM-ref
offset
所有对象的第一个字节都有一个 16 位的标记。该标签有一个对象类型的位域。因此,忽略范围检查,getinterval
是这样实现的:
object
getinterval ( object array, uint16_t offset, uint16_t length ){
array.offset = offset;
array.length = length;
return array;
}
超级简单,超级快速。没有分配,没有复制。
HTH
要真正回答这个问题,我认为绿皮书和 PLRM 描述的是相同的东西,但侧重点不同。 "stack-machine" 设计的一个结果是:
- 只有一个循环
所有各种循环运算符只是将内容压入 exec 堆栈,return,每次只处理一小部分工作。这甚至适用于像 image
这样的复杂循环运算符。所以就像上面的数组处理程序一样, forall
之类的东西在实现它的函数中没有 "loop" 。他们只使用一个循环,解释器的执行循环。
例如
/forall { % arr proc
dup length 0 eq { pop pop }{
/forall cvx exec % comp proc
dup 0 get 3 1 roll % comp[0] proc comp
1 1 index length 1 sub getinterval % comp[0] proc comp[1..n-1]
1 index 2 array astore cvx exec % comp[0] proc
exec % comp[0]
} ifelse
} def
这是根据我的 debugger 修改的,它模拟了 postscript 中的内部循环。并重新实现所有循环运算符以使用它。
请注意,在这个版本的 forall
中,它 确实 将最终的 0 长度尾部推送到最后一次迭代。这样 proc 的最终调用仍然可以使用 exit
退出循环,并且 exit
必须仍然在 exec 堆栈上搜索一些东西。
我很难执行数组。
PostScript 语言参考手册第 50 页:
An executable array or executable packed array (procedure) object is pushed on the operand stack if it is encountered directly by the interpreter. If it is invoked indirectly as a result of executing some other object (a name or an operator), it is called instead. The interpreter calls a procedure by pushing it on the execution stack and then executing the array elements in turn. When the interpreter reaches the end of the procedure, it pops the procedure object off the execution stack. (Actually, it pops the procedure object when there is one element remaining and then pushes that element; this permits unlimited depth of “tail recursion” without overflowing the execution stack.)
绿皮书第 33 页:
The PostScript interpreter executes array objects one element at a time, leaving the unused part of the array on the execution stack. The interpreter always checks the remainder of the procedure body before it executes the first element. If the procedure body is empty, the interpreter discards it. This permits infinitely deep tail recursion, since the empty procedure bodies will not accumulate on the execution stack. (Tail recursion means that the recursive procedure call is the very last element (the tail) of the procedure body.)
当我阅读两者时,我看到了细微的差别:
Actually, it pops the procedure object when there is one element remaining and then pushes that element;
对比
The interpreter always checks the remainder of the procedure body before it executes the first element. If the procedure body is empty, the interpreter discards it.
PLRM:在执行最后一个元素之前 你从执行堆栈中弹出数组主体并将最后一个元素推入执行(?)堆栈。
绿皮书:PostScript解释器执行数组对象一个元素 一次,将数组中未使用的部分留在执行中 堆栈。解释器 总是 在执行 first 元素之前检查过程主体的其余部分。
对于此代码:
%!PS-Abode-2.0
/F {
findfont exch scalefont setfont
} bind def
24 /Helvetica F
0 0 moveto
(Red Book) show
showpage
当 F 标记被扫描和解释时,您查找过程主体并将其放在执行堆栈中:
{ findfont --exch-- --scalefont-- --setfont--}
-file-
示例 1: 是否像这样工作 (PLRM):
执行堆栈:
{ findfont --exch-- --scalefont-- --setfont--}
-file-
执行 scalefont 后,我们在最后一个元素,因此弹出最后一个元素,弹出过程并将最后一个元素推回:
--setfont--
-file-
示例 2: 还是像这样(绿皮书):
执行堆栈:
{ findfont --exch-- --scalefont-- --setfont--}
-file-
执行堆栈:
{ --exch-- --scalefont-- --setfont--}
-file-
执行堆栈:
{ --scalefont-- --setfont--}
-file-
执行堆栈:
{ --setfont--}
-file-
执行堆栈:(检查余数,如果为空则弹出最后一个元素,弹出数组并压入最后一个元素:
--setfont--
-file-
我不清楚,因为两者的解释不同。绿皮书建议每次修改(收缩)数组体
更新:一些伪代码
示例 1:
array[1, 2, 3, 4]
for (int i = 0; i < array.Length; i++)
{
if (i == array.Length -1)
{
exectionstack.Pop() // pop the array
}
Execute(array[i]); // execute element
}
示例 2:
//array[1, 2, 3, 4]
do {
var obj = executionstack.Pop();
if (obj is ArrayObject) {
var arr = executionstack.Pop();
var item = arr[0]; // first element
if (arr.Length > 1)
{
var newArray = new Array(arr.Length - 1); // create new array size -1
arr.CopyTo(newArray, 1, arr.Length - 1); // copy remaining elements
executionstack.Push(newArray); // push the remaining array body
}
executionstack.Push(item); // push the item
}
else
{
Execute(obj); // not an array, execute it
}
} while (executionstack.Count > 0)
除非您使用的是 1 级红皮书 (PLRM),否则请记住,绿皮书描述的是与 2 级或 3 级 PLRM 不同的解释器化身。
如有疑问,请选择 PLRM。
ISTM,您的伪代码没有抓住问题的关键。恕我直言,... Postscript 的执行就像一种低级语言,就像一种汇编语言。让我使用您的示例过程并详细介绍。
首先,"when the F token is scanned and interpreted"是什么意思?这意味着执行堆栈上有一个文件对象。
op-stack>
exec-stack> --file--
执行循环通过执行以下操作来处理可执行文件:
--file-- exec % push back on exec stack
--file-- token {
xcheck 1 index type /arraytype ne and {
exec
} if
}{
pop
} ifelse
token
returns 可执行文件名 F,因为它是可执行文件而不是数组,所以它被推回可执行堆栈。这使得堆栈像这样:
op-stack>
exec-stack> --file-- F
执行循环通过执行以下操作来处理可执行文件名称:
--name-- load
xcheck {
exec
} if
查找名称。如果该值是可执行的,则执行它。这使得堆栈像这样:
op-stack>
exec-stack> --file-- { findfont --exch-- --scalefont-- --setfont--}
执行循环通过执行以下操作来处理可执行数组:
--array--
dup length 1 gt {
dup 1 1 index 2 sub getinterval exec % push back remainder
} if
dup length 0 gt {
0 get
dup xcheck 1 index type /arraytype ne {
exec
} if
}{
% leave on stack
} ifelse
它使用 getinterval
的等价物将过程剩余部分的子数组推回 exec 堆栈。但是,如果有 1 个或更少的元素,则不要压入余数,但它会浪费堆栈 space 来保存长度为 0 的数组。这使得堆栈像这样:
op-stack>
exec-stack> --file-- { --exch-- --scalefont-- --setfont--} findfont
这种情况由可执行文件名大小写处理。
每个处理程序只做尽可能少的工作,并将事情推送到 exec 堆栈上以供稍后完成。
为了提高效率,可以非常便宜地制造子阵列是非常重要的。不要复制数组,而是设计数组类型,使两个不同的指针可以具有不同的长度和不同的起始偏移量。许多运算符都使用此功能,例如 search
其中 returns 参数字符串的子字符串。对于 Xpost,我将所有对象打包成一个 8 字节的表示形式。数组对象有 4 个 16 位字段:
tag
length
VM-ref
offset
所有对象的第一个字节都有一个 16 位的标记。该标签有一个对象类型的位域。因此,忽略范围检查,getinterval
是这样实现的:
object
getinterval ( object array, uint16_t offset, uint16_t length ){
array.offset = offset;
array.length = length;
return array;
}
超级简单,超级快速。没有分配,没有复制。
HTH
要真正回答这个问题,我认为绿皮书和 PLRM 描述的是相同的东西,但侧重点不同。 "stack-machine" 设计的一个结果是:
- 只有一个循环
所有各种循环运算符只是将内容压入 exec 堆栈,return,每次只处理一小部分工作。这甚至适用于像 image
这样的复杂循环运算符。所以就像上面的数组处理程序一样, forall
之类的东西在实现它的函数中没有 "loop" 。他们只使用一个循环,解释器的执行循环。
例如
/forall { % arr proc
dup length 0 eq { pop pop }{
/forall cvx exec % comp proc
dup 0 get 3 1 roll % comp[0] proc comp
1 1 index length 1 sub getinterval % comp[0] proc comp[1..n-1]
1 index 2 array astore cvx exec % comp[0] proc
exec % comp[0]
} ifelse
} def
这是根据我的 debugger 修改的,它模拟了 postscript 中的内部循环。并重新实现所有循环运算符以使用它。
请注意,在这个版本的 forall
中,它 确实 将最终的 0 长度尾部推送到最后一次迭代。这样 proc 的最终调用仍然可以使用 exit
退出循环,并且 exit
必须仍然在 exec 堆栈上搜索一些东西。