Last active
June 5, 2019 18:55
-
-
Save jpillora/5855794 to your computer and use it in GitHub Desktop.
Acorn (SpiderMonkey) AST Renderer [Work in progress - Not fully tested]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#render function (ast-object) -> code-string | |
render = (-> | |
#handlers for each node type | |
handlers = | |
Literal: (n) -> | |
n.raw | |
Identifier: (n) -> | |
n.name | |
ThisExpression: (n) -> | |
"this" | |
VariableDeclaration: (n) -> | |
"var " + mrender(n.declarations) | |
VariableDeclarator: (n) -> | |
n.id.name + wrender(n.init,'=') | |
BlockStatement: (n) -> | |
"{\n" + ind(mrender(n.body,'\n')) + "\n}" | |
FunctionExpression: (n) -> | |
"function" + wrender(n.id,' ') + '(' + mrender(n.params) + ')' + render(n.body) | |
FunctionDeclaration: 'FunctionExpression' | |
ReturnStatement: (n) -> | |
"return " + render(n.argument) | |
ExpressionStatement: (n) -> | |
render(n.expression) + wrender(mrender(n.arguments),'.') | |
CallExpression: (n) -> | |
render(n.callee)+'('+mrender(n.arguments)+')'+render(n.body) | |
MemberExpression: (n) -> | |
render(n.object) + if n.computed then wrender(n.property,'[',']') else wrender(n.property,'.') | |
IfStatement: (n) -> | |
"if(" + render(n.test) + ")" + | |
render(n.consequent) + | |
(if n.alternate then 'else'+render(n.alternate) else '') | |
ConditionalExpression: (n) -> | |
"(" + render(n.test) + "?" + | |
render(n.consequent) + ":" + | |
render(n.alternate) + ")" | |
UnaryExpression: (n) -> | |
(if n.prefix then n.operator else '') + | |
(if n.operator is 'typeof' then ' ' else '') + | |
render(n.argument) + | |
(if n.prefix then '' else n.operator) | |
UpdateExpression: 'UnaryExpression' | |
BinaryExpression: (n) -> | |
render(n.left) + n.operator + render(n.right) | |
LogicalExpression: 'BinaryExpression' | |
AssignmentExpression: 'BinaryExpression' | |
ForStatement: (n) -> | |
"for(" + render(n.init) + ";" + | |
render(n.test) + ";" + | |
render(n.update) + ")" + | |
render(n.body) | |
ForInStatement: (n) -> | |
"for(" + render(n.left) + " in " + render(n.right) + ")" + ind(render(n.body)) | |
SwitchStatement: (n) -> | |
'switch(' + render(n.discriminant) + ') {\n' + ind(mrender(n.cases, '\n')) + '\n}' | |
SwitchCase: (n) -> | |
test = render(n.test) | |
(if test then 'case '+test else 'default') + ': ' + mrender(n.consequent,';\n') | |
WhileStatement: (n) -> | |
'while(' + render(n.test) + ')' + render(n.body) | |
ThrowStatement: (n) -> | |
"throw " + render(n.argument) | |
ArrayExpression: (n) -> | |
'[' + mrender(n.elements) + ']' | |
ObjectExpression: (n) -> | |
"{}" | |
NewExpression: (n) -> | |
"new " + render(n.callee) + wrender(mrender(n.arguments), '(',')') | |
Program: (n) -> | |
mrender(n.body) | |
#resolve handler alias's | |
for name,handler of handlers | |
if typeof handler is 'string' | |
handlers[name] = handlers[handler] | |
#indent by 2 spaces | |
ind = (str) -> | |
str.replace(/^/gm, ' ') | |
#render then wrap | |
wrender = (n, pre = '', post = '') -> | |
return '' unless n | |
pre + render(n) + post | |
#map over nodes and render | |
mrender = (arr, str = ',') -> | |
return '' unless arr | |
arr.map(render).join(str) | |
#render a node | |
render = (n) -> | |
return n if _.isString n | |
return '' unless n | |
unless n?.type | |
print "missing type on: " | |
json n | |
return '!' | |
if handlers[n.type] | |
return handlers[n.type](n) | |
else | |
print "unknown type: #{n.type}" | |
json n | |
return "" | |
return render | |
)() | |
#syntax check | |
check = (code) -> | |
try | |
new Function(code) | |
print 'ok' | |
catch e | |
print e | |
code = """ | |
var foo = 42; | |
""" | |
newcode = render(acorn.parse(code)) | |
#test validity | |
check newcode | |
print newcode | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment