新プログラミング言語「Zero」を作成しました。
と言いたいところですが、ほぼ以下の書籍に記載の「Diksam」という言語のパクリです。。
※まあまあ文章長いので、興味ない方は読まないことをおすすめします。
※「そのままパクってもいいよ」と仰ってくれているので、お言葉に甘えてパクらせていただきました。
が、何の前置きもなく「新しい言語作ったで」と言っちゃうのは人としてどうかと思うので、一言添えさせていただきました。
※実はパクったのには理由があって、僕が作ろうとしていた言語にDiksamがかなり近かったのです。。
また、Diksamのソースコードを見た時に、「これを一から作る気はしなかった」ということで甘えました。。
どちらかと言うと、僕は言語を作りたかったというよりも、言語を使ってやりたいことがあったのですが、それは追々。
僕のプロフィールはこちら
はじめにソースコードを
「ソースコードだけ見たいんじゃ、ぼけ!」って人のために。
■GItHub
新プログラミング言語の話の前に
まず、「お前のことなんか興味ないから言語の説明しろよ」って方は、ここらへんの前置きはすっ飛ばしてもらってOKです。
なぜ新プログラミング言語を作ったのか
なぜ新プログラミング言語を作ったのかですが、それはもう「興味があったから」、それだけです。
今や無数のプログラミング言語がありますが、そのプログラミング言語がどうやってできているのかを知っている人はかなり少ないはずです。
ましてや、プログラミング言語を作ったことがある人はほぼいないでしょう。
まあ、僕もほぼパクっているので何とも言えないですが。。
新プログラミング言語を作ることに意味があるのか?
「意味があるのか?」と聞かれたら意味はないでしょうね。。
そもそも、自分の作った言語が流行ることなんて、そうそうありません。
大前提多くの人に使われなくてはいけませんし、今はプログラミング言語もかなりの数があるので、よっぽどの理由がないと流行らないでしょう。
でも、流行らないから作らないとかなんだとか、個人的にはどうでもいいです。
エンジニアなら、「自分が作りたいと思ったものを作る」。
それだけで十分かと思います。
新プログラミング言語を作るのは楽しい
プログラミング言語を作るには、最低限「lex」「yacc」等の知識が必要です。
また、各ツールはC言語でできていることもあり、C言語に関する知識も多少必要です。
C言語はさておき、「lex」「yacc」なんて、普通に生きていたら使うことはないツールです。
こういう古めかしいツールを触るのが、実は地味に楽しかったりします。
また、字句解析・構文解析をやってる自分に酔うこともできます。
まあ、ざっくり言うと「楽しいこといっぱいあるよ~」ってことです。
新プログラミング言語を作ることの良さ
プログラミング言語を作ろうとすると、まず「自分が作りたい言語のデザイン」を考えることになります。
そうなると、ある程度現在流行っている言語のデザインを把握しておく必要があります。
その上で、「この言語のこういうところはいいな~」「この言語のこういうところは気にくわんな~」とかをグダグダ考えるわけです。
個人的には、この「言語デザインを考える」という部分が、プログラミング言語を作る一番の醍醐味だと思います。
言語のすべてを自分で決めることができる、というのが一番楽しいポイントであり良さなわけです。
Zeroを作るまでの経緯
ここも、「お前のことなんか興味ないから言語の説明しろよ」って方は、すっ飛ばしてもらってOKです。
新プログラミング言語を作る前に
まず、プログラミング言語の作り方が全く分からなかったので色々とググりました。
一つだけそれらしいサイトを見つけましたが、それ以外はあまり参考にならない。。
で、あとから気付いたのですが、この「それらしいサイト」の内容を書籍にしたものが冒頭で紹介した本になります。
僕はかなりの恩恵を受けているので個人名を出したいくらいですが、勝手に出すのもあれなので書籍の方を再度紹介しておきます。
■参考サイト
URL:「http://kmaebashi.com/programmer/devlang/index.html」
書籍で情報収集
日本人でプログラミング言語の神と言えば、Ruby開発者の「まつもとゆきひろ」さんです。
ってことで、Matzさんの本も一冊購入して読みました。
この本の面白いところは、言語のデザインに深く触れている点です。
まあ、後は説得力がありますよね。
有名なプログラミング言語を片っ端から触る
先ほどの本を2冊読めば、プログラミング言語の作り方は何となく掴めます。
次に、ひたすら有名なプログラミング言語を触りました。
僕は元々「Java」「PHP」「フロント関連」は業務で触ったことがあります。
これでは足りないということで、「C」「C++」「Python」「Go」など、それなりに有名な言語を片っ端から触りました。
ローカルに開発環境を作るのは面倒なので「AWS Cloud9」等を使用しました。非常に便利。
※Rubyを触らなかったのは、資格勉強をした時に触っていたのと、先ほどのMatzさんの書籍でRubyの思想は何となく理解できたからです。
以下は僕が実際に購入した本です。
新プログラミング言語のデザインを決める
大体やるべきことはやったということで、新プログラミング言語のデザインを決めました。
以下が新プログラミング言語に搭載したいポイントだったのですが、やはり僕はJavaが好きなので、かなりJavaチックです。
・静的型付け言語
・オブジェクト指向
・バイトコードはファイルとして出力しない
・拡張子は「ze」
色々と勉強した割にはこだわりがない。。
が、個人的には大枠を作ってしまって、後々言語仕様をちょっとずつ変えていく方針でもいいかなと思いました。
どうせ誰にも使われないでしょうし。
新プログラミング言語作成
さて、後は新プログラミング言語を作成するだけです。
環境構築
まずはプログラミング言語を作るための環境を作る必要がありますが、それは以下に書いたのでそちらで。
電卓を作成
新プログラミング言語を作るにあたり、まずはツールの理解をするべきなので、すごく簡単な電卓を作成しました。
まあ大体以下に書いてるので参考までに。
Zeroの作成に着手
冒頭でもお話しした通り、基本的には書籍のソースコードを複製したものです。
コピー元のソースコードをZero用に書き換えていますが、機能自体は変わりません。
※機能は追々変えていくかもしれませんが、変更する場合は本記事を更新します。
※コピー元のソースコードのダウンロードサイトは書籍に記載されており、本記事で勝手に記載するのもどうかと思ったので割愛します。
欲しい場合は、先ほど紹介した参考サイトからダウンロードするか、ZeroをパクってもらえればOKです。
ちなみに後で書きますが、Zeroも無償で複製してもらっていいので、どんどんパクっちゃってください。
Zeroという名前について
Zeroについて説明する前に、Zeroという名前にした理由をお話ししていませんでした。
実は命名には色々理由があるので、箇条書きしてみます。
・ゼロから言語を作れるように(誰もが無償で複製可能という意味)。
・B’zの大ファンで「ZERO」という曲が好きだから。
・日本らしい名前を付けたかったのですが、靖国神社で零戦を見て「日本の象徴と言えば零戦じゃね?」と思ったから。
まあ最初の理由以外は後付けくさいですが、そこはまあ勘弁。
ただ、一番は「ゼロから言語を作れるように」という点で、ソースコード&本記事もできる限り分かりやすく書いていくつもりです。
飽き性なので途中で飽きるかもしれません。
あ、あと注意した点として、世の中にあるプログラミング言語と名称が被らないようにだけは気を付けました。
↓らへんを見て、Zeroってないよね?って感じで。命名するときには気を付けましょう。
■プログラミング言語一覧 | Wikipedia
URL:「https://ja.wikipedia.org/wiki/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E%E4%B8%80%E8%A6%A7」
Zeroについて
さて、ようやくZeroの説明です。
何はともあれ、サンプルがあるとイメージが湧きやすいかと思うので、FizzBuzzのサンプルコードを載せておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
int i; for(i = 1; i <= 100; i++) { if(i % 15 == 0) { print("FizzBuzz\n"); } elsif(i % 3 == 0) { print("Fizz\n"); } elsif(i % 5 == 0) { print("Buzz\n"); } else { print("" + i + "\n"); } } |
まあ、JavaとかCに似てますね。
細かい所は下の方に言語仕様を書いておいたのでそちらを見てください。
ここでは、Zeroのざっくり概要をお話していきます。
静的型付け言語である
サンプルにある通り、Zeroは「静的型付け言語」です。
個人的に型がない言語はメッサ嫌いです。
最近はPythonとかPHPとかが流行っていますが、PHPなんかだと型がないのにタイプヒンティングがあったりと、どうもいけてない感じがすごい嫌です。
しかもPHPは型がないのではなくて、暗黙的に解釈されてるから意識する必要がないってだけで、型はありますからね。。
僕がJavaから入ったからキモイと感じるだけなのかもしれませんが。。
オブジェクト指向言語である
Zeroは、オブジェクト指向言語です。
当然クラスやインタフェース、継承という概念を持ち合わせていますが、詳細については言語仕様の方で書くのでこちらでは割愛します。
バイトコードは出力しない
Javaなんかだとコンパイルした場合、クラスファイルが出力されます。
が、これうっとおしくないっすか?
ってことで、Zeroはバイトコードをメモリ上に持ちます。
つまり、バイトコードが出力されないので、変な訳の分からないファイルは吐き出されないのです。
素敵。
これはGo言語と同じですね。
コンパイルはしてもファイルは出力しないという思想。
拡張子は「ze」
拡張子は悩みましたが、「ze」としました。
最初は「zero」にしようかなとも考えましたが、長いのでやめました。
個人的には、世の中にある全拡張子と被らなければいいやと思っていたので、気を付けたのはそこだけでしたね。
どうでもいいですけど、「ze」って何かカッコよくないですか?
バイトコードは仮想マシン「ZVM」が実行する
JavaだとJVMという仮想マシンが存在します。
それと同じで、Zeroにもバイトコードを実行するZVMが存在します。
ZVMは、「Zero Virtual Machine」の略です。
Zeroの言語仕様
さて、ここからはZeroの詳細な言語仕様を書いていきます。
各説明箇所にサンプルのソースコードも載せておきます。
※詳細に書きたいところですが、書くこと多すぎるので随時更新していきます。。すまそん。。
※変数名などは「hoge」「foo」「bar」を使用しています。特に意味がないので。
規則
予約語
Zeroには当たり前ですが予約語があります。
以下に記載の予約語は変数名などに使用することはできません。
abstract boolean break case catch class const constructor continue default delegate do double else elsif enum false final finally for foreach if instanceof int interface native_pointer new null override private public rename require return string super switch this throw throws true try virtual void while
識別子
これも一般的なプログラミング言語と基本的には同じです。
・最初の1文字目は、英字もしくはアンダーバー(_)であること。
・2文字目以降は、英字・数字・アンダーバー(_)であること。
ちなみに、識別子とは「変数名」「関数名」「クラス名」などです。
ホワイトスペース
Zeroにおけるホワイトスペースは、「空白」「タブ」「改行」です。
上記3つは識別子の区切り文字以上の機能を持ちません。
コメント
コメントは、「/* */」「//」の2種類が使用可能です。
※ここは今後変えていきたいと思っています。
個人的に、コメントの書き方にはある程度縛りが欲しいですね。
皆自由にかけるから統一感が失われていくわけなので。
型
基本的に、型の取り得る値の範囲は、ZVMがコンパイルされるC言語環境のものと一致します。
整数型(int)
整数型です。
1 2 |
// 整数型 int hoge; |
実数型(double)
実数型です。
1 2 |
// 実数型 double hoge; |
論理型(boolean)
論理型です。
値は「true」、もしくは「false」です。
1 2 |
// 論理型 boolean hoge; |
文字列型(string)
文字列型です。
null代入可能であり、変更不可(immutable)です。
1 2 |
// 文字列型 string hoge; |
void型(void)
void型です。
特殊型で、関数の戻り値がない場合のみ使用可能です。
1 2 3 4 |
// void型 void hoge() { // 処理 } |
配列型
配列の要素数は宣言時に決定しなくても問題なしです。
要素数は動的に確保されます。
1 2 |
// 配列型 int[] hoge = {1, 2, 3}; |
列挙型
列挙型です。
1 2 3 4 5 6 7 |
enum Hoge { BLACK, BLUE, RED, GREEN, WHITE }; |
delegate型
delegate型は、関数の型定義を行うことで変数としての宣言が可能となります。
これに関してはサンプルを見るのが一番分かりやすいかと思います。
1 2 3 4 5 6 7 8 9 10 11 12 |
delegate int Hoge(double foo); int bar(double foo) { println("Hello World!!"); return 0; } Hoge hoge; hoge = bar; println("return " + hoge(0.3)); |
文
宣言文
宣言文とは、変数の宣言のことを指します。
1 2 3 4 5 |
// 変数宣言 int val; // 変数宣言(初期化子あり) int val = 1; |
変数宣言時には初期化子を設定できます。
1 2 |
// 変数宣言(final) final int val = 1; |
「final」を先頭に付与することで値の上書きが禁止されます。
1 2 |
// 変数宣言(const) const VAL = 1; |
「const」を先頭に付与することで定数定義が可能となります。
初期化子によって型を決定するため、型の指定は不要です。
if文
1 2 3 4 5 6 7 8 9 |
if(i % 15 == 0) { print("FizzBuzz\n"); } elsif(i % 3 == 0) { print("Fizz\n"); } elsif(i % 5 == 0) { print("Buzz\n"); } else { print("" + i + "\n"); } |
「波括弧({})は省略不可」です。
switch文
1 2 3 4 5 6 7 8 9 10 |
switch (1) case 1 { println("case 1"); } case 2, 3 { println("case 2, 3"); } case 4 { println("case 4"); } default { println("default"); } |
Zeroのswitch文は「break」を書く必要がありません。
あるcaseにマッチし処理が実行されたらswitch文を抜ける仕様です。
「波括弧({})は省略不可」です。
while文
1 2 3 |
while (true) { print("Hello World!!"); } |
「波括弧({})は省略不可」です。
do while文
1 2 3 |
do { print("Hello World!!"); } while (true); |
「波括弧({})は省略不可」です。
for文
1 2 3 4 5 |
int i; for (i = 0; i < 10; i++) { print("Hello World!!"); } |
try文
1 2 3 4 5 6 7 |
try { // 処理 } catch (Exception e) { // 処理 } finally { // 処理 } |
関数
1 2 3 4 5 6 7 8 9 |
// 例外記載なし int hoge() { // 処理 } // 例外記載あり int hoge() throws ArrayIndexOutOfBoundsException { // 処理 } |
※上の関数が下の関数を呼べないということはありません。
クラス・インターフェース
クラス
1 2 3 4 5 |
アクセス修飾子 class クラス名 : スーパークラス, インターフェース { フィールド コンストラクタ メソッド } |
ソースコードだけだと伝わりにくいので、日本語で構文規則を記載しておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Child : Parent { private int val; constructor initialize(int val) { this.val = val; } int getVal() { return this.val; } void setVal(int val) { this.val = val; } } |
抽象クラス
1 2 |
abstract class Hoge { } |
子クラスは抽象クラスのみ継承をすることが可能です。
言い換えると、あらゆるクラスは抽象クラスしか継承できません。
インターフェース
1 2 |
アクセス修飾子 interface Hoge { } |
インターフェースはabstractメソッドしか記述できません。
その他
ファイル名
ファイル名は「スネークケース」とします。
※ただし、スネークケースでないと動作しないというわけではなく、あくまでZero内での決まりです。
暗黙の型変換
型変換に関しては、以下の規則とします。
・int型とdouble型の演算は、int型をdouble型に型変換して行う。
・文字列型に数字型を結合した場合、数字型を文字列型に型変換して結合する。
Zeroの使い方
Zeroの使い方は環境が整っているかどうかで変わってきます。
Windowsであれば、「gcc」「bison」「flex」などがPCにインストールされていないと使えません。
というのも、Zeroに対して上記のツールを実行することで、最終的に「zero.exe」が出力されます。
要はこれが、Zeroのソースコードを実行するための実行ファイルになります。
ってことで、誰でも使えるように出力ファイル軒並み「develop」ブランチにプッシュしているので、「最終的な成果物を寄こせ!」って方は以下からダウンロードしてください。
Zeroの環境が整っていない方はこちら!
先ほどお話しした通り、PCに諸々インストールされていない方は「develop」ブランチからZeroをダウンロードしてください。
で、「powershell」を起動し、「compiler」フォルダで実行ファイルを実行します。
1 |
Zero\compiler> .\zero.exe ..\test\test.ze |
今回は「test」フォルダにある「test.ze」を実行してみました。
ソースコードが長いので、以下にアクセス!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 |
Zero\compiler> .\zero.exe ..\test\test.ze hoge piyo \nabc abccde 3 + 5..8 3 - 5..-2 3 + -5..-2 3 * 5..15 3 / 5..0 10 % 3..1 3.0 + 5.0..8.000000 3.0 - 5.0..-2.000000 3.0 + -5.0..-2.000000 3.0 * 5.0..15.000000 3.0 / 5.0..0.600000 10.0 % 3.0..1.000000 3 + 5.0..8.000000 3 - 5.0..-2.000000 3 + -5.0..-2.000000 3 * 5.0..15.000000 3 / 5.0..0.600000 10 % 3.0..1.000000 3.0 + 5..8.000000 3.0 - 5..-2.000000 3.0 + -5..-2.000000 3.0 * 5..15.000000 3.0 / 5..0.600000 10.0 % 3..1.000000 3 + 5..8 3 - 5..-2 3 + -5..-2 3 * 5..15 3 / 5..0 10 % 3..1 3.0 + 5.0..8.000000 3.0 - 5.0..-2.000000 3.0 + -5.0..-2.000000 3.0 * 5.0..15.000000 3.0 / 5.0..0.600000 10.0 % 3.0..1.000000 3 + 5.0..8.000000 3 - 5.0..-2.000000 3 + -5.0..-2.000000 3 * 5.0..15.000000 3 / 5.0..0.600000 10 % 3.0..1.000000 3.0 + 5..8.000000 3.0 - 5..-2.000000 3.0 + -5..-2.000000 3.0 * 5..15.000000 3.0 / 5..0.600000 10.0 % 3..1.000000 1 < 3..true 3 < 1..false 1 <= 3..true 3 <= 1..false 1 == 1..true 1 == 3..false 1 != 3..true 3 != 3..false 1 >= 3..false 3 >= 1..true 3 > 1..true 1 > 3..false 1.0 < 3..true 3 < 1.0..false 1.0 <= 3..true 3 <= 1.0..false 1.0 == 1.0..true 1.0 == 3..false 1 != 3.0..true 3.0 != 3.0..false 3.0 >= 1..true 1 >= 3.0..false 3.0 > 1..true 1 > 3.0..false 1 < 3..true 3 < 1..false 1 <= 3..true 3 <= 1..false 1 == 1..true 1 == 3..false 1 != 3..true 3 != 3..false 1 >= 3..false 3 >= 1..true 3 > 1..true 1 > 3..false 1.0 < 3..true 3 < 1.0..false 1.0 <= 3..true 3 <= 1.0..false 1.0 == 1.0..true 1.0 == 3..false 1.0 != 3..true 3 >= 1.0..true 1.0 >= 3.0..false 3 > 1.0..true 1.0 > 3..false == good. != good. < good. <= good. <= good. >= good. >= good. == good. != good. < good. <= good. <= good. >= good. >= good. int_val..5 int_val..3 int_val..9 int_val..3 int_val..1 double_val..5.000000 double_val..3.000000 double_val..9.000000 double_val..3.000000 double_val..1.000000 str_val..strhoge3 int_val..3 double_val..0.000000 a..3, b..4.000000 no_arg a + b..28 a + b + c..10.000000 boolean_func..true int_func..3 double_func..3.000000 string_func..hoge ********** recursive call ********** a..10 a..9 a..8 a..7 a..6 a..5 a..4 a..3 a..2 a..1 a..0 a..0 a..1 a..2 a..3 a..4 a..5 a..6 a..7 a..8 a..9 a..10 boolean_value..true !boolean_value..false true boolean_value || false true == true good true != false good ! operator good. true_value == true good true_value != false good ! operator good. true good good good ********** while statement ********** i..0 i..1 i..2 i..3 i..4 i..5 i..6 i..7 i..8 i..9 i..0 i..1 i..2 i..3 i..4 i..5 i..6 ********** for statement ********** i..0 i..1 i..2 i..3 i..4 i..5 i..6 i..0 i..1 i..2 i..3 i..4 i..5 i..6 i..7 i..8 i..9 ********** break with label in while ********** i..0, j..0 i..0, j..1 i..0, j..2 i..0, j..3 i..0, j..4 i..0, j..5 ********** break with label in for ********** i..0, j..0 i..0, j..1 i..0, j..2 i..0, j..3 i..0, j..4 i..0, j..5 ********** continue in while ********** i..5 i..6 i..7 i..8 i..9 i..10 i..10 ********** continue in for ********** i..5 i..6 i..7 i..8 i..9 i..10 ********** continue with label in while ********** i..1 j..0, i..2 j..0, i..3 j..0, i..3 j..0 ********** continue with label in for ********** i..0 j..0, i..1 j..1, i..2 j..2, i..3 j..3 *** i..0*** i == 0 i != 3 i != 2 && i != 3 *** i..1*** i == 1 i != 3 i != 2 && i != 3 *** i..2*** i == 2 i != 3 i == 2 || i == 3 i == 2 && j == 3 *** i..3*** i == else i == 2 || i == 3 *** i..4*** i == else i != 3 i != 2 && i != 3 i..11 ii = 10..10 array[3][0]..10 1 2 3 4 5 6 7 8 9 2 4 6 8 10 12 14 16 18 3 6 9 12 15 18 21 24 27 4 8 12 16 20 24 28 32 36 5 10 15 20 25 30 35 40 45 6 12 18 24 30 36 42 48 54 7 14 21 28 35 42 49 56 63 8 16 24 32 40 48 56 64 72 9 18 27 36 45 54 63 72 81 array2[0]..1 array2[1]..2 array2[2]..3 array2[3]..4 array2[4]..5 1 2 3 4 5 6 7 8 9 2 4 6 8 10 12 14 16 18 3 6 9 12 15 18 21 24 27 4 8 12 16 20 24 28 32 36 5 10 15 20 25 30 35 40 45 6 12 18 24 30 36 42 48 54 7 14 21 28 35 42 49 56 63 8 16 24 32 40 48 56 64 72 9 18 27 36 45 54 63 72 81 darray[0]..0.000000 darray[1]..1.000000 darray[2]..2.000000 darray[3]..3.000000 darray[4]..4.000000 darray[5]..5.000000 darray[6]..6.000000 darray[7]..7.000000 darray[8]..8.000000 darray[9]..9.000000 darray[0]..1.000000 darray[1]..2.000000 darray[2]..3.000000 darray[3]..4.000000 darray[4]..5.000000 darray[5]..6.000000 sarray[0]..str0 sarray[1]..str1 sarray[2]..str2 sarray[3]..str3 sarray[4]..str4 sarray[5]..str5 sarray[6]..str6 sarray[7]..str7 sarray[8]..str8 sarray[9]..str9 sarray[0]..1 sarray[1]..2 sarray[2]..3 sarray[3]..4 null_str..null abcnull null OK OK OK OK { |
実行すると、「test.ze」ファイル内のソースコードが実行されました。
Zeroの環境が整っている方はこちら!
Zeroを実行するための環境、というか以下の記事に記載のことを一通りやっている方ですね。
環境が整っている方は「master」ブランチからZeroをダウンロードします。
1 |
Zero\compiler> gmake |
「compiler」フォルダで、「make」コマンドを実行します。
実行が完了すると、「compiler」フォルダに「zero.exe」が出力されます。
あとの手順は環境が整っていない方と同じです。
※MinGWのデフォルトは「mingw32-make.exe」なんですが、これを「gmake.exe」にしています。
Zeroのテストコード
↑にテストコード、というかサンプルコードがあります。
Zeroの色々
ソースコード管理
今更ですが、ソースコードは「GitHub」上で管理しています。
ブランチ | 説明 |
---|---|
master | Zeroのモノホン |
develop | masterブランチの内容+outputファイル ※Zeroの全ファイル |
また、masterブランチのみ「.gitignore」ファイルを配置しています。
理由としては、「出力されたファイルをGit管理対象外としたい」ためです。
Zeroのライセンスについて
※あとで書きますが、基本コピペしてもらってOKです。
「Zero」っていう名前だけ変えて、新しい言語作ったで!って声を高らかに言ってもらってもOKです。
もちろん、僕への許可も何もかも必要ありません。
できれば、「Zero」っていう名前だけは被るからやめてね。。
今後やりたいこと
どちらかというと、これが一番やりたかったことでもあります。
Zeroに関連したやりたいこと
・Zeroのインストーラを作る
・Zero用のVSCode拡張プラグイン(シンタックスハイライト)を作る
・Wikipediaに載せたい
・Dockerイメージ作成(DockerHubで公開)
・Zeroのマスコットキャラみたいなの作る(Go言語みたいに)
Zero内部に対してやりたいこと
・makeコマンド実行時に表示されるコンソールのワーニング的なものを消したい
・UTF8対応
・コーディング規約
・.editorconfigを適用
・コメントの書き方をもう少し考えたい
・言語仕様をもう少し詳しく書く(もっと良い言語にならないか検討)
・テストをもっといい感じに自動化したい(でないとリファクタリングがやりにくい)
・outputファイルの削除シェルを作る
・エラー一覧を作る(コンパイルエラーなど)
言語デザインについて
おまけみたいなものですが、言語デザインについて色々とグダグダ考えたので、そこら辺も本記事に集約しちゃいます。
※ちなみに、ここでお話しする内容はあくまで僕の考えなので、強いツッコミは入れないでください。凹みます。
新プログラミング言語の命名について
新プログラミング言語の名称を「Zero」にした理由は先ほどお話しした通りですが、実は英字の「大文字小文字問題」はぼちぼち悩みました。
つまりは、「ZERO」なのか、「Zero」なのか、「zero」なのか、ってことですね。
個人的に「zero」はダサいので却下として、「ZERO」と「Zero」は結構悩みました。
例えば、PHPは全部大文字で、Pythonは先頭だけ大文字、Rubyも先頭だけ、でもCOBOLは全部大文字。。
こうなってくると、「自分の好きな言語に倣うべきだ!」ってことで「Java」に倣って「Zero」にしました。
あとは、「ZERO」ってちょっと堅苦しいですよね。。それだけ。。
ちなみに、これを考えた後にGitのリポジトリを切ったんですが、「ZERO」になっているのに途中まで気付きませんでした。
まあそんなもんです。
ファイル名について
ファイル名の規則は、正直何だろうとZero自体は動くのですが、これも色々考えました。
元々「Hoge.ze」みたいにパスカルケースにしようと考えていたのですが、スネークケースを見慣れているせいか、なんかしっくりこない。。
ちなみに、パスカルケースにしようと考えていた理由としては、ファイル名がスネークケースよりも短くなるからです。
ただ、まあしっくりこないってことで、Zeroはファイル名を「スネークケース」にすることとしました。
あくまでZero内のルールなだけで、スネークケースじゃないといけないというわけではありません。
同じこと少し前にも言った気がする。。
ファイルの拡張子について
Zeroの拡張子は「ze」です。
元々「zero」とかにもしようと考えていたのですが、最終的に「ze」に落ち着きました。
大前提、世の中にある拡張子と被らないことが基本ですが、それを考慮すれば、後は極力拡張子を含むファイル名は短い方が良いという考えです。
個人的に、ファイル名が長くなることでメリットがあるとは思えないので、極力短くしました。
また、短くするとしても「zr」「zo」の選択肢はあったのですが、なんか見た目がダサいってことで却下しました。
ってことで「ze」にしたのですが、「zero」にするか渋っていたのには理由があってですね。。
Groovyっていう言語の拡張子が「groovy」っていうメッサ長い拡張子なんですよね。
「きもっ」て思いつつも、「世の中にこういう長い拡張子があるのなら考えるべきだな」ということでグダグダ考えてたわけです。
まあ結果的に「zero」にはしなかったわけですが。。
「elseif」なのか「elsif」なのか「elif」なのか問題
if文を使う際に用いる「else if」ですが、これは言語によって書き方が様々です。
Zeroでは「elsif」を採用していますが、これには理由があります。
というよりは消去法ですが、他の文法を採用しなかったのは以下の理由からです。
・「elseif」:長い
・「elif」:短くて良いが、普通に発音すると「エルイフ」になってしまう。
「try catch」か「begin rescue」か
例外処理の文法として多いのは「try catch」です。
ただ、Rubyは「begin rescue」です。
Matzさん的には、tryというのは「やってみよう」という意味合いが強く、そもそもエラーが発生しないコードはないという立場で、「やってみようはおかしくない?」ということから「try catch」を採用していないそう。
僕も「try」に関しては違和感を持っていますが、エラーの補足と言う意味だと「rescue」よりも「catch」の方が好きです。
こうなると、「begin catchか?」とも思いましたが、変に文法を変えるとこれはこれで違和感がすごいので、Zeroでは「try catch」のままにしています。
rescueって「救援する」っていう意味らしいですね。
個人的には救援しているわけではなくて、あくまでエラーが投げられて「捕まえた!!」みたいなイメージなのでcatchが好きです。
「def」について
Python・Rubyには「def」があります。
関数の定義をする際に記載しますが、僕はこれが好きではありません。
Javaとかだと「def」は記載不要ですが、関数名の後ろに「()」が付いていれば大体関数(というかメソッド)というのは分かります。
ま、それはさておき一番嫌いな理由としては、defの意味が「define:定義する」という点です。
「関数を定義する」ではなく「定義する」なんです。
defの意味が「定義する」なので、defを付けても「それって関数を定義するっていう意味合いになるの?」と疑問に思っちゃうわけです。
まあ分かるんですけどね、分かるんですよ、でもこういうのをネチネチ考えるのが楽しいんですよね。
「end」について
Rubyは構文の終わりに「end」を書きますし、Excel VBAなんかだと「End If」とかを書いたりします。
個人的にはこういうのは好きではないのですが、その理由は以下です。
・「end」がない言語もあり、かつ違和感を抱かないということは、「end」を書かなくても終了していることは認識できていると判断できるため。もちろん構文全体のバランスは重要。
・書くのが面倒。。
多重継承という考え方について
C++を触っていて、嫌いですが面白いと思ったのが「多重継承」です。
Javaとかは「多重継承禁止」、っていうか大体他の言語も禁止ですが、実際に多重継承をやってみて分かったことがあります。
それは、「やっぱ多重継承あきまへん」ってことです。
正直、何が何やら分からなくなってきます。
個人で何かを開発するならまだいいですが、業務だと基本チームで開発をするわけで、そうなると多分皆好き勝手継承やっちゃいまボンビーだと思います。
例えば、目の前に欲しい機能を持っていたクラスが転がっていたら継承できちゃうわけですよ?
そりゃやっちゃうわって感じですよね。
皆楽したいですからね~。
でもそれがいいかと言われたら、ダメだよね~。
ってことで、「Zeroは多重継承禁止」です。
もっと言うと、「Zeroは抽象クラスしか継承できない」言語です。
以上です。
■関連記事
継承は「extends」か「:」か問題
Javaは「extends」ですよね。
C++は「:」ですよね。
最初C++の構文を見た時は違和感が凄かったのですが、これ圧倒的に書くの楽なんですよね。。
ってことで、Zeroではクラスの継承を「:」で表現します。
Pythonという言語の良さの違和感
Pythonって面白いですよね。
まあそんなにゴリゴリ触ったことはないんですが、ライブラリが豊富なので分析などにはもってこいですよね。
で、よくPythonの良さを聞いてみたりすると、大体「ライブラリが豊富」という答えが返ってきます。
前から思ってたんですが、これ変ですよね?
それはPythonの良さっていうんですかね?
ライブラリが多ければ素晴らしいっていうのはライブラリが少なければクソ雑魚ってことになります。
僕個人としてはそういうことを聞いているのではなくて、「Pythonというプログラミング言語の良さは何ですか?」って聞いてるんですけどね。
例えば、「インデントでブロックを表現するんやで!!」とかなら納得できます。
そう、つまり、ただの愚痴です。
まあ、「ライブラリの豊富さも言語の良さ」、と解釈はできますが、なんかいつも気になるんです。。
ちなみに、僕はPython嫌いなわけではないです。
どっちかっていうと、データ分析とかに1ミリも興味がないって感じです!
ちゃっす!
Pythonのインデントについて
1 2 3 |
if 条件式: ブロック内の処理1 ブロック内の処理2 |
Pythonではインデントでブロックを表現します。
1 2 3 4 |
if (条件式) { ブロック内の処理1 ブロック内の処理2 } |
Javaではこうなります。
どちらかがすごい嫌いとかいう気持ちはないのですが、個人的にはJavaの方が好きです。
なぜかというと、以下でも動作するからです。
1 2 3 4 5 6 |
if (条件式) { ブロック内の処理1 ブロック内の処理2 } |
インデントでブロックを表現しない(括弧で表現する)言語は、括弧内がどう崩れても動作は変わりません(ソースコードが汚くなるだけ)。
ただ、Pythonだとそういうわけにはいきません。
まあ、「そこらへんを制限しているからのPythonだ!」とも取れますが、インデントってうっかり変えちゃうことも稀にあると思います(ないか。。)
そうなった時に、ソースコードとして動作しなくなるのはどうもしっくりこないんですよね。
なので、ZeroでもJavaのように括弧でブロックを表現します。
と言いつつ、インデントで表現するのは案外好きです。
インデントで表現するということは、Javaみたいに「書く人によってインデントがばらばら」みたいな事態を極力防げますし、ソースコード全体の統一感が生まれます。
ただそれを採用しなかったのは、僕に気力がなかったこともありますし、Javaが好きというのもありますし、正味どうやったらいいのか分からなかったというのもあります。。
自由度の高い言語について
最近は自由度の高い言語が流行っているように思います。
Python, Rubyなど、一般的に自由度が高いと言われている言語を僕はあまり好きではありません。
この自由度というのは、「やれることの豊富さ」「文法としての書き方の豊富さ」など、色々あると思います。
自由度の高さだけ見ると良いことのように思えますし、僕も素晴らしいことだと思います。
では、「それを人間がちゃんと使いこなせているのか?」と言われれば、Noではないでしょうか?
自由度の高い素晴らしいアイテムを与えられても、人間がそれを正しく使いこなせないと意味がありません。
そもそも、「なぜ人間が正しく使いこなせないのか?」と言うと、皆が皆勉強しているとは限らないからですよね。
結果的に、チームで開発をしているのに自由度が高いゆえに、記載粒度はバラバラみたいなことになってクソみたいなすばらしいシステムの出来上がりです。
ってことで、Zeroの今の言語仕様はさておき、今後はどんどん自由を縛っていこうと思いますww
※あくまで個人的見解です。
ちなみに、今まで色んなプロジェクトに参画してきましたが、「綺麗なソースコードでできたシステムやな~」と思ったことは一度もありません。
まあ、そんなもんですし、かくいう僕も綺麗には書けません。。
まとめ
新しいプログラミング言語を作るのは何より楽しいです。
ただ、作ったところで日の目を浴びないのは確実です。
でも、「エンジニアなら作りたいときに作りたいものを作る」、それが何より大事だと思います。
プログラミング言語を作るのなんて正味ただの自己満足ですが、自分が楽しけりゃいいんじゃないですかね。
あと、なんやかんやで言語デザインをグダグダ考えるのは楽しいですよ。
今流行りの言語が「なぜそういう構文なのか?」を考えるだけでも十分楽しいですし、それを楽しいと思えるならとっとと言語作っちゃいましょう。
Zeroパクっちゃいましょう!
さて、次は何をやろうかな。