Rationales behind CamphorScript
1. そもそもBrainf*ckは何が問題なの?
散々難読プログラミング言語だと言われるBrainf*ckだが、難読言語の割には直感的に理解もしやすく、内容も簡単な言語である。
とはいえ、その機能の少なさにより簡単なことをするにも様々なイディオムが必要となること、
そしてそれらを関数化することができないのでいちいち覚えて毎回毎回書かなければならないことが問題である。
あと、メモリを変数として抽象化できず、メモリとして扱わなければいけないことも問題。
要するに、「ライブラリの作成」を拒む設計になっているのが問題だ。
ということで、上記のもろもろができて、しかもまともな効率の良いBrainf*ckコードを吐き出せるような言語が作りたいなーと思って作り始めたわけだ。
つまり、Brainf*ckコードの思想を残しながら、イディオムを隠蔽し直感的にコーディングができる代物を作りたいと考えた。
私はJavaScriptとかCとかで今までコードを書いてきているし、そんな感じの見た目の言語でかけたらいいよねってことで構文はCっぽく設計。
変数宣言によりメモリを抽象化ができるし、Cのようにブロックの先頭で行う必要はない。
さらに、brainとf*ckなとどいうゲルマン系の荒々しい[独自研究]単語を廃した名前にしたいと考えた。
そこで、そういや「樟脳」に「脳」の字があることだし、CamphorScriptって名前にすればいいんじゃないかなと思い、開発を始めたのである。
あと、Cとかだと普通作れるのは「関数」、C++でも「演算子のオーバーロード」までしか行けないけど、後述の理由により、Haskellをパクッて演算子が自由に自作できるようにも設計した。
さらに、制御構文とかまで自作できるようにしようと考えた。Brainf*ckではifを書くのにさえイディオムが要るので。
結論:
・Cっぽい見た目
・高い拡張性(できるだけライブラリに委任する)
2. CamphorScript概略
まずは見た目から。
C譲りのフリーフォーマットであって、改行やら空白やらはどこにでも入れられる。ただ、当然char
をch ar
と書いたり、char a
をchara
と書くことはできない。
ただし、演算子だけは例外である。+=
を+ =
と表記することは、Cなどとは異なり許される。そういう設計にした理由は後述する。
現状のCamphorScriptには、変数と定数がある。
変数はメモリを抽象化したものであり、Brainf*ckに翻訳する際にはメモリ上に割り当てられる。変数名の規則はCと同様である。
Brainf*ckの++++++++
をchar a; a += 8;
と書きたいわけで、その8
を書くために定数が在る。
現状では、非負整数リテラルと文字リテラルという2種類のリテラルが存在し、共に定数を表す。非負整数リテラルは35
とか114
とかであり、文字リテラルは'z'
とか'?'
とかである。
エスケープシーケンスも用意されているが、Cの丸パクリなのでCamphorScriptには本来不要な'\?'
とかまで無批判に採用している。
'\a' | 7 |
'\b' | 8 |
'\f' | 12 |
'\n' | 10 |
'\r' | 13 |
'\t' | 9 |
'\v' | 11 |
'\0' | 0 |
'\?' | 63 |
'\'' | 39 |
'\"' | 34 |
'\\' | 92 |
まず、Brainf*ckを吐き出せる以上、8命令のどれにも対応する処理が存在しなければならないはずだ。
とはいえ、メモリの管理を変数に委託していることで、<
と>
は不要となる。
+
と-
については、+=
と-=
という演算子を組み込むことで解決した。当然左オペランドは変数、右オペランドは定数である。
前述のとおり、Brainf*ckにおいて++++++++
と表されるコードは、普通にa+=8;
などと表現される。
なお、いちいちchar a; a += 8;
と書くのは鬱陶しいので、char a = 8;
と書けるようにしてある。
この構文で登場する=
は便宜上のものであり、デフォルトではchar a; a=8;
というコードはコンパイルエラーであることに注意が必要である。
(<stdcalc>
をインクルードすれば同じ挙動を見せるが、非効率的である。)
.
と,
はそれぞれwrite()
とread()
という組み込み関数。引数は変数。
さて、最大の問題児が[
と]
である。特に、逆翻訳時に問題を起こす。
現状のCamphorScriptでは、[
と]
の中に<
と>
が同数入っているときのみ、逆翻訳を受け付ける。そうでなければエラーを吐く。
それにはそれなりの理由がある。配列に対応できてないのもこれが原因だが、そもそも現状のCamphorScriptはあくまでアドレスを変数として抽象化するにとどまっている。
ループでアドレスがズレられると、当然困るというわけだ。
さて、メモリを変数として抽象化するということは、どうせなら色々実現したい機能というのもあるわけだ。特に、メモリの再利用は実装したい。
逆に言うと、Brainf*ckにおいて同一メモリを時間で切り分けて用いるなんて日常茶飯事だが、高級言語みを出すにはやっぱりシステムの側でリサイクルできるようにしないとということである。
Cなら普通に「使い終わった変数は放置」という方針で構わんだろう。しかし、今相手にしているのはCamphorScript。未使用メモリは0であることが期待される。
だからこそのdelete
である。delete
は中身が0であることをプログラマ側が保証してからでないと使ってはいけないが、それはつまりこういうことである。
なお、変数のスコープはブロックスコープである。ブロック内で宣言した変数はブロック外からは参照できない…というだけなら普通の言語と同じだが、CamphorScriptはもう一ひねりある。
「ブロック内で宣言した変数は、ブロック内でdelete
しないとコンパイルエラー」という仕様になっているのだ。
まあこれはメモリを浪費しないためのある意味当然の仕様である。
関数。お分かりのとおり、Brainf*ckに関数やサブルーチンなどというものは無いわけで、関数は全て(組み込みを除いて)マクロとして実現されている。
さて、マクロを使うと疑似的に参照渡しができるのはあまりにも有名な話である。Cのswapマクロを思い出していただければわかるだろう。
そこで考えた。Brainf*ckではお呼びでない「型システム」を、マクロの特性を表現するために濫用できないだろうか?と。
ということでできたのがchar&
とconstant char
である。
仮引数の型 | 真引数 |
char& | 変数 |
constant char | 定数 |
具体的には、関数は次のように記述する。
void clear(char& a) {
while(a){a-=1;}
}
|
(この関数は<stdcalc>
ライブラリにて既に実装済み)
この関数を定義したうえで、clear(b);
と書くと、(CCS→HCCSのときに){ while(b){b-=1;} }
と変換される。
要するに、結局はマクロである。だが、Cのマクロより高機能であり、オーバーロードもできることから、インライン関数に例える方が適切かもしれない。
関数内で新しい変数宣言をすることも可能である。しかし、その場合にも制約がある。
Cの場合、関数内で宣言された変数の実体はコールスタック上に取られる領域である。
しかし、CamphorScriptやBrainf*ckにコールスタックなどというものはないし、そもそも関数やサブルーチンではなくマクロである。
ということで、当然ではあるが、「先頭で新たな変数を定義し、終わりで全てdelete
する」ことによって代用とする。
関数はブロックに展開されるので、内部で宣言した変数にも同様の扱いが必要ということである。
というか、現状だと「関数宣言の時点ではdeleteがないことは検出されず、ブロックに展開された時に初めて検出され、コンパイルエラーとなる」というクソ仕様である。これはクソなので直さねばならない。
さて、演算子だが、これはちょっと特殊な仕様になっている。
そもそもCamphorScriptはあくまでBrainf*ckでのプログラミングを補助するための存在であり、決してCのサブセットをBrainf*ckでエミュレートするためのものではない。
故に、C的要素はBrainf*ckのイディオムを体現するために用いるべきであって、決してCなどのイディオムをBrainf*ckに持ち込むために用いるべきではない。
さて、Brainf*ckで頻出[独自研究]するイディオム(に相当するCamphorScript)に、次のようなものがある。
while(a){
a-=1;
b+=1;
c+=1;
}
|
これは、結果として「aの値を破壊し0にしながら、bとcにaの値を加える」という操作を意味する。つまり、Cなら普通以下のように書く操作である。
しかし、これはあくまでCのやり方である。Cにおいて空気のように自然なb+=a;
という処理は、Brainf*ckではそれほど自然な考え方ではない。
それどころか、Cのb+=a;
をBrainf*ckで再現するには、その部分として上記のwhile(a){ a-=1; b+=1; c+=1; }
を含んだコードを書く必要がある。
故に、while(a){ a-=1; b+=1; c+=1; }
をb+=a; c+=a; a=0;
と表現するわけにはいかない。
まずは、一行減った
を考えよう。Cのb+=a; a=0;
というコードだが、これを当然1つの式として表すべきである。
もちろん、clear_the_first_and_add_to_the_second(a,b);
という関数とすれば解決される問題ではあるが、
このような長い関数名はどう考えても実用的でも直感的でもない。故に、Haskellをパクり、演算子を自作できるようにしたのである。
私は、この演算子に+=~
という名前を与えた。b +=~ a
という風に用いる。この~
はビット演算的な意味ではなく、C++のデストラクタのニュアンスを表すものである。
b+=a
を行いながら、同時にaが壊されていくというニュアンスを表現しようと思ったのである。
なお、演算子として使える文字は、!%&*+,-:<=>?@^/|~
である。
さて、具体的な宣言の書き方について説明する。結論から言うと、以下のような形になる。
void (+=~)(char&b;char&a){
while(a){b+=1;a-=1;}
}
|
引数の区切りはコンマではなく、セミコロンである。これは重要なところである。理由は後ほど判明する。
ちなみに、宣言の形に合わせ、(+=~)(b;a);
とも呼べる仕様になっている。使い道があるかどうかは知らん。
また、CamphorScriptでは演算子中にスペースを入れることを許容している。これは、a+= ~b
という表記をすることで、
あたかも~b
が「bを破壊しながらbの元の値を返す演算子」であってその結果をa+=
しているかのように見せることができるようにするためである。
こいつを説明したところで、次にwhile(a){ a-=1; b+=1; c+=1;}
を説明していく。
これまた、clear_the_first_and_add_to_the_second_and_to_the_third(a,b,c);
という関数名にはしたくないわけであるが、今度は3引数。2項演算子では当然扱えない。
じゃあどうするか?答えは簡単。「こういうのも演算子で扱えるように文法を拡張する」のである。
<eq_til>
ライブラリ内での実際の定義を見てみよう。
void (+=~)(char& a,char& b;char& z){
while(z){a+=1;b+=1;z-=1;}
} /* (a,b)+= ~z*/
|
なんと、区切りの文字にコンマとセミコロンの両方が使われている。どういうことだろうか。
下のコメントを見ると、(a,b)+= ~z
と書いてある。実際、この書き方をすると上手く動く。
以下、そのからくりを解説する。
まず、演算子の結合性について説明する。
<fixdecl>
ライブラリに、以下のような宣言がある。
infixl 0 (,); infixr 5 (+=~);
infixl
, infixr
はHaskellからパクった仕様であり、左結合・右結合を指定する。
数値が大きい方が結合性が高い。Haskellとは異なり、演算子の前後にはカッコを必要とし、また数値には任意の非負整数を指定することができる。
さて、CamphorScriptの演算子呼び出しにおいては、「文中で最も優先順位の低い演算子に対応する定義が参照される」というルールがある…はずなのだが挙動がちょっとよく分からないことになってるなどうしよう
また、以上のように定義されているので、組み込みのread
やwrite
はchar&
を引数とする関数であり、
組み込みの演算子+=
や-=
は左オペランドがchar&
、右オペランドがconstant char
である演算子として捉えることができる。
ユーザー側は、これらの演算子が言語に組み込みのものであるかライブラリによって提供されるものなのかという差をほとんど意識することなく、コードが書けるようになっている。
演算子も関数もオーバーロードできることから、
void (+=)(char& a;char& b){
char c2;
while(b){a+=1;c2+=1;b-=1;}
while(c2){b+=1;c2-=1;}
delete c2;
}
|
というコードを書いてやれば、(<stdcalc>
ライブラリにて既に実装済み)ユーザ側はa+=b;
とa+=3;
との間に差を覚えずにコードを書くことさえ可能である。
(勿論、Brainf*ckに翻訳したときの実体は大きく異なるので、実際は差を意識してコードを書くべきだが。)
ということで[どういうことで?]、仮引数の型としてconst char
というのも用意してある。これは、constant char
とchar&
の両方を受け取れる仮引数の型である。
内部実装としては、constant char
の場合とchar&
の場合の関数(or演算子)が両方宣言されるという仕様になっていたはずである[要検証]。2年半前に実装したものの詳細なんて覚えてるわけないし、どこを調べれば答えが書いてあるかも知らない
現状の最大の問題点としては、配列とかにはまだ対応できていないことが挙げられる。
案は一応あるが、まだ実装に至っていない。
3. 全体像
全体像は、このようになっている。
コンパイル: |
CamphorScript(CS)
*↓Step1(C的マクロ展開) {- C macro expansion -}
PreprocessedCamphorScript(PCS)
*↓Step2(関数的マクロ展開) {- Functional macro expansion -}
Half-CompiledCamphorScript(HCCS)
*↓Step3-I(糖衣構文プリプロセス) {- Desugaring -}
CompiledCamphorScript(CCS)
*↓Step3-II(逐語訳&ブロック削除) {- translation -}
nouDarake
*↓Step7(記号化) {- symbolization -}
Brainf*ck_spaced
*↓Step8(空白削除) {- comment deletion -}
Brainf*ck_compressed
|
逆コンパイル: |
Brainf*ck
↓Step8(空白追加) {- indentation -}
↓Step7(非記号化) {- desymbolization -}
nouDarake
↓Step654(逐語訳) {- reverse translation -}
CompiledCamphorScript
|
Stepの数字が飛んでいるのは、空いている番号のところに将来処理を追加する予定だからである。
さて、百聞は一見に如かずというし、CamphorScriptが順にどうやって変換されていくかを見ていこう。(例として、サンプルファイル内のxShow
を用いる)
CamphorScript |
|
#include <stdcalc>
#include <div>
char a;char b; char c; char d;
read(a);
b += 48; c += 48; d += 48;
sprint(b,c,d + <-~ a);
write(b);write(c);write(d);
PreprocessedCamphorScript (冗長な空行やコメントを削除し掲載) |
/*# LINE start "lib\\stdcalc.txt" #*/
/*# LINE start "lib\\fixdecl.txt" #*/
infixl 0 (,);
infixr 5 (=); infixr 5 (+=); infixr 5 (-=);
infixr 5 (=~); infixr 5 (+=~); infixr 5 (-=~);
infixr 5 (+=!~); infixr 5 (=!~); infixr 5 (-|=); infixr 5 (-|=~);
infixl 35 (+); infixl 35 (-) ; infixl 35 (+~); infixl 35 (-~) ;
infixl 40 (*); infixl 40 (%) ; infixl 40 (*~) ;
infixl 40 (/) ; infixl 40 (/~) ;
/*# LINE end "lib\\fixdecl.txt" #*/
void clear(char&a) {
while(a){a-=1;}
}
void (=)(char&a;constant char N){
clear(a);a+=N;
}
/*# LINE start "lib\\eq_til.txt" #*/
/*# LINE start "lib\\fixdecl.txt" #*/
/*# LINE end "lib\\fixdecl.txt" #*/
void (+=~)(char&a;char&z){
while(z){a+=1;z-=1;}
}
void (+=~)(char&a,char&b;char&z){
while(z){a+=1;b+=1;z-=1;}
}
void (+=~)(char&a;char&z *constant char N){
while(z){a+=N;z-=1;}
}
void (-=~)(char&a;char&z){
while(z){a-=1;z-=1;}
}
void (-=~)(char&a,char&b;char&z){
while(z){a-=1;b-=1;z-=1;}
}
void (-=~)(char&a;char&z *constant char N){
while(z){a-=N;z-=1;}
}
void (=~)(char&a;char&z){
clear(a);
a+=~z;
}
void (=~)(char&a,char&b;char&z){
clear(a);clear(b);
(a,b)+=~z;
}
void (=~)(char&a;char&z *constant char N){
clear(a);
a+=~z*N;
}
/*# LINE end "lib\\eq_til.txt" #*/
/*# LINE start "lib\\eq_bang_til.txt" #*/
/*# LINE start "lib\\fixdecl.txt" #*/
/*# LINE end "lib\\fixdecl.txt" #*/
void (+=!~)(char&a;char&z){
a+=1;
while(z){clear(z);a-=1;}
}
void (+=!~)(char&a,char&b;char&z){
a+=1; b+=1;
while(z){clear(z);a-=1;b-=1;}
}
void (=!~)(char&a;char&z){
clear(a); a+=!~z;
}
void (=!~)(char&a,char&b;char&z){
clear(a); clear(b); (a,b)+=!~z;
}
/*# LINE end "lib\\eq_bang_til.txt" #*/
void (+=)(char&a;char&b){
char c2;
(a,c2)+=~b;
b+=~c2;
delete c2;
}
void (+=)(char&a;constant char N *~char&z){
while(z){a+=N;z-=1;}
}
void (-|=~)(char&a;char&b){
char c2;char d2;
while(b)
{
(c2,d2)+=~a;
a +=~d2;
while(c2){
clear(c2);
a-=1;
}
b-=1;
}
delete c2;delete d2;
}
/*# LINE end "lib\\stdcalc.txt" #*/
/*# LINE start "lib\\div.txt" #*/
/*# LINE start "lib\\stdcalc.txt" #*/
/*# LINE end "lib\\stdcalc.txt" #*/
infixr 5 (+<-~); infixr 5 (+<--~); infixr 5 (<-~); infixl 40 (/); infixr 5 (-|=);
void (-|=) (char&a; const char N)
{
char b; b+=N;
a -|=~b;
delete b;
}
void (+=)(char&a,char&b;char&c){
char d;
d +=c;
(a,b) +=~d;
delete d;
}
void (+<-~) (char",char&rem; char&a /const char N)
{
char orig; char new_; char not_; char tmp; char tmp2;
while(a)
{
/*# MEMORY using tmp #*/ orig +=a;
/*# MEMORY using tmp #*/ a -|=N;
/*# MEMORY using tmp #*/(tmp2,new_) +=a;
not_ +=!~tmp2;
while(new_){clear(new_); quot+=1;}
while(not_){not_-=1; rem+=orig;}
clear(orig);
}
delete orig; delete new_; delete not_; delete tmp; delete tmp2;
}
void sprint(char&x1,char&x2,char&x3 +<-~char&a)
{
char k;
(k,x3)+<-~a/10;
(x1,x2)+<-~k/10;
delete k;
}
void (+<--~) (char",char&rem; char&a /const char N)
{
char orig; char a3; char not_; char tmp; char a2;
while(a)
{
(tmp,orig) +=~a; a +=~tmp;
a -|=N;
(a2,a3) +=a;
not_ +=1;
while(a2){clear(a2); quot+=1;}
while(a3){clear(a3); clear(not_);}
while(not_){not_-=1; rem+=orig;}
clear(orig);
}
delete orig; delete a3; delete not_; delete tmp; delete a2;
}
/*# LINE end "lib\\div.txt" #*/
char a;char b; char c; char d;
read(a);
b +=48; c +=48; d +=48;
sprint(b,c,d +<-~a);
write(b);write(c);write(d);
|
Half-CompiledCamphorScript (冗長な空行やコメントを削除し掲載) |
/*# LINE start "lib\\stdcalc.txt" #*/
/*# LINE start "lib\\fixdecl.txt" #*/
/*# LINE end "lib\\fixdecl.txt" #*/
/*# LINE start "lib\\eq_til.txt" #*/
/*# LINE start "lib\\fixdecl.txt" #*/
/*# LINE end "lib\\fixdecl.txt" #*/
/*# LINE end "lib\\eq_til.txt" #*/
/*# LINE start "lib\\eq_bang_til.txt" #*/
/*# LINE start "lib\\fixdecl.txt" #*/
/*# LINE end "lib\\fixdecl.txt" #*/
/*# LINE end "lib\\eq_bang_til.txt" #*/
/*# LINE end "lib\\stdcalc.txt" #*/
/*# LINE start "lib\\div.txt" #*/
/*# LINE start "lib\\stdcalc.txt" #*/
/*# LINE end "lib\\stdcalc.txt" #*/
/*# LINE end "lib\\div.txt" #*/
char a;char b; char c; char d;
read(a);
b+=48; c+=48; d+=48;
{
char k__TMP_1;
{
char orig__TMP_1; char new___TMP_1; char not___TMP_1; char tmp__TMP_1; char tmp2__TMP_1;
while(a){
assert_zero tmp__TMP_1; {
char c2__TMP_1;
{
while(a){orig__TMP_1+=1;c2__TMP_1+=1;a-=1;}
}
{
while(c2__TMP_1){a+=1;c2__TMP_1-=1;}
}
delete c2__TMP_1;
}
assert_zero tmp__TMP_1; {
char b__TMP_1; b__TMP_1+=10;
{
char c2__TMP_1;char d2__TMP_1;
while(b__TMP_1){
{
while(a){c2__TMP_1+=1;d2__TMP_1+=1;a-=1;}
}
{
while(d2__TMP_1){a+=1;d2__TMP_1-=1;}
}
while(c2__TMP_1){
{
while(c2__TMP_1){c2__TMP_1-=1;}
}
a-=1;
}
b__TMP_1-=1;
}
delete c2__TMP_1;delete d2__TMP_1;
}
delete b__TMP_1;
}
assert_zero tmp__TMP_1;{
char d__TMP_1;
{
char c2__TMP_1;
{
while(a){d__TMP_1+=1;c2__TMP_1+=1;a-=1;}
}
{
while(c2__TMP_1){a+=1;c2__TMP_1-=1;}
}
delete c2__TMP_1;
}
{
while(d__TMP_1){tmp2__TMP_1+=1;new___TMP_1+=1;d__TMP_1-=1;}
}
delete d__TMP_1;
}
{
not___TMP_1+=1;
while(tmp2__TMP_1){{
while(tmp2__TMP_1){tmp2__TMP_1-=1;}
}not___TMP_1-=1;}
}
while(new___TMP_1){{
while(new___TMP_1){new___TMP_1-=1;}
} k__TMP_1+=1;}
while(not___TMP_1){not___TMP_1-=1; {
char c2__TMP_1;
{
while(orig__TMP_1){d+=1;c2__TMP_1+=1;orig__TMP_1-=1;}
}
{
while(c2__TMP_1){orig__TMP_1+=1;c2__TMP_1-=1;}
}
delete c2__TMP_1;
}}
{
while(orig__TMP_1){orig__TMP_1-=1;}
}
}
delete orig__TMP_1; delete new___TMP_1; delete not___TMP_1; delete tmp__TMP_1; delete tmp2__TMP_1;
}
{
char orig__TMP_1; char new___TMP_1; char not___TMP_1; char tmp__TMP_1; char tmp2__TMP_1;
while(k__TMP_1){
assert_zero tmp__TMP_1; {
char c2__TMP_1;
{
while(k__TMP_1){orig__TMP_1+=1;c2__TMP_1+=1;k__TMP_1-=1;}
}
{
while(c2__TMP_1){k__TMP_1+=1;c2__TMP_1-=1;}
}
delete c2__TMP_1;
}
assert_zero tmp__TMP_1; {
char b__TMP_1; b__TMP_1+=10;
{
char c2__TMP_1;char d2__TMP_1;
while(b__TMP_1){
{
while(k__TMP_1){c2__TMP_1+=1;d2__TMP_1+=1;k__TMP_1-=1;}
}
{
while(d2__TMP_1){k__TMP_1+=1;d2__TMP_1-=1;}
}
while(c2__TMP_1){
{
while(c2__TMP_1){c2__TMP_1-=1;}
}
k__TMP_1-=1;
}
b__TMP_1-=1;
}
delete c2__TMP_1;delete d2__TMP_1;
}
delete b__TMP_1;
}
assert_zero tmp__TMP_1;{
char d__TMP_1;
{
char c2__TMP_1;
{
while(k__TMP_1){d__TMP_1+=1;c2__TMP_1+=1;k__TMP_1-=1;}
}
{
while(c2__TMP_1){k__TMP_1+=1;c2__TMP_1-=1;}
}
delete c2__TMP_1;
}
{
while(d__TMP_1){tmp2__TMP_1+=1;new___TMP_1+=1;d__TMP_1-=1;}
}
delete d__TMP_1;
}
{
not___TMP_1+=1;
while(tmp2__TMP_1){{
while(tmp2__TMP_1){tmp2__TMP_1-=1;}
}not___TMP_1-=1;}
}
while(new___TMP_1){{
while(new___TMP_1){new___TMP_1-=1;}
} b+=1;}
while(not___TMP_1){not___TMP_1-=1; {
char c2__TMP_1;
{
while(orig__TMP_1){c+=1;c2__TMP_1+=1;orig__TMP_1-=1;}
}
{
while(c2__TMP_1){orig__TMP_1+=1;c2__TMP_1-=1;}
}
delete c2__TMP_1;
}}
{
while(orig__TMP_1){orig__TMP_1-=1;}
}
}
delete orig__TMP_1; delete new___TMP_1; delete not___TMP_1; delete tmp__TMP_1; delete tmp2__TMP_1;
}
delete k__TMP_1;
}
write(b);write(c);write(d);
|
CompiledCamphorScript (冗長削除) |
char a;char b; char c; char d;
read(a);
b+=48; c+=48; d+=48;
{
char k__TMP_1;
{
char orig__TMP_1; char new___TMP_1; char not___TMP_1; char tmp__TMP_1; char tmp2__TMP_1;
while(a){
assert_zero tmp__TMP_1; {
char c2__TMP_1;
{
while(a){orig__TMP_1+=1;c2__TMP_1+=1;a-=1;}
}
{
while(c2__TMP_1){a+=1;c2__TMP_1-=1;}
}
delete c2__TMP_1;
}
assert_zero tmp__TMP_1; {
char b__TMP_1; b__TMP_1+=10;
{
char c2__TMP_1;char d2__TMP_1;
while(b__TMP_1){
{
while(a){c2__TMP_1+=1;d2__TMP_1+=1;a-=1;}
}
{
while(d2__TMP_1){a+=1;d2__TMP_1-=1;}
}
while(c2__TMP_1){
{
while(c2__TMP_1){c2__TMP_1-=1;}
}
a-=1;
}
b__TMP_1-=1;
}
delete c2__TMP_1;delete d2__TMP_1;
}
delete b__TMP_1;
}
assert_zero tmp__TMP_1;{
char d__TMP_1;
{
char c2__TMP_1;
{
while(a){d__TMP_1+=1;c2__TMP_1+=1;a-=1;}
}
{
while(c2__TMP_1){a+=1;c2__TMP_1-=1;}
}
delete c2__TMP_1;
}
{
while(d__TMP_1){tmp2__TMP_1+=1;new___TMP_1+=1;d__TMP_1-=1;}
}
delete d__TMP_1;
}
{
not___TMP_1+=1;
while(tmp2__TMP_1){{
while(tmp2__TMP_1){tmp2__TMP_1-=1;}
}not___TMP_1-=1;}
}
while(new___TMP_1){{
while(new___TMP_1){new___TMP_1-=1;}
} k__TMP_1+=1;}
while(not___TMP_1){not___TMP_1-=1; {
char c2__TMP_1;
{
while(orig__TMP_1){d+=1;c2__TMP_1+=1;orig__TMP_1-=1;}
}
{
while(c2__TMP_1){orig__TMP_1+=1;c2__TMP_1-=1;}
}
delete c2__TMP_1;
}}
{
while(orig__TMP_1){orig__TMP_1-=1;}
}
}
delete orig__TMP_1; delete new___TMP_1; delete not___TMP_1; delete tmp__TMP_1; delete tmp2__TMP_1;
}
{
char orig__TMP_1; char new___TMP_1; char not___TMP_1; char tmp__TMP_1; char tmp2__TMP_1;
while(k__TMP_1){
assert_zero tmp__TMP_1; {
char c2__TMP_1;
{
while(k__TMP_1){orig__TMP_1+=1;c2__TMP_1+=1;k__TMP_1-=1;}
}
{
while(c2__TMP_1){k__TMP_1+=1;c2__TMP_1-=1;}
}
delete c2__TMP_1;
}
assert_zero tmp__TMP_1; {
char b__TMP_1; b__TMP_1+=10;
{
char c2__TMP_1;char d2__TMP_1;
while(b__TMP_1){
{
while(k__TMP_1){c2__TMP_1+=1;d2__TMP_1+=1;k__TMP_1-=1;}
}
{
while(d2__TMP_1){k__TMP_1+=1;d2__TMP_1-=1;}
}
while(c2__TMP_1){
{
while(c2__TMP_1){c2__TMP_1-=1;}
}
k__TMP_1-=1;
}
b__TMP_1-=1;
}
delete c2__TMP_1;delete d2__TMP_1;
}
delete b__TMP_1;
}
assert_zero tmp__TMP_1;{
char d__TMP_1;
{
char c2__TMP_1;
{
while(k__TMP_1){d__TMP_1+=1;c2__TMP_1+=1;k__TMP_1-=1;}
}
{
while(c2__TMP_1){k__TMP_1+=1;c2__TMP_1-=1;}
}
delete c2__TMP_1;
}
{
while(d__TMP_1){tmp2__TMP_1+=1;new___TMP_1+=1;d__TMP_1-=1;}
}
delete d__TMP_1;
}
{
not___TMP_1+=1;
while(tmp2__TMP_1){{
while(tmp2__TMP_1){tmp2__TMP_1-=1;}
}not___TMP_1-=1;}
}
while(new___TMP_1){{
while(new___TMP_1){new___TMP_1-=1;}
} b+=1;}
while(not___TMP_1){not___TMP_1-=1; {
char c2__TMP_1;
{
while(orig__TMP_1){c+=1;c2__TMP_1+=1;orig__TMP_1-=1;}
}
{
while(c2__TMP_1){orig__TMP_1+=1;c2__TMP_1-=1;}
}
delete c2__TMP_1;
}}
{
while(orig__TMP_1){orig__TMP_1-=1;}
}
}
delete orig__TMP_1; delete new___TMP_1; delete not___TMP_1; delete tmp__TMP_1; delete tmp2__TMP_1;
}
delete k__TMP_1;
}
write(b);write(c);write(d);
|
nouDarake (微編集) |
mov 0; _input;
mov 1; inc 48; mov 2; inc 48; mov 3; inc 48;
mov 0; loop;
mov 0; loop; mov 5; inc 1; mov 10; inc 1; mov 0; dec 1; mov 0; pool;
mov 10; loop; mov 0; inc 1; mov 10; dec 1; mov 10; pool;
mov 10; inc 10;
mov 10; loop; mov 0; loop; mov 11; inc 1; mov 12; inc 1; mov 0; dec 1; mov 0; pool;
mov 12; loop; mov 0; inc 1; mov 12; dec 1; mov 12; pool;
mov 11; loop; mov 11; loop; mov 11; dec 1; mov 11; pool;
mov 0; dec 1;
mov 11; pool;
mov 10; dec 1;
mov 10; pool;
mov 0; loop; mov 10; inc 1; mov 11; inc 1; mov 0; dec 1; mov 0; pool;
mov 11; loop; mov 0; inc 1; mov 11; dec 1; mov 11; pool;
mov 10; loop; mov 9; inc 1; mov 6; inc 1; mov 10; dec 1; mov 10; pool;
mov 7; inc 1;
mov 9; loop; mov 9; loop; mov 9; dec 1; mov 9; pool;
mov 7; dec 1; mov 9; pool;
mov 6; loop; mov 6; loop; mov 6; dec 1; mov 6; pool;
mov 4; inc 1; mov 6; pool;
mov 7; loop; mov 7; dec 1;
mov 5; loop; mov 3; inc 1; mov 10; inc 1; mov 5; dec 1; mov 5; pool;
mov 10; loop; mov 5; inc 1; mov 10; dec 1; mov 10; pool;
mov 7; pool;
mov 5; loop; mov 5; dec 1; mov 5; pool;
mov 0; pool;
mov 4; loop;
mov 4; loop; mov 5; inc 1; mov 10; inc 1; mov 4; dec 1; mov 4; pool;
mov 10; loop; mov 4; inc 1; mov 10; dec 1; mov 10; pool;
mov 10; inc 10;
mov 10; loop; mov 4; loop; mov 11; inc 1; mov 12; inc 1; mov 4; dec 1; mov 4; pool;
mov 12; loop; mov 4; inc 1; mov 12; dec 1; mov 12; pool;
mov 11; loop; mov 11; loop; mov 11; dec 1; mov 11; pool;
mov 4; dec 1;
mov 11; pool;
mov 10; dec 1;
mov 10; pool;
mov 4; loop; mov 10; inc 1; mov 11; inc 1; mov 4; dec 1; mov 4; pool;
mov 11; loop; mov 4; inc 1; mov 11; dec 1; mov 11; pool;
mov 10; loop; mov 9; inc 1; mov 6; inc 1; mov 10; dec 1; mov 10; pool;
mov 7; inc 1;
mov 9; loop; mov 9; loop; mov 9; dec 1; mov 9; pool;
mov 7; dec 1; mov 9; pool;
mov 6; loop; mov 6; loop; mov 6; dec 1; mov 6; pool;
mov 1; inc 1; mov 6; pool;
mov 7; loop; mov 7; dec 1;
mov 5; loop; mov 2; inc 1; mov 10; inc 1; mov 5; dec 1; mov 5; pool;
mov 10; loop; mov 5; inc 1; mov 10; dec 1; mov 10; pool;
mov 7; pool;
mov 5; loop; mov 5; dec 1; mov 5; pool;
mov 4; pool;
mov 1; output; mov 2; output; mov 3; output;
|
Brainf*ck_spaced(微編集) |
,
> ++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++
<<< [
[ >>>>> + >>>>> + <<<<<<<<<< - ]
>>>>>>>>>> [ <<<<<<<<<< + >>>>>>>>>> - ]
++++++++++
[ <<<<<<<<<< [ >>>>>>>>>>> + > + <<<<<<<<<<<< - ]
>>>>>>>>>>>> [ <<<<<<<<<<<< + >>>>>>>>>>>> - ]
< [ [ - ]
<<<<<<<<<<< -
>>>>>>>>>>> ]
< -
]
<<<<<<<<<< [ >>>>>>>>>> + > + <<<<<<<<<<< - ]
>>>>>>>>>>> [ <<<<<<<<<<< + >>>>>>>>>>> - ]
< [ < + <<< + >>>> - ]
<<< +
>> [ [ - ]
<< - >> ]
<<< [ [ - ]
<< + >> ]
> [ -
<< [ << + >>>>>>> + <<<<< - ]
>>>>> [ <<<<< + >>>>> - ]
<<< ]
<< [ - ]
<<<<< ]
>>>> [
[ > + >>>>> + <<<<<< - ]
>>>>>> [ <<<<<< + >>>>>> - ]
++++++++++
[ <<<<<< [ >>>>>>> + > + <<<<<<<< - ]
>>>>>>>> [ <<<<<<<< + >>>>>>>> - ]
< [ [ - ]
<<<<<<< -
>>>>>>> ]
< -
]
<<<<<< [ >>>>>> + > + <<<<<<< - ]
>>>>>>> [ <<<<<<< + >>>>>>> - ]
< [ < + <<< + >>>> - ]
<<< +
>> [ [ - ]
<< - >> ]
/* `not = !a`*/
<<< [ [ - ]
<<<<< + >>>>> ]
> [ -
<< [ <<< + >>>>>>>> + <<<<< - ]
>>>>> [ <<<<< + >>>>> - ]
<<< ]
<< [ - ]
< ]
<<< . > . > .
|
Brainf*ck_compressed |
,>++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++<<<[[>>>>>+>>>>>+<<<<<<<<<<-]>>>>>>>>>>[<<<<<<<<<<+>>>>>>>>>>-]++++++++++[<<<<<<<<<<[>>>>>>>>>>>+>+<<<<<<<<<<<<-]>>>>>>>>>>>>[<<<<<<<<<<<<+>>>>>>>>>>>>-]<[[-]<<<<<<<<<<<->>>>>>>>>>>]<-]<<<<<<<<<<[>>>>>>>>>>+>+<<<<<<<<<<<-]>>>>>>>>>>>[<<<<<<<<<<<+>>>>>>>>>>>-]<[<+<<<+>>>>-]<<<+>>[[-]<<->>]<<<[[-]<<+>>]>[-<<[<<+>>>>>>>+<<<<<-]>>>>>[<<<<<+>>>>>-]<<<]<<[-]<<<<<]>>>>[[>+>>>>>+<<<<<<-]>>>>>>[<<<<<<+>>>>>>-]++++++++++[<<<<<<[>>>>>>>+>+<<<<<<<<-]>>>>>>>>[<<<<<<<<+>>>>>>>>-]<[[-]<<<<<<<->>>>>>>]<-]<<<<<<[>>>>>>+>+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<[<+<<<+>>>>-]<<<+>>[[-]<<->>]<<<[[-]<<<<<+>>>>>]>[-<<[<<<+>>>>>>>>+<<<<<-]>>>>>[<<<<<+>>>>>-]<<<]<<[-]<]<<<.>.>.
|
少なくとも前半4つはCっぽいことがわかるだろう。
CamphorScript → PreprocessedCamphorScriptはただのCプリプロセッサもどきの仕事である。というか劣化版再発明するくらいなら既存のを使えばいいんじゃないかという気すらする。
基本的に、ライブラリの読み込みを行うために存在する操作である。ちなみに、余計なスペースを削除したり、コメント内の*
を_star_
に置き換えるなどの謎の挙動を示すらしい。
PreprocessedCamphorScript → Half-CompiledCamphorScriptでは、CamphorScriptの特徴である「関数」「演算子」「構文拡張」を処理する。これが終われば半分終わったようなものである。
ライブラリは、Cと異なり関数定義を含む。これは、CamphorScriptの「関数」の実態が文字列置換であることを考えれば当然のことである。
故に、ライブラリには基本的に二重インクルード防止イディオムを書く必要がある。というかこれを書くためだけに#ifndefやらを実装した。だって#pragma onceなんて実装するのめんどくさいじゃん
きっかけは、難読プログラミング言語をサクサク書くためのマクロ機能付超簡易言語 - aikeの日記という記事を読んだことである。