module RKelly module Visitors class ECMAVisitor < Visitor def initialize @indent = 0 end def visit_ParentheticalNode(o) "(#{o.value.accept(self)})" end def visit_SourceElementsNode(o) o.value.map { |x| "#{indent}#{x.accept(self)}" }.join("\n") end def visit_VarStatementNode(o) "var #{o.value.map { |x| x.accept(self) }.join(', ')};" end def visit_ConstStatementNode(o) "const #{o.value.map { |x| x.accept(self) }.join(', ')};" end def visit_VarDeclNode(o) "#{o.name}#{o.value ? o.value.accept(self) : nil}" end def visit_AssignExprNode(o) " = #{o.value.accept(self)}" end def visit_NumberNode(o) o.value.to_s end def visit_ForNode(o) init = o.init ? o.init.accept(self) : ';' test = o.test ? o.test.accept(self) : '' counter = o.counter ? o.counter.accept(self) : '' # VarStatementNodes will have a ; appended automatically, but other # expressions won't, so make sure it's there if init[-1,1] != ';' init += ';' end "for(#{init} #{test}; #{counter}) #{o.value.accept(self)}" end def visit_LessNode(o) "#{o.left.accept(self)} < #{o.value.accept(self)}" end def visit_ResolveNode(o) o.value end def visit_PostfixNode(o) "#{o.operand.accept(self)}#{o.value}" end def visit_PrefixNode(o) "#{o.value}#{o.operand.accept(self)}" end def visit_BlockNode(o) @indent += 1 "{\n#{o.value.accept(self)}\n#{@indent -=1; indent}}" end def visit_ExpressionStatementNode(o) "#{o.value.accept(self)};" end def visit_OpEqualNode(o) "#{o.left.accept(self)} = #{o.value.accept(self)}" end def visit_FunctionCallNode(o) "#{o.value.accept(self)}(#{o.arguments.accept(self)})" end def visit_ArgumentsNode(o) o.value.map { |x| x.accept(self) }.join(', ') end def visit_StringNode(o) o.value end def visit_NullNode(o) "null" end def visit_FunctionDeclNode(o) "#{indent}function #{o.value}(" + "#{o.arguments.map { |x| x.accept(self) }.join(', ')})" + "#{o.function_body.accept(self)}" end def visit_ParameterNode(o) o.value end def visit_FunctionBodyNode(o) @indent += 1 "{\n#{o.value.accept(self)}\n#{@indent -=1; indent}}" end def visit_BreakNode(o) "break" + (o.value ? " #{o.value}" : '') + ';' end def visit_ContinueNode(o) "continue" + (o.value ? " #{o.value}" : '') + ';' end def visit_TrueNode(o) "true" end def visit_FalseNode(o) "false" end def visit_EmptyStatementNode(o) ';' end def visit_RegexpNode(o) o.value end def visit_DotAccessorNode(o) "#{o.value.accept(self)}.#{o.accessor}" end def visit_ThisNode(o) "this" end def visit_BitwiseNotNode(o) "~#{o.value.accept(self)}" end def visit_DeleteNode(o) "delete #{o.value.accept(self)}" end def visit_ArrayNode(o) "[#{o.value.map { |x| x ? x.accept(self) : '' }.join(', ')}]" end def visit_ElementNode(o) o.value.accept(self) end def visit_LogicalNotNode(o) "!#{o.value.accept(self)}" end def visit_UnaryMinusNode(o) "-#{o.value.accept(self)}" end def visit_UnaryPlusNode(o) "+#{o.value.accept(self)}" end def visit_ReturnNode(o) "return" + (o.value ? " #{o.value.accept(self)}" : '') + ';' end def visit_ThrowNode(o) "throw #{o.value.accept(self)};" end def visit_TypeOfNode(o) "typeof #{o.value.accept(self)}" end def visit_VoidNode(o) "void(#{o.value.accept(self)})" end [ [:Add, '+'], [:BitAnd, '&'], [:BitOr, '|'], [:BitXOr, '^'], [:Divide, '/'], [:Equal, '=='], [:Greater, '>'], [:Greater, '>'], [:GreaterOrEqual, '>='], [:GreaterOrEqual, '>='], [:In, 'in'], [:InstanceOf, 'instanceof'], [:LeftShift, '<<'], [:LessOrEqual, '<='], [:LogicalAnd, '&&'], [:LogicalOr, '||'], [:Modulus, '%'], [:Multiply, '*'], [:NotEqual, '!='], [:NotStrictEqual, '!=='], [:OpAndEqual, '&='], [:OpDivideEqual, '/='], [:OpLShiftEqual, '<<='], [:OpMinusEqual, '-='], [:OpModEqual, '%='], [:OpMultiplyEqual, '*='], [:OpOrEqual, '|='], [:OpPlusEqual, '+='], [:OpRShiftEqual, '>>='], [:OpURShiftEqual, '>>>='], [:OpXOrEqual, '^='], [:RightShift, '>>'], [:StrictEqual, '==='], [:Subtract, '-'], [:UnsignedRightShift, '>>>'], ].each do |name,op| define_method(:"visit_#{name}Node") do |o| "#{o.left.accept(self)} #{op} #{o.value.accept(self)}" end end def visit_WhileNode(o) "while(#{o.left.accept(self)}) #{o.value.accept(self)}" end def visit_SwitchNode(o) "switch(#{o.left.accept(self)}) #{o.value.accept(self)}" end def visit_CaseBlockNode(o) @indent += 1 "{\n" + (o.value ? o.value.map { |x| x.accept(self) }.join('') : '') + "#{@indent -=1; indent}}" end def visit_CaseClauseNode(o) if o.left case_code = "#{indent}case #{o.left.accept(self)}:\n" else case_code = "#{indent}default:\n" end @indent += 1 case_code += "#{o.value.accept(self)}\n" @indent -= 1 case_code end def visit_DoWhileNode(o) "do #{o.left.accept(self)} while(#{o.value.accept(self)});" end def visit_WithNode(o) "with(#{o.left.accept(self)}) #{o.value.accept(self)}" end def visit_LabelNode(o) "#{o.name}: #{o.value.accept(self)}" end def visit_ObjectLiteralNode(o) @indent += 1 lit = "{" + (o.value.length > 0 ? "\n" : ' ') + o.value.map { |x| "#{indent}#{x.accept(self)}" }.join(",\n") + (o.value.length > 0 ? "\n" : '') + '}' @indent -= 1 lit end def visit_PropertyNode(o) "#{o.name}: #{o.value.accept(self)}" end def visit_GetterPropertyNode(o) "get #{o.name}#{o.value.accept(self)}" end def visit_SetterPropertyNode(o) "set #{o.name}#{o.value.accept(self)}" end def visit_FunctionExprNode(o) "#{o.value}(#{o.arguments.map { |x| x.accept(self) }.join(', ')}) " + "#{o.function_body.accept(self)}" end def visit_CommaNode(o) "#{o.left.accept(self)}, #{o.value.accept(self)}" end def visit_IfNode(o) "if(#{o.conditions.accept(self)}) #{o.value.accept(self)}" + (o.else ? " else #{o.else.accept(self)}" : '') end def visit_ConditionalNode(o) "#{o.conditions.accept(self)} ? #{o.value.accept(self)} : " + "#{o.else.accept(self)}" end def visit_ForInNode(o) "for(#{o.left.accept(self)} in #{o.right.accept(self)}) " + "#{o.value.accept(self)}" end def visit_TryNode(o) "try #{o.value.accept(self)}" + (o.catch_block ? " catch(#{o.catch_var}) #{o.catch_block.accept(self)}" : '') + (o.finally_block ? " finally #{o.finally_block.accept(self)}" : '') end def visit_BracketAccessorNode(o) "#{o.value.accept(self)}[#{o.accessor.accept(self)}]" end def visit_NewExprNode(o) "new #{o.value.accept(self)}(#{o.arguments.accept(self)})" end private def indent; ' ' * @indent * 2; end end end end