下图逐步显示了 PostScript 表达式及其堆栈图。是否有任何包可以开箱即用地生成它?如果没有这样的包,如何为任何 PostScript 表达式自动生成它?我肯定不想编写自己的解释器。
欢迎使用任何方法。
答案1
使用 TikZ 只是为了好玩(或者打字练习或者其他什么)。
它还不完整,但实现剩余的 postscript 堆栈命令应该很简单。
\documentclass[border=0.125cm]{standalone}
\usepackage{tikz}
\makeatletter
\newcount\stacktop
\def\stackclear{\global\stacktop=0\relax}
\def\stackpush#1{%
\global\advance\stacktop by1\relax
\expandafter\xdef\csname stack@item@\the\stacktop\endcsname{#1}%
}
\def\stackpeek#1#2{%
\pgfmathparse{int(#1)}%
\edef#2{\csname stack@item@\pgfmathresult\endcsname}%
}
\def\stackpop#1{%
\ifnum\stacktop>0\relax
\edef#1{\csname stack@item@\the\stacktop\endcsname}%
\global\advance\stacktop by-1\relax
\fi%
}
\def\stackswap#1#2{%
\pgfmathparse{int(#1)}\let\a=\pgfmathresult
\pgfmathparse{int(#2)}\let\b=\pgfmathresult
\edef\tmp{\csname stack@item@\a\endcsname}%
\expandafter\xdef\csname stack@item@\a\endcsname{\csname stack@item@\b\endcsname}%
\expandafter\xdef\csname stack@item@\b\endcsname{\tmp}%
}
\def\stackroll{%
\stackpop\R%
\stackpop\r%
\pgfmathparse{int(\R < 0 ? -\R : \R)}\let\RR=\pgfmathresult
\pgfmathloop%
\ifnum\pgfmathcounter>\RR\relax
\else%
\ifnum\R<0\relax%
\stackrollneg%
\else%
\stackrollpos%
\fi%
\repeatpgfmathloop%
}
\def\stackrollpos{%
\begingroup%
\pgfmathloop%
\ifnum\pgfmathcounter=\r
\else%
\stackswap{\stacktop-\pgfmathcounter+1}{\stacktop-\pgfmathcounter}%
\repeatpgfmathloop%
\endgroup%
}
\def\stackrollneg{%
\begingroup%
\pgfmathloop%
\ifnum\pgfmathcounter=\r
\else%
\stackswap{\stacktop-\r+\pgfmathcounter+1}{\stacktop-\r+\pgfmathcounter}%
\repeatpgfmathloop%
\endgroup%
}
\def\at{@}
\def\stackmathparse#1{%
\pgfmathparse{(#1) == int(#1) ? int(#1) : (#1)}%
}
\tikzset{%
postscript parse/.code={%
\stackclear%
\tikzset{ps/parse={#1 @}}%
},
ps/.cd,
parse/.code args={#1 #2}{%
\def\rest{#2}%
\def\current{#1}%
\tikzset{ps/commands/#1={#2}}%
\tikzset{xshift=1cm}
\node at (0,.75) {#1};
\foreach \i [evaluate={\j=int(\stacktop-\i);}] in {0,...,5}
\node [draw, minimum width=.75cm, minimum height=0.5cm] at (0,-\i/2) {\stackpeek{\j}{\x}\x};
\ifx\rest\at%
\else
\tikzset{ps/parse={#2}}%
\fi%
},
commands/.cd,
.unknown/.code={\stackpush{\current}},
exch/.code={\stackswap{\stacktop}{\stacktop-1}},
roll/.code={\stackroll},
dup/.code={\stackpeek{\stacktop}{\x}\stackpush{\x}},
mul/.code={\stackpop{\x}\stackpop{\y}\stackmathparse{\y*\x}\stackpush{\pgfmathresult}},
add/.code={\stackpop{\x}\stackpop{\y}\stackmathparse{\y+\x}\stackpush{\pgfmathresult}},
sub/.code={\stackpop{\x}\stackpop{\y}\stackmathparse{\y-\x}\stackpush{\pgfmathresult}},
div/.code={\stackpop{\x}\stackpop{\y}\stackmathparse{\y/\x}\stackpush{\pgfmathresult}}
}
\begin{document}
\begin{tabular}{l}
\begin{tikzpicture}
\tikzset{postscript parse={4 2 1 exch add 1 sub add}}
\end{tikzpicture}
\\[0.25in]
\begin{tikzpicture}
\tikzset{postscript parse={4 3 2 1 3 1 roll 3 -1 roll}}
\end{tikzpicture}
\\[0.25in]
\begin{tikzpicture}
\tikzset{postscript parse={3 2 dup mul 5 add exch div}}
\end{tikzpicture}
\end{tabular}
\end{document}
答案2
使用 bash 或类似的命令行,执行
s="";for i in 4 2 1 add 1 sub add ; do s="$s $i"; echo " $s pstack" | gs -q -sDEVICE=pbm | sed -e "s/GS>/\\\\foo{$i}{/" -e "s/GS<[0-9]*>/}/" ; done
生产
\foo{4}{4
}\foo{2}{2
4
}\foo{1}{1
2
4
}\foo{add}{3
4
}\foo{1}{1
3
4
}\foo{sub}{2
4
}\foo{add}{6
}
然后用乳胶包裹它:
\documentclass{article}
\def\foo#1#2{%
\parbox[t]{3em}{\centering
\textbf{#1}%
\xfoo#2\$ }}
\def\xfoo#1 {\ifx\$#1\else\\#1 \expandafter\xfoo\fi}
\begin{document}
% s="";for i in 4 2 1 add 1 sub add ; do s="$s $i"; echo " $s pstack" | gs -q -sDEVICE=pbm | sed -e "s/GS>/\\\\foo{$i}{/" -e "s/GS<[0-9]*>/}/" ; done
\foo{4}{4
}\foo{2}{2
4
}\foo{1}{1
2
4
}\foo{add}{3
4
}\foo{1}{1
3
4
}\foo{sub}{2
4
}\foo{add}{6
}
\end{document}
你得到
答案3
为了渲染这样的堆栈,我提供了下面的解决方案。但正如杰克指出的那样,它没有消化生的后记表达式。但是,请参阅本答案的结尾,了解我如何消化 David 的 bash 输出。
这里我使用了一堆嵌套堆栈。\psdepth
(堆栈管深度)、\pswidth
(堆栈管宽度)和\psrule
(堆栈规则厚度)都可以调整。
重新编辑以使输入语法更简单。堆栈深度没有限制,就像之前的编辑一样。
\documentclass{article}
\usepackage[usestackEOL]{stackengine}
\usepackage{readarray}
\usepackage{ifthen}
\def\psdepth{2cm}
\def\pswidth{.5cm}
\def\psrule{.15ex}
\def\psbar{\rule[-\psdepth]{\psrule}{\psdepth}}
\def\pspipe{\psbar\kern\pswidth\psbar}
\def\pscell#1{\stackunder{\footnotesize#1}{\rule{\pswidth}{\psrule}}}
\newcounter{index}
\makeatletter
\newcommand\psstack[2]{%
\getargsC{#2}%
\setcounter{index}{0}%
\def\psstackdata{}%
\whiledo{\theindex<\narg}{%
\stepcounter{index}%
\protected@edef\thisarg{\csname arg\romannumeral\theindex\endcsname}%
\protected@edef\psstackdata{%
\psstackdata\protect\pscell{\thisarg}}%
\ifthenelse{\theindex<\narg}{\protected@edef\psstackdata{%
\psstackdata \\}}{}%
}%
\sffamily\def\stacktype{L}%
\stackunder{%
\def\stacktype{S}\stackunder{#1}{\pspipe}%
}{%
\def\stacktype{S}\setstackgap{S}{3pt}\expandafter%
\Shortunderstack\expandafter{\psstackdata}%
}%
\hspace{1ex}%
}
\makeatother
\begin{document}
\psstack{4}{4}
\psstack{2}{2 4}
\psstack{1}{1 2 4}
\psstack{exch}{2 1 4}
\psstack{add}{3 4}
\psstack{1}{1 3 4}
\psstack{sub}{2 4}
\psstack{add}{6}
\end{document}
定义\foo
为
\newcommand\foo[2]{%
\getargsC{#2}%
\setcounter{index}{0}%
\def\psstackdata{}%
\whiledo{\theindex<\numexpr\narg-1}{%
\stepcounter{index}%
\protected@edef\thisarg{\csname arg\romannumeral\theindex\endcsname}%
\protected@edef\psstackdata{%
\psstackdata\protect\pscell{\thisarg}}%
\ifthenelse{\theindex<\narg}{\protected@edef\psstackdata{%
\psstackdata \\}}{}%
}%
\sffamily\def\stacktype{L}%
\stackunder{%
\def\stacktype{S}\stackunder{#1}{\pspipe}%
}{%
\def\stacktype{S}\setstackgap{S}{3pt}\expandafter%
\Shortunderstack\expandafter{\psstackdata}%
}%
\hspace{1.85ex}%
}
在我的语法中,允许人们消化 David 的 bash 输出并将其与我的渲染一起呈现。
答案4
至于生成堆栈图片的内容,我受到这个问题的启发,清理了一些旧代码,现在我们可以为任意后记生成漂亮的痕迹。
该计划已发布至github(以及一些早期的草稿计算机语言后记),因为剪切粘贴有点长。它也是一个分步调试器,接受可执行文件名称、字符串、数组或文件。除和之外,代码大部分都是 1 级后记,<<
可以>>
向下移植以在 1 级解释器上运行,但它非常浪费内存,并且在没有垃圾收集器的内存受限环境中可能不实用。
对于这些代码片段:
1 2 3 add add
6 eq { 8 9 add = } { 9 10 add = } ifelse
和:
1 2 3 add add
6 eq { 7 } if
9 {
5
count 4 gt {
exit
} if
} repeat
{ 55 exit } loop
(continuation) =
0 1 10 {
dup 5 eq {exit} if
} for
我得到以下堆栈跟踪输出:
GPL Ghostscript 9.06 (2012-08-08)
Copyright (C) 2012 Artifex Software, Inc. All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
%|-
1 %|- 1
2 %|- 1 2
3 %|- 1 2 3
add %|- 1 5
add %|- 6
6 %|- 6 6
eq %|- true
{8 9 add =} %|- true {8 9 add =}
{9 10 add =} %|- true {8 9 add =} {9 10 add =}
ifelse %|-
8 %|- 8
9 %|- 8 9
add %|- 17
= 17
%|-
1 %|- 1
2 %|- 1 2
3 %|- 1 2 3
add %|- 1 5
add %|- 6
6 %|- 6 6
eq %|- true
{7} %|- true {7}
if %|-
7 %|- 7
9 %|- 7 9
{5 count 4 gt {exit} if} %|- 7 9 {5 count 4 gt {exit} if}
repeat %|- 7
5 %|- 7 5
count %|- 7 5 2
4 %|- 7 5 2 4
gt %|- 7 5 false
{exit} %|- 7 5 false {exit}
if %|- 7 5
8 %|- 7 5 8
{5 count 4 gt {exit} if} %|- 7 5 8 {5 count 4 gt {exit} if}
repeat %|- 7 5
5 %|- 7 5 5
count %|- 7 5 5 3
4 %|- 7 5 5 3 4
gt %|- 7 5 5 false
{exit} %|- 7 5 5 false {exit}
if %|- 7 5 5
7 %|- 7 5 5 7
{5 count 4 gt {exit} if} %|- 7 5 5 7 {5 count 4 gt {exit} if}
repeat %|- 7 5 5
5 %|- 7 5 5 5
count %|- 7 5 5 5 4
4 %|- 7 5 5 5 4 4
gt %|- 7 5 5 5 false
{exit} %|- 7 5 5 5 false {exit}
if %|- 7 5 5 5
6 %|- 7 5 5 5 6
{5 count 4 gt {exit} if} %|- 7 5 5 5 6 {5 count 4 gt {exit} if}
repeat %|- 7 5 5 5
5 %|- 7 5 5 5 5
count %|- 7 5 5 5 5 5
4 %|- 7 5 5 5 5 5 4
gt %|- 7 5 5 5 5 true
{exit} %|- 7 5 5 5 5 true {exit}
if %|- 7 5 5 5 5
exit %|- 7 5 5 5 5
{55 exit} %|- 7 5 5 5 5 {55 exit}
loop %|- 7 5 5 5 5
55 %|- 7 5 5 5 5 55
exit %|- 7 5 5 5 5 55
(continuation) %|- 7 5 5 5 5 55 (continuation)
= continuation
%|- 7 5 5 5 5 55
0 %|- 7 5 5 5 5 55 0
1 %|- 7 5 5 5 5 55 0 1
10 %|- 7 5 5 5 5 55 0 1 10
{dup 5 eq {exit} if} %|- 7 5 5 5 5 55 0 1 10 {dup 5 eq {exit} if}
for %|- 7 5 5 5 5 55 0
dup %|- 7 5 5 5 5 55 0 0
5 %|- 7 5 5 5 5 55 0 0 5
eq %|- 7 5 5 5 5 55 0 false
{exit} %|- 7 5 5 5 5 55 0 false {exit}
if %|- 7 5 5 5 5 55 0
1 %|- 7 5 5 5 5 55 0 1
1 %|- 7 5 5 5 5 55 0 1 1
10 %|- 7 5 5 5 5 55 0 1 1 10
{dup 5 eq {exit} if} %|- 7 5 5 5 5 55 0 1 1 10 {dup 5 eq {exit} if}
for %|- 7 5 5 5 5 55 0 1
dup %|- 7 5 5 5 5 55 0 1 1
5 %|- 7 5 5 5 5 55 0 1 1 5
eq %|- 7 5 5 5 5 55 0 1 false
{exit} %|- 7 5 5 5 5 55 0 1 false {exit}
if %|- 7 5 5 5 5 55 0 1
2 %|- 7 5 5 5 5 55 0 1 2
1 %|- 7 5 5 5 5 55 0 1 2 1
10 %|- 7 5 5 5 5 55 0 1 2 1 10
{dup 5 eq {exit} if} %|- 7 5 5 5 5 55 0 1 2 1 10 {dup 5 eq {exit} if}
for %|- 7 5 5 5 5 55 0 1 2
dup %|- 7 5 5 5 5 55 0 1 2 2
5 %|- 7 5 5 5 5 55 0 1 2 2 5
eq %|- 7 5 5 5 5 55 0 1 2 false
{exit} %|- 7 5 5 5 5 55 0 1 2 false {exit}
if %|- 7 5 5 5 5 55 0 1 2
3 %|- 7 5 5 5 5 55 0 1 2 3
1 %|- 7 5 5 5 5 55 0 1 2 3 1
10 %|- 7 5 5 5 5 55 0 1 2 3 1 10
{dup 5 eq {exit} if} %|- 7 5 5 5 5 55 0 1 2 3 1 10 {dup 5 eq {exit} if}
for %|- 7 5 5 5 5 55 0 1 2 3
dup %|- 7 5 5 5 5 55 0 1 2 3 3
5 %|- 7 5 5 5 5 55 0 1 2 3 3 5
eq %|- 7 5 5 5 5 55 0 1 2 3 false
{exit} %|- 7 5 5 5 5 55 0 1 2 3 false {exit}
if %|- 7 5 5 5 5 55 0 1 2 3
4 %|- 7 5 5 5 5 55 0 1 2 3 4
1 %|- 7 5 5 5 5 55 0 1 2 3 4 1
10 %|- 7 5 5 5 5 55 0 1 2 3 4 1 10
{dup 5 eq {exit} if} %|- 7 5 5 5 5 55 0 1 2 3 4 1 10 {dup 5 eq {exit} if}
for %|- 7 5 5 5 5 55 0 1 2 3 4
dup %|- 7 5 5 5 5 55 0 1 2 3 4 4
5 %|- 7 5 5 5 5 55 0 1 2 3 4 4 5
eq %|- 7 5 5 5 5 55 0 1 2 3 4 false
{exit} %|- 7 5 5 5 5 55 0 1 2 3 4 false {exit}
if %|- 7 5 5 5 5 55 0 1 2 3 4
5 %|- 7 5 5 5 5 55 0 1 2 3 4 5
1 %|- 7 5 5 5 5 55 0 1 2 3 4 5 1
10 %|- 7 5 5 5 5 55 0 1 2 3 4 5 1 10
{dup 5 eq {exit} if} %|- 7 5 5 5 5 55 0 1 2 3 4 5 1 10 {dup 5 eq {exit} if}
for %|- 7 5 5 5 5 55 0 1 2 3 4 5
dup %|- 7 5 5 5 5 55 0 1 2 3 4 5 5
5 %|- 7 5 5 5 5 55 0 1 2 3 4 5 5 5
eq %|- 7 5 5 5 5 55 0 1 2 3 4 5 true
{exit} %|- 7 5 5 5 5 55 0 1 2 3 4 5 true {exit}
if %|- 7 5 5 5 5 55 0 1 2 3 4 5
exit GS<12>