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