構文解析の結果として抽象木構文(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に分けている。