独自のプログラミング言語のページです。
NoFollowは僕の作ったプログラミング言語です。その名の通り、「辿らないこと」を目標にしています。
普通、Cのコードはこのように書きます。
#include <stdio.h> int test(int x, int y){ return (x + y); } int main(void){ int x; int y; int a; x = 5; y = 7; a = test(x,y); printf("%d\n", a); }
このC言語のコードでは、testを辿っていくことで、プログラムの理解へと近づきます。
NFは、こういう時、「プログラマが明示することで、辿らなくても大体のプログラムは分かる」ようにするための言語です。
例えば、
int main(void)
だけでも、このように分析出来ます。
int: func type;
main: func name;
void: func argument;
そして、それらはdefinitionです。
ですが、長くなってしまうと、そういうことを、コードを見て少し考えないと、分からなくなってしまいます。
このNFでは、このような「プログラムをあとで理解する思考コスト」を削減します。
そして、出来る限り、見ただけで分かるようにします。入門書を読む必要を無くします。
そういうわけで、NFでは以下のようなプログラムになります。
@def &main { @head { @type: main @return_type: int @arguments{ $$_ { @type: void } } } @body { @def { $x { @type: int } $y { @type: int } $a { @type: int } } @run { $x = 5 $y = 7 $a = [return] [run] &test { $$value1 = $x $$value2 = $y } [run] &printf { $$format = "%d\n" $$option = $a } } } } @def &test { @head { @type: method @return_type: int @arguments{ $$value1 { @type: int } $$value2 { @type: int } } } @body { @run { [run] &return { [run] &calculation { ($$value1 + $$value2) } } } } }
なんとなく、最後の方のreturnやcalculationを何故引数の名前を明記しないのか、など、課題は残りますが、とりあえず、変数の引数まで名前をきちんと明記するような、そういう言語にしたいと思っています。
あまり意味がないですが、最近Rustと言う言語に関心があります。自分が見て、こうしたら良いと思うな、などと思うこともあります。技術力をつけて、Rustの改良が出来るようになったら良いなと思っています。
2016.12.15
この言語を用いて、僕の人工知能のロボットを書いてみました。
[headers] #include <stdio.h> #include <stdlib.h> @def _obj:: { @type: struct @items { $o { @type: int } $n { @type: int } } } @def &action { @head { @type: mathod @return_type: void @arguments{ $$[p]p { @type: [p]int } $$[p]obj { @type: [p]struct _obj:: } } } @body { [block "action-body"] //インデントを一度解除します。 [run] &case { @if "1" : //今回はPython風のインデントです。 @match: { ($$*p == 0) } @run { [run] &printf { $$format = "[1] テレビは、つまらない\n"} } @if "2" : @match: { ($$*p == 1) } @run { [run] &printf { $$format = "[2] テレビは、面白い\n"} } @if "3" : @match: { ($$*p == 2) } @run { [run] &printf { $$format = "[3] パソコンは、つまらない\n"} } @if "4" : @match: { ($$*p == 3) } @run { [run] &printf { $$format = "[4] パソコンは、面白い\n"} } @if "5" : @match: { ($$*p == 4) } @run { [run] &printf { $$format = "[5] テレビやパソコンは、機械だ。\n"} } @if "6" : @match: { ($$*p == 5) } @run { [run] &printf { $$format = "[6] テレビやパソコンは、生き物ではない。\n"} } @if "7" : @match: { ($$*p == 6) } @run { [run] &printf { $$format = "[7] テレビって何なのだろう。\n"} } @if "8" : @match: { ($$*p == 7) } @run { [run] &printf { $$format = "[8] テレビとパソコンは好きだ。\n"} } @if "9" : @match: { ($$*p == 8) } @run { [run] &printf { $$format = "[9] テレビとパソコンは嫌いだ。\n"} } @if "10" : @match: { ($$*p == 9) } @run { [run] &case { @if "A" : @match: { ($$obj->$n == 0) } @run { [run] &printf { $$format = "[10] 比較すると、テレビの方が普通だ。\n"} } @if "B" : @match: { ($$obj->$n == 1) } @run { [run] &printf { $$format = "[11] 比較すると、パソコンの方が普通だ。\n"} } } @if "11": @match: { ($$*p == 10) } @run { [run] &case { @if "C" : @match: { ($$obj->$n == 0) } @run { [run] &printf { $$format = "[12] テレビよりパソコンの方が普通かな。\n"} $$obj->$n = 1 } @else_if "D" : @match: { ($$obj->$n == 1) } @run { [run] &printf { $$format = "[13] パソコンよりテレビの方が普通かな。\n"} $$obj->$n = 0 } } @if "12": @match: { ($$*p == 11) } @run { [run] &case { @if "E" : @match: { ($$obj->$o == 0) } @run { [run] &printf { $$format = "[14] リンゴが好きです。\n"} } @if "F" : @match: { ($$obj->$o == 1) } @run { [run] &printf { $$format = "[15] みかんが好きです。\n"} } } @if "13": @match: { ($$*p == 12) } @run { [run] &case { @if "G" : @match: { ($$obj->$o == 0) } @run { [run] &printf { $$format = "[16] リンゴより、みかんが好きになりました。\n"} $$obj->$o = 1 } @else_if "H" : @match: { ($$obj->$o == 1) } @run { [run] &printf { $$format = "[17] みかんより、リンゴが好きになりました。\n"} $$obj->$o = 0 } } } [/block "action-body"] } } @def &main { @head { @type: main @return_type: int @arguments{ $$_ { @type: void } } } @body { @def { $p { @type: int } $obj { @type: struct _obj:: } } @run { $p = 0 $obj.$o = 0 $obj.$n = 0 [run] &loop { @terms { (1) } @run { $p = [run] &calc { [run] &rand {} % 13 } [run] &action { $$[p]p = &($p) $$[p]obj = &($obj) } } } } } }
色々と大変だ。これで読みやすくなったとは言うが、自分が見てもただ無駄に長くなっただけだ。
2016.12.16
ちなみに、この言語ではマクロテンプレートと言うものを用意して、ファイル処理やWindowsメッセージループを簡単に見やすく書けるようにします。
メッセージループの場合:
@@windows_msg { @msg "button1のクリック" { @event: button_click @select: button1 @run: { } } @msg "button2のクリック" { @event: button_click @select: button2 @run: { } } }
ファイル処理の場合
@@file_open { @open { @file: <DATAFILE> @path: /home/myname/file @loop { @get { $line = <DATAFILE> } @run { [run] &print { $$format = "$line\n" } } } }
WindowsからPerlまで、何でもこなせる優れた言語が、NFです。
出来れば、jQueryのような、セレクタに対してイベントが起きた時の処理をするような、そういう言語にしたいです。
マクロテンプレートは、プラグインのようなものだと思ってください。標準では、メッセージループとファイル処理を搭載して、誰もが手軽に作れるようにします。
コンパイラはありません。頑張って、C言語に変換出来るようになると良いと思います。誰か、作ってください!
2016.12.17
テキスト処理はこういう風に書く。
@@text { @file: "file.txt" @block '<p>' to '</p>' { @rep {'\(.*\):\(.*\)<br>' to '\1->\2\n'} } }
どこからどこまでの範囲で - @block、何をどのように置換するか - \(.*\) to \1、を簡単に書ける。
以下のようにすると、最初の1~2行だけを編集できる。
@@text { @line (1-2) { } }
2017.02.07
GUIはこういう風にする。
@@method_def { @def: { %image: &show/© } @def: { %text: &edit/© } @def: { %link: &go} }
まず、マウスをクリックした時は、その領域を選択(処理ではなく、選択)する。
そして、画像なら、showとcopyのメニューを表示する。
テキストなら、editとcopyのメニューを表示する。
リンクなら、goのメニューを表示する。
そして、それらのメニューをクリックした時に、それぞれの処理を行う。
メインメニューと言うよりは、コンテキスト・メニューに近い。
2017.03.06
ツイッターに投稿した、奴隷的言語について。
2017-03-20より。
僕は、今のところパソコンが人間に忠実に従っているが、これがパソコンが人間に意見するようになると、それですでにパソコンは人間だと思う。何かをする時に、パソコンの方から「こういう風に操作してはいかがですか?」と言ったことを問いかけるようにする。人工知能を作るより、その方が先進的だ。
ただ、プログラムを描く、と言う中で、いつも関数の実行の側に主導権があるが、これを関数の呼ばれる側に主導権を作っても良い。関数を呼び出した時の処理を、関数の中で変えられるようにし、関数を変えることで全ての処理を変えられるようにする。グローバル・メソッドと言う名前にしても良い。
自分でも、グローバル・メソッドが何を意味しているのか、良く分かっていない。要は、実行の主導権を呼び出される側に与える、と言うことだから、
gm(3,4);とするのではなく、
[id:1]gm();として
gm(void){ if(id==1) action1(3,4); }とするのだと思う。
分からない。書いている本人も分かっていない。本当は、ただ従うだけではなく、相手から注文をつけてくるような、そういう言語を作りたかった。
int calc(int x, int y){ [method1: x=3;y=4;] [method2: x=2;y=1;] return x+y; } int method1{ calc[method1](?); } int method2{ calc[method2](?); }とする。
ある意味、相手によって言うことを変える、二枚舌のようなプログラミング言語になった。
僕は、もう少し改良して、人間が呼び出すコードを関数の方で変えられるようにしたい。
int main(){ [action(block1)] printf("\n"); [action(block2)] } void action(){ [block1]{gm1();} [block2]{gm2();} }これで、呼び出し元にあるblock1とblock2の宣言場所で、action()関数を実行し、gm1()関数とgm2()関数を関数の中で実行することが出来る。
人間みたいになった。今までは人間が主導権を持って関数を実行していた。人間がコマンドを打ち込む感覚だ。今からは、関数が主導権を持つ。コマンドの方が人間がやることを決める、と言う感覚だ。
ただ、それならもっと違う形態で、main()関数の中にあるブロックを関数から実行出来る。
int main(){ [action(): test(int x) {printf("%d", x); } ] } void action(){ test(2); }となる。
要は、パソコンの方にコマンドを打ってほしい。その中でやることは自分が決める。そういう、「奴隷的言語」がこの言語だ。
2017.03.20
ツイッターに投稿した、構文を関数として実装することで、自由に構文を定義することの出来るメタ言語「MetaC」について。
2017-04-03より。
僕は、C言語で言うifやforなどの文で、特に{}で囲まれた部分を変数や引数とすることで、C言語の構文を少なくし、ユーザーが自由に定義出来るようにし、ifやforも関数で実現することが出来ると思う。それは、エレガントで美しい。
ある意味、言語的仕様を少なくすることで、構文を関数の範疇として、全てを関数にすることが出来る。あるいは、関数でもなく、全てをブロック的実行とすることも出来る。ある意味、代入や演算も何かもっと根源的なものとして、実装出来るかもしれない。
オブジェクト指向では全てをオブジェクトにするが、それは僕はあまり良い実装ではないような気がする。()と{}の違いを無くするならLispのようにも出来る。Rubyのようにしても良い。if () {}をもっと変えることで、if () {} {} {}のようにも出来るのだ。
例えば、spifと言う関数を宣言して、if(){}とする代わりに、spif(){}(){}と出来るかもしれない。そこでどんな風に処理を実行するかは、プログラマの自由だ。
ある意味、この言語はNFと融和して、「言語を作る言語」にすると面白い。Java、C++、Perl、Ruby、PHPなど、全部の言語をこの言語の拡張として実装する。そして、誰でも外部の人間が自由に言語を作れるようにする。spifだけではなく、newを改良してspnewにも出来る。
例えば、
spnew(Object_name, Class_name).Function_name(x,y,z).{print;save;}のように、ありえないほど高度で複雑怪奇な言語を作れる。ただ、全てを関数として実行するだけで、構文を独自に定義し、メタ言語を作れる。
むしろ、
Class::Object.Function(values){Messages(Events){Code}}と言う風にするのは、美しい。そういう風に、関数を定義すると良いかもしれない。
例えば、
View::v.list(page1){open(clicked(menu1)){new_open(this)};close(clicked(menu2)){kill(this)};}と言うコードが書ける。
頑張れば、GUIのイベント駆動型プログラムを一行で書くことも出来るだろう。メタ言語のCだから、名前は「MetaC」とする。
2017.04.03
2017-04-05より。
なんとなく、こんな並列処理言語があっても良い。
multi-thread { while(1){printf("1\n");} } { while(1){printf("2\n");} }これを、ifやforの拡張として、関数として実現すると、面白い。
2017.04.05
オペレーション言語と言うものを作ってみた。プログラミングを技術や計算ではなく、操作と考える。
file_open ($file) { @name: 'text.txt'; %message(1) 'convert' { @run: { ®ex ($file, 'right' to 'left') }; } %message(2) 'print' { @before: { &display ( 'Document is coverting from "right" to "left"' ) }; @after: { &display ( 'end' ) }; @main: { &loop { 'print $1', (&read_line_next($file)) }; } } } file_close($file);
もっと、いかにも「メッセージを送ってオペレーションするぞ」と言った言語にしたい。
2017.05.09
文章の書式を指定したい時は、以下のようにする。
string:"text\n" //textに改行を加えた文字列 string_noformat:"text\a\a" //text\a\aと言う文字列(\が付けられても特殊文字と解釈しない) int:3 //数字の3 string:3 //文字列の3
また、オブジェクト指向については、「型」と「オブジェクト」を同一のものと見なす。
int型は即座にintオブジェクトであり、WindowForm型は即座にWindowFormオブジェクトである。
そして、型は継承やカプセル化の出来るオブジェクトであると同時に、コンパイルする時の静的な型チェックもすることが出来る。
また、オブジェクト指向として、プロパティ操作指向を行う。全てはメソッドを呼び出した時、そのクラスの「内部処理」として操作を行う。
これにより、CORBAのような万能コンポーネントを一言語的に実現することが出来る。
printを行った時にも、setやgetを行って変数を変えた時にも、クラスとオブジェクトの内部で処理を行う。
そのために、それぞれに処理が分散するのではなく、それぞれのオブジェクトの関係性として、メッセージの伝達をすることが出来る。
そうすることで、コンポーネントを作りやすくする。
Adobe Illustratorのようなソフトウェアを作る時に、何かを「伝える側」はメッセージを送るだけにし、「伝わった側」はその時その時の処理を単純に行う。
その上で、「伝える側」が「高度に伝えること」も可能にする。そのように、「アーキテクチャ指向」のようなプログラミングが可能になる。
$filename = string_path:"text.txt"; @def: window::&open (FILE: $$file) { 処理 } @def: window::&view (string: $$output) { 処理 } @def: window::&update (string: $$output) { 処理 } @def: window::&calc (int: $$int) { 処理 } $w = window: new window{}; @@messages => $w { [run] &open{ $$file = (FILE)$filename}; [run] &view{ $$output = string:"hello"}; [run] &update{ $$output = string:"world"}; [run] &calc{ $$int = int:2+3}; } @def_ex window_ex @ex_from window; @def_ex window_ex:: @ex_method:&open (FILE: $$file) { 処理 }; $w2 = window_ex: new window_ex{}; @@messages => $w2 { [run] &open { $$file = (FILE)string_path:"image.jpg"}; }
2017.07.10
こんな言語を作ってみた。
shared_datas ClassType { int num; String str; } method func1 (val int x, ref String y) for ClassType @ct { print(x); y = "text"; @ct.num = x; @ct.str = y; } method func2 (ref ClassType ct) { func1 (42, "format") using ct; } method main(val int argc, val String[] argv) { ClassType ct = new ClassType; String str = "document"; func1(32, str) using ct; func2(ct); }
継承や委譲、インターフェースの実装もできるようにしたい。
2018.11.05
僕は、プログラミング言語の基本は、宣言、代入、変数、関数だと思う。これをエレガントに書きたい。
たとえば、クラスの宣言は以下のように書ける。
Test = class; Test.datas = { @num = int; @str = String; }; Test.methods = { func1 = func (return int) (args val int x, ref String y) { print(x); y = "text"; @num = x; @str = y; return (x * 2); } }; test = (Test) new Test; str = (String) "document"; print(test.func1());
頭を柔らかくして考えること。ある意味、宣言と代入は同じものであると考えることができる。変数や関数も、一種の宣言であり、代入であると考えられる。
このコードはJavaScriptのプロトタイプチェーンに近いかもしれない。上の方のコードはDelphiやAdaに近い。
2018.12.09
こんな言語はどうか。
main.start(); data1 is data named XAndY = { int x; int y; }; data1.new(); func1 is func (d is data as XAndY) = { print("{0}, {1}", d.x, d.y); } func1.run(-d=data1); data.end(); main.end();
メソッドの始まりと終わりであるstart();とend();は、他のプログラム言語である{}に相当する。この言語では{}すら関数として書く。
そして、変数はnew();とend();でインスタンスを作る。上のstart()とend()と全く同じ仕組みであり、関数のend()と変数のend()を統一的に記述できる。
ガーベッジコレクションは行わない。その代り、ヒープ領域をリアルタイムで監視する「ヒープ監視ツール」を作る。これをモニタリングすることで、メモリリークを「デバッガのように」監視できるようにする。
関数に引数を与える時は、UNIXのコマンドのように-d=dataとする。これにより、可読性が向上する。
上のコードはstart(), end()と{}が混在しているが、これをどうするかが課題である。
また、この言語では、単なる関数だけではなく、組み込みの命令文を多くサポートする。シェルスクリプトのようなものに近い。ifやforなどは、この組み込みの命令文で実現する。よってifではなく%ifと表記する。また、ポインタはpointerで宣言する。ただしアロー演算子(->)は使わない。
func1 is func(d is pointer to data as XAndY) = { %if (d.x == d.y) { print("x = y"); } }
returnは関数定義に記述する。また、dataは省略できる。データ構造体は-d.x=1, -d.y=2のように、引数や代入で使う場合、Perlのリストのように要素の羅列として書くことができる。
func2 is func(d is pointer as XAndY) return as int = { return d.x + d.y; } c = func2.run(-d.x = 1, -d.y = 2); print("{0}", c);
関数定義は単なる代入であるため、変数に格納して持ち運んだり、別の関数の引数に渡すことができる。実行はfunc1.run();とする。func1();では実行できない。
2019.02.21
僕は、UNIXのオプションを模した「オプション引数」や、sed, viのアドレス指定を取り入れた言語を作れないかと思う。
まず、通常の関数は以下のように記述する。
function grep(text: string, pat: pattern): string { ... }
ここで、改行文字を選択できるオプション-end_linesをつける場合、オプション引数という機能を使う。オプション引数は「-」を先につける。
function grep(text: string, pat: pattern, -end_lines: string): string { ... }
オプション引数は、「指定しても指定しなくてもよい」「指定する場合は必ず引数名をつける」という特徴がある。
このgrep()関数は以下のように呼び出す。
str:string = grep("test document\ntest document2\n", /test/, -end-lines="\r\n");
また、sedやviのアドレス指定のように、「パターンマッチングで選択されたこの行からこの行までを別のパターンで置換する」ということができるようにする。
たとえば、<pre>から</pre>の中のstringをnumberに置換するには、
str:string = replace(s/string/number/g, -select=range/<pre>\(.*?\)</pre>/\1/sg);
となる。ここで独自の正規表現コマンドrange///を使っている。これは「<pre>\(.*?\)</pre>にマッチングした中で、\1の中身を編集の対象とする」という意味。
この言語は、コンパイラを独自に作るのではなく、既にあるコンパイラの機能を組み合わせて、ライブラリとして実現するといいかもしれない。
2021.08.15