二次元言語(Befunge, Fish, Rail, Hexagony,Piet 等)はesolangだが書きにくいわけではない。 コードがn次元ならより書きづらい言語になるのでは? HyperTorusは、コードがn次元トーラスでできている言語です。

実行

python3 interpreter.py sourcecode

デバッグオプション

  • -n [number] 実行の number ステップ毎に内部状態をダンプする
  • -b [str] str の d 文字目が # なら、プログラムの座標 d を実行するたびに内部状態をダンプする (-n と -b を同時に指定することもできます)

仕様

プログラムの記述

プログラムは1行で入力される。 たとえば、

abcdefgh

というプログラムは、

     g------h
    /|     /|
   / |    / |
  e--+---f  |
  |  c---+--d
  | /    | /
  |/     |/
  a------b

という3次元トーラス上に配置される。

一般的に、長さ 2^n 以上 2^(n+1) 未満 のプログラムはn次元トーラスを表現する。

以降ではプログラムの各座標を2進数で表現する。たとえば座標(0,0,0) は2進数000、 座標 (0,1,1) は二進数 110 といった具合である。leading zero は適宜省略する。

座標 k にはプログラムの k 文字目が入る。

命令ポインタ

命令ポインタが1つ、いずれかの座標上に、座標軸と並行な向きを向いて存在している。 実行の1ステップごとに、命令ポインタは現在の座標にある命令を実行し、その後今向いている方向に移動する。

命令ポインタの状態は、命令ポインタの座標と向きによって表現される。 たとえば上の図において、(010,+4) は b に存在して f のほうを向いている命令ポインタの状態を表現している。

初期状態では、メモリポインタの状態は (0,+1) である。

例えば上の例だと、初期状態から命令が a b a b a b ... と実行される。

メモリセル

メモリは、大きさ2^Z(このZは整数の集合)のトーラスでできており、各辺上に(多倍長)整数値を保存する。(発想元はhexagonyのメモリですね) 実際にプログラミングする際に箱性が使われないかんじなので、(befunge,fish,pietなどで)一般的なスタックにします。うまい仕様が思いついたらHyperHyperTorusとかしてみたいですね...

以降、スタックをpopしてその値を返す手続きをpop()、 値xをスタックの先頭に積む手続きをpush(x)と略記する。

命令一覧

命令ポインタ操作命令

char operation
| 命令ポインタの向き、つまり向きの符号を反転させる。
< 命令ポインタの向きを左に向ける(たとえばプログラムが4次元トーラスの場合、向きは +1→+2→+4→+8→+1 もしくは -1→-8→-4→-2→-1 と変化する)
> 命令ポインタの向きを右に向ける(たとえばプログラムが4次元トーラスの場合、向きは +1→+8→+4→+2→+1 もしくは -1→-2→-4→-8→-1 と変化する)
? pop()が0なら < と、そうでないなら > と同じ動きをする
j 「pop()をプログラムサイズで割ったあまりの位置」に(命令ポインタの向きはそのままで)ジャンプする
. 何もしない(NOP)
q プログラムの実行を終了ずる

スタック操作系命令

スタック状態操作

char operation
$ スタックトップ2つの交換(a = pop(); b = pop(); push(a); push(b);)
@ スタックトップ3つの交換(a = pop(); b = pop(); c = pop(); push(a); push(c); push(b);)
: スタックトップの複製(a = pop(); push(a); push(a);)
~ スタックトップの破棄(pop())
} スタックの先頭要素を底にもっていく
{ スタックの底の要素を先頭にもっていく

リフレクション

char operation
g push(「pop()をプログラムサイズで割ったあまりの位置」にあるプログラムのアスキーコード値)
p x = pop(); v = pop(); した後、「xをプログラムサイズで割ったあまりの位置」のプログラムの文字を 「vを256で割った値」にする

値のセット

char operation
[0-9a-f] push(文字の表現する16進の値)
& スタックトップをレジスタに出し入れする。&の実行ごとに、命令の意味が「レジスタの値をpop()にする」<->「push(レジスタの値)」 となる。

二項演算命令

二項演算は、 演算をop として、

r = pop()
l = pop()
push(l op r)

が実行される。

char operation
+ 加算
- 減算
* 乗算
/ 整数除算 (0割りはエラー)
% 剰余 (0割りはエラー)
= l=rなら1,そうでないなら0
( l<rなら1,そうでないなら0
) l>rなら1,そうでないなら0

IO命令

char operation
r push(読み込んだ文字のアスキーコード値(EOFなら-1))
w pop()を256で割ったあまりの文字を出力する
i push((区切り文字まで)読み込んだ十進数値)
o pop()を十進数で出力する

コード例

cat

0<wr.:>j1<.<q+?>

これは、以下のような4次元超立方体状のプログラムを表現している

     >------j          ?------>
    /|     /|         /|     /|
   / |    / |        / |    / |
  .--+---:  |       q--+---+  |
  |  w---+--r       |  .---+--<
  | /    | /        | /    | /
  |/     |/         |/     |/
  0------<          1------<

募集

いいかんじのビジュアライザ