blob: a894135f7a87c0a9cf49cb8e42b587b1cfa13933 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.flex.compiler.internal.as.codegen;
import java.io.FilterWriter;
import java.io.IOException;
import java.util.ArrayList;
import org.apache.flex.compiler.as.IASEmitter;
import org.apache.flex.compiler.common.ASModifier;
import org.apache.flex.compiler.common.ModifiersSet;
import org.apache.flex.compiler.definitions.IDefinition;
import org.apache.flex.compiler.definitions.IFunctionDefinition;
import org.apache.flex.compiler.definitions.IVariableDefinition;
import org.apache.flex.compiler.definitions.references.INamespaceReference;
import org.apache.flex.compiler.internal.tree.as.ChainedVariableNode;
import org.apache.flex.compiler.internal.tree.as.FunctionNode;
import org.apache.flex.compiler.internal.tree.as.FunctionObjectNode;
import org.apache.flex.compiler.problems.ICompilerProblem;
import org.apache.flex.compiler.tree.ASTNodeID;
import org.apache.flex.compiler.tree.as.IASNode;
import org.apache.flex.compiler.tree.as.IAccessorNode;
import org.apache.flex.compiler.tree.as.IDefinitionNode;
import org.apache.flex.compiler.tree.as.IExpressionNode;
import org.apache.flex.compiler.tree.as.IFunctionNode;
import org.apache.flex.compiler.tree.as.IGetterNode;
import org.apache.flex.compiler.tree.as.IKeywordNode;
import org.apache.flex.compiler.tree.as.IParameterNode;
import org.apache.flex.compiler.tree.as.IScopedNode;
import org.apache.flex.compiler.tree.as.ISetterNode;
import org.apache.flex.compiler.tree.as.IVariableNode;
import org.apache.flex.compiler.visitor.IASBlockWalker;
/**
* The base implementation for an ActionScript emitter.
*
* @author Michael Schmalle
*/
public class ASEmitter implements IASEmitter
{
protected static final String NL = "\n";
protected static final String INDENT_STRING = "\t";
private final FilterWriter out;
private int currentIndent = 0;
private IASBlockWalker walker;
public IASBlockWalker getWalker()
{
return walker;
}
public void setWalker(IASBlockWalker value)
{
walker = value;
}
public ASEmitter(FilterWriter out)
{
this.out = out;
}
@Override
public void write(String value)
{
try
{
out.write(value);
String indent = "";
if (value.indexOf(NL) != -1)
{
// TODO (mschmalle) is StringBuilder faster?
for (int i = 0; i < currentIndent; i++)
indent += INDENT_STRING;
out.write(indent);
}
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
@Override
public void indentPush()
{
currentIndent++;
}
@Override
public void indentPop()
{
currentIndent--;
}
public void writeIndent()
{
String indent = "";
for (int i = 0; i < currentIndent; i++)
indent += INDENT_STRING;
write(indent);
}
public void writeNewline()
{
write(NL);
}
public void writeToken(String value)
{
write(value);
}
public void writeSymbol(String value)
{
write(value);
}
//--------------------------------------------------------------------------
//
//--------------------------------------------------------------------------
@Override
public void emitVarDeclaration(IVariableNode node)
{
if (!(node instanceof ChainedVariableNode))
{
emitMemberKeyword(node);
}
emitDeclarationName(node);
emitType(node.getVariableTypeNode());
emitAssignedValue(node.getAssignedValueNode());
if (!(node instanceof ChainedVariableNode))
{
// check for chained variables
int len = node.getChildCount();
for (int i = 0; i < len; i++)
{
IASNode child = node.getChild(i);
if (child instanceof ChainedVariableNode)
{
write(",");
write(" ");
emitVarDeclaration((IVariableNode) child);
}
}
}
// the client such as IASBlockWalker is responsible for the
// semi-colon and newline handling
}
//--------------------------------------------------------------------------
//
//--------------------------------------------------------------------------
@Override
public void emitFieldDocumentation(IVariableNode node)
{
}
@Override
public void emitField(IVariableNode node)
{
emitFieldDocumentation(node);
IVariableDefinition definition = (IVariableDefinition) node
.getDefinition();
if (!(node instanceof ChainedVariableNode))
{
emitNamespace2(node);
emitModifiers(definition);
emitMemberKeyword(node);
}
emitMemberName(node);
emitType(node.getVariableTypeNode());
emitAssignedValue(node.getAssignedValueNode());
if (!(node instanceof ChainedVariableNode))
{
// check for chained variables
int len = node.getChildCount();
for (int i = 0; i < len; i++)
{
IASNode child = node.getChild(i);
if (child instanceof ChainedVariableNode)
{
write(",");
write(" ");
emitField((IVariableNode) child);
}
}
}
// the client such as IASBlockWalker is responsible for the
// semi-colon and newline handling
}
//--------------------------------------------------------------------------
//
//--------------------------------------------------------------------------
@Override
public void emitMethodDocumentation(IFunctionNode node)
{
}
@Override
public void emitMethod(IFunctionNode node)
{
// see below, this is temp, I don't want a bunch of duplicated code
// at them moment, subclasses can refine anyways, we are generalizing
if (node instanceof IGetterNode)
{
emitGetAccessorDocumentation((IGetterNode) node);
}
else if (node instanceof ISetterNode)
{
emitSetAccessorDocumentation((ISetterNode) node);
}
else
{
emitMethodDocumentation(node);
}
FunctionNode fn = (FunctionNode) node;
// XXX (mschmalle) parseFunctionBody() TEMP until I figure out the correct way to do this
// will need to pass these problems back to the visitor
// Figure out where this is getting parsed!
fn.parseFunctionBody(new ArrayList<ICompilerProblem>());
IFunctionDefinition definition = node.getDefinition();
emitNamespace2(node);
emitModifiers(definition);
emitMemberKeyword(node);
// TODO (mschmalle) I'm cheating right here, I haven't "seen" the light
// on how to properly and efficiently deal with accessors since they are SO alike
// I don't want to lump them in with methods because implementations in the
// future need to know the difference without loopholes
if (node instanceof IAccessorNode)
{
emitAccessorKeyword(((IAccessorNode) node).getAccessorKeywordNode());
}
emitMemberName(node);
emitParamters(node.getParameterNodes());
emitType(node.getReturnTypeNode());
if (node.getParent().getParent().getNodeID() == ASTNodeID.ClassID)
{
emitMethodScope(node.getScopedNode());
}
// the client such as IASBlockWalker is responsible for the
// semi-colon and newline handling
}
@Override
public void emitGetAccessorDocumentation(IGetterNode node)
{
}
@Override
public void emitGetAccessor(IGetterNode node)
{
// just cheat for now, IGetterNode is a IFunctionNode
emitMethod(node);
}
@Override
public void emitSetAccessorDocumentation(ISetterNode node)
{
}
@Override
public void emitSetAccessor(ISetterNode node)
{
// just cheat for now, ISetterNode is a IFunctionNode
emitMethod(node);
}
@Override
public void emitFunctionObject(IExpressionNode node)
{
FunctionObjectNode f = (FunctionObjectNode) node;
FunctionNode fnode = f.getFunctionNode();
write("function");
emitParamters(fnode.getParameterNodes());
emitType(fnode.getTypeNode());
emitFunctionScope(fnode.getScopedNode());
}
//--------------------------------------------------------------------------
//
//--------------------------------------------------------------------------
protected void emitNamespace2(IDefinitionNode node)
{
String namespace = node.getNamespace();
if (namespace != null && !namespace.equals("internal"))
{
write(namespace);
write(" ");
}
}
protected void emitNamespace(IDefinition definition)
{
// namespace (public, protected, private, foo_bar)
// TODO (mschmalle) figure out what to do if there is an explicit internal
// right now if it's internal, code not produced (implied)
if (!definition.isInternal())
{
INamespaceReference reference = definition.getNamespaceReference();
write(reference.getBaseName());
write(" ");
}
}
protected void emitModifiers(IDefinition definition)
{
ModifiersSet modifierSet = definition.getModifiers();
if (modifierSet.hasModifiers())
{
for (ASModifier modifier : modifierSet.getAllModifiers())
{
write(modifier.toString());
write(" ");
}
}
}
protected void emitMemberKeyword(IDefinitionNode node)
{
if (node instanceof IFunctionNode)
{
write("function");
write(" ");
}
else if (node instanceof IVariableNode)
{
write(((IVariableNode) node).isConst() ? "const" : "var");
write(" ");
}
}
protected void emitMemberName(IDefinitionNode node)
{
getWalker().walk(node.getNameExpressionNode());
}
protected void emitDeclarationName(IDefinitionNode node)
{
getWalker().walk(node.getNameExpressionNode());
}
protected void emitParamters(IParameterNode[] nodes)
{
write("(");
int len = nodes.length;
for (int i = 0; i < len; i++)
{
IParameterNode node = nodes[i];
getWalker().walk(node);
if (i < len - 1)
write(", ");
}
write(")");
}
protected void emitType(IExpressionNode node)
{
// TODO (mschmalle) node.getVariableTypeNode() will return "*" if undefined, what to use?
// or node.getReturnTypeNode()
if (node != null)
{
write(":");
getWalker().walk(node);
}
}
protected void emitAssignedValue(IExpressionNode node)
{
if (node != null)
{
write(" ");
write("=");
write(" ");
getWalker().walk(node);
}
}
protected void emitMethodScope(IScopedNode node)
{
write(" ");
getWalker().walk(node);
}
protected void emitAccessorKeyword(IKeywordNode node)
{
getWalker().walk(node);
write(" ");
}
protected void emitFunctionScope(IScopedNode node)
{
// TODO (mschmalle) FunctionObjectNode; does this need specific treatment?
emitMethodScope(node);
}
}