/**
*
* ----------------------------------------------------------------------------
* 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.) | }
* | }
* | }]
* | }
* ----------------------------------------------------------------------------
*/
function traverser(ast,visitor){
function traverserArray(array, parent){
array.forEach(node=>{
traverserNode(node, parent)
})
}
function traverserNode(node,parent){
const method = visitor[node.type]
if(method&&method.enter){
method.enter(node, parent)
}
switch(node.type){
case 'Program':
traverserArray(node.body,node);
break;
case 'CallExpression':
traverserArray(node.params,node);
break;
case 'NumberLiteral':
case 'NumberLiteral':
break;
default:
throw new TypeError(node.type)
}
if(method&&method.exit){
method.exit(node, parent)
}
return
}
traverserNode(ast, null)
}
function transformer(ast) {
let newAst = {
type:'Program',
body:[]
}
ast._context = newAst.body
traverser(ast, {
NumberLiteral:{
enter(node, parent){
parent._context.push({
type: 'NumberLiteral',
value:node.value
})
}
},
StringLiteral:{
enter(node, parent){
parent._context.push({
type: 'StringLiteral',
value:node.value
})
}
},
CallExpression:{
enter(node,parent){
let expression = {
type: 'CallExpression',
callee:{
type: 'Identifier',
name: node.name
},
arguments:[]
}
node._context = expression.arguments
if(parent.type!=="CallExpression"){
expression = {
type: "ExpressionStatement",
expression: expression
}
}
parent._context.push(expression)
}
}
})
console.log(newAst)
}
const ast1 = {
"type": "Program",
"body": [
{
"type": "CallExpression",
"name": "add",
"params": [
{
"type": "NumberLiteral",
"value": "2"
},
{
"type": "CallExpression",
"name": "subtract",
"params": [
{
"type": "NumberLiteral",
"value": "4"
},
{
"type": "NumberLiteral",
"value": "2"
}
]
}
]
}
]
}
transformer(ast1)
console