プログラミング言語の作り方

javascript/C言語/アセンブラを用い、 字句解析、構文解析、インタプリタ、コンパイラのプログラムをスクラッチから作りながら、 「プログラミング言語の作り方」を解説する。

掛け算、割り算対応
javascript版プログラミング言語の作り方(インタプリタ開発)

掛け算、割り算に対応しよう。

ページメニュー

現時点の言語仕様

言語仕様として、掛け算、割り算を追加する。

自作言語で記述されたソース

掛け算、割り算がちゃんと計算できるか。 足し算より、掛け算が優先されるか。

$ cat source.3
//実数の割り算1.24÷2
a = 1.24 / 2 -1;
print("a = 1.24 / 2 - 1 = ",a);

//足し算より、乗算が優先されるか
b=5 - 2 * 3;
print("b = 5 - 2 * 3 = ",b);

インタプリタのファイル構成

インタプリタを作るにあたり、以下のファイル構成で実装した。

parser.jsの実装

掛け算、割り算に対応するため、mulを追加した。 標準形なので簡単だ。

$ 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 (@mul@)(){
    //左辺を取得
    var left = funccall();

    //演算子が続く間は連続する
    var op;
    while(op = accept(tokens,(@"*","/"@))){
        //右辺を取得
        var right = funccall();
        //新しいノードを作成し階層を深める
        left = {left,op,right};
    }
    return left;
}

//足し算、引き算
function plus(){
    //左辺を取得
    var left = mul();

    //演算子が続く間は連続する
    var op;
    while(op = accept(tokens,"+","-")){
        //右辺を取得
        var right = mul();
        //新しいノードを作成し階層を深める
        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.24',                  '/',
  '2',                     '-',
  '1',                     ';',
  'print',                 '(',
  '"a = 1.24 / 2 - 1 = "', ',',
  'a',                     ')',
  ';',                     'b',
  '=',                     '5',
  '-',                     '2',
  '*',                     '3',
  ';',                     'print',
  '(',                     '"b = 5 - 2 * 3 = "',
  ',',                     'b',
  ')',                     ';'
]
抽象構文木ast={
  left: {
    left: {
      left: {
        left: {
          left: 'a',
          op: '=',
          right: {
            left: { left: '1.24', op: '/', right: '2' },
            op: '-',
            right: '1'
          }
        },
        op: ';',
        right: {
          left: 'print',
          op: '()',
          right: { left: '"a = 1.24 / 2 - 1 = "', op: ',', right: 'a' }
        }
      },
      op: ';',
      right: {
        left: 'b',
        op: '=',
        right: {
          left: '5',
          op: '-',
          right: { left: '2', op: '*', right: '3' }
        }
      }
    },
    op: ';',
    right: {
      left: 'print',
      op: '()',
      right: { left: '"b = 5 - 2 * 3 = "', op: ',', right: 'b' }
    }
  },
  op: ';',
  right: undefined
}

a = 1.24 / 2 - 1 = -0.38
b = 5 - 2 * 3 = -1


このページの目次へ戻るサイトの最上位へ戻る