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の切り抜きをやっている。