[プログラミング言語の作り方] Javascriptでinterpretorを作る

hello worldを出力する言語を開発する。
言語仕様としては文字列とprint関数だ。
source.3というファイルを作成する。

print("hello world")

### インタプリタのファイル構成
– interpretor.js
– lexer.js
– parser.js
– utils.js

### interpretor.js

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

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

var tokens = lexer(source);

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

parser(tokens);

### lexer.js
split(/(“.*”|print)|\n/)で、文字列、print、改行で分割
filter(a=>a);で不要な物を捨てる

module.exports = function(source){
    var tokens = source.split(/(".*"|print)|\n/);

    tokens = tokens.filter(a=>a);

    return tokens;
}

### parser.js
callprintでは文法規則に沿って関数名(文字列)の順番通りになっているかを確認し、問題なければその場で文字列の部分を表示(実行)
substr(開始位置、文字数)なので、msg.substr(1, msg.legnth-2);として文字列を切り出す

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;

    expect(tokens, "print");

    expect(tokens, "(");

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

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

    
    console.log(msg2);

    expect(tokens,")");
}

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

### utils.js
shift()は配列の最初の要素を取り除く

module.exports = {read, show, error, accept, expect}

function read(filename) {
    return require('fs').readFileSync(filename,"utf-8");
}

function show(msg, obj){
    obj = require('util').inspect(obj, {
        showHidden: false, depth: null, maxArrayLength: null, colors: true
    });
    console.log(msg + obj);
}

function error(...msgs){
    console.log(msgs.join(""));
    process.exit();
}

function accept(tokens, ...cs){
    if(tokens.length==0) return;

    if(cs.includes(tokens[0])) return tokens.shift();
    return;
}

function expect(tokens, ...cs){
    var t = accept(tokens, ...cs);

    if(t) return t;

    error("tokens[0]=",tokens[0],"が",cs,"に含まれていたため終了");
}

lexer.jsはsplit以外は殆ど何もしていないのね。parseの方で、msgの切り抜きをやっている。