はじめに
「RubyでつくるRuby ゼロから学びなおすプログラミング言語入門」(ラムダノート, Amazon) と PythonでつくるPythonに影響を受けて、Node.jsでミニNode.js作りにチャンレンジ中です。
前回はソースをパースするところまでやりました。今回はAST(構文木)をシンプルに整形するところと、ミニNode.jsのカバー範囲を考えてみます。
ASTの単純化
前回のStep1ではesprimaでASTを作りました。想像以上に複雑で、単に'1'を変換しただけでこんな大げさな構造になっています。
Script {
type: 'Program',
body:
[ ExpressionStatement {
type: 'ExpressionStatement',
expression: Literal { type: 'Literal', value: 1, raw: '1' } } ],
sourceType: 'script' }
この構造をそのまま使うのは正直面倒です。「RubyでつくるRuby」や「PythonでつくるPython」のコードを見ると、simplify()という処理でどうやら単純化しているようです。
それを真似してみることにします。
'1'を与えた場合に欲しいのは次のような結果です。
["lit", 1]
まずはこれだけを目指して、simplify()を作ります。
function simplify(ast) {
const exp = ast.body[0].expression;
if (exp.type === 'Literal') {
return ['lit', exp.value];
}
return null;
}
'1' をパース、simplify()した結果はこうなりました。
[ 'lit', 1 ]
このsimplify()を、今後拡張していく予定です。
仕様の単純化
今回のミニNode.jsでは、言語仕様もバッサリ単純化します。基本は「RubyでつくるRuby」を真似て、こんな感じで考えています。
- リテラル(定数)が扱える。最低限は整数、文字列、可能なら実数も
- 変数が扱える。宣言は let のみ
- 条件分岐は if のみ
- 繰り返しは while のみ
- ユーザ定義関数が作れて、呼べる
- 最低限の組み込み関数を用意する
- FizzBuzzが実装できる
- 最後はブートストラップで自分自身を実行できる(のか??)
進行に従って仕様は変えるかもしれませんが、ひとまずこれで進めてみます。
次回
今回のソースコード
Step2までの全体のコードはこちらです。
const esprima = require("esprima");
function parseSrc(src) {
const ast = esprima.parseScript(src);
return ast;
}
function printObj(obj) {
console.dir(obj, {depth: 10});
}
function simplify(ast) {
const exp = ast.body[0].expression;
if (exp.type === 'Literal') {
return ['lit', exp.value];
}
return null;
}
// --------
const ast1 = parseSrc('1');
printObj(ast1);
const tree1 = simplify(ast1);
printObj(tree1);