/*
* Copyright (c) 2012-2015, Microsoft Mobile
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.juniversal.translator.swift;
import org.eclipse.jdt.core.dom.*;
import org.juniversal.translator.core.*;
import java.io.Writer;
import java.util.List;
public class SwiftSourceFileWriter extends SourceFileWriter {
private SwiftTranslator swiftTranslator;
private Context context;
public SwiftSourceFileWriter(SwiftTranslator swiftTranslator, SourceFile sourceFile, Writer writer) {
super(swiftTranslator, sourceFile, writer);
this.swiftTranslator = swiftTranslator;
this.context = new Context();
addDeclarationWriters();
addStatementWriters();
addExpressionWriters();
// Simple name
addWriter(SimpleName.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
SimpleName simpleName = (SimpleName) node;
matchAndWrite(simpleName.getIdentifier());
}
});
}
@Override
public SwiftTranslator getTranslator() {
return swiftTranslator;
}
@Override
public Context getContext() {
return getContext();
}
/**
* Add visitors for class, method, field, and type declarations.
*/
private void addDeclarationWriters() {
// TODO: Implement this
// Compilation unit
addWriter(CompilationUnit.class, new CompilationUnitWriter(this));
// TODO: Implement this
// Type (class/interface) declaration
addWriter(TypeDeclaration.class, new TypeDeclarationWriter(this));
// TODO: Implement this
// Method declaration (which includes implementation)
addWriter(MethodDeclaration.class, new MethodDeclarationWriter(this));
// TODO: Implement this
// Field declaration
/*
addWriter(FieldDeclaration.class, new FieldDeclarationWriter(this));
*/
// TODO: Implement this
// Variable declaration fragment
addWriter(VariableDeclarationFragment.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
VariableDeclarationFragment variableDeclarationFragment = (VariableDeclarationFragment) node;
// TODO: Handle syntax with extra dimensions on array
if (variableDeclarationFragment.getExtraDimensions() > 0)
throw sourceNotSupported("\"int foo[]\" syntax not currently supported; use \"int[] foo\" instead");
writeNode(variableDeclarationFragment.getName());
Expression initializer = variableDeclarationFragment.getInitializer();
if (initializer != null) {
copySpaceAndComments();
matchAndWrite("=");
copySpaceAndComments();
writeNode(initializer);
}
}
});
// TODO: Implement this
// Single variable declaration (used in parameter list & catch clauses)
addWriter(SingleVariableDeclaration.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
SingleVariableDeclaration singleVariableDeclaration = (SingleVariableDeclaration) node;
// TODO: Handle syntax with extra dimensions on array
if (singleVariableDeclaration.getExtraDimensions() > 0)
throw sourceNotSupported("\"int foo[]\" syntax not currently supported; use \"int[] foo\" instead");
// TODO: Handle final & varargs
SimpleName name = singleVariableDeclaration.getName();
setPositionToStartOfNode(name);
writeNode(name);
write(": ");
int endOfNamePosition = getPosition();
Type type = singleVariableDeclaration.getType();
setPositionToStartOfNode(type);
writeNode(type);
setPosition(endOfNamePosition);
// TODO: Handle initializer
Expression initializer = singleVariableDeclaration.getInitializer();
if (initializer != null)
throw new JUniversalException("Unexpected initializer present for SingleVariableDeclaration");
}
});
// TODO: Implement this
// Simple type
addWriter(SimpleType.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
SimpleType simpleType = (SimpleType) node;
Name name = simpleType.getName();
if (name instanceof QualifiedName) {
QualifiedName qualifiedName = (QualifiedName) name;
write(ASTWriterUtil.getNamespaceNameForPackageName(qualifiedName.getQualifier()));
setPositionToEndOfNode(qualifiedName.getQualifier());
copySpaceAndComments();
matchAndWrite(".", "::");
matchAndWrite(qualifiedName.getName().getIdentifier());
} else {
SimpleName simpleName = (SimpleName) name;
matchAndWrite(simpleName.getIdentifier());
}
}
});
// TODO: Implement this
// Parameterized type
addWriter(ParameterizedType.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
ParameterizedType parameterizedType = (ParameterizedType) node;
writeNode(parameterizedType.getType());
copySpaceAndComments();
matchAndWrite("<");
boolean first = true;
List<?> typeArguments = parameterizedType.typeArguments();
for (Object typeArgumentObject : typeArguments) {
Type typeArgument = (Type) typeArgumentObject;
if (!first) {
copySpaceAndComments();
matchAndWrite(",");
}
copySpaceAndComments();
writeNode(typeArgument);
first = false;
}
matchAndWrite(">");
}
});
// TODO: Implement this
// Array type
addWriter(ArrayType.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
ArrayType arrayType = (ArrayType) node;
write("Array<");
writeNode(arrayType.getElementType());
skipSpaceAndComments();
write(">");
match("[");
skipSpaceAndComments();
match("]");
}
});
// Primitive type
addWriter(PrimitiveType.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
PrimitiveType primitiveType = (PrimitiveType) node;
PrimitiveType.Code code = primitiveType.getPrimitiveTypeCode();
if (code == PrimitiveType.BYTE)
matchAndWrite("byte", "Int8");
else if (code == PrimitiveType.SHORT)
matchAndWrite("short", "Int16");
else if (code == PrimitiveType.CHAR)
matchAndWrite("char", "Character");
// TODO: For now, map 32 bit Java int to Int type in Swift, which can be 32 or 64 bits.
// Later probably add @PreserveJavaIntSemantics annotation to force 32 bit + no overflow checking
else if (code == PrimitiveType.INT)
matchAndWrite("int", "Int");
else if (code == PrimitiveType.LONG) {
matchAndWrite("long", "Int64");
} else if (code == PrimitiveType.FLOAT)
matchAndWrite("float", "Float");
else if (code == PrimitiveType.DOUBLE)
matchAndWrite("double", "Double");
else if (code == PrimitiveType.BOOLEAN)
matchAndWrite("boolean", "Bool");
else if (code == PrimitiveType.VOID)
throw new JUniversalException("Should detect void return types before it gets here");
else
throw invalidAST("Unknown primitive type: " + code);
}
});
}
/**
* Add visitors for the different kinds of statements.
*/
private void addStatementWriters() {
// TODO: Implement this
// Block
addWriter(Block.class, new SwiftASTNodeWriter(this) {
@SuppressWarnings("unchecked")
@Override
public void write(ASTNode node) {
Block block = (Block) node;
matchAndWrite("{");
boolean firstStatement = true;
for (Statement statement : (List<Statement>) block.statements()) {
// If the first statement is a super constructor invocation, we skip it since
// it's included as part of the method declaration in C++. If a super
// constructor invocation is a statement other than the first, which it should
// never be, we let that error out since writeNode won't find a match for it.
if (firstStatement && statement instanceof SuperConstructorInvocation)
setPositionToEndOfNodeSpaceAndComments(statement);
else {
copySpaceAndComments();
writeNode(statement);
}
firstStatement = false;
}
copySpaceAndComments();
matchAndWrite("}");
}
});
// TODO: Implement this
// Empty statement (";")
addWriter(EmptyStatement.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
matchAndWrite(";");
}
});
// TODO: Implement this
// Expression statement
addWriter(ExpressionStatement.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
ExpressionStatement expressionStatement = (ExpressionStatement) node;
writeNode(expressionStatement.getExpression());
copySpaceAndComments();
matchAndWrite(";");
}
});
// If statement
addWriter(IfStatement.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
IfStatement ifStatement = (IfStatement) node;
int ifColumn = getTargetColumn();
matchAndWrite("if");
copySpaceAndCommentsEnsuringDelimiter();
writeConditionNoParens(ifStatement.getExpression());
writeStatementEnsuringBraces(ifStatement.getThenStatement(), ifColumn, false);
Statement elseStatement = ifStatement.getElseStatement();
if (elseStatement != null) {
copySpaceAndComments();
matchAndWrite("else");
writeStatementEnsuringBraces(ifStatement.getElseStatement(), ifColumn, true);
}
}
});
// While statement
addWriter(WhileStatement.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
WhileStatement whileStatement = (WhileStatement) node;
int whileColumn = getTargetColumn();
matchAndWrite("while");
copySpaceAndCommentsEnsuringDelimiter();
writeConditionNoParens(whileStatement.getExpression());
writeStatementEnsuringBraces(whileStatement.getBody(), whileColumn, false);
}
});
// Do while statement
addWriter(DoStatement.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
DoStatement doStatement = (DoStatement) node;
int doColumn = getTargetColumn();
matchAndWrite("do");
writeStatementEnsuringBraces(doStatement.getBody(), doColumn, false);
copySpaceAndComments();
matchAndWrite("while");
copySpaceAndComments();
writeConditionNoParens(doStatement.getExpression());
copySpaceAndComments();
matchAndWrite(";");
}
});
// TODO: Implement this
// Continue statement
addWriter(ContinueStatement.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
ContinueStatement continueStatement = (ContinueStatement) node;
if (continueStatement.getLabel() != null)
throw sourceNotSupported("continue statement with a label isn't supported as that construct doesn't exist in C++; change the code to not use a label");
matchAndWrite("continue");
copySpaceAndComments();
matchAndWrite(";");
}
});
// TODO: Implement this
// Break statement
addWriter(BreakStatement.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
BreakStatement breakStatement = (BreakStatement) node;
if (breakStatement.getLabel() != null)
throw sourceNotSupported("break statement with a label isn't supported as that construct doesn't exist in C++; change the code to not use a label");
matchAndWrite("break");
copySpaceAndComments();
matchAndWrite(";");
}
});
// TODO: Implement this
// For statement
/*
addWriter(ForStatement.class, new ForStatementWriter(this));
*/
// TODO: Implement this
// Return statement
addWriter(ReturnStatement.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
ReturnStatement returnStatement = (ReturnStatement) node;
matchAndWrite("return");
Expression expression = returnStatement.getExpression();
if (expression != null) {
copySpaceAndComments();
writeNode(returnStatement.getExpression());
}
copySpaceAndComments();
matchAndWrite(";");
}
});
// TODO: Implement this
// Local variable declaration statement
addWriter(VariableDeclarationStatement.class, new VariableDeclarationWriter(this));
// TODO: Implement this
// Throw statement
addWriter(ThrowStatement.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
ThrowStatement throwStatement = (ThrowStatement) node;
matchAndWrite("throw");
copySpaceAndComments();
writeNode(throwStatement.getExpression());
copySpaceAndComments();
matchAndWrite(";");
}
});
// TODO: Implement this
// Delegating constructor invocation
addWriter(ConstructorInvocation.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
throw sourceNotSupported("Delegating constructors aren't currently supported; for now you have to change the code to not use them (e.g. by adding an init method)");
}
});
}
/**
* Add visitors for the different kinds of expressions.
*/
private void addExpressionWriters() {
// TODO: Implement this
// Assignment expression
/*
addWriter(Assignment.class, new AssignmentWriter(this));
*/
// TODO: Implement this
// Method invocation
addWriter(MethodInvocation.class, new MethodInvocationWriter(this));
// TODO: Implement this
// Super Method invocation
addWriter(SuperMethodInvocation.class, new MethodInvocationWriter(this));
// TODO: Implement this
// Class instance creation
addWriter(ClassInstanceCreation.class, new ClassInstanceCreationWriter(this));
// TODO: Implement this
// Array creation
/*
addWriter(ArrayCreation.class, new ArrayCreationWriter(this));
*/
// TODO: Implement this
// Variable declaration expression (used in a for statement)
addWriter(VariableDeclarationExpression.class, new VariableDeclarationWriter(this));
// Infix expression
addWriter(InfixExpression.class, new InfixExpressionWriter(this));
// Prefix expression
addWriter(PrefixExpression.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
PrefixExpression prefixExpression = (PrefixExpression) node;
PrefixExpression.Operator operator = prefixExpression.getOperator();
if (operator == PrefixExpression.Operator.INCREMENT)
matchAndWrite("++");
else if (operator == PrefixExpression.Operator.DECREMENT)
matchAndWrite("--");
else if (operator == PrefixExpression.Operator.PLUS)
matchAndWrite("+");
else if (operator == PrefixExpression.Operator.MINUS)
matchAndWrite("-");
else if (operator == PrefixExpression.Operator.COMPLEMENT)
matchAndWrite("~");
else if (operator == PrefixExpression.Operator.NOT)
matchAndWrite("!");
else throw invalidAST("Unknown prefix operator type: " + operator);
// In Swift there can't be any whitespace or comments between a unary prefix operator & its operand, so
// strip it, not copying anything here
skipSpaceAndComments();
writeNode(prefixExpression.getOperand());
}
});
// Postfix expression
addWriter(PostfixExpression.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
PostfixExpression postfixExpression = (PostfixExpression) node;
writeNode(postfixExpression.getOperand());
// In Swift there can't be any whitespace or comments between a postfix operator & its operand, so
// strip it, not copying anything here
skipSpaceAndComments();
PostfixExpression.Operator operator = postfixExpression.getOperator();
if (operator == PostfixExpression.Operator.INCREMENT)
matchAndWrite("++");
else if (operator == PostfixExpression.Operator.DECREMENT)
matchAndWrite("--");
else throw invalidAST("Unknown postfix operator type: " + operator);
}
});
// TODO: Implement this
// instanceof expression
addWriter(InstanceofExpression.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
InstanceofExpression instanceofExpression = (InstanceofExpression) node;
write("INSTANCEOF(");
Expression expression = instanceofExpression.getLeftOperand();
writeNode(expression);
skipSpaceAndComments();
match("instanceof");
skipSpaceAndComments();
Type type = instanceofExpression.getRightOperand();
writeNode(type);
write(")");
}
});
// conditional expression
addWriter(ConditionalExpression.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
ConditionalExpression conditionalExpression = (ConditionalExpression) node;
writeNode(conditionalExpression.getExpression());
copySpaceAndComments();
matchAndWrite("?");
copySpaceAndComments();
writeNode(conditionalExpression.getThenExpression());
copySpaceAndComments();
matchAndWrite(":");
copySpaceAndComments();
writeNode(conditionalExpression.getElseExpression());
}
});
// TODO: Implement this
// this
addWriter(ThisExpression.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
ThisExpression thisExpression = (ThisExpression) node;
// TODO: Handle qualified this expressions; probably need to do from parent invoking
// node & disallow qualified this accesses if not field reference / method
// invocation; it's allowed otherwise in Java but I don't think it does anything
// MyClass.this. --> this->MyClass::
if (thisExpression.getQualifier() != null)
throw new JUniversalException("Qualified this expression isn't supported yet");
matchAndWrite("this");
}
});
// TODO: Implement this
// Field access
addWriter(FieldAccess.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
FieldAccess fieldAccess = (FieldAccess) node;
writeNode(fieldAccess.getExpression());
copySpaceAndComments();
matchAndWrite(".", "->");
writeNode(fieldAccess.getName());
}
});
// TODO: Implement this
// Array access
addWriter(ArrayAccess.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
ArrayAccess arrayAccess = (ArrayAccess) node;
writeNode(arrayAccess.getArray());
copySpaceAndComments();
matchAndWrite("[");
copySpaceAndComments();
writeNode(arrayAccess.getIndex());
copySpaceAndComments();
matchAndWrite("]");
}
});
// TODO: Implement this
// Qualified name
addWriter(QualifiedName.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
QualifiedName qualifiedName = (QualifiedName) node;
// TODO: Figure out the other cases where this can occur & make them all correct
// Here assume that a QualifiedName refers to field access; if it refers to a type,
// the caller should catch that case itself and ensure it never gets here
writeNode(qualifiedName.getQualifier());
copySpaceAndComments();
matchAndWrite(".", "->");
writeNode(qualifiedName.getName());
}
});
// TODO: Implement this
// Parenthesized expression
addWriter(ParenthesizedExpression.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
ParenthesizedExpression parenthesizedExpression = (ParenthesizedExpression) node;
matchAndWrite("(");
copySpaceAndComments();
writeNode(parenthesizedExpression.getExpression());
copySpaceAndComments();
matchAndWrite(")");
}
});
// TODO: Implement this
// Cast expression
addWriter(CastExpression.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
CastExpression castExpression = (CastExpression) node;
matchAndWrite("(", "static_cast<");
copySpaceAndComments();
writeNode(castExpression.getType());
copySpaceAndComments();
matchAndWrite(")", ">");
// Skip just whitespace as that's not normally present here in C++ unlike Java, but
// if there's a newline or comment, preserve that
skipSpaceAndComments();
copySpaceAndComments();
// Write out the parentheses unless by chance the casted expression already includes
// them
boolean needParentheses = !(castExpression.getExpression() instanceof ParenthesizedExpression);
if (needParentheses)
write("(");
writeNode(castExpression.getExpression());
if (needParentheses)
write(")");
}
});
// Number literal
addWriter(NumberLiteral.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
NumberLiteral numberLiteral = (NumberLiteral) node;
String token = numberLiteral.getToken();
boolean isOctal = false;
if (token.length() >= 2 && token.startsWith("0")) {
char secondChar = token.charAt(1);
if (secondChar >= '0' && secondChar <= '7') {
isOctal = true;
}
}
// Swift prefixes octal with 0o whereas Java just uses a leading 0
if (isOctal) {
write("0o");
write(token.substring(1));
} else write(token);
match(token);
}
});
// Boolean literal
addWriter(BooleanLiteral.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
BooleanLiteral booleanLiteral = (BooleanLiteral) node;
matchAndWrite(booleanLiteral.booleanValue() ? "true" : "false");
}
});
// Character literal
addWriter(CharacterLiteral.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
CharacterLiteral characterLiteral = (CharacterLiteral) node;
// TODO: Map character escape sequences
// TODO: Handle conversion of characters to integers where that's normally implicit in Java
write("Character(\"" + characterLiteral.charValue() + "\")");
match(characterLiteral.getEscapedValue());
}
});
// Null literal
addWriter(NullLiteral.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
matchAndWrite("null", "nil");
}
});
// TODO: Implement this
// String literal
addWriter(StringLiteral.class, new SwiftASTNodeWriter(this) {
@Override
public void write(ASTNode node) {
StringLiteral stringLiteral = (StringLiteral) node;
write("new String(" + stringLiteral.getEscapedValue() + "L)");
match(stringLiteral.getEscapedValue());
}
});
}
}