329 lines
No EOL
11 KiB
C#
329 lines
No EOL
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using AspectedRouting.Language.Functions;
|
|
using AspectedRouting.Language.Typ;
|
|
using static AspectedRouting.Language.Deconstruct;
|
|
using Type = AspectedRouting.Language.Typ.Type;
|
|
|
|
namespace AspectedRouting.Language.Expression
|
|
{
|
|
public class Apply : IExpression
|
|
{
|
|
// Only used for when there is no typechecking possible
|
|
private readonly string _debugInfo;
|
|
|
|
/// <summary>
|
|
/// Maps the expected return type onto the argument needed for that.
|
|
/// The argument is specialized for this return type
|
|
/// </summary>
|
|
public readonly Dictionary<Type, (IExpression f, IExpression a)> FunctionApplications;
|
|
|
|
private Apply(string debugInfo, Dictionary<Type, (IExpression f, IExpression a)> argument)
|
|
{
|
|
_debugInfo = debugInfo;
|
|
FunctionApplications = argument;
|
|
}
|
|
|
|
public Apply(IExpression f, IExpression argument)
|
|
{
|
|
if (f == null || argument == null) {
|
|
throw new NullReferenceException();
|
|
}
|
|
|
|
FunctionApplications = new Dictionary<Type, (IExpression f, IExpression a)>();
|
|
|
|
var typesCleaned = argument.Types.RenameVars(f.Types).ToList();
|
|
foreach (var funcType in f.Types) {
|
|
if (!(funcType is Curry c)) {
|
|
continue;
|
|
}
|
|
|
|
var expectedArgType = c.ArgType;
|
|
var expectedResultType = c.ResultType;
|
|
|
|
|
|
foreach (var argType in typesCleaned) {
|
|
// we try to unify the argType with the expected type
|
|
var substitutions = expectedArgType.UnificationTable(argType);
|
|
if (substitutions == null) {
|
|
continue;
|
|
}
|
|
|
|
var actualArgType = expectedArgType.Substitute(substitutions);
|
|
var actualResultType = expectedResultType.Substitute(substitutions);
|
|
|
|
var actualFunction = f.Specialize(new Curry(actualArgType, actualResultType));
|
|
var actualArgument = argument.Specialize(actualArgType);
|
|
|
|
if (actualFunction == null || actualArgument == null) {
|
|
continue;
|
|
}
|
|
|
|
if (FunctionApplications.ContainsKey(actualResultType)) {
|
|
continue;
|
|
}
|
|
|
|
FunctionApplications.Add(actualResultType, (actualFunction, actualArgument));
|
|
}
|
|
}
|
|
|
|
if (!FunctionApplications.Any()) {
|
|
try {
|
|
_debugInfo = $"\n{f.Optimize().TypeBreakdown().Indent()}\n" +
|
|
"is given the argument: " +
|
|
"(" + argument.Optimize().TypeBreakdown() + ")";
|
|
}
|
|
catch (Exception) {
|
|
_debugInfo = $"\n (NO OPT) {f.TypeBreakdown().Indent()}\n" +
|
|
"is given the argument: " +
|
|
"(" + argument.TypeBreakdown() + ")";
|
|
}
|
|
}
|
|
}
|
|
|
|
public IExpression F => FunctionApplications.Values.First().f;
|
|
public IExpression A => FunctionApplications.Values.First().a;
|
|
|
|
public IEnumerable<Type> Types => FunctionApplications.Keys;
|
|
|
|
|
|
public object Evaluate(Context c, params IExpression[] arguments)
|
|
{
|
|
if (!Types.Any()) {
|
|
throw new ArgumentException("Trying to invoke an invalid expression: " + this);
|
|
}
|
|
|
|
var type = Types.First();
|
|
var (fExpr, argExpr) = FunctionApplications[type];
|
|
|
|
|
|
var arg = argExpr;
|
|
var allArgs = new IExpression[arguments.Length + 1];
|
|
allArgs[0] = arg;
|
|
for (var i = 0; i < arguments.Length; i++) {
|
|
allArgs[i + 1] = arguments[i];
|
|
}
|
|
|
|
return fExpr.Evaluate(c, allArgs);
|
|
}
|
|
|
|
|
|
IExpression IExpression.Specialize(IEnumerable<Type> allowedTypes)
|
|
{
|
|
return Specialize(allowedTypes);
|
|
}
|
|
|
|
public IExpression Optimize()
|
|
{
|
|
if (Types.Count() == 0) {
|
|
throw new ArgumentException("This application contain no valid types, so cannot be optimized" + this);
|
|
}
|
|
|
|
// (eitherfunc dot id) id
|
|
// => (const dot _) id => dot id => id
|
|
// or => (constRight _ id) id => id id => id
|
|
if (
|
|
UnApplyAny(
|
|
UnApplyAny(
|
|
UnApplyAny(
|
|
IsFunc(Funcs.Const),
|
|
IsFunc(Funcs.Dot)),
|
|
Any),
|
|
IsFunc(Funcs.Id)
|
|
).Invoke(this)
|
|
&& UnApplyAny(UnApplyAny(
|
|
UnApplyAny(
|
|
IsFunc(Funcs.ConstRight),
|
|
Any),
|
|
IsFunc(Funcs.Id)),
|
|
IsFunc(Funcs.Id)
|
|
).Invoke(this)) {
|
|
return Funcs.Id;
|
|
}
|
|
|
|
|
|
if (Types.Count() > 1) {
|
|
// Too much types to optimize
|
|
var optimized = new Dictionary<Type, (IExpression f, IExpression a)>();
|
|
foreach (var (resultType, (f, a)) in FunctionApplications) {
|
|
var fOpt = f.Optimize();
|
|
var aOpt = a.Optimize();
|
|
optimized.Add(resultType, (fOpt, aOpt));
|
|
}
|
|
|
|
return new Apply(_debugInfo, optimized);
|
|
}
|
|
|
|
{
|
|
// id a => a
|
|
var arg = new List<IExpression>();
|
|
if (
|
|
UnApplyAny(
|
|
IsFunc(Funcs.Id),
|
|
Assign(arg)
|
|
).Invoke(this)) {
|
|
return arg.First();
|
|
}
|
|
}
|
|
|
|
|
|
{
|
|
// ifdotted fcondition fthen felse arg => if (fcondition arg) (fthen arg) (felse arg)
|
|
var fcondition = new List<IExpression>();
|
|
var fthen = new List<IExpression>();
|
|
var felse = new List<IExpression>();
|
|
var arg = new List<IExpression>();
|
|
|
|
if (
|
|
UnApplyAny(
|
|
UnApply(
|
|
UnApply(
|
|
UnApply(
|
|
IsFunc(Funcs.IfDotted),
|
|
Assign(fcondition)),
|
|
Assign(fthen)),
|
|
Assign(felse)),
|
|
Assign(arg)
|
|
).Invoke(this)) {
|
|
var a = arg.First();
|
|
return
|
|
Funcs.If.Apply(
|
|
fcondition.First().Apply(a),
|
|
fthen.First().Apply(a),
|
|
felse.First().Apply(a)
|
|
);
|
|
}
|
|
}
|
|
|
|
{
|
|
var (f, a) = FunctionApplications.Values.First();
|
|
|
|
var (newFa, expr) = OptimizeApplicationPair(f, a);
|
|
if (expr != null) {
|
|
return expr;
|
|
}
|
|
|
|
(f, a) = newFa.Value;
|
|
return new Apply(f, a);
|
|
}
|
|
}
|
|
|
|
public void Visit(Func<IExpression, bool> visitor)
|
|
{
|
|
var continueVisit = visitor(this);
|
|
if (!continueVisit) {
|
|
return;
|
|
}
|
|
|
|
foreach (var (_, (f, a)) in FunctionApplications) {
|
|
f.Visit(visitor);
|
|
a.Visit(visitor);
|
|
}
|
|
}
|
|
|
|
private Apply Specialize(IEnumerable<Type> allowedTypes)
|
|
{
|
|
var newArgs = new Dictionary<Type, (IExpression f, IExpression a)>();
|
|
|
|
foreach (var allowedType in allowedTypes) {
|
|
foreach (var (resultType, (funExpr, argExpr)) in FunctionApplications) {
|
|
var substitutions = resultType.UnificationTable(allowedType, true);
|
|
if (substitutions == null) {
|
|
continue;
|
|
}
|
|
|
|
var actualResultType = allowedType.Substitute(substitutions);
|
|
|
|
// f : a -> b
|
|
// actualResultType = b (b which was retrieved after a reverse substitution)
|
|
|
|
var actualFunction = funExpr.Specialize(substitutions);
|
|
var actualArgument = argExpr.Specialize(substitutions);
|
|
|
|
if (actualFunction == null || actualArgument == null) {
|
|
// One of the subexpressions can't be optimized
|
|
return null;
|
|
}
|
|
|
|
newArgs[actualResultType] = (actualFunction, actualArgument);
|
|
}
|
|
}
|
|
|
|
if (!newArgs.Any()) {
|
|
return null;
|
|
}
|
|
|
|
return new Apply(_debugInfo, newArgs);
|
|
}
|
|
|
|
private ((IExpression fOpt, IExpression fArg)?, IExpression result) OptimizeApplicationPair(IExpression f,
|
|
IExpression a)
|
|
{
|
|
f = f.Optimize();
|
|
|
|
a = a.Optimize();
|
|
|
|
switch (f) {
|
|
case Id _:
|
|
return (null, a);
|
|
|
|
case Apply apply:
|
|
|
|
if (apply.F is Const _) {
|
|
// (const x) y -> y
|
|
// apply == (const x) thus we return 'x' and ignore 'a'
|
|
|
|
return (null, apply.A);
|
|
}
|
|
|
|
if (apply.F is ConstRight _) {
|
|
// constRight x y -> y
|
|
// apply == (constRight x) so we return a
|
|
return (null, a);
|
|
}
|
|
|
|
var f0 = new List<IExpression>();
|
|
var f1 = new List<IExpression>();
|
|
if (UnApply(
|
|
UnApply(
|
|
IsFunc(Funcs.Dot),
|
|
Assign(f0)
|
|
),
|
|
Assign(f1)).Invoke(apply)
|
|
) {
|
|
// apply == ((dot f0) f1)
|
|
// ((dot f0) f1) a is the actual expression, but arg is already split of
|
|
|
|
// f0 (f1 arg)
|
|
// which used to be (f0 . f1) arg
|
|
return ((f0.First(), new Apply(f1.First(), a)), null);
|
|
}
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
return ((f, a), null);
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
if (!FunctionApplications.Any()) {
|
|
return "NOT-TYPECHECKABLE APPLICATION: " + _debugInfo;
|
|
}
|
|
|
|
var (f, arg) = FunctionApplications.Values.First();
|
|
if (f is Id _) {
|
|
return arg.ToString();
|
|
}
|
|
|
|
var extra = "";
|
|
if (FunctionApplications.Count() > 1) {
|
|
extra = " [" + FunctionApplications.Count + " IMPLEMENTATIONS]";
|
|
}
|
|
|
|
return $"({f} {arg.ToString().Indent()})" + extra;
|
|
}
|
|
}
|
|
} |