Skip to content

Instantly share code, notes, and snippets.

@jpillora
Last active June 5, 2019 18:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jpillora/5855794 to your computer and use it in GitHub Desktop.
Save jpillora/5855794 to your computer and use it in GitHub Desktop.
Acorn (SpiderMonkey) AST Renderer [Work in progress - Not fully tested]
#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