//-----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All Rights Reserved.
//
//-----------------------------------------------------------------------------
// This file specifies the expression language used by the Abstract
// Interpretation Framework.
//
// expressions e ::= x variables
// | f(e1,...,en) uninterpreted functions
// | \x:t.e lambda expressions
//
// types t ::= b user-defined/built-in base types
// | t1 * ... * tn -> t' function type
namespace Microsoft.AbstractInterpretationFramework
{
using System.Collections;
using Microsoft.Contracts;
//----------------------------- Expressions -----------------------------
///
/// An interface for expressions. This expression language is specified
/// by interfaces to allow the client to be able to use their existing
/// AST nodes as AIF expressions.
///
/// This only serves as a place for operations on expressions. Clients
/// should implement directly either IVariable or IFunApp.
///
public interface IExpr
{
///
/// Execute a visit over the expression.
///
/// The expression visitor.
/// The result of the visit.
[Pure] object DoVisit(ExprVisitor! visitor);
// TODO: Type checking of the expressions.
}
///
/// An interface for variables.
///
/// This interface should be implemented by the client.
///
public interface IVariable : IExpr
{
string! Name { get; } // Each client must define the name for variables
}
///
/// An interface for function applications.
///
/// This interface should be implemented by the client.
///
public interface IFunApp : IExpr
{
IFunctionSymbol! FunctionSymbol { get; }
IList/**/! Arguments
{
[Pure][Rep] get
ensures result.IsReadOnly;
;
}
///
/// Provides a method to create a new uninterpreted function
/// with the same function symbol but with the arguments with
/// args.
///
/// The new arguments.
/// A copy of the function with the new arguments.
IFunApp! CloneWithArguments(IList/**/! args)
//TODO requires this.Arguments.Count == args.Count;
;
}
///
/// An interface for anonymous functions (i.e., lambda expressions)
///
public interface IFunction : IExpr
{
IVariable! Param { get; }
AIType! ParamType { get; }
IExpr! Body { get; }
IFunction! CloneWithBody(IExpr! body);
}
///
/// An abstract class that provides an interface for expression visitors.
///
public abstract class ExprVisitor
{
public abstract object Default(IExpr! expr);
public virtual object VisitVariable(IVariable! var)
{
return Default(var);
}
public virtual object VisitFunApp(IFunApp! funapp)
{
return Default(funapp);
}
public virtual object VisitFunction(IFunction! fun)
{
return Default(fun);
}
}
///
/// A utility class for dealing with expressions.
///
public sealed class ExprUtil
{
///
/// Yield an expression that is 'inexpr' with 'var' replaced by 'subst'.
///
/// The expression to substitute.
/// The variable to substitute for.
/// The expression to substitute into.
public static IExpr! Substitute(IExpr! subst, IVariable! var, IExpr! inexpr)
{
IExpr result = null;
if (inexpr is IVariable)
{
result = inexpr.Equals(var) ? subst : inexpr;
}
else if (inexpr is IFunApp)
{
IFunApp! funapp = (IFunApp!)inexpr;
IList newargs = null;
newargs = new ArrayList{ IExpr! arg in funapp.Arguments; Substitute(subst, var, arg) };
result = funapp.CloneWithArguments(newargs);
}
else if (inexpr is IFunction)
{
IFunction! fun = (IFunction!)inexpr;
if (fun.Param.Equals(var))
result = fun;
else
result = fun.CloneWithBody(Substitute(subst, var, fun.Body));
}
else
{
assert false;
}
return result;
}
//
// Poor man's pattern matching.
//
// The methods below implement pattern matching for AI expressions.
//
// Example Usage:
// Match(e, Prop.Imp,
// (Matcher)delegate (IExpr e) { return Match(e, Prop.And, out x, out y); }
// out z)
// which sees if 'e' matches Prop.Imp(Prop.And(x,y),z) binding x,y,z to the subtrees.
//
public delegate bool Matcher(IExpr! expr);
private static IFunApp/*?*/ MatchFunctionSymbol(IExpr! expr, IFunctionSymbol! f)
{
IFunApp app = expr as IFunApp;
if (app != null)
{
if (app.FunctionSymbol.Equals(f))
return app;
else
return null;
}
else
return null;
}
public static bool Match(IExpr! expr, IFunctionSymbol! f, params Matcher[]! subs)
{
IFunApp app = MatchFunctionSymbol(expr,f);
if (app != null)
{
int i = 0; // Note ***0***
foreach (Matcher! s in subs)
{
if (!s((IExpr!)app.Arguments[i])) { return false; }
i++;
}
return true;
}
else { return false; }
}
// Unary Binding
public static bool Match(IExpr! expr, IFunctionSymbol! f, out IExpr arg0, params Matcher[]! subs)
{
arg0 = null;
IFunApp app = MatchFunctionSymbol(expr,f);
if (app != null)
{
arg0 = (IExpr!)app.Arguments[0];
int i = 1; // Note ***1***
foreach (Matcher! s in subs)
{
if (!s((IExpr!)app.Arguments[i])) { return false; }
i++;
}
return true;
}
else { return false; }
}
// Binary Binding
public static bool Match(IExpr! expr, IFunctionSymbol! f, Matcher! sub0, out IExpr arg1, params Matcher[]! subs)
{
arg1 = null;
IFunApp app = MatchFunctionSymbol(expr,f);
if (app != null)
{
if (!sub0((IExpr!)app.Arguments[0])) { return false; }
arg1 = (IExpr!)app.Arguments[1];
int i = 2; // Note ***2***
foreach (Matcher! s in subs)
{
if (!s((IExpr!)app.Arguments[i])) { return false; }
i++;
}
return true;
}
else { return false; }
}
public static bool Match(IExpr! expr, IFunctionSymbol! f, out IExpr arg0, out IExpr arg1, params Matcher[]! subs)
{
arg0 = null;
arg1 = null;
IFunApp app = MatchFunctionSymbol(expr,f);
if (app != null)
{
arg0 = (IExpr!)app.Arguments[0];
arg1 = (IExpr!)app.Arguments[1];
int i = 2; // Note ***2***
foreach (Matcher! s in subs)
{
if (!s((IExpr!)app.Arguments[i])) { return false; }
i++;
}
return true;
}
else { return false; }
}
// Ternary Binding
public static bool Match(IExpr! expr, IFunctionSymbol! f, out IExpr arg0, out IExpr arg1, out IExpr arg2, params Matcher[]! subs)
{
arg0 = null;
arg1 = null;
arg2 = null;
IFunApp app = MatchFunctionSymbol(expr,f);
if (app != null)
{
arg0 = (IExpr!)app.Arguments[0];
arg1 = (IExpr!)app.Arguments[1];
arg2 = (IExpr!)app.Arguments[2];
int i = 3; // Note ***3***
foreach (Matcher! s in subs)
{
if (!s((IExpr!)app.Arguments[i])) { return false; }
i++;
}
return true;
}
else { return false; }
}
///
/// Not intended to be instantiated.
///
private ExprUtil() { }
}
//------------------------------ Symbols --------------------------------
///
/// An interface for function symbols. Constants are represented by
/// 0-ary function symbols.
///
/// This interface should be implemented by abstract domains, but client
/// expressions need keep track of function symbols.
///
public interface IFunctionSymbol
{
AIType! AIType { [Rep][ResultNotNewlyAllocated]
get; }
}
///
/// The type of the arguments to ExprUtil.Match, a poor man's pattern
/// matching.
///
public interface IMatchable
{
}
//-------------------------------- Types --------------------------------
///
/// Types.
///
public interface AIType
{
}
///
/// Function type constructor.
///
public sealed class FunctionType : AIType
{
/*[Own]*/ private readonly IList/**/! argTypes;
/*[Own]*/ private readonly AIType! retType;
public FunctionType(params AIType[]! types)
requires types.Length >= 2;
{
AIType type = types[types.Length-1];
assume type != null;
this.retType = type;
ArrayList argTypes = new ArrayList();
for (int i = 0; i < types.Length-1; i++)
{
type = types[i];
assume type != null;
argTypes.Add(types);
}
this.argTypes = ArrayList.ReadOnly(argTypes);
}
public IList/**/! Arguments
{
[Pure][Rep]
get
ensures result.IsReadOnly;
{
return argTypes;
}
}
public int Arity
{
get { return argTypes.Count; }
}
public AIType! ReturnType
{
get { return retType; }
}
/* TODO Do we have the invariant that two functions are equal iff they're the same object.
public override bool Equals(object o)
{
if (o != null && o is FunctionType)
{
FunctionType other = (FunctionType) o;
if (Arity == other.Arity
&& ReturnType.Equals(other.ReturnType))
{
for (int i = 0; i < Arity; i++)
{
if (!argTypes[i].Equals(other.argTypes[i]))
return false;
}
return true;
}
else
return false;
}
else
return false;
}
*/
}
//------------------------------ Queries -------------------------------
public enum Answer { Yes, No, Maybe };
///
/// An interface that specifies a queryable object that can answer
/// whether a predicate holds.
///
public interface IQueryable
{
///
/// Answers the query whether the given predicate holds.
///
/// The given predicate.
/// Yes, No, or Maybe.
Answer CheckPredicate(IExpr! pred);
///
/// A simplified interface for disequalities. One can always
/// implement this by calling CheckPredicate, but it may be
/// more efficient with this method.
///
Answer CheckVariableDisequality(IVariable! var1, IVariable! var2);
}
public static class QueryUtil
{
public static Answer Negate(Answer ans)
{
switch (ans)
{
case Answer.Yes:
return Answer.No;
case Answer.No:
return Answer.Yes;
default:
return Answer.Maybe;
}
}
}
//----------------------------- Exceptions -----------------------------
public class TypeError : CheckedException
{
}
}