の続きです。Node.jsで動くJavaScriptインタプリタを実装しようとする試みです。
作戦
下調べ
EsprimaがどのようなASTを返すか確認します。
準備
Esprimaをインストールします。
npm init -y npm install esprima
ASTを見る
REPLでパース結果を確認します。
node
コマンドでREPLを起動し
~ node > const esprima = require('esprima') undefined > const util = require('util') undefined > console.log(util.inspect(esprima.parse('1 + 1'), false, null)) Script { type: 'Program', body: [ ExpressionStatement { type: 'ExpressionStatement', expression: BinaryExpression { type: 'BinaryExpression', operator: '+', left: Literal { type: 'Literal', value: 1, raw: '1' }, right: Literal { type: 'Literal', value: 1, raw: '1' } } } ], sourceType: 'script' } undefined
1 + 1を実行する
const esprima = require('esprima') const util = require('util') console.assert(test('1 + 1') === 2) function test(expresssion) { const parsed = esprima.parse(expresssion) console.log(util.inspect(parsed, false, null)) const body = parsed.body for (const statement of body) { return evaluate(statement) } } function evaluate(statement) { switch (statement.type) { case 'ExpressionStatement': switch (statement.expression.type) { case 'BinaryExpression': switch (statement.expression.operator) { case '+': let left; if (statement.expression.left.type === 'Literal') { left = statement.expression.left.value } else { console.log(`unknown type ${statement.expression.left.type}`); } let right; if (statement.expression.right.type === 'Literal') { right = statement.expression.right.value } else { console.log(`unknown type ${statement.expression.right.type}`); } return left + right break; default: console.log(`unknown operator ${statement.expression.operator}`); } break; default: console.log(`unknown expression ${statement.expression}`); } break; default: console.log(`unknown type ${statement.type}`); } }
- console.assertを使って実行結果を評価
- ASTを表示(見ながら実装を進めたい)
- test関数でスクリプトを実行
- evaluate関数で文を実行(「RubyでつくるRuby」の最終形に引きづられた、evaluateStatementがベター?)
- for ofもTemplate literalも使う(現時点でセルフホスティングは考えない)
- 二項分岐なのにswitchを使った(「RubyでつくるRuby」の最終形に引きづられた、ifで十分)
- ;有無は統一していない(普段は無し派、ESLintの助けが必要)
実行すると
~ node . Script { type: 'Program', body: [ ExpressionStatement { type: 'ExpressionStatement', expression: BinaryExpression { type: 'BinaryExpression', operator: '+', left: Literal { type: 'Literal', value: 1, raw: '1' }, right: Literal { type: 'Literal', value: 1, raw: '1' } } } ], sourceType: 'script' }
ASTを表示するだけです。
結果が間違っているときは、console.assert
で引っかかってAssertionErrorがでます。
とりあえずここまでです。 次は対応するoperator(-, *, /)を増やします。