Expression.java

package org.sterling.runtime.expression;

import static org.sterling.runtime.expression.BooleanConstant.FALSE;
import static org.sterling.runtime.expression.ExpressionConversions.convertSymbol;
import static org.sterling.runtime.expression.ExpressionFactory.constant;

import org.sterling.SterlingException;
import org.sterling.runtime.exception.AppliedExpressionException;
import org.sterling.runtime.exception.UndefinedMemberException;

public abstract class Expression {

    public <R, D> R accept(ExpressionVisitor<R, D> visitor, D data) throws SterlingException {
        return visitor.visitPrimaryExpression(this, data);
    }

    public Expression access(Expression member) throws SterlingException {
        Symbol symbol = convertSymbol(member);
        switch (symbol.getValue()) {
            case "isNothing": return FALSE;
            case "toString": return constant(toString());
            default: throw new UndefinedMemberException(
                "Expression '" + getName() + "' does not define member '" + symbol.getValue() + "'"
            );
        }
    }

    public Expression apply(Expression argument) throws SterlingException {
        throw new AppliedExpressionException("Cannot apply arguments to '" + getName() + "' expression");
    }

    public Expression evaluate() throws SterlingException {
        Expression expression = reduce();
        while (expression.isReducible()) {
            expression = expression.reduce();
        }
        return expression;
    }

    public Expression reduce() throws SterlingException {
        return this;
    }

    protected boolean isReducible() {
        return false;
    }

    private String getName() {
        return getClass().getSimpleName();
    }
}