[プログラミング言語の作り方] 抽象木構文(AST)

構文解析の結果として抽象木構文(AST)で出力する

### 抽象木構文(AST)とは
木構造はオブジェクトで表す

{
	left: "print",
	op: "()",
	right: "hello world"
}

以下のように表す

var left = "print";
var op = "()";
var right = "hello world";

var obj1 = {left:left, op:op, right:right};

var obj2 = {left, op, right};

run.jsを追加する
$ tree
.
├── interpretor.js
├── lexer.js
├── main.js
├── parser.js
├── run.js
├── source.3
└── utils.js

### interpretor.js

var {read, show} = require("./utils.js");
var lexer = require("./lexer.js");
var parser = require("./parser.js");
var run = require("./run.js");

var source = read("source.3");

var tokens = lexer(source);

var token = lexer(source);
show("token =", tokens);

var ast = parser(tokens);
show("ast=", ast);

run(ast);

### parser.js

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

var tokens;

function parser(t){
    tokens = t;
    return callprint();
}

function callprint(){
    if(tokens.length==0) return;

    var left = expect(tokens, "print");

    var op = expect(tokens, "(");

    var msg = tokens.shift();
    num = calc(msg.length);

    var right = msg.substr(1, num);

    op += expect(tokens,")");

    return {left, op, right};
}

function calc(len) {
    var total = len - 2;
    return Number.isInteger(total) > 0 ? total : 0;
}

### run.js

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

module.exports = run;

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

$ node interpretor.js
token =[ ‘print’, ‘(‘, ‘”hello world”‘, ‘)’ ]
ast={ left: ‘print’, op: ‘()’, right: ‘hello world’ }
hello world

最初にopを見て、それからprintかどうかを判定している。
astでは、ソースコードをleft, right, opに分けている。