edu.mit.csail.sdg.ast.ExprQt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of alloy-core Show documentation
Show all versions of alloy-core Show documentation
The core of the Alloy tools
The newest version!
/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
*
* 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 edu.mit.csail.sdg.ast;
import static edu.mit.csail.sdg.ast.Type.EMPTY;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.NoSuchElementException;
import edu.mit.csail.sdg.alloy4.ConstList;
import edu.mit.csail.sdg.alloy4.ConstList.TempList;
import edu.mit.csail.sdg.alloy4.Err;
import edu.mit.csail.sdg.alloy4.ErrorSyntax;
import edu.mit.csail.sdg.alloy4.ErrorType;
import edu.mit.csail.sdg.alloy4.ErrorWarning;
import edu.mit.csail.sdg.alloy4.JoinableList;
import edu.mit.csail.sdg.alloy4.Pos;
/**
* Immutable; represents a quantified expression. It can have one of the
* following forms:
*
* ( all a,b:t | formula )
* ( no a,b:t | formula )
* ( lone a,b:t | formula )
* ( one a,b:t | formula )
* ( some a,b:t | formula )
* ( sum a,b:t | integer expression )
* { a,b:t | formula }
* { a,b:t }
*
* Invariant: type!=EMPTY => sub.mult==0
* Invariant: type!=EMPTY => vars.size()>0
*/
public final class ExprQt extends Expr {
/**
* The operator (ALL, NO, LONE, ONE, SOME, SUM, or COMPREHENSION)
*/
public final Op op;
/** The unmodifiable list of variables. */
public final ConstList decls;
/** The body of the quantified expression. */
public final Expr sub;
/** Caches the span() result. */
private Pos span;
/** Return the number of variables. */
public int count() {
int n = 0;
for (Decl d : decls)
n = n + d.names.size();
return n;
}
/** Return the i-th variable. */
public ExprVar get(int i) {
if (i < 0)
throw new NoSuchElementException();
for (Decl d : decls) {
if (i < d.names.size())
return (ExprVar) (d.names.get(i));
i = i - d.names.size();
}
throw new NoSuchElementException();
}
/** Return the i-th variable's bound. */
public Expr getBound(int i) {
if (i < 0)
throw new NoSuchElementException();
for (Decl d : decls) {
if (i < d.names.size())
return d.expr;
i = i - d.names.size();
}
throw new NoSuchElementException();
}
// =============================================================================================================//
/** {@inheritDoc} */
@Override
public Pos span() {
Pos p = span;
// We intentionally do NOT merge the VAR's position into the span.
// That allows us to control the highlighting of this component
// simply by deciding this.pos and this.closingBracket
if (p == null)
span = (p = pos.merge(closingBracket).merge(sub.span()));
return p;
}
// =============================================================================================================//
/** {@inheritDoc} */
@Override
public void toString(StringBuilder out, int indent) {
if (indent < 0) {
boolean first = true;
if (op != Op.COMPREHENSION)
out.append('(').append(op).append(' ');
else
out.append('{');
for (Decl d : decls)
for (ExprHasName v : d.names) {
if (!first)
out.append(',');
first = false;
out.append(v.label);
}
if (op != Op.COMPREHENSION || !(sub instanceof ExprConstant) || ((ExprConstant) sub).op != ExprConstant.Op.TRUE) {
out.append(" | ");
sub.toString(out, -1);
}
if (op != Op.COMPREHENSION)
out.append(')');
else
out.append('}');
} else {
for (int i = 0; i < indent; i++) {
out.append(' ');
}
out.append("Quantification(").append(op).append(") of ");
out.append(count()).append(" vars with type=").append(type).append('\n');
for (Decl d : decls)
for (ExprHasName v : d.names) {
v.toString(out, indent + 2);
d.expr.toString(out, indent + 4);
}
sub.toString(out, indent + 2);
}
}
// =============================================================================================================//
/** Constructs a new quantified expression. */
private ExprQt(Pos pos, Pos closingBracket, Op op, Type type, ConstList decls, Expr sub, boolean ambiguous, long weight, JoinableList errs) {
super(pos, closingBracket, ambiguous, type, 0, weight, errs);
this.op = op;
this.decls = decls;
this.sub = sub;
}
// =============================================================================================================//
/**
* This class contains all possible quantification operators.
*/
public enum Op {
/** all a,b:x | formula */
ALL("all"),
/** no a,b:x | formula */
NO("no"),
/** lone a,b:x | formula */
LONE("lone"),
/** one a,b:x | formula */
ONE("one"),
/** some a,b:x | formula */
SOME("some"),
/** sum a,b:x | intExpression */
SUM("sum"),
/** { a,b:x | formula } */
COMPREHENSION("comprehension");
/** The constructor. */
private Op(String label) {
this.label = label;
}
/** The human readable label for this operator. */
private final String label;
/**
* Constructs a quantification expression with "this" as the operator.
*
* @param pos - the position of the "quantifier" in the source file (or null if
* unknown)
* @param closingBracket - the position of the "closing bracket" in the source
* file (or null if unknown)
* @param decls - the list of variable declarations (each variable must be over
* a set or relation)
* @param sub - the body of the expression
*/
public final Expr make(Pos pos, Pos closingBracket, List decls, Expr sub) {
Type t = this == SUM ? Type.smallIntType() : (this == COMPREHENSION ? Type.EMPTY : Type.FORMULA);
if (this != SUM)
sub = sub.typecheck_as_formula();
else
sub = sub.typecheck_as_int();
boolean ambiguous = sub.ambiguous;
JoinableList errs = emptyListOfErrors;
if (sub.mult != 0)
errs = errs.make(new ErrorSyntax(sub.span(), "Multiplicity expression not allowed here."));
long weight = sub.weight;
if (decls.size() == 0)
errs = errs.make(new ErrorSyntax(pos, "List of variables cannot be empty."));
for (Decl d : decls) {
Expr v = d.expr;
ambiguous = ambiguous || v.ambiguous;
weight = weight + v.weight;
errs = errs.make(v.errors);
if (v.errors.size() > 0)
continue;
if (v.type.size() == 0) {
errs = errs.make(new ErrorType(v.span(), "This must be a set or relation. Instead, its type is " + v.type));
continue;
}
ExprUnary.Op op = v.mult();
if (op == ExprUnary.Op.EXACTLYOF) {
errs = errs.make(new ErrorType(v.span(), "This cannot be an exactly-of expression."));
continue;
}
if (this != SUM && this != COMPREHENSION)
continue;
if (!v.type.hasArity(1)) {
errs = errs.make(new ErrorType(v.span(), "This must be a unary set. Instead, its type is " + v.type));
continue;
}
if (v.mult == 1) {
if (op == ExprUnary.Op.SETOF)
errs = errs.make(new ErrorType(v.span(), "This cannot be a set-of expression."));
else if (op == ExprUnary.Op.SOMEOF)
errs = errs.make(new ErrorType(v.span(), "This cannot be a some-of expression."));
else if (op == ExprUnary.Op.LONEOF)
errs = errs.make(new ErrorType(v.span(), "This cannot be a lone-of expression."));
}
if (this == COMPREHENSION) {
Type t1 = v.type.extract(1);
for (int n = d.names.size(); n > 0; n--)
if (t == EMPTY)
t = t1;
else
t = t.product(t1);
}
}
if (errs.isEmpty())
errs = sub.errors; // if the vars have errors, then the
// subexpression's errors will be too
// confusing, so let's skip them
return new ExprQt(pos, closingBracket, this, t, ConstList.make(decls), sub, ambiguous, weight, errs);
}
/** Returns the human readable label for this operator */
@Override
public final String toString() {
return label;
}
}
// =============================================================================================================//
/** {@inheritDoc} */
@Override
public Expr resolve(Type unused, Collection warns) {
if (warns != null && op != Op.COMPREHENSION) {
for (int i = 0; i < decls.size(); i++) {
again: for (ExprHasName n : decls.get(i).names) {
ExprVar x = (ExprVar) n;
for (int j = i + 1; j < decls.size(); j++)
if (decls.get(j).expr.hasVar(x))
continue again;
if (!sub.hasVar(x))
warns.add(new ErrorWarning(x.pos, "This variable is unused."));
}
}
}
return this;
}
// =============================================================================================================//
/**
* This method desugars away the "disjoint" keyword by prefixing the
* subexpression with the appropriate disjointness guard condition.
*/
public Expr desugar() throws ErrorSyntax {
boolean hasDisjoint = false;
for (Decl d : decls) {
if (d.isPrivate != null) {
ExprHasName n = d.names.get(0);
throw new ErrorSyntax(d.isPrivate.merge(n.pos), "Local variable \"" + n.label + "\" is always private already.");
}
if (d.disjoint2 != null) {
ExprHasName n = d.names.get(d.names.size() - 1);
throw new ErrorSyntax(d.disjoint2.merge(n.pos), "Local variable \"" + n.label + "\" cannot be bound to a 'disjoint' expression.");
}
hasDisjoint = hasDisjoint || (d.names.size() > 1 && d.disjoint != null);
}
if (!hasDisjoint)
return this;
TempList newdecls = new TempList(decls.size());
Expr guard = null;
for (Decl d : decls) {
if (d.names.size() <= 1 || d.disjoint == null) {
newdecls.add(d);
continue;
}
guard = ExprList.makeDISJOINT(d.disjoint, null, d.names).and(guard);
newdecls.add(new Decl(null, null, null, d.names, d.expr));
}
if (guard == null)
return this;
Expr sub;
switch (op) {
case SUM :
sub = guard.ite(this.sub, ExprConstant.ZERO);
break;
case ALL :
sub = guard.implies(this.sub);
break;
default :
sub = guard.and(this.sub);
}
return op.make(pos, closingBracket, newdecls.makeConst(), sub);
}
// =============================================================================================================//
/** {@inheritDoc} */
@Override
public int getDepth() {
int max = sub.getDepth();
for (Decl d : decls)
for (ExprHasName x : d.names) {
int tmp = x.getDepth();
if (max < tmp)
max = tmp;
}
return 1 + max;
}
/** {@inheritDoc} */
@Override
public final T accept(VisitReturn visitor) throws Err {
return visitor.visit(this);
}
/** {@inheritDoc} */
@Override
public String getHTML() {
StringBuilder sb = new StringBuilder("").append(op).append(" ");
boolean first = true;
for (Decl d : decls)
for (ExprHasName v : d.names) {
if (!first)
sb.append(", ");
sb.append(v.label);
first = false;
}
return sb.append("... ").append(type).append("").toString();
}
/** {@inheritDoc} */
@Override
public List< ? extends Browsable> getSubnodes() {
ArrayList ans = new ArrayList();
for (Decl d : decls)
for (ExprHasName v : d.names) {
ans.add(make(v.pos, v.pos, "var " + v.label + " " + v.type + "", d.expr));
}
ans.add(make(sub.span(), sub.span(), "body", sub));
return ans;
}
}