[プログラミング言語の作り方] 掛け算、割り算を実装

言語仕様
– 文字列
– 整数123, 実数123.4
– グローバル変数、変数の値の参照
– 代入演算子(=)
– print関数でカンマ区切りで複数の引数指定
– 複数文は、セミコロンで区切る
– // から行末まではコメント
– 足し算、引き算
– 掛け算、割り算

### source.3

a = 1.24 / 2 - 1;
print("a = 1.24 / 2 - 1 = ", a);

b = 5 - 2 * 3;
print("b = 5 - 2 * 3 = ", b);

### parse.js

module.exports = parser;
var {expect, accept, show, error} = require("./utils.js");

var tokens;

function parser(t){
    tokens = t;
    var ast = semi();
    if(tokens.length>0){
        show("ast=", ast);
        show("処理後tokens =", tokens);
        error("tokensが余っています。");
    }
    return ast;
}

function value(){
    if(tokens.length == 0) return;
    return tokens.shift();
}

function funccall(){
    var left = value();

    var op;
    while(op = accept(tokens, "(")){
        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, "=")){
        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;
}

### run.js

const { error } = require("./utils");

module.exports = run;

var global = {};

function run(a){
    if(!a) return;
    if(!a.op){
        if(a[0] == '"') return a.substr(1, a.length-2);
        if(/\d/.test(a[0])) return 1*a;
        if(global.hasOwnProperty(a)) return global[a];
        return a;
    } else if (a.op==";"){
        run(a.left);
        run(a.right);
    } else if (a.op==","){
        return [run(a.left), run(a.right)].flat();
    } else if (a.op=="="){
        return global[run(a.left)] = run(a.right);
    } else if (a.op=="+"){
        return run(a.left) + run(a.right);
    } else if (a.op=="-"){
        return run(a.left) - run(a.right);
    } else if (a.op=="*"){
        return run(a.left) * run(a.right);
    } else if (a.op=="/"){
        return run(a.left) / run(a.right);
    } else if(a.op == "()") {
        var func = run(a.left);
        if(func == "print"){
            var msg = [run(a.right)].flat().join("");
            console.log(msg);
        } else {
            error("未実装の関数呼び出し func=", func);
        } 
    } else {
        error("未実装の演算子 op=", a.op)
    }
} 

ASTのparse.jsで優先したいopをより深くvalueに近い位置でcallすれば、mulはplusよりも優先して処理される。