314 lines
11 KiB
Plaintext
314 lines
11 KiB
Plaintext
package slangc.model;
|
|
|
|
import slangc.api.BytecodeInstructionWriter;
|
|
import slangc.api.Reporter;
|
|
import slangc.bytecode.TypeSignature;
|
|
import slangc.model.clauses.Arguments;
|
|
import slangc.model.expressions.*;
|
|
import slangc.parser.Branch;
|
|
import slangc.parser.ErrorType;
|
|
import slangc.parser.Node;
|
|
import slangc.parser.NodeType;
|
|
import slangc.parser.NoteAnnotation;
|
|
|
|
public abstract class ExpressionModel extends StatementOrExpression implements ExpressionOwner {
|
|
ExpressionOwner owner;
|
|
Node source;
|
|
ExpressionResult resolvedResult;
|
|
|
|
public ExpressionModel(ExpressionOwner owner, Node source) {
|
|
this.owner = owner;
|
|
this.source = source;
|
|
}
|
|
|
|
public ExpressionOwner getOwner() {
|
|
return owner;
|
|
}
|
|
|
|
public Node getSource() {
|
|
return source;
|
|
}
|
|
|
|
public static ExpressionModel construct(ExpressionOwner owner, Node source) {
|
|
switch (source.getNodeType()) {
|
|
case NodeType.BRACED_EXPRESSION:
|
|
return construct(owner, ((Branch) source).getSubnode(1));
|
|
case NodeType.INTEGER_LITERAL_EXPRESSION:
|
|
case NodeType.CHAR_LITERAL_EXPRESSION:
|
|
case NodeType.BOOLEAN_LITERAL_EXPRESSION:
|
|
case NodeType.STRING_LITERAL_EXPRESSION:
|
|
case NodeType.FLOAT_LITERAL_EXPRESSION:
|
|
case NodeType.NULL_LITERAL_EXPRESSION:
|
|
return new LiteralExpression(owner, source);
|
|
case NodeType.ADDITIVE_EXPRESSION:
|
|
case NodeType.MULTIPLICATIVE_EXPRESSION:
|
|
case NodeType.COMPARISON_EXPRESSION:
|
|
case NodeType.LOGICAL_EXPRESSION:
|
|
case NodeType.SHIFT_EXPRESSION:
|
|
case NodeType.EQUALITY_EXPRESSION:
|
|
case NodeType.BITWISE_AND_EXPRESSION:
|
|
case NodeType.BITWISE_XOR_EXPRESSION:
|
|
case NodeType.BITWISE_OR_EXPRESSION:
|
|
case NodeType.LOGICAL_AND_EXPRESSION:
|
|
case NodeType.LOGICAL_OR_EXPRESSION:
|
|
return new BinaryExpression(owner, source);
|
|
case NodeType.CONDITIONAL_EXPRESSION:
|
|
return new ConditionalExpression(owner, source);
|
|
case NodeType.REFERENCE_EXPRESSION:
|
|
return new ReferenceExpression(owner, source);
|
|
case NodeType.GENERIC_REFERENCE_EXPRESSION:
|
|
return new GenericReferenceExpression(owner, source);
|
|
case NodeType.THIS_EXPRESSION:
|
|
return new ThisExpression(owner, source);
|
|
case NodeType.SUBREFERENCE_EXPRESSION:
|
|
if (((Branch)source).getSubnode(0).getNodeType() == NodeType.SUPER) {
|
|
return new SuperReferenceExpression(owner, source);
|
|
} else {
|
|
return new SubreferenceExpression(owner, source);
|
|
}
|
|
case NodeType.ASSIGNMENT_EXPRESSION:
|
|
return new AssignmentExpression(owner, source);
|
|
case NodeType.UNARY_EXPRESSION:
|
|
return new UnaryExpression(owner, source);
|
|
case NodeType.COUNT_EXPRESSION:
|
|
return new CountExpression(owner, source);
|
|
case NodeType.CAST_EXPRESSION:
|
|
return new CastExpression(owner, source);
|
|
case NodeType.INSTANCEOF_EXPRESSION:
|
|
return new InstanceofExpression(owner, source);
|
|
case NodeType.TYPE_EXPRESSION:
|
|
return new TypeExpression(owner, source);
|
|
case NodeType.OUTER_THIS_EXPRESSION:
|
|
return new OuterThisExpression(owner, source);
|
|
case NodeType.NEW_OBJECT_EXPRESSION:
|
|
return new NewObjectExpression(owner, source);
|
|
case NodeType.NEW_CLASS_EXPRESSION:
|
|
return new NewClassExpression(owner, source);
|
|
case NodeType.NEW_CLEARED_ARRAY_EXPRESSION:
|
|
return new NewClearedArrayExpression(owner, source);
|
|
case NodeType.NEW_INITIALISED_ARRAY_EXPRESSION:
|
|
return new NewInitialisedArrayExpression(owner, source);
|
|
case NodeType.SUPER_CONSTRUCTOR_CALL_EXPRESSION:
|
|
return new SuperConstructorCallExpression(owner, source);
|
|
case NodeType.THIS_CONSTRUCTOR_CALL_EXPRESSION:
|
|
return new ThisConstructorCallExpression(owner, source);
|
|
case NodeType.NORMAL_METHOD_CALL_EXPRESSION:
|
|
return new NormalMethodCallExpression(owner, source);
|
|
case NodeType.AUTOMATIC_METHOD_CALL_EXPRESSION:
|
|
return new AutomaticMethodCallExpression(owner, source);
|
|
case NodeType.SUPER_METHOD_CALL_EXPRESSION:
|
|
return new SuperMethodCallExpression(owner, source);
|
|
case NodeType.THIS_METHOD_CALL_EXPRESSION:
|
|
return new ThisMethodCallExpression(owner, source);
|
|
case NodeType.ARRAY_INDEX_EXPRESSION:
|
|
return new ArrayIndexExpression(owner, source);
|
|
case NodeType.ARRAY_INITIALISER:
|
|
return new ArrayInitialiserExpression(owner, source);
|
|
default:
|
|
return new UnrecognisedExpression(owner, source);
|
|
}
|
|
}
|
|
|
|
public void dump(Reporter reporter, String indent, String incr) {
|
|
reporter.note("DUMP", indent + incr + "TODO: dump " + Type.of(this));
|
|
dumpResolved(reporter, indent + incr, incr);
|
|
}
|
|
|
|
public void dumpResolved(Reporter reporter, String indent, String incr) {
|
|
if (isResolved()) {
|
|
if (hasTargetMethod()) {
|
|
reporter.note("DUMP", indent + incr + "Target method is " + getTargetMethod());
|
|
}
|
|
reporter.note("DUMP", indent + incr + "Result is " + getResult());
|
|
}
|
|
}
|
|
|
|
public InnerTypeScope getTypeScope() {
|
|
// TODO: Better interface support
|
|
if (getOwner() instanceof ExpressionModel) {
|
|
return ((ExpressionModel)getOwner()).getTypeScope();
|
|
}
|
|
return getOwner().getTypeScope();
|
|
}
|
|
|
|
public TypeModel resolveType(Node subnode) {
|
|
// TODO: Better interface support
|
|
if (getOwner() instanceof ExpressionModel) {
|
|
return ((ExpressionModel)getOwner()).resolveType(subnode);
|
|
}
|
|
return getOwner().resolveType(subnode);
|
|
}
|
|
|
|
public MemberModel getMethodOrField() {
|
|
// TODO: Better interface support
|
|
if (getOwner() instanceof ExpressionModel) {
|
|
return ((ExpressionModel)getOwner()).getMethodOrField();
|
|
}
|
|
return getOwner().getMethodOrField();
|
|
}
|
|
|
|
public Named lookupSimpleName(String name) {
|
|
// TODO: Better interface support
|
|
if (getOwner() instanceof ExpressionModel) {
|
|
return ((ExpressionModel)getOwner()).lookupSimpleName(name);
|
|
}
|
|
return getOwner().lookupSimpleName(name);
|
|
}
|
|
|
|
public MethodModel[] lookupSimpleMethod(String name) {
|
|
// TODO: Better interface support
|
|
if (getOwner() instanceof ExpressionModel) {
|
|
return ((ExpressionModel)getOwner()).lookupSimpleMethod(name);
|
|
}
|
|
return getOwner().lookupSimpleMethod(name);
|
|
}
|
|
|
|
//@Override
|
|
public TypeModel getExpectedResult(ExpressionModel e) {
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Used as a default for getSubexpressions() to avoid creating empty arrays.
|
|
*/
|
|
public static final ExpressionModel[] NO_SUBEXPRESSIONS = new ExpressionModel[0];
|
|
|
|
public abstract ExpressionModel[] getSubexpressions();
|
|
|
|
protected int resolveSubexpressions() {
|
|
int nresolved = 0;
|
|
ExpressionModel[] exprs = getSubexpressions();
|
|
for (int i = 0; i < exprs.length; i++) {
|
|
nresolved += exprs[i].resolveExpressions();
|
|
}
|
|
return nresolved;
|
|
}
|
|
|
|
@Override
|
|
public final int resolveExpressions() {
|
|
if (isResolved()) {
|
|
return 0;
|
|
}
|
|
int nresolved = resolveSubexpressions();
|
|
/** If subexpressions resulted in an error, return early to avoid making concentric nonsense errors. */
|
|
if (getSource().countErrorsRecursively() > 0) {
|
|
return nresolved;
|
|
}
|
|
resolvedResult = resolveResult();
|
|
if (resolvedResult == null) {
|
|
/* For consistency, failed resolutions logically resolve to an invalid result. */
|
|
resolvedResult = ExpressionResult.INVALID;
|
|
}
|
|
|
|
if (resolvedResult.getKind() == ExpressionResult.Kind.INVALID) {
|
|
if (getSource() == null) {
|
|
throw new Error("Internal error: Failed to resolve synthetic expression " + this);
|
|
}
|
|
if (getSource() != null && getSource().countErrorsRecursively() == 0) {
|
|
getSource().annotate(ErrorType.INTERNAL_ERROR, "Compiler failed to derive any result information in " + Type.of(this));
|
|
}
|
|
} else {
|
|
nresolved++;
|
|
}
|
|
|
|
return nresolved;
|
|
}
|
|
|
|
/**
|
|
* This should only usually be called once by resolveExpressions(). Before the first call,
|
|
* any subexpressions are resolved recursively. After the first call,
|
|
* the expression result is stored by ExpressionModel and accessible from getResult().
|
|
*
|
|
* @return Either an ExpressionResult instance holding the resolved information
|
|
* about the expression's result, or null (which will be taken as an invalid result).
|
|
*/
|
|
//protected abstract ExpressionResult resolveResult();
|
|
protected ExpressionResult resolveResult() {return null;}
|
|
|
|
public boolean isResolved() {
|
|
return resolvedResult != null;
|
|
}
|
|
|
|
public ExpressionResult getResult() {
|
|
if (!isResolved()) {
|
|
throw new Error("Attempting to get result of unresolved expression!");
|
|
}
|
|
return resolvedResult;
|
|
}
|
|
|
|
public SystemModel getSystem() {
|
|
return getOwner().getMethodOrField().getOwner().getPackage().getSystem();
|
|
}
|
|
|
|
public boolean hasTargetMethod() {
|
|
return false;
|
|
}
|
|
|
|
public MethodModel getTargetMethod() {
|
|
throw new Error("Can't get target method of a " + Type.of(this));
|
|
}
|
|
|
|
/**
|
|
* Attempts to select the best applicable method from a list of available options, using
|
|
* the static methods of MethodModel to sort through them. The reason for doing the final
|
|
* selection here is so that we can report errors in the right place and in a consistent way.
|
|
*
|
|
* <p/>
|
|
* If a single "best" method is found, it is returned. Otherwise (if zero methods match or
|
|
* if multiple match and we can't select a best one from them) an error is reported and
|
|
* null is returned.
|
|
*
|
|
* @param availableMethods
|
|
* @param arguments
|
|
* @return
|
|
*/
|
|
public MethodModel bestTargetMethod(MethodModel[] availableMethods, Arguments arguments) {
|
|
MethodModel[] bestChoices = //MethodModel.findApplicableMethods(availableMethods, arguments);
|
|
MethodModel.bestApplicableMethods(availableMethods, arguments, false);
|
|
|
|
/* If we can't find a relevant method without varargs, only then try varargs. */
|
|
if (bestChoices.length != 1) {
|
|
MethodModel[] newChoices = MethodModel.bestApplicableMethods(availableMethods, arguments, true);
|
|
if (newChoices.length == 1) {
|
|
return newChoices[0];
|
|
}
|
|
}
|
|
|
|
if (bestChoices.length == 1) {
|
|
return bestChoices[0];
|
|
} else if (bestChoices.length > 1) {
|
|
getSource().annotate(ErrorType.INTERNAL_ERROR, "Ambiguous method invocation");
|
|
for (int i = 0; i < bestChoices.length; i++) {
|
|
getSource().annotate(new NoteAnnotation("Could be referring to " + bestChoices[i]));
|
|
}
|
|
return null;
|
|
} else { // No choices
|
|
getSource().annotate(ErrorType.INTERNAL_ERROR, "No matching method for " + arguments.resultString());
|
|
if (availableMethods.length == 0) {
|
|
getSource().annotate(new NoteAnnotation("There appear to be no such methods to select from"));
|
|
} else {
|
|
for (int i = 0; i < availableMethods.length; i++) {
|
|
getSource().annotate(new NoteAnnotation("Failed to match " + availableMethods[i]));
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public abstract TypeSignature innerGenerate(BytecodeInstructionWriter w);
|
|
|
|
public final TypeSignature generate(BytecodeInstructionWriter w) {
|
|
w.pushSource(getSource());
|
|
TypeSignature result = innerGenerate(w);
|
|
w.popSource();
|
|
if (result == null) {
|
|
result = TypeSignature.VOID;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public void generateStore(BytecodeInstructionWriter w, TypeSignature storing) {
|
|
w.genError("TODO: generateStore for " + Type.of(this));
|
|
}
|
|
}
|