[Piet]ASCIIコード表
Pietでプログラミングしていると文字出力のためにASCIIコード表が欲しくなります。表を作ること自体は難しくありません。でもめんどくさい。
そういう面倒なことはプログラムにお任せするのが良いと思います。というわけでPietで書いてみました(なぜ)
概要
調べてみるとASCIIコードの印字可能文字は、16進数で 0x20-0x7Fの範囲にあるそうで、とりあえずこの 範囲の表ができれば問題ありません。
出力されるASCIIコード表は次のようなものにします。
|' ' (32)|'!' (33)|'"' (34)|'#' (35)|'$' (36)|'%' (37)|'&' (38)|''' (39)|'(' (40)|')' (41)|'*' (42)|'+' (43)|',' (44)|'-' (45)|'.' (46)|'/' (47)| |'0' (48)|'1' (49)|'2' (50)|'3' (51)|'4' (52)|'5' (53)|'6' (54)|'7' (55)|'8' (56)|'9' (57)|':' (58)|';' (59)|'<' (60)|'=' (61)|'>' (62)|'?' (63)| |'@' (64)|'A' (65)|'B' (66)|'C' (67)|'D' (68)|'E' (69)|'F' (70)|'G' (71)|'H' (72)|'I' (73)|'J' (74)|'K' (75)|'L' (76)|'M' (77)|'N' (78)|'O' (79)| |'P' (80)|'Q' (81)|'R' (82)|'S' (83)|'T' (84)|'U' (85)|'V' (86)|'W' (87)|'X' (88)|'Y' (89)|'Z' (90)|'[' (91)|'\' (92)|']' (93)|'^' (94)|'_' (95)| |'`' (96)|'a' (97)|'b' (98)|'c' (99)|'d'(100)|'e'(101)|'f'(102)|'g'(103)|'h'(104)|'i'(105)|'j'(106)|'k'(107)|'l'(108)|'m'(109)|'n'(110)|'o'(111)| |'p'(112)|'q'(113)|'r'(114)|'s'(115)|'t'(116)|'u'(117)|'v'(118)|'w'(119)|'x'(120)|'y'(121)|'z'(122)|'{'(123)|'|'(124)|'}'(125)|'~'(126)|''(127)|
文字をシングルクォーテーション'
でくくり、カッコでASCIIコードの数値を10進数で示します。
表全体は垂直バー|
で区切って表現します。Pietでシンプルに表を出力するならこんなもんでしょう。
1行は16文字とします。 1行めが文字コード0x20-0x2Fに対応、2行めが文字コード0x30-0x3Fに対応、・・・とします。
プログラムのフロー
- 表の縦方向のループカウンタ
y
を用意する - 縦方向ループ開始
- 表の横方向のループカウンタ
x
を用意する - 横方向ループ開始
- 表示する文字コードを計算(
x+16*y
) - 垂直バーを表示(
|
) - 文字を表示(
'文字'
) - 文字コードを表示(
(文字コード)
) x
を更新(x<-x+1
)x
<16なら「横方向ループ開始」に戻る- 垂直バーと改行文字を出力する(次の行へ)
y
を更新(y<-y+1
)y
<8なら「縦方向ループ開始」に戻る- 終了
プログラム
まずは作ったプログラムと自動生成したPietソースコードを示します。プログラムは長いので、見たい方は下の文字をクリックして開いてください。
クリックしてプログラムをみる
push2 :loopY push1 dup sub :loopX dup push3 push2 roll dup push4 push1 roll push2 dup mul dup mul mul add push1 goto:out_bar :out_bar_return1 pop goto:out_char :out_char_return goto:out_num :out_num_return pop push1 add dup push3 push5 mul gt if::loopX push2 goto:out_bar :out_bar_return2 pop goto:out_NL :out_NL_return push2 push1 roll push1 add dup push3 push2 dup add add gt if:end: push2 push1 roll pop goto:loopY :out_char dup push2 dup dup dup dup push1 add add mul mul mul push1 sub dup push3 push1 roll outc outc outc goto:out_char_return :out_bar push2 dup dup dup mul mul dup mul mul push2 dup mul sub outc :out_bar_return dup push1 sub if::out_bar_return1 dup push2 sub if::out_bar_return2 end :out_num dup push2 dup dup push1 add add mul dup mul push1 sub gt if:out_num_out: push1 goto:out_space :out_space_return1 pop :out_num_out dup push2 dup dup mul mul push2 push3 add mul dup push1 add push3 push1 roll outc outn outc goto:out_num_return :out_space push2 dup dup mul dup mul mul outc :out_space_return dup push1 sub if::out_space_return1 end :out_NL push2 push5 mul outc goto:out_NL_return :end end
これをnpietで実行すると次のようになります
> ./npiet -cs 6 asciix6.png |' ' (32)|'!' (33)|'"' (34)|'#' (35)|'$' (36)|'%' (37)|'&' (38)|''' (39)|'(' (40)|')' (41)|'*' (42)|'+' (43)|',' (44)|'-' (45)|'.' (46)|'/' (47)| |'0' (48)|'1' (49)|'2' (50)|'3' (51)|'4' (52)|'5' (53)|'6' (54)|'7' (55)|'8' (56)|'9' (57)|':' (58)|';' (59)|'<' (60)|'=' (61)|'>' (62)|'?' (63)| |'@' (64)|'A' (65)|'B' (66)|'C' (67)|'D' (68)|'E' (69)|'F' (70)|'G' (71)|'H' (72)|'I' (73)|'J' (74)|'K' (75)|'L' (76)|'M' (77)|'N' (78)|'O' (79)| |'P' (80)|'Q' (81)|'R' (82)|'S' (83)|'T' (84)|'U' (85)|'V' (86)|'W' (87)|'X' (88)|'Y' (89)|'Z' (90)|'[' (91)|'\' (92)|']' (93)|'^' (94)|'_' (95)| |'`' (96)|'a' (97)|'b' (98)|'c' (99)|'d'(100)|'e'(101)|'f'(102)|'g'(103)|'h'(104)|'i'(105)|'j'(106)|'k'(107)|'l'(108)|'m'(109)|'n'(110)|'o'(111)| |'p'(112)|'q'(113)|'r'(114)|'s'(115)|'t'(116)|'u'(117)|'v'(118)|'w'(119)|'x'(120)|'y'(121)|'z'(122)|'{'(123)|'|'(124)|'}'(125)|'~'(126)|''(127)|
最後のASCIIコード127は実は印字できない文字なので、
ここだけ垂直バー|
がずれているのはご愛嬌です。
プログラムの構造解説
さて、プログラムにはコロン「:」で始まる行がありますが、 それが概ねプログラムの処理のひとかたまりを表します。
それらがどのような順で実行されるかを示したものが下図になります。
詳しく解説します。
プログラム開始地点
まず、プログラムの開始地点は「:1」というブロックです。 そこから表の縦方向(行)のループの開始地点「:loopY」と、横方向(列)のループの開始地点「:loopX」を通ります。
この間、スタックにはループカウンタの役割を果たす値がプッシュされます。
文字コード計算と出力
次に、「表示する文字コードを計算」から「文字コードを表示」までの処理です。次の部分にあたります。
例えば、文字コードが100(英小文字d
)のときは、この処理で次の文字列が出力されます。
|'d'(100)
各ブロックの処理は次のとおりです。
ブロック名 | 処理説明 |
---|---|
:loopX | 文字コードの計算(X+16*Y ) |
:out_bar | 垂直バー| の出力 |
:out_bar_return | 次の処理の決定 |
:out_bar_return1 | (次の処理) |
:out_char | 文字出力(例:'d' ) |
:out_char_return | (次の処理) |
:out_num, :out_num_out | 文字コードの出力(例:(100) ) |
改行判定〜改行しない場合
次に、「:out_num_return」というブロックで改行判定をします。
まだ1行ぶんの出力が終わっていないなら改行不要と判定されます。この場合は、横方向のループカウンタx
に1を足して、「:loopX」に戻り、
次の文字の出力を続けます。
このループで表の1行が出来上がっていきます。 ただし、右端に垂直バーが出ないことに注意です。
|'`' (96) |'`' (96)|'a' (97) |'`' (96)|'a' (97)|'b' (98) |'`' (96)|'a' (97)|'b' (98)|'c' (99) |'`' (96)|'a' (97)|'b' (98)|'c' (99)|'d'(100) |'`' (96)|'a' (97)|'b' (98)|'c' (99)|'d'(100)|'e'(101) ...
改行判定〜改行する場合
1行ぶんの表示が終わった場合は、改行します。
ただし、改行の前に表の右端を閉じるため垂直バー|
を出力します。
垂直バーを出力する処理は「:out_bar」ブロックで一度書いたので、使い回すことにします。
その後、ループカウンタを更新(y
に1を足し、x
を0にする)して、横方向のループを再開します。
改行処理は次の部分で行なっています。
ブロック名 | 処理説明 |
---|---|
:out_num_return | 文字コード出力後の処理先兼、改行判定 |
:out_bar | 垂直バー| の出力 |
:out_bar_return | 次の処理の決定 |
:out_bar_return2 | (次の処理) |
:out_NL | 改行文字出力 |
:out_NL_return | (次の処理) |
改行する前に、「:out_num_return」ブロックから「:out_bar」に入り、垂直バー|
を出力します。
同じ「:out_bar」の処理でも、「:loopX」から入った時と、「:out_num_return」から入った時とで、処理ルートを変える必要があります。そのために「:out_bar」に入る直前にスタックにフラグをプッシュしておき、その値に応じて行き先を変えます。
プログラムをみると、「:loopX」と「:out_num_return」の最後はいずれもgoto:out_bar
です。
その直前で、「:loopX」ではpush1
を、「:out_num_return」ではpush2
を実行して、フラグをプッシュしています。
「:out_bar_return」では、フラグが1なら「:out_bar_return1」に進み、フラグが2なら「:out_bar_return2」に進むように設計されているため、処理ルートを変えることができます。
Pietはスタックが唯一のメモリですので、同じ処理を使い回す場合は、フラグを使って処理順序を変えます。
終了判定
「:out_NL_return」ブロックは、プログラムの終了判定もしています。ここでは、縦方向ループカウンタy
の値をチェックし、7を超えたら終了、「:end」に進みます。
おまけ〜表の見栄えを良くする
ここまでの説明に出てこなかった下図の部分の補足です。
この部分は、表の1マスのサイズを揃えるために、 半角スペースを出力する処理をしています。
この処理が抜けると、下に示すように、 文字コードの数値が2桁か3桁かによって表のマスの長さが変わり、垂直バーの位置がずれてしまいます。 (右端までスクロールするとわかりやすいです。申し訳ないですが、スマホだとわからないかもしれません・・・。)
|' '(32)|'!'(33)|'"'(34)|'#'(35)|'$'(36)|'%'(37)|'&'(38)|'''(39)|'('(40)|')'(41)|'*'(42)|'+'(43)|','(44)|'-'(45)|'.'(46)|'/'(47)| |'0'(48)|'1'(49)|'2'(50)|'3'(51)|'4'(52)|'5'(53)|'6'(54)|'7'(55)|'8'(56)|'9'(57)|':'(58)|';'(59)|'<'(60)|'='(61)|'>'(62)|'?'(63)| |'@'(64)|'A'(65)|'B'(66)|'C'(67)|'D'(68)|'E'(69)|'F'(70)|'G'(71)|'H'(72)|'I'(73)|'J'(74)|'K'(75)|'L'(76)|'M'(77)|'N'(78)|'O'(79)| |'P'(80)|'Q'(81)|'R'(82)|'S'(83)|'T'(84)|'U'(85)|'V'(86)|'W'(87)|'X'(88)|'Y'(89)|'Z'(90)|'['(91)|'\'(92)|']'(93)|'^'(94)|'_'(95)| |'`'(96)|'a'(97)|'b'(98)|'c'(99)|'d'(100)|'e'(101)|'f'(102)|'g'(103)|'h'(104)|'i'(105)|'j'(106)|'k'(107)|'l'(108)|'m'(109)|'n'(110)|'o'(111)| |'p'(112)|'q'(113)|'r'(114)|'s'(115)|'t'(116)|'u'(117)|'v'(118)|'w'(119)|'x'(120)|'y'(121)|'z'(122)|'{'(123)|'|'(124)|'}'(125)|'~'(126)|''(127)|
これを防ぐため、文字コードが2桁の場合は、文字出力('c'
など)の後ろに半角スペース' '
を1文字出力してから、文字コードの出力((99)
など)をしています。これで垂直バーの位置ずれを防ぐことができます。
おわりに
今回は、プログラムの流れを図示しながら解説してみました。 ブログの更新を1週間サボってこれらの図を半自動で作成できるツールを作っていました。 おかげで説明する側としても労力がだいぶ減りましたし、 多少なりともわかりやすくなっていれば良いと思います。
今回はスタックの細かい変化は省きましたが、 Pietでプログラミングするときにはスタックの変化も追っていく必要があります。 今後はスタックの変化も図にできたらいいなあと思いつつ、 図示用ツールを作成中です。
次回は何を題材にしようかちょっと迷ってますが、 算数・数学チックな話になるのではないかと思います。 (素数判定とか、分数計算とか)
ではまた。