mirror of
https://github.com/jamiebuilds/the-super-tiny-compiler.git
synced 2024-10-27 20:34:08 +00:00
Add transformer/code generator/compiler inline annotations
This commit is contained in:
parent
7fa8ab691f
commit
7d5c7592bb
@ -710,14 +710,6 @@ function traverser(ast, visitor) {
|
|||||||
traverseNode(ast, null);
|
traverseNode(ast, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
* *Note:* This is all I've written so far, so the code below isn't annnotated
|
|
||||||
* yet. You can still read it all and it totally works, but I plan on improving
|
|
||||||
* this in the near future
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ============================================================================
|
* ============================================================================
|
||||||
* ⁽(◍˃̵͈̑ᴗ˂̵͈̑)⁽
|
* ⁽(◍˃̵͈̑ᴗ˂̵͈̑)⁽
|
||||||
@ -725,23 +717,84 @@ function traverser(ast, visitor) {
|
|||||||
* ============================================================================
|
* ============================================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
function transformer(ast) {
|
||||||
|
|
||||||
|
// We'll create a `newAst` which like our previous AST will have a program
|
||||||
|
// node.
|
||||||
var newAst = {
|
var newAst = {
|
||||||
type: 'Program',
|
type: 'Program',
|
||||||
body: []
|
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;
|
ast._context = newAst.body;
|
||||||
|
|
||||||
|
// We'll start by calling the traverser function with our ast and a visitor.
|
||||||
traverser(ast, {
|
traverser(ast, {
|
||||||
|
|
||||||
|
// The first visitor method accepts `NumberLiterals`
|
||||||
NumberLiteral: function(node, parent) {
|
NumberLiteral: function(node, parent) {
|
||||||
|
// We'll create a new node also named `NumberLiteral` that we will push to
|
||||||
|
// the parent context.
|
||||||
parent._context.push({
|
parent._context.push({
|
||||||
type: 'NumberLiteral',
|
type: 'NumberLiteral',
|
||||||
value: node.value
|
value: node.value
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Next up, `CallExpressions`.
|
||||||
CallExpression: function(node, parent) {
|
CallExpression: function(node, parent) {
|
||||||
|
|
||||||
|
// We start creating a new node `CallExpression` with a nested
|
||||||
|
// `Identifier`.
|
||||||
var expression = {
|
var expression = {
|
||||||
type: 'CallExpression',
|
type: 'CallExpression',
|
||||||
callee: {
|
callee: {
|
||||||
@ -751,19 +804,32 @@ function transformer(ast) {
|
|||||||
arguments: []
|
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;
|
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') {
|
if (parent.type !== 'CallExpression') {
|
||||||
|
|
||||||
|
// We're going to wrap our `CallExpression` node with an
|
||||||
|
// `ExpressionStatement`. We do this because the top level
|
||||||
|
// `CallExpressions` in JavaScript are actually statements.
|
||||||
expression = {
|
expression = {
|
||||||
type: 'ExpressionStatement',
|
type: 'ExpressionStatement',
|
||||||
expression: expression
|
expression: expression
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Last, we push our (possibly wrapped) `CallExpression` to the `parent`'s
|
||||||
|
// `context`.
|
||||||
parent._context.push(expression);
|
parent._context.push(expression);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// At the end of our transformer function we'll return the new ast that we
|
||||||
|
// just created.
|
||||||
return newAst;
|
return newAst;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -774,18 +840,36 @@ function transformer(ast) {
|
|||||||
* ============================================================================
|
* ============================================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Now let's move onto our last phase: The Code Generator.
|
||||||
|
*
|
||||||
|
* Our code generator is going to recursively call itself to print each node in
|
||||||
|
* the tree into one giant string.
|
||||||
|
*/
|
||||||
|
|
||||||
function codeGenerator(node) {
|
function codeGenerator(node) {
|
||||||
|
|
||||||
|
// We'll break things down by the `type` of the `node`.
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
|
|
||||||
|
// If we have a `Program` node. We will map through each node in the `body`
|
||||||
|
// and run them through the code generator and join them with a newline.
|
||||||
case 'Program':
|
case 'Program':
|
||||||
return node.body.map(codeGenerator)
|
return node.body.map(codeGenerator)
|
||||||
.join('\n');
|
.join('\n');
|
||||||
|
|
||||||
|
// For `ExpressionStatements` we'll call the code generator on the nested
|
||||||
|
// expression and we'll add a semicolon...
|
||||||
case 'ExpressionStatement':
|
case 'ExpressionStatement':
|
||||||
return (
|
return (
|
||||||
codeGenerator(node.expression) +
|
codeGenerator(node.expression) +
|
||||||
';'
|
';' // << (...because we like to code the *correct* way)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// For `CallExpressions` we will print the `callee`, add an open
|
||||||
|
// parenthesis, we'll map through each node in the `arguments` array and run
|
||||||
|
// them through the code generator, joining them with a comma, and then
|
||||||
|
// we'll add a closing parenthesis.
|
||||||
case 'CallExpression':
|
case 'CallExpression':
|
||||||
return (
|
return (
|
||||||
codeGenerator(node.callee) +
|
codeGenerator(node.callee) +
|
||||||
@ -795,12 +879,15 @@ function codeGenerator(node) {
|
|||||||
')'
|
')'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// For `Identifiers` we'll just return the `node`'s name.
|
||||||
case 'Identifier':
|
case 'Identifier':
|
||||||
return node.name;
|
return node.name;
|
||||||
|
|
||||||
|
// For `NumberLiterals` we'll just return the `node`'s value.
|
||||||
case 'NumberLiteral':
|
case 'NumberLiteral':
|
||||||
return node.value;
|
return node.value;
|
||||||
|
|
||||||
|
// And if we haven't recognized the node, we'll throw an error.
|
||||||
default:
|
default:
|
||||||
throw new TypeError(node.type);
|
throw new TypeError(node.type);
|
||||||
}
|
}
|
||||||
@ -813,16 +900,26 @@ function codeGenerator(node) {
|
|||||||
* ============================================================================
|
* ============================================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FINALLY! We'll create our `compiler` function. Here we will link together
|
||||||
|
* every part of the pipeline.
|
||||||
|
*
|
||||||
|
* 1. input => tokenizer => tokens
|
||||||
|
* 2. tokens => parser => ast
|
||||||
|
* 3. ast => transformer => newAst
|
||||||
|
* 4. newAst => generator => output
|
||||||
|
*/
|
||||||
|
|
||||||
function compiler(input) {
|
function compiler(input) {
|
||||||
var tokens = tokenizer(input);
|
var tokens = tokenizer(input);
|
||||||
var ast = parser(tokens);
|
var ast = parser(tokens);
|
||||||
var newAst = transformer(ast);
|
var newAst = transformer(ast);
|
||||||
var output = codeGenerator(newAst);
|
var output = codeGenerator(newAst);
|
||||||
|
|
||||||
|
// and simply return the output!
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ============================================================================
|
* ============================================================================
|
||||||
* (๑˃̵ᴗ˂̵)و
|
* (๑˃̵ᴗ˂̵)و
|
||||||
|
Loading…
Reference in New Issue
Block a user