javascript/C言語/アセンブラを用い、 字句解析、構文解析、インタプリタ、コンパイラのプログラムをスクラッチから作りながら、 「プログラミング言語の作り方」を解説する。
足し算、引き算に対応しよう。
言語仕様として、足し算、引き算を追加する。
足し算、引き算がちゃんと計算できるか。
$ cat source.3 //足し算、引き算 a = 1 + 2 - 4; print("a = 1 + 2 - 4 = ",a); //実数の引き算 b = 3.5 - 1.2 + 0.1; print("b = 3.5 - 1.2 + 0.1= ",b);
インタプリタを作るにあたり、以下のファイル構成で実装した。
足し算、引き算に対応するため、plusを追加した。 標準形なので簡単だ。
$ cat parser.js //---------------構文解析---------------// var {expect,accept,show,error} = require("./utils.js"); //その他は値としてそのまま返す function value(){ if(tokens.length ==0) return; //そのまま返す return tokens.shift(); } //関数呼び出し function funccall(){ //関数名を取得 var left = value(); //関数呼び出しのカッコ var op; if(op = accept(tokens,"(")){ //丸カッコの中の複数の引数を取得 //ここはvalueではなく、commaであることに注意 var right = comma(); //閉じカッコであることを確認して取得 op += expect(tokens,")"); //新しいノードを作成し階層を深める left = {left,op,right}; } return left; } //足し算、引き算 function (@plus@)(){ //左辺を取得 var left = funccall(); //演算子が続く間は連続する var op; while(op = accept(tokens,(@"+","-"@))){ //右辺を取得 var right = funccall(); //新しいノードを作成し階層を深める left = {left,op,right}; } return left; } //イコール代入は右結合 function assign(){ //左辺を取得 var left = plus(); //演算子が続く間は連続する var op; while(op = accept(tokens,"=")){ //右辺を取得 //ここはplusでなく、commaにリセット var right = comma(); //新しいノードを作成し階層を深める left = {left,op,right}; } return left; } //カンマ=複数引数 function comma(){ //左辺を取得 var left = assign(); //演算子が続く間は連続する var op; while(op = accept(tokens,",")){ //右辺を取得 var right = assign(); //新しいノードを作成し階層を深める left = {left,op,right}; } return left; } //セミコロン=複数文 function semi(){ //左辺を取得 var left = comma(); //演算子が続く間は連続する var op; while(op = accept(tokens,";")){ //右辺を取得 var right = comma(); //新しいノードを作成し階層を深める left = {left,op,right}; } return left; } var tokens; function parser(t){ tokens = t; var ast = semi(); //処理後のtokensを表示 if(tokens.length>0){ show("ast=",ast); show("処理後tokens =",tokens); error("tokensが余っているので、どこかおかしいので終了"); } return ast; } module.exports = parser;
interpretor.jsを実行する。
まず、tokensとastが意図どおりに分割されているか、念のため表示しているので、確認しよう。
問題なく、自作プログラミング言語を実装できていることが分かる。
$ ./interpretor.js 処理前tokens =[ 'a', '=', '1', '+', '2', '-', '4', ';', 'print', '(', '"a = 1 + 2 - 4 = "', ',', 'a', ')', ';', 'b', '=', '3.5', '-', '1.2', '+', '0.1', ';', 'print', '(', '"b = 3.5 - 1.2 + 0.1= "', ',', 'b', ')', ';' ] 抽象構文木ast={ left: { left: { left: { left: { left: 'a', op: '=', right: { left: { left: '1', op: '+', right: '2' }, op: '-', right: '4' } }, op: ';', right: { left: 'print', op: '()', right: { left: '"a = 1 + 2 - 4 = "', op: ',', right: 'a' } } }, op: ';', right: { left: 'b', op: '=', right: { left: { left: '3.5', op: '-', right: '1.2' }, op: '+', right: '0.1' } } }, op: ';', right: { left: 'print', op: '()', right: { left: '"b = 3.5 - 1.2 + 0.1= "', op: ',', right: 'b' } } }, op: ';', right: undefined } a = 1 + 2 - 4 = -1 b = 3.5 - 1.2 + 0.1= 2.4