Translator.java

package org.sterling.source.translator;

import static org.sterling.runtime.expression.ExpressionFactory.*;
import static org.sterling.runtime.expression.JavaExpression.JAVA;

import java.util.Set;
import org.sterling.SterlingException;
import org.sterling.runtime.GlobalModule;
import org.sterling.runtime.expression.DeclaredExpression;
import org.sterling.runtime.expression.Expression;
import org.sterling.runtime.expression.Variable;
import org.sterling.source.syntax.*;

public class Translator implements SourceVisitor<Expression, TranslatorState> {

    public Set<DeclaredExpression> translate(SourceNode tree, GlobalModule globalModule) throws SterlingException {
        TranslatorState state = new TranslatorState(globalModule);
        visit(tree, state);
        return state.getDeclarations();
    }

    @Override
    public Expression visit(SourceNode node, TranslatorState state) throws SterlingException {
        return node.accept(this, state);
    }

    @Override
    public Expression visitAccessorExpression(AccessorExpression accessor, TranslatorState state) throws SterlingException {
        return access(state.popExpression(), symbol(accessor.getIdentifier().getValue()));
    }

    @Override
    public Expression visitApplyExpression(ApplyExpression expression, TranslatorState state) throws SterlingException {
        return visit(expression.getOperand(), state);
    }

    @Override
    public Expression visitArgument(ArgumentExpression argument, TranslatorState state) throws SterlingException {
        return apply(state.popExpression(), visit(argument.getExpression(), state));
    }

    @Override
    public Expression visitArgumentsExpression(ArgumentsExpression arguments, TranslatorState state) throws SterlingException {
        return join(arguments.getArgument(), arguments.getTail(), state);
    }

    @Override
    public Expression visitArgumentsExpressionTail(ArgumentsExpressionTail tail, TranslatorState state) throws SterlingException {
        return join(tail.getArgument(), tail.getTail(), state);
    }

    @Override
    public Expression visitBinaryExpression(BinaryExpression binary, TranslatorState state) throws SterlingException {
        return join(binary.getOperand(), binary.getTail(), state);
    }

    @Override
    public Expression visitBinaryExpressionTail(BinaryExpressionTail tail, TranslatorState state) throws SterlingException {
        Expression expression = access(state.popExpression(), symbol(tail.getOperator().getValue()));
        Expression application = apply(expression, visit(tail.getOperand(), state));
        return join(application, tail.getTail(), state);
    }

    @Override
    public Expression visitBooleanLiteral(BooleanLiteral literal, TranslatorState state) throws SterlingException {
        return constant(literal.getLiteral().isTrue());
    }

    @Override
    public Expression visitCharacterLiteral(CharacterLiteral literal, TranslatorState state) throws SterlingException {
        return constant(literal.getLiteral().getCharValue());
    }

    @Override
    public Expression visitConstantExpression(ConstantExpression lambda, TranslatorState state) throws SterlingException {
        return visit(lambda.getBody(), state);
    }

    @Override
    public Expression visitDeclaration(Declaration lambda, TranslatorState state) throws SterlingException {
        state.declare(lambda.getIdentifier(), visit(lambda.getTail(), state));
        return null;
    }

    @Override
    public Expression visitDeclarationLiteral(DeclarationLiteral tail, TranslatorState state) throws SterlingException {
        return visit(tail.getLambda(), state);
    }

    @Override
    public Expression visitDeclarationSequence(DeclarationSequence sequence, TranslatorState state) throws SterlingException {
        visit(sequence.getDeclaration(), state);
        visit(sequence.getTail(), state);
        return null;
    }

    @Override
    public Expression visitDeclarationSequenceTail(DeclarationSequenceTail tail, TranslatorState state) throws SterlingException {
        visit(tail.getSequence(), state);
        return null;
    }

    @Override
    public Expression visitDoubleLiteral(DoubleLiteral literal, TranslatorState state) throws SterlingException {
        return constant(literal.getLiteral().getDoubleValue());
    }

    @Override
    public Expression visitExpression(SourceExpression expression, TranslatorState state) throws SterlingException {
        return visit(expression.getOperand(), state);
    }

    @Override
    public Expression visitFromIdentifier(FromIdentifier identifier, TranslatorState state) throws SterlingException {
        state.appendFrom(identifier.getValue());
        visit(identifier.getTail(), state);
        return null;
    }

