独自のプログラミング言語のページです。
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