mirror of
https://github.com/jamiebuilds/the-super-tiny-compiler.git
synced 2024-10-27 20:34:08 +00:00
142 lines
5.4 KiB
JavaScript
142 lines
5.4 KiB
JavaScript
var traverser = require('./3-traverser');
|
|
|
|
/**
|
|
* ============================================================================
|
|
* ⁽(◍˃̵͈̑ᴗ˂̵͈̑)⁽
|
|
* THE TRANSFORMER!!!
|
|
* ============================================================================
|
|
*/
|
|
|
|
/**
|
|
* Next up, the transformer. Our transformer is going to take the AST that we
|
|
* have built and pass it to our traverser function with a visitor and will
|
|
* create a new ast.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
* Original AST | Transformed AST
|
|
* ----------------------------------------------------------------------------
|
|
* { | {
|
|
* type: 'Program', | type: 'Program',
|
|
* body: [{ | body: [{
|
|
* type: 'CallExpression', | type: 'ExpressionStatement',
|
|
* name: 'add', | expression: {
|
|
* params: [{ | type: 'CallExpression',
|
|
* type: 'NumberLiteral', | callee: {
|
|
* value: '2' | type: 'Identifier',
|
|
* }, { | name: 'add'
|
|
* type: 'CallExpression', | },
|
|
* name: 'subtract', | arguments: [{
|
|
* params: [{ | type: 'NumberLiteral',
|
|
* type: 'NumberLiteral', | value: '2'
|
|
* value: '4' | }, {
|
|
* }, { | type: 'CallExpression',
|
|
* type: 'NumberLiteral', | callee: {
|
|
* value: '2' | type: 'Identifier',
|
|
* }] | name: 'subtract'
|
|
* }] | },
|
|
* }] | arguments: [{
|
|
* } | type: 'NumberLiteral',
|
|
* | value: '4'
|
|
* ---------------------------------- | }, {
|
|
* | type: 'NumberLiteral',
|
|
* | value: '2'
|
|
* | }]
|
|
* (sorry the other one is longer.) | }
|
|
* | }
|
|
* | }]
|
|
* | }
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
// So we have our transformer function which will accept the lisp ast.
|
|
function transformer(ast) {
|
|
|
|
// We'll create a `newAst` which like our previous AST will have a program
|
|
// node.
|
|
let newAst = {
|
|
type: 'Program',
|
|
body: [],
|
|
};
|
|
|
|
// Next I'm going to cheat a little and create a bit of a hack. We're going to
|
|
// use a property named `context` on our parent nodes that we're going to push
|
|
// nodes to their parent's `context`. Normally you would have a better
|
|
// abstraction than this, but for our purposes this keeps things simple.
|
|
//
|
|
// Just take note that the context is a reference *from* the old ast *to* the
|
|
// new ast.
|
|
ast._context = newAst.body;
|
|
|
|
// We'll start by calling the traverser function with our ast and a visitor.
|
|
traverser(ast, {
|
|
|
|
// The first visitor method accepts any `NumberLiteral`
|
|
NumberLiteral: {
|
|
// We'll visit them on enter.
|
|
enter(node, parent) {
|
|
// We'll create a new node also named `NumberLiteral` that we will push to
|
|
// the parent context.
|
|
parent._context.push({
|
|
type: 'NumberLiteral',
|
|
value: node.value,
|
|
});
|
|
},
|
|
},
|
|
|
|
// Next we have `StringLiteral`
|
|
StringLiteral: {
|
|
enter(node, parent) {
|
|
parent._context.push({
|
|
type: 'StringLiteral',
|
|
value: node.value,
|
|
});
|
|
},
|
|
},
|
|
|
|
// Next up, `CallExpression`.
|
|
CallExpression: {
|
|
enter(node, parent) {
|
|
|
|
// We start creating a new node `CallExpression` with a nested
|
|
// `Identifier`.
|
|
let expression = {
|
|
type: 'CallExpression',
|
|
callee: {
|
|
type: 'Identifier',
|
|
name: node.name,
|
|
},
|
|
arguments: [],
|
|
};
|
|
|
|
// Next we're going to define a new context on the original
|
|
// `CallExpression` node that will reference the `expression`'s arguments
|
|
// so that we can push arguments.
|
|
node._context = expression.arguments;
|
|
|
|
// Then we're going to check if the parent node is a `CallExpression`.
|
|
// If it is not...
|
|
if (parent.type !== 'CallExpression') {
|
|
|
|
// We're going to wrap our `CallExpression` node with an
|
|
// `ExpressionStatement`. We do this because the top level
|
|
// `CallExpression` in JavaScript are actually statements.
|
|
expression = {
|
|
type: 'ExpressionStatement',
|
|
expression: expression,
|
|
};
|
|
}
|
|
|
|
// Last, we push our (possibly wrapped) `CallExpression` to the `parent`'s
|
|
// `context`.
|
|
parent._context.push(expression);
|
|
},
|
|
}
|
|
});
|
|
|
|
// At the end of our transformer function we'll return the new ast that we
|
|
// just created.
|
|
return newAst;
|
|
}
|
|
|
|
// Just exporting our transformer to be used in the final compiler...
|
|
module.exports = transformer; |