    @Override
    public Expression visitFromIdentifierAlias(ImportIdentifierAlias alias, TranslatorState state) throws SterlingException {
        state.setAlias(alias.getValue());
        return null;
    }

    @Override
    public Expression visitFromIdentifierList(FromIdentifierList list, TranslatorState state) throws SterlingException {
        state.addImport(list.getValue());
        visit(list.getAlias(), state);
        visit(list.getTail(), state);
        return null;
    }

    @Override
    public Expression visitFromIdentifierListTail(FromIdentifierListTail tail, TranslatorState state) throws SterlingException {
        visit(tail.getList(), state);
        return null;
    }

    @Override
    public Expression visitFromIdentifierTail(FromIdentifierTail tail, TranslatorState state) throws SterlingException {
        state.appendFrom(tail.getValue());
        visit(tail.getTail(), state);
        return null;
    }

    @Override
    public Expression visitFromIdentifiers(FromIdentifiers identifiers, TranslatorState state) throws SterlingException {
        visit(identifiers.getList(), state);
        return null;
    }

    @Override
    public Expression visitFromStatement(FromStatement from, TranslatorState state) throws SterlingException {
        visit(from.getModule(), state);
        visit(from.getAliases(), state);
        state.acceptImports();
        return null;
    }

    @Override
    public Expression visitFunctionArguments(FunctionArguments arguments, TranslatorState state) throws SterlingException {
        return lambda(
            state.define((Variable) visit(arguments.getArgument(), state)),
            visitLambdaArguments(arguments.getTail(), state)
        );
    }

    @Override
    public Expression visitFunctionArgumentsTail(FunctionArgumentsTail list, TranslatorState state) throws SterlingException {
        return lambda(
            state.define((Variable) visit(list.getArgument(), state)),
            visitLambdaArguments(list.getTail(), state)
        );
    }

    @Override
    public Expression visitFunctionLiteral(FunctionLiteral lambda, TranslatorState state) throws SterlingException {
        state.pushSource(lambda.getExpression());
        state.enterScope();
        Expression expression = visit(lambda.getArguments(), state);
        state.leaveScope();
        return expression;
    }

    @Override
    public Expression visitImportHeader(ImportHeader header, TranslatorState state) throws SterlingException {
        visit(header.getStatement(), state);
        visit(header.getSuffix(), state);
        return null;
    }

    @Override
    public Expression visitImportHeaderSuffix(ImportHeaderSuffix suffix, TranslatorState state) throws SterlingException {
        visit(suffix.getTail(), state);
        return null;
    }

    @Override
    public Expression visitImportHeaderTail(ImportHeaderTail tail, TranslatorState state) throws SterlingException {
        visit(tail.getHeader(), state);
        visit(tail.getSuffix(), state);
        return null;
    }

    @Override
    public Expression visitImportHeaders(ImportHeaders headers, TranslatorState state) throws SterlingException {
        if (headers.isDeclared()) {
            visit(headers.getHeader(), state);
        }
        return null;
    }

    @Override
    public Expression visitImportIdentifier(ImportIdentifier identifier, TranslatorState state) throws SterlingException {
        state.appendFrom(identifier.getValue());
        visit(identifier.getTail(), state);
        visit(identifier.getAlias(), state);
        state.acceptImports();
        return null;
    }

    @Override
    public Expression visitImportIdentifierTail(ImportIdentifierTail tail, TranslatorState state) throws SterlingException {
        if (tail.hasTail()) {
            state.appendFrom(tail.getValue());
            visit(tail.getTail(), state);
        } else {
            state.addImport(tail.getValue());
        }
        return null;
    }

    @Override
    public Expression visitImportStatement(ImportStatement statement, TranslatorState state) throws SterlingException {
        visit(statement.getAlias(), state);
        return null;
    }

    @Override
    public Expression visitIndexerArgument(IndexerArgument argument, TranslatorState state) throws SterlingException {
        return apply(state.popExpression(), visit(argument.getExpression(), state));
    }

    @Override
    public Expression visitIndexerArguments(IndexerArguments arguments, TranslatorState state) throws SterlingException {
        return join(arguments.getArgument(), arguments.getSuffix(), state);
    }

    @Override
    public Expression visitIndexerArgumentsSuffix(IndexerArgumentsSuffix suffix, TranslatorState state) throws SterlingException {
        return visitTail(suffix.getTail(), state);
    }

