yaccによるJavascript電卓
※「catch.jp-wiki」から移動したページです。
つい作ってみたくなりました。とりあえずは、yacc/bison系ツール(kmyacc)を使ったJavascript電卓です。特に、日本語プログラミング言語を作るところまで、たどり着きたいのですが・・・
基本的なこと
スクリプト言語は、概ねこんな内部構造になっている(はず)。
スクリプト言語というソフトウェアは、ソースコードを入れると、ライブラリや入力データと組み合わせて、出力結果を得られるプログラム。その中身は、大きく3つに分かれている。字句解析は、ソースコードの文字を切り分ける。構文解析は、それをコンピュータに分かる命令に変換する。で、それをVM(バーチャルマシン)が実行する。大昔は、リアルなCPUが実行するために、バーチャルマシンではなく、マシン語に変換していたんだけれど、今では、CPUが高性能すぎたり、特定CPUへの依存性をなくすために、こんなふうになっている。
で、スクリプト言語を作るためには、この3つ(字句解析、構文解析、VM)が必要。字句解析は、割と簡単(らしい)。有り物を拾ってくればなんとかなる(だろう)。VMは、他のプログラミング言語でも持っていたり、他の言語自体をVMとして使うとか(つまり、プログラミング言語のトランスレータ/変換器にする)。で、構文解析は、設定ファイルを書くと、yaccが自動変換して構文解析プログラムを作ってくれる。
kmyaccのサンプルファイルでやってみる
というわけで、スクリプト言語の構文解析プログラムを作るために、yaccの使い方でも覚えてみるかなぁ。ここでは、kmyaccを使って、Javascript製の電卓を作ってみます。
実行形式版の入手
とりあえず、下記からダウンロード。
ソースコード版とサンプルファイルの入手
kmyaccのサンプルファイルは、ソースコード版に収録されている。だから、実行形式版だけ使う場合も、ソースコード版を入手しておいたほうがいいです。
準備
kmyaccで、必要なものは以下。
- kmyacc.exe
- cygwin1.dll
- libフォルダ一式
- calc.jsy(Javascript用電卓のサンプルファイル:ソースコード版に収録)
なぜ、電卓を作るのかというと、「3 + 4 * 5」という式があると、掛け算(*)のほうが足し算(+)より、優先順位が高い、というように手頃な例題になるから。ちなみに、kmyaccのJavascriptサンプルには、字句解析のコードも含まれている。
変換する
では、kmyaccで、サンプルファイル(calc.jsy)を実際の電卓プログラムに変換します。Windowsのコマンドラインで、次のように実行する。
> kmyacc calc.jsy
これで、calc.jsファイルが生成されました。
なおkmyaccは、設定ファイルの拡張子から、以下のルールに従ってホスト言語を推定します。
yacc拡張子 | ホスト言語 | 出力ファイル拡張子 |
---|---|---|
.y | C | y.tab.c |
.jy | Java | .java |
.jsy | JavaScript | .js |
.ply | Perl | .pl |
実行してみる
この「calc.js」ファイルには、Javascript以外にも、すでに必要なコードとかhtmlが組み込まれているので、これを「calc.html」とファイル名を変更してブラウザで開くと、計算できます。ちゃんと、優先順位やカッコも認識してくれる。ただし、整数だけ。
- サンプルページ 例:1+2*3
計算には、次の記号が使えます。
- + 足し算
- - 引き算
- * 掛け算
- / 割り算
- (...) カッコ内を先に計算する
yaccソースファイルは、こんな感じ。これは、kmyaccのサンプルファイルそのまま(パブリックドメイン)。構文定義の前後に、出力するhtmlファイルやjavascriptも含まれています。
原理などは、下記が参考になります。
- プログラミング言語を作る/yaccとlex
- Bison 1.28マニュアル: 1章と2章が、よくまとまっている。
電卓練習
上記のサンプルを改良して、いろいろな電卓を作ってみます。
逆ポーランド記法の電卓
上記のBison 1.28マニュアルに記載されているサンプルコードを元にして、逆ポーランド記法(reverse polish notation)の電卓を作ってみた。
例:1 5 + 2 3 + *
> 30
この例は、(1+5)*(2+3)と同じになります。
べき乗と剰余を追加
最初のサンプル電卓に、べき乗(累乗:^)と剰余(割り算の余り:%)を追加。
2 ^ 3 => 8
7 % 2 => 1
複数行電卓
電卓(べき乗と剰余付き)を複数の行で同時に計算できるようにしてみました。
クリアボタンで、計算式と計算結果を消します。
変数付き複数行電卓
さらに、変数を使えるようにしてみた。わーい、わーい!
変数名には、先頭に半角英字(a-z、A-Z)、2文字目以降に半角英数字(a-z、A-Z、0-9)が使えます。
字句解析も少し修正。
a = 10
b = 20
a * b => 200
組み込み関数を追加した電卓
また電卓。次の組み込み関数を追加しました。
- test() 引数をそのまま返す
- sqr() 引数の平方根を返す
-
rnd() 乱数を引数倍して返す
- yaccソースファイル
使い方は、こんな感じ
test(123) > 123
sqr(4) > 2
rnd(10) > 6.290423613972962
カッコ内に、引数を数値で一個だけ書けます。
2個以上の引数の関数は組み込めません。(num1, num2)とか(num1 num2)といった記法に対応していないためです。
実装的には、組み込んだ関数名をメソッドとして呼び出せるようにしています。
ソース
参考資料
-
Rubyで作る奇妙なプログラミング言語~Esoteric Language~ Amazon
- あなたは「+-><.,[]」の8つの記号しかないプログラミング言語や、空白だけで構成されるプログラミング言語があるのをご存じだろうか。本書では、そんな奇妙な言語(Esoteric Language)を題材にプログラミング言語の作り方を解説します。
- Rubyを256倍使うための本_無道編 Amazon
- Ruby+Raccなら、プログラミング言語開発だって楽しくなる!パーサジェネレータという硬派なネタを256倍流に完全解説。コレさえあれば、キミも今日から言語開発者だ!
- まつもと直伝 プログラミングのオキテ 第6回 メタプログラミング
- http://itpro.nikkeibp.co.jp/article/COLUMN/20070604/273453/
- まとめてみると,Rubyは非常にDSL向きな言語だと分かります。
-
Racc で遊ぼう
- Raccで電卓をいろいろ作るチュートリアル
- http://www.mnet.ne.jp/~tnomura/racc.html
-
極めよRuby道:第5回 Rubyで書くフロントエンドとパーザ
- Groovyの日本語DSL - Grな日々(uehajの日記)
-
人気の言語を作るには - Being Popular ポール・グレアム