// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz) // All rights reserved. // // This software may be modified and distributed under the terms // of the BSD license. See the LICENSE file for details. package wyc.lang; import java.util.*; import wybs.lang.Attribute; import wybs.lang.NameID; import wybs.lang.SyntacticElement; import wyc.builder.FlowTypeChecker; import wyc.io.WhileyFileLexer; import wycc.util.Pair; import wycc.util.Triple; import wyfs.lang.Path; import wyil.lang.*; /** * Provides classes for representing expressions in Whiley's source language. * Examples include <i>binary operators</i>, <i>integer constants</i>, <i>field * accesses</i>, etc. Each class is an instance of <code>SyntacticElement</code> * and, hence, can be adorned with certain information (such as source location, * etc). * * @author David J. Pearce * */ public interface Expr extends SyntacticElement { /** * Get the type that this expression will evaluate to. This type splits into * a nominal and raw component. The nominal component retains name * information and, as such, is incomplete. This means one should not use * the nominal component for subtype testing; rather it should only be used * for reporting information to the user (e.g. type errors, etc). The raw * component represents the fully expanded type, and can safely be used for * type testing. However, it can be rather long and cumbersome to read so * should not be reported to the user. * * @return */ public Type result(); /** * An LVal is a special form of expression which may appear on the left-hand * side of an assignment. * * @author David J. Pearce * */ public interface LVal extends Expr {} /** * A Multi expression is one which returns multiple values. Certain * expression forms are permitted to return multiple values and these * implement Multi. * * @author David J. Pearce * */ public interface Multi extends SyntacticElement{ /** * Get all the return types this expression can produce. * * @return */ public List<Type> returns(); } public static class AbstractVariable extends SyntacticElement.Impl implements Expr, LVal { public final String var; public AbstractVariable(String var, Attribute... attributes) { super(attributes); this.var = var; } public AbstractVariable(String var, Collection<Attribute> attributes) { super(attributes); this.var = var; } @Override public Type result() { return null; } @Override public String toString() { return var; } } public static class LocalVariable extends AbstractVariable { public Type type; public LocalVariable(String var, Attribute... attributes) { super(var, attributes); } public LocalVariable(String var, Collection<Attribute> attributes) { super(var, attributes); } @Override public Type result() { return type; } @Override public String toString() { return var; } } public static class AssignedVariable extends LocalVariable { public Type afterType; public AssignedVariable(String var, Attribute... attributes) { super(var, attributes); } public AssignedVariable(String var, Collection<Attribute> attributes) { super(var, attributes); } } public static class Constant extends SyntacticElement.Impl implements Expr { public final wyil.lang.Constant value; public Constant(wyil.lang.Constant val, Attribute... attributes) { super(attributes); this.value = val; } @Override public Type result() { return value.type(); } @Override public String toString() { return value.toString(); } } /** * Represents a cast expression, which has the form: * * <pre> * Term ::= ... * | '(' Type ')' Expression * </pre> * * @param start * @return */ public static class Cast extends SyntacticElement.Impl implements Expr { public final SyntacticType unresolvedType; public Type type; public Expr expr; public Cast(SyntacticType type, Expr expr, Attribute... attributes) { super(attributes); this.unresolvedType = type; this.expr = expr; } @Override public Type result() { return type; } @Override public String toString() { return "(" + unresolvedType.toString() + ") " + expr; } } public static class TypeVal extends SyntacticElement.Impl implements Expr { public final SyntacticType unresolvedType; public Type type; public TypeVal(SyntacticType val, Attribute... attributes) { super(attributes); this.unresolvedType = val; } @Override public Type result() { return Type.T_META; } } public static class AbstractFunctionOrMethod extends SyntacticElement.Impl implements Expr { public final String name; public final ArrayList<SyntacticType> paramTypes; public final ArrayList<String> lifetimeParameters; public Type.FunctionOrMethod type; public AbstractFunctionOrMethod(String name, Collection<SyntacticType> paramTypes, Collection<String> lifetimeParameters, Attribute... attributes) { super(attributes); this.name = name; if(paramTypes != null) { this.paramTypes = new ArrayList<>(paramTypes); } else { this.paramTypes = null; } if(lifetimeParameters != null) { this.lifetimeParameters = new ArrayList<>(lifetimeParameters); } else { this.lifetimeParameters = null; } } public AbstractFunctionOrMethod(String name, Collection<SyntacticType> paramTypes, Collection<String> lifetimeParameters, Collection<Attribute> attributes) { super(attributes); this.name = name; if(paramTypes != null) { this.paramTypes = new ArrayList<>(paramTypes); } else { this.paramTypes = null; } if(lifetimeParameters != null) { this.lifetimeParameters = new ArrayList<>(lifetimeParameters); } else { this.lifetimeParameters = null; } } @Override public Type.FunctionOrMethod result() { return type; } } public static class FunctionOrMethod extends AbstractFunctionOrMethod { public final NameID nid; public FunctionOrMethod(NameID nid, Collection<SyntacticType> paramTypes, Collection<String> lifetimeParameters, Attribute... attributes) { super(nid.name(), paramTypes, lifetimeParameters, attributes); this.nid = nid; } public FunctionOrMethod(NameID nid, Collection<SyntacticType> paramTypes, Collection<String> lifetimeParameters, Collection<Attribute> attributes) { super(nid.name(), paramTypes, lifetimeParameters, attributes); this.nid = nid; } } public static class Lambda extends SyntacticElement.Impl implements Expr { public final ArrayList<WhileyFile.Parameter> parameters; public final HashSet<String> contextLifetimes; public final ArrayList<String> lifetimeParameters; public Expr body; public Type.FunctionOrMethod type; public Lambda(Collection<WhileyFile.Parameter> parameters, Collection<String> contextLifetimes, Collection<String> lifetimeParameters, Expr body, Attribute... attributes) { super(attributes); this.parameters = new ArrayList<>(parameters); this.contextLifetimes = new HashSet<>(contextLifetimes); this.lifetimeParameters = new ArrayList<>(lifetimeParameters); this.body = body; } public Lambda(Collection<WhileyFile.Parameter> parameters, Collection<String> contextLifetimes, Collection<String> lifetimeParameters, Expr body, Collection<Attribute> attributes) { super(attributes); this.parameters = new ArrayList<>(parameters); this.contextLifetimes = new HashSet<>(contextLifetimes); this.lifetimeParameters = new ArrayList<>(lifetimeParameters); this.body = body; } @Override public Type.FunctionOrMethod result() { return type; } } public static class BinOp extends SyntacticElement.Impl implements Expr { public BOp op; public Expr lhs; public Expr rhs; public Type srcType; public BinOp(BOp op, Expr lhs, Expr rhs, Attribute... attributes) { super(attributes); this.op = op; this.lhs = lhs; this.rhs = rhs; } public BinOp(BOp op, Expr lhs, Expr rhs, Collection<Attribute> attributes) { super(attributes); this.op = op; this.lhs = lhs; this.rhs = rhs; } @Override public Type result() { switch(op) { case EQ: case NEQ: case LT: case LTEQ: case GT: case GTEQ: case IS: return Type.T_BOOL; default: return srcType; } } public Type srcType() { return srcType; } @Override public String toString() { return "(" + op + " " + lhs + " " + rhs + ")"; } } // A list access is very similar to a BinOp, except that it can be assiged. public static class IndexOf extends SyntacticElement.Impl implements Expr, LVal { public Expr src; public Expr index; public Type.EffectiveArray srcType; public IndexOf(Expr src, Expr index, Attribute... attributes) { super(attributes); this.src = src; this.index = index; } public IndexOf(Expr src, Expr index, Collection<Attribute> attributes) { super(attributes); this.src = src; this.index = index; } @Override public Type result() { return srcType.getReadableElementType(); } @Override public String toString() { return src + "[" + index + "]"; } } public enum UOp { NOT, NEG, INVERT, ARRAYLENGTH } public static class UnOp extends SyntacticElement.Impl implements Expr { public final UOp op; public Expr mhs; public Type type; public UnOp(UOp op, Expr mhs, Attribute... attributes) { super(attributes); this.op = op; this.mhs = mhs; } @Override public Type result() { if(op == UOp.ARRAYLENGTH) { return Type.T_INT; } else { return type; } } @Override public String toString() { return op + mhs.toString(); } } /** * Represents an array initialiser expression, which is of the form: * * <pre> * ArrayInitialiser ::= '[' [ Expression (',' Expression)+ ] ']' * </pre> * * @return */ public static class ArrayInitialiser extends SyntacticElement.Impl implements Expr { public final ArrayList<Expr> arguments; public Type.Array type; public ArrayInitialiser(Collection<Expr> arguments, Attribute... attributes) { super(attributes); this.arguments = new ArrayList<>(arguments); } public ArrayInitialiser(Attribute attribute, Expr... arguments) { super(attribute); this.arguments = new ArrayList<>(); for(Expr a : arguments) { this.arguments.add(a); } } @Override public Type.Array result() { return type; } } /** * Represents an array generator expression, which is of the form: * * <pre> * ArrayGenerator ::= '[' Expression ';' Expression ']' * </pre> * * @return */ public static class ArrayGenerator extends SyntacticElement.Impl implements Expr { public Expr element; public Expr count; public Type.Array type; public ArrayGenerator(Expr element, Expr count, Attribute... attributes) { super(attributes); this.element = element; this.count = count; } @Override public Type.Array result() { return type; } } public static class Quantifier extends SyntacticElement.Impl implements Expr { public final QOp cop; public final ArrayList<Triple<String,Expr,Expr>> sources; public Expr condition; public Type type; public Quantifier(QOp cop, Collection<Triple<String, Expr, Expr>> sources, Expr condition, Attribute... attributes) { super(attributes); this.cop = cop; this.condition = condition; this.sources = new ArrayList<>(sources); } @Override public Type result() { return type; } } public enum QOp { SOME, ALL, } public static class FieldAccess extends SyntacticElement.Impl implements LVal { public Expr src; public final String name; public Type.EffectiveRecord srcType; public FieldAccess(Expr lhs, String name, Attribute... attributes) { super(attributes); this.src = lhs; this.name = name; } public FieldAccess(Expr lhs, String name, Collection<Attribute> attributes) { super(attributes); this.src = lhs; this.name = name; } @Override public Type result() { return srcType.getReadableFieldType(name); } @Override public String toString() { return src + "." + name; } } public static class ConstantAccess extends SyntacticElement.Impl implements Expr { public final String name; public Path.ID qualification; public wyil.lang.Constant value; public Type type; public ConstantAccess(String name, Path.ID qualification, Attribute... attributes) { super(attributes); this.name = name; this.qualification = qualification; } public ConstantAccess(String name, Path.ID qualification, Collection<Attribute> attributes) { super(attributes); this.name = name; this.qualification = qualification; } @Override public Type result() { // Note: must return our type here, rather than value.type(). This // is because value.type() does not distinguish nominal and raw // types. See #544. return type; } @Override public String toString() { if(qualification == null) { // root return name; } else { return qualification + "." + name; } } } public static class Dereference extends SyntacticElement.Impl implements LVal { public Expr src; public Type.Reference srcType; public Dereference(Expr src, Attribute... attributes) { super(attributes); this.src = src; } @Override public Type result() { return srcType.element(); } @Override public String toString() { return "*" + src.toString(); } } public static class Record extends SyntacticElement.Impl implements Expr { public final String name; public final HashMap<String, Expr> fields; public Type type; public Record(String name, java.util.Map<String, Expr> fields, Attribute... attributes) { super(attributes); this.name = name; this.fields = new HashMap<>(fields); } @Override public Type result() { return type; } } public static class AbstractInvoke extends SyntacticElement.Impl implements Expr, Stmt { public final String name; public Path.ID qualification; public final ArrayList<Expr> arguments; public final ArrayList<String> lifetimeArguments; public AbstractInvoke(String name, Path.ID receiver, Collection<Expr> arguments, Collection<String> lifetimeArguments, Attribute... attributes) { super(attributes); this.name = name; this.qualification = receiver; this.arguments = new ArrayList<>(arguments); if (lifetimeArguments != null) { this.lifetimeArguments = new ArrayList<>(lifetimeArguments); } else { this.lifetimeArguments = null; } } public AbstractInvoke(String name, Path.ID receiver, Collection<Expr> arguments, Collection<String> lifetimeArguments, Collection<Attribute> attributes) { super(attributes); this.name = name; this.qualification = receiver; this.arguments = new ArrayList<>(arguments); if (lifetimeArguments != null) { this.lifetimeArguments = new ArrayList<>(lifetimeArguments); } else { this.lifetimeArguments = null; } } @Override public Type result() { return null; } } public static abstract class FunctionOrMethodCall extends AbstractInvoke implements Multi { public final NameID nid; public FunctionOrMethodCall(NameID nid, Path.ID qualification, Collection<Expr> arguments, Collection<String> lifetimeArguments, Attribute... attributes) { super(nid.name(),qualification,arguments,lifetimeArguments,attributes); this.nid = nid; } public FunctionOrMethodCall(NameID nid, Path.ID qualification, Collection<Expr> arguments, Collection<String> lifetimeArguments, Collection<Attribute> attributes) { super(nid.name(),qualification,arguments,lifetimeArguments,attributes); this.nid = nid; } public NameID nid() { return nid; } public abstract Type.FunctionOrMethod type(); @Override public List<Type> returns() { return Arrays.asList(type().returns()); } } public static class MethodCall extends FunctionOrMethodCall { public Type.Method methodType; public MethodCall(NameID nid, Path.ID qualification, Collection<Expr> arguments, Collection<String> lifetimeArguments, Attribute... attributes) { super(nid,qualification,arguments,lifetimeArguments,attributes); } public MethodCall(NameID nid, Path.ID qualification, Collection<Expr> arguments, Collection<String> lifetimeArguments, Collection<Attribute> attributes) { super(nid,qualification,arguments,lifetimeArguments,attributes); } @Override public Type.Method type() { return methodType; } @Override public Type result() { if (methodType.returns().length == 1) { Type returnType = methodType.returns()[0]; if (this.lifetimeArguments == null || this.lifetimeArguments.isEmpty()) { return returnType; } List<String> lifetimeParams = Arrays.asList(methodType.lifetimeParams()); return FlowTypeChecker.applySubstitution(lifetimeParams, this.lifetimeArguments, returnType); } else { throw new IllegalArgumentException("incorrect number of returns for function call"); } } } /** * Parse a function invocation expression, which has the form: * * <pre> * NameIdentifier '(' [ Expression ( ',' Expression )* ] ')' * </pre> * * @return */ public static class FunctionCall extends FunctionOrMethodCall { public Type.Function functionType; public FunctionCall(NameID nid, Path.ID qualification, Collection<Expr> arguments, Attribute... attributes) { super(nid,qualification,arguments,Collections.<String>emptyList(),attributes); } public FunctionCall(NameID nid, Path.ID qualification, Collection<Expr> arguments, Collection<Attribute> attributes) { super(nid,qualification,arguments,Collections.<String>emptyList(),attributes); } @Override public Type.Function type() { return functionType; } @Override public Type result() { if(functionType.returns().length == 1) { return functionType.returns()[0]; } else { throw new IllegalArgumentException("incorrect number of returns for function call"); } } } public static class PropertyCall extends FunctionOrMethodCall { public Type.Property propertyType; public PropertyCall(NameID nid, Path.ID qualification, Collection<Expr> arguments, Attribute... attributes) { super(nid,qualification,arguments,Collections.<String>emptyList(),attributes); } public PropertyCall(NameID nid, Path.ID qualification, Collection<Expr> arguments, Collection<Attribute> attributes) { super(nid,qualification,arguments,Collections.<String>emptyList(),attributes); } @Override public Type.Property type() { return propertyType; } @Override public Type result() { return Type.T_BOOL; } } public static class AbstractIndirectInvoke extends SyntacticElement.Impl implements Expr, Stmt { public Expr src; public final ArrayList<Expr> arguments; public final ArrayList<String> lifetimeArguments; public AbstractIndirectInvoke(Expr src, Collection<Expr> arguments, Collection<String> lifetimeArguments, Attribute... attributes) { super(attributes); this.src = src; this.arguments = new ArrayList<>(arguments); this.lifetimeArguments = lifetimeArguments == null ? null : new ArrayList<>(lifetimeArguments); } public AbstractIndirectInvoke(Expr src, Collection<Expr> arguments, Collection<String> lifetimeArguments, Collection<Attribute> attributes) { super(attributes); this.src = src; this.arguments = new ArrayList<>(arguments); this.lifetimeArguments = lifetimeArguments == null ? null : new ArrayList<>(lifetimeArguments); } @Override public Type result() { return null; } } public static abstract class IndirectFunctionOrMethodCall extends AbstractIndirectInvoke implements Multi { public IndirectFunctionOrMethodCall(Expr src, Collection<Expr> arguments, Collection<String> lifetimeArguments, Attribute... attributes) { super(src,arguments,lifetimeArguments,attributes); } public IndirectFunctionOrMethodCall(Expr src, Collection<Expr> arguments, Collection<String> lifetimeArguments, Collection<Attribute> attributes) { super(src,arguments,lifetimeArguments,attributes); } public abstract Type.FunctionOrMethod type(); @Override public List<Type> returns() { return Arrays.asList(type().returns()); } } public static class IndirectMethodCall extends IndirectFunctionOrMethodCall { public Type.Method methodType; public IndirectMethodCall(Expr src, Collection<Expr> arguments, Collection<String> lifetimeArguments, Attribute... attributes) { super(src,arguments,lifetimeArguments,attributes); } public IndirectMethodCall(Expr src, Collection<Expr> arguments, Collection<String> lifetimeArguments, Collection<Attribute> attributes) { super(src,arguments,lifetimeArguments,attributes); } @Override public Type result() { if (methodType.returns().length == 1) { Type returnType = methodType.returns()[0]; if (this.lifetimeArguments == null || this.lifetimeArguments.isEmpty()) { return returnType; } List<String> lifetimeParams = Arrays.asList(methodType.lifetimeParams()); return FlowTypeChecker.applySubstitution(lifetimeParams, this.lifetimeArguments, returnType); } else { throw new IllegalArgumentException("incorrect number of returns for indirect method call"); } } @Override public Type.FunctionOrMethod type() { return methodType; } } public static class IndirectFunctionCall extends IndirectFunctionOrMethodCall { public Type.Function functionType; public IndirectFunctionCall(Expr src, Collection<Expr> arguments, Attribute... attributes) { super(src,arguments,Collections.<String>emptyList(),attributes); } public IndirectFunctionCall(Expr src, Collection<Expr> arguments, Collection<Attribute> attributes) { super(src,arguments,Collections.<String>emptyList(),attributes); } @Override public Type result() { if(functionType.returns().length == 1) { return functionType.returns()[0]; } else { throw new IllegalArgumentException("incorrect number of returns for indirect function call"); } } @Override public Type.FunctionOrMethod type() { return functionType; } } public static class New extends SyntacticElement.Impl implements Expr,Stmt { public Expr expr; public Type.Reference type; public String lifetime; public New(Expr expr, String lifetime, Attribute... attributes) { super(attributes); this.lifetime = lifetime; this.expr = expr; } @Override public Type.Reference result() { return type; } } public enum BOp { AND { @Override public String toString() { return "&&"; } }, OR{ @Override public String toString() { return "||"; } }, XOR { @Override public String toString() { return "^^"; } }, ADD{ @Override public String toString() { return "+"; } }, SUB{ @Override public String toString() { return "-"; } }, MUL{ @Override public String toString() { return "*"; } }, DIV{ @Override public String toString() { return "/"; } }, REM{ @Override public String toString() { return "%"; } }, UNION{ @Override public String toString() { return "+"; } }, INTERSECTION{ @Override public String toString() { return "&"; } }, DIFFERENCE{ @Override public String toString() { return "-"; } }, EQ{ @Override public String toString() { return "=="; } }, NEQ{ @Override public String toString() { return "!="; } }, LT{ @Override public String toString() { return "<"; } }, LTEQ{ @Override public String toString() { return "<="; } }, GT{ @Override public String toString() { return ">"; } }, GTEQ{ @Override public String toString() { return ">="; } }, RANGE{ @Override public String toString() { return ".."; } }, IS{ @Override public String toString() { return "is"; } }, BITWISEAND { @Override public String toString() { return "&"; } }, BITWISEOR{ @Override public String toString() { return "|"; } }, BITWISEXOR { @Override public String toString() { return "^"; } }, LEFTSHIFT { @Override public String toString() { return "<<"; } }, RIGHTSHIFT { @Override public String toString() { return ">>"; } }, }; }