    @Override
    public Expression visitIndexerArgumentsTail(IndexerArgumentsTail tail, TranslatorState state) throws SterlingException {
        return join(tail.getArgument(), tail.getSuffix(), state);
    }

    @Override
    public Expression visitIndexerExpression(IndexerExpression indexer, TranslatorState state) throws SterlingException {
        state.pushExpression(access(state.popExpression(), symbol("[]")));
        return visit(indexer.getArguments(), state);
    }

    @Override
    public Expression visitIntegerLiteral(IntegerLiteral integer, TranslatorState state) throws SterlingException {
        return constant(integer.getLiteral().getIntValue());
    }

    @Override
    public Expression visitJavaExpression(JavaExpression expression, TranslatorState state) throws SterlingException {
        return JAVA;
    }

    @Override
    public Expression visitLambdaArgument(LambdaArgument argument, TranslatorState state) throws SterlingException {
        return variable(argument.getValue());
    }

    @Override
    public Expression visitLambdaLiteral(LambdaLiteral lambda, TranslatorState state) throws SterlingException {
        return lambda(
            state.define((Variable) visit(lambda.getArgument(), state)),
            visitLambdaArguments(lambda.getExpression(), state)
        );
    }

    @Override
    public Expression visitLiteralExpression(LiteralExpression literal, TranslatorState state) throws SterlingException {
        return visit(literal.getOperand(), state);
    }

    @Override
    public Expression visitModuleDeclaration(ModuleDeclaration module, TranslatorState state) throws SterlingException {
        visit(module.getModuleHeader(), state);
        visit(module.getUseHeaders(), state);
        visit(module.getDeclarations(), state);
        return null;
    }

    @Override
    public Expression visitModuleHeader(ModuleHeader header, TranslatorState state) throws SterlingException {
        if (header.isDeclared()) {
            visit(header.getIdentifier(), state);
            state.acceptModule();
        }
        return null;
    }

    @Override
    public Expression visitModuleIdentifier(ModuleIdentifier identifier, TranslatorState state) throws SterlingException {
        state.appendModule(identifier.getValue());
        visit(identifier.getTail(), state);
        return null;
    }

    @Override
    public Expression visitModuleIdentifierTail(ModuleIdentifierTail tail, TranslatorState state) throws SterlingException {
        state.appendModule(tail.getValue());
        visit(tail.getTail(), state);
        return null;
    }

    @Override
    public Expression visitNullLiteral(NullLiteral literal, TranslatorState state) throws SterlingException {
        return nothing();
    }

    @Override
    public Expression visitObjectArgument(ObjectArgument argument, TranslatorState state) throws SterlingException {
        return variable(argument.getValue());
    }

    @Override
    public Expression visitObjectArguments(ObjectArguments arguments, TranslatorState state) throws SterlingException {
        if (arguments.hasList()) {
            return visit(arguments.getList(), state);
        } else {
            return visit(state.popSource(), state);
        }
    }

    @Override
    public Expression visitObjectArgumentsList(ObjectArgumentsList arguments, TranslatorState state) throws SterlingException {
        return lambda(
            state.define((Variable) visit(arguments.getArgument(), state)),
            visitLambdaArguments(arguments.getList(), state)
        );
    }

    @Override
    public Expression visitObjectBody(ObjectBody body, TranslatorState state) throws SterlingException {
        return visit(body.getMembers(), state);
    }

    @Override
    public Expression visitObjectHeader(ObjectHeader header, TranslatorState state) throws SterlingException {
        if (header.isDeclared()) {
            return visit(header.getArguments(), state);
        } else {
            return visit(state.popSource(), state);
        }
    }

    @Override
    public Expression visitObjectLiteral(ObjectLiteral literal, TranslatorState state) throws SterlingException {
        state.pushSource(literal.getBody());
        state.enterScope();
        state.define(SELF);
        Expression expression = visit(literal.getHeader(), state);
        state.leaveScope();
        return expression;
    }

    @Override
    public Expression visitObjectMember(ObjectMember member, TranslatorState state) throws SterlingException {
        state.declareMember(member.getName(), visit(member.getLambda(), state));
        return null;
    }

    @Override
    public Expression visitObjectMemberName(ObjectMemberName name, TranslatorState state) throws SterlingException {
        throw new UnsupportedOperationException();
    }

    @Override
    public Expression visitObjectMembers(ObjectMembers members, TranslatorState state) throws SterlingException {
        state.beginObject();
        visit(members.getMember(), state);
        visit(members.getSuffix(), state);
        return state.acceptObject();
    }

    @Override
    public Expression visitObjectMembersSuffix(ObjectMembersSuffix suffix, TranslatorState state) throws SterlingException {
        return visit(suffix.getTail(), state);
    }

    @Override
    public Expression visitObjectMembersTail(ObjectMembersTail tail, TranslatorState state) throws SterlingException {
        visit(tail.getMember(), state);
        visit(tail.getSuffix(), state);
        return null;
    }

    @Override
    public Expression visitParentheticalExpression(ParentheticalExpression parenthetical, TranslatorState state) throws SterlingException {
        return visit(parenthetical.getOperand(), state);
    }

    @Override
    public Expression visitPrimaryExpression(PrimaryExpression primary, TranslatorState state) throws SterlingException {
        return visit(primary.getOperand(), state);
    }

    @Override
    public Expression visitQualifiedIdentifier(QualifiedIdentifier qualified, TranslatorState state) throws SterlingException {
        return join(state.reference(qualified.getIdentifier().getValue()), qualified.getTail(), state);
    }

    @Override
    public Expression visitQualifiedIdentifierTail(QualifiedIdentifierTail tail, TranslatorState state) throws SterlingException {
        state.pushExpression(access(state.popExpression(), symbol(tail.getIdentifier().getValue())));
        if (tail.hasTail()) {
            return visit(tail.getTail(), state);
        } else {
            return state.popExpression();
        }
    }

    @Override
    public Expression visitSelectorExpression(SelectorExpression selector, TranslatorState state) throws SterlingException {
        return join(selector.getOperand(), selector.getTail(), state);
    }

    @Override
    public Expression visitSelectorExpressionTail(SelectorExpressionTail tail, TranslatorState state) throws SterlingException {
        return join(tail.getOperand(), tail.getTail(), state);
    }

    @Override
    public Expression visitSingleObjectArgument(SingleObjectArgument argument, TranslatorState state) throws SterlingException {
        return lambda(state.define((Variable) visit(argument.getArgument(), state)), visit(state.popSource(), state));
    }

    @Override
    public Expression visitStringLiteral(StringLiteral literal, TranslatorState state) throws SterlingException {
        return constant(literal.getLiteral().getStringValue());
    }

    @Override
    public Expression visitTernaryExpression(TernaryExpression ternary, TranslatorState state) throws SterlingException {
        return conditional(
            visit(ternary.getCondition(), state),
            visit(ternary.getTruePath(), state),
            visit(ternary.getFalsePath(), state)
        );
    }

    @Override
    public Expression visitTernaryExpressionTail(TernaryExpressionTail tail, TranslatorState state) throws SterlingException {
        return visit(tail.getExpression(), state);
    }

    @Override
    public Expression visitUnaryExpression(UnaryExpression unary, TranslatorState state) throws SterlingException {
        return visitUnaryExpression(visit(unary.getOperand(), state), unary);
    }

    private Expression join(SourceNode primary, SourceNode maybe, TranslatorState state) throws SterlingException {
        return join(visit(primary, state), maybe, state);
    }

    private Expression join(Expression tree, SourceNode maybe, TranslatorState state) throws SterlingException {
        state.pushExpression(tree);
        if (maybe.isEmpty()) {
            return state.popExpression();
        } else {
            return visit(maybe, state);
        }
    }

    private Expression visitLambdaArguments(SourceNode list, TranslatorState state) throws SterlingException {
        if (list.isEmpty()) {
            return visit(state.popSource(), state);
        } else {
            return visit(list, state);
        }
    }

    private Expression visitTail(SourceNode tail, TranslatorState state) throws SterlingException {
        if (tail.isEmpty()) {
            return state.popExpression();
        } else {
            return visit(tail, state);
        }
    }

    private Expression visitUnaryExpression(Expression operand, UnaryExpression unary) throws SterlingException {
        if (unary.hasOperator()) {
            String name = unary.getOperator().getValue();
            if ("+".equals(name)) {
                name = "positive";
            } else if ("-".equals(name)) {
                name = "negative";
            }
            return access(operand, symbol(name));
        } else {
            return operand;
        }
    }
}