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

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

足し算、引き算対応
javascript版プログラミング言語の作り方(インタプリタ開発)

足し算、引き算に対応しよう。

ページメニュー

現時点の言語仕様

言語仕様として、足し算、引き算を追加する。

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

足し算、引き算がちゃんと計算できるか。

$ 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);

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

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

parser.jsの実装

足し算、引き算に対応するため、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


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