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

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

符号対応
javascript版プログラミング言語の作り方(インタプリタ開発)

単項演算子の符号に対応しよう。

ページメニュー

現時点の言語仕様

言語仕様として、単項演算子の符号を追加する。

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

単項符号がちゃんと計算できるか。 掛け算の後に符号が来ることで、演算子が連続しても動くか。

//単項の符号
a = -2 * 3;
print("a = -2 *  3=",a);
//単項の符号2つ
b = -2 * -3;
print("b = -2 * -3=",b);

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

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

parser.jsの実装

単項の符号に対応するため、flagを追加した。 注意点としては、右結合なので、リセットで、commaであることと、 単項であるため、左辺読まず、最後のreturnでfunccallを呼んでいることだ。

さらに、プラスの符号がついても何も処理しないので、そのままrightを返し、 マイナスの符号がついたら、rightに(0-1)、つまり-1を掛ける階層を作って返している。

$ 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 flag(){
    (@//左辺を取得しない@)

    //演算子が続く間は連続する
    var op;
    while(op = accept(tokens,"+","-")){
        //右辺を取得
        var right = comma();
        (@//プラス符号なら何もしない
        if(op == "+") return right;
        //マイナス符号なら、(0-1)を掛ける階層を作る
	return {left:{left:"0",op:"-",right:"1"},op:"*",right};@)
    }

    (@//ここがleftじゃなく、funccallなことに注意
    return funccall();@)
}


//掛け算、割り算
function mul(){
    //左辺を取得
    var left = flag();

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

a = -2 *  3=-6
b = -2 * -3=6


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