Proper calculation of the type of a list of expressions - turns out it was broken
This commit is contained in:
parent
38a50a6713
commit
6739dd8b25
15 changed files with 511 additions and 276 deletions
|
@ -60,8 +60,9 @@ namespace AspectedRouting.Test.Snippets
|
|||
tags
|
||||
}
|
||||
);
|
||||
// First the more general ones!
|
||||
Assert.Equal(
|
||||
"if (tags[\"bicycle\"] ~= nil) then\n result = tags[\"bicycle\"]\nelseif (tags[\"access\"] ~= nil) then\n result = tags[\"access\"]\nend\n",
|
||||
"if (tags[\"access\"] ~= nil) then\n result = tags[\"access\"]\n \nend\nif (tags[\"bicycle\"] ~= nil) then\n result = tags[\"bicycle\"]\n \nend\n",
|
||||
code);
|
||||
}
|
||||
|
||||
|
@ -82,27 +83,9 @@ namespace AspectedRouting.Test.Snippets
|
|||
});
|
||||
|
||||
var expected =
|
||||
"local v\nv = tags.oneway\nif (v == \"1\") then\n result = \"with\"\nelseif (v == \"-1\") then\n result = \"against\"\nend";
|
||||
"local v\nv = tags.oneway\n\nif (v == \"1\") then\n result = \"with\"\nelseif (v == \"-1\") then\n result = \"against\"\nend";
|
||||
Assert.Equal(expected, code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultiplySnippet_SimpleMultiply_GeneratesLua()
|
||||
{
|
||||
var gen = new MultiplySnippet();
|
||||
var f = new Apply(
|
||||
Funcs.Multiply,
|
||||
null
|
||||
);
|
||||
|
||||
|
||||
var code = gen.Convert(new LuaSkeleton(new Context(), true),
|
||||
"result", new List<IExpression>()
|
||||
);
|
||||
|
||||
var expected =
|
||||
"local v\nv = tags.oneway\nif (v == \"1\") then\n result = \"with\"\nelseif (v == \"-1\") then\n result = \"against\"\nend";
|
||||
Assert.Equal(expected, code);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,88 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.Language;
|
||||
using AspectedRouting.Language.Functions;
|
||||
using AspectedRouting.Language.Typ;
|
||||
using Xunit;
|
||||
using Type = AspectedRouting.Language.Typ.Type;
|
||||
|
||||
namespace AspectedRouting.Test
|
||||
{
|
||||
public class TypingTests
|
||||
{
|
||||
[Fact]
|
||||
public void SpecializeToCommonTypes_X2PDouble_Y2Double_Gives_X2Double()
|
||||
{
|
||||
// Regression test:
|
||||
// [ x : (x -> pdouble), y : (y -> double)] is wrongly types as (x -> pdouble), hence killing y in the subsequent typing
|
||||
var exprs = new List<IExpression> {
|
||||
new Constant(new List<Type> {
|
||||
new Curry(Typs.Tags, Typs.Double),
|
||||
new Curry(new Var("b"), new Curry(Typs.Tags, Typs.Double))
|
||||
}, "x"),
|
||||
|
||||
new Constant(
|
||||
new List<Type> {
|
||||
Typs.PDouble,
|
||||
new Curry(new Var("b"), Typs.PDouble)
|
||||
}
|
||||
, "y")
|
||||
};
|
||||
|
||||
|
||||
exprs.SpecializeToCommonTypes(out var specializedTypes, out var specializedExpressions);
|
||||
Assert.All(specializedTypes, Assert.NotNull);
|
||||
Assert.All(specializedExpressions, Assert.NotNull);
|
||||
Assert.Single(specializedTypes);
|
||||
Assert.Equal(new Curry(Typs.Tags, Typs.Double), specializedTypes.First());
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void WidestCommonGround_A2PdoubleAndT2Double_T2Double()
|
||||
{
|
||||
var v = Typs.WidestCommonType(new Curry(new Var("a"), Typs.PDouble),
|
||||
new Curry(Typs.Tags, Typs.Double)
|
||||
);
|
||||
Assert.NotNull(v);
|
||||
var (x, subsTable) = v.Value;
|
||||
Assert.NotNull(x);
|
||||
Assert.Equal(
|
||||
new Curry(Typs.Tags, Typs.Double), x
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void WidestCommonGround_StringAndString_String()
|
||||
{
|
||||
var v = Typs.WidestCommonType(Typs.String, Typs.String);
|
||||
Assert.NotNull(v);
|
||||
var (x, subsTable) = v.Value;
|
||||
Assert.NotNull(x);
|
||||
Assert.Equal(
|
||||
Typs.String, x
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpecializeToCommonTypes_ValueAndFuncType_ShouldFail()
|
||||
{
|
||||
// Regression test:
|
||||
// [ x : (x -> pdouble), y : (y -> double)] is wrongly types as (x -> pdouble), hence killing y in the subsequent typing
|
||||
var exprs = new List<IExpression> {
|
||||
new Constant(
|
||||
new Curry(Typs.Tags, Typs.Double),
|
||||
"x"),
|
||||
new Constant(
|
||||
Typs.PDouble, "y")
|
||||
};
|
||||
|
||||
Assert.Throws(new ArgumentException().GetType(),
|
||||
() => exprs.SpecializeToCommonTypes(out _, out _));
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void JoinApply_Id()
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=3112587d_002D1c06_002D4ad3_002Da845_002D73105d4b723a/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="DefaultSnippet_SimpleDefault_GetsLua" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=3112587d_002D1c06_002D4ad3_002Da845_002D73105d4b723a/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" Name="DefaultSnippet_SimpleDefault_GetsLua" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<TestAncestor>
|
||||
<TestId>xUnit::A1309041-8AAE-42D7-A886-94C9FFC6A28C::.NETCoreApp,Version=v3.1::AspectedRouting.Test.Snippets.SnippetTests</TestId>
|
||||
<TestId>xUnit::A1309041-8AAE-42D7-A886-94C9FFC6A28C::.NETCoreApp,Version=v3.1::AspectedRouting.Test.TypingTests.SpecializeToCommonTypes_X2PDouble_Y2Double_Gives_X2Double</TestId>
|
||||
</TestAncestor>
|
||||
</SessionState></s:String>
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=5e89d9f5_002D24ed_002D4ea2_002Da7ea_002Dcc7faea6d057/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" Name="JoinApply_Id" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
|
@ -13,5 +14,8 @@
|
|||
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=a6a74f48_002D8456_002D43c7_002Dbbee_002Dd3da33a8a4be/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" Name="Integration_TestExamples" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<Project Location="/home/pietervdvn/git/AspectedRouting/AspectedRouting.Test" Presentation="&lt;AspectedRouting.Test&gt;" />
|
||||
</SessionState></s:String>
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=d2e3d58f_002Debff_002D4fb5_002D8d18_002Deafe85f4773d/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="SpecializeToCommonTypes_ValueAndFuncType_ShouldFail" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<Project Location="/home/pietervdvn/git/AspectedRouting/AspectedRouting.Test" Presentation="&lt;AspectedRouting.Test&gt;" />
|
||||
</SessionState></s:String>
|
||||
</wpf:ResourceDictionary>
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.Language;
|
||||
using Type = AspectedRouting.Language.Typ.Type;
|
||||
|
||||
|
@ -31,6 +32,16 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
return this;
|
||||
}
|
||||
|
||||
public IExpression PruneTypes(Func<Type, bool> allowedTypes)
|
||||
{
|
||||
var passed = this.Types.Where(allowedTypes);
|
||||
if (passed.Any()) {
|
||||
return new LuaLiteral(passed, this.Lua);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public IExpression Optimize()
|
||||
{
|
||||
return this;
|
||||
|
@ -40,5 +51,6 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -248,6 +248,7 @@ namespace AspectedRouting.IO.jsonParser
|
|||
var exprs = e.EnumerateArray().Select(json =>
|
||||
Funcs.Either(Funcs.Id, Funcs.Const, json.ParseExpression(context)))
|
||||
.ToList();
|
||||
|
||||
var list = new Constant(exprs);
|
||||
return Funcs.Either(Funcs.Id, Funcs.ListDot, list);
|
||||
}
|
||||
|
|
|
@ -12,35 +12,49 @@ namespace AspectedRouting.IO.jsonParser
|
|||
{
|
||||
private static IExpression ParseProfileProperty(JsonElement e, Context c, string property)
|
||||
{
|
||||
if (!e.TryGetProperty(property, out var prop))
|
||||
{
|
||||
if (!e.TryGetProperty(property, out var prop)) {
|
||||
throw new ArgumentException("Not a valid profile: the declaration expression for '" + property +
|
||||
"' is missing");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
var expr = ParseExpression(prop, c);
|
||||
if (!expr.Types.Any())
|
||||
{
|
||||
if (!expr.Types.Any()) {
|
||||
throw new Exception($"Could not parse field {property}, no valid typing for expression found");
|
||||
}
|
||||
|
||||
expr = expr.Optimize();
|
||||
expr = Funcs.Either(Funcs.Id, Funcs.Const, expr);
|
||||
|
||||
var specialized = expr.Specialize(new Curry(Typs.Tags, new Var("a")));
|
||||
if (specialized == null)
|
||||
{
|
||||
|
||||
if (specialized == null) {
|
||||
throw new ArgumentException("The expression for " + property +
|
||||
" hasn't the right type of 'Tags -> a'; it has types " +
|
||||
string.Join(",", expr.Types) + "\n" + expr.TypeBreakdown());
|
||||
}
|
||||
|
||||
return specialized.Optimize();
|
||||
var pruned = specialized.PruneTypes(type => {
|
||||
if (!(type is Curry c)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Equals(c.ArgType, Typs.Tags)) {
|
||||
return false;
|
||||
}
|
||||
if (c.ResultType is Curry) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (pruned.SpecializeToSmallestType().Types.Count() != 1) {
|
||||
throw new ArgumentException("The expression for " + property +
|
||||
" hasn't the right type of 'Tags -> a'; it has multiple types " +
|
||||
string.Join(",", pruned.Types) + "\n" + pruned.TypeBreakdown());
|
||||
}
|
||||
|
||||
return pruned.Optimize();
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
catch (Exception exc) {
|
||||
throw new Exception("While parsing the property " + property, exc);
|
||||
}
|
||||
}
|
||||
|
@ -48,24 +62,20 @@ namespace AspectedRouting.IO.jsonParser
|
|||
private static Dictionary<string, IExpression> ParseParameters(this JsonElement e)
|
||||
{
|
||||
var ps = new Dictionary<string, IExpression>();
|
||||
foreach (var obj in e.EnumerateObject())
|
||||
{
|
||||
foreach (var obj in e.EnumerateObject()) {
|
||||
var nm = obj.Name.TrimStart('#');
|
||||
if (nm == "")
|
||||
{
|
||||
if (nm == "") {
|
||||
// This is a comment - not a parameter!
|
||||
continue;
|
||||
}
|
||||
switch (obj.Value.ValueKind)
|
||||
{
|
||||
|
||||
switch (obj.Value.ValueKind) {
|
||||
case JsonValueKind.String:
|
||||
var v = obj.Value.ToString();
|
||||
if (v.Equals("yes") || v.Equals("no"))
|
||||
{
|
||||
if (v.Equals("yes") || v.Equals("no")) {
|
||||
ps[nm] = new Constant(Typs.Bool, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
ps[nm] = new Constant(v);
|
||||
}
|
||||
|
||||
|
@ -96,8 +106,7 @@ namespace AspectedRouting.IO.jsonParser
|
|||
|
||||
private static string Get(this JsonElement json, string key)
|
||||
{
|
||||
if (json.TryGetProperty(key, out var p))
|
||||
{
|
||||
if (json.TryGetProperty(key, out var p)) {
|
||||
return p.GetString();
|
||||
}
|
||||
|
||||
|
@ -106,8 +115,7 @@ namespace AspectedRouting.IO.jsonParser
|
|||
|
||||
private static string TryGet(this JsonElement json, string key)
|
||||
{
|
||||
if (json.TryGetProperty(key, out var p))
|
||||
{
|
||||
if (json.TryGetProperty(key, out var p)) {
|
||||
return p.GetString();
|
||||
}
|
||||
|
||||
|
|
|
@ -114,6 +114,15 @@ namespace AspectedRouting.Language.Expression
|
|||
return Specialize(allowedTypes);
|
||||
}
|
||||
|
||||
public IExpression PruneTypes(Func<Type, bool> allowedTypes)
|
||||
{
|
||||
var passed = this.FunctionApplications.Where(kv => allowedTypes.Invoke(kv.Key));
|
||||
if (!passed.Any()) {
|
||||
return null;
|
||||
}
|
||||
return new Apply("pruned", new Dictionary<Type, (IExpression A, IExpression F)>(passed));
|
||||
}
|
||||
|
||||
public IExpression Optimize()
|
||||
{
|
||||
if (Types.Count() == 0) {
|
||||
|
|
|
@ -43,6 +43,16 @@ namespace AspectedRouting.Language.Expression
|
|||
Name, Description, Author, Unit, Filepath);
|
||||
}
|
||||
|
||||
public IExpression PruneTypes(System.Func<Type, bool> allowedTypes)
|
||||
{
|
||||
var e = ExpressionImplementation.PruneTypes(allowedTypes);
|
||||
if (e == null) {
|
||||
return null;
|
||||
}
|
||||
return new AspectMetadata(e, Name, Description, Author, Unit,
|
||||
Filepath, ProfileInternal);
|
||||
}
|
||||
|
||||
public IExpression Optimize()
|
||||
{
|
||||
return new AspectMetadata(ExpressionImplementation.Optimize(),
|
||||
|
|
|
@ -34,17 +34,21 @@ namespace AspectedRouting.Language.Expression
|
|||
|
||||
public abstract object Evaluate(Context c, params IExpression[] arguments);
|
||||
public abstract IExpression Specialize(IEnumerable<Type> allowedTypes);
|
||||
public IExpression PruneTypes(Func<Type, bool> allowedTypes)
|
||||
{
|
||||
var passedTypes = this.Types.Where(allowedTypes);
|
||||
if (!passedTypes.Any()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new FunctionCall(this.Name, passedTypes);
|
||||
}
|
||||
|
||||
public virtual IExpression Optimize()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public IExpression OptimizeWithArgument(IExpression argument)
|
||||
{
|
||||
return this.Apply(argument);
|
||||
}
|
||||
|
||||
public virtual void Visit(Func<IExpression, bool> f)
|
||||
{
|
||||
f(this);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.Language.Typ;
|
||||
using Type = AspectedRouting.Language.Typ.Type;
|
||||
|
||||
|
@ -51,6 +52,16 @@ namespace AspectedRouting.Language.Expression
|
|||
return new FunctionCall(_name, unified);
|
||||
}
|
||||
|
||||
public IExpression PruneTypes(Func<Type, bool> allowedTypes)
|
||||
{
|
||||
var passedTypes = this.Types.Where(allowedTypes);
|
||||
if (!passedTypes.Any()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new FunctionCall(this._name, passedTypes);
|
||||
}
|
||||
|
||||
public IExpression Optimize()
|
||||
{
|
||||
return this;
|
||||
|
|
|
@ -53,6 +53,20 @@ namespace AspectedRouting.Language.Expression
|
|||
LastChange = lastChange;
|
||||
DefaultParameters = defaultParameters;
|
||||
Behaviours = behaviours;
|
||||
|
||||
CheckTypes(Access, "access");
|
||||
CheckTypes(Oneway, "oneway");
|
||||
CheckTypes(Speed, "speed");
|
||||
|
||||
}
|
||||
|
||||
private static void CheckTypes(IExpression e, string name)
|
||||
{
|
||||
if (e.Types.Count() == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Exception("Insufficient specialization: " + name + " has multiple types left, namely " + e.Types.Pretty());
|
||||
}
|
||||
|
||||
public List<IExpression> AllExpressions(Context ctx)
|
||||
|
|
|
@ -8,18 +8,15 @@ namespace AspectedRouting.Language.Functions
|
|||
{
|
||||
public class Constant : IExpression
|
||||
{
|
||||
public IEnumerable<Type> Types { get; }
|
||||
private readonly object _o;
|
||||
|
||||
|
||||
public Constant(IEnumerable<Type> types, object o)
|
||||
{
|
||||
Types = types.ToList();
|
||||
if (o is IEnumerable<IExpression> enumerable)
|
||||
{
|
||||
if (o is IEnumerable<IExpression> enumerable) {
|
||||
o = enumerable.ToList();
|
||||
if (enumerable.Any(x => x == null))
|
||||
{
|
||||
if (enumerable.Any(x => x == null)) {
|
||||
throw new NullReferenceException("Some subexpression is null");
|
||||
}
|
||||
}
|
||||
|
@ -30,11 +27,9 @@ namespace AspectedRouting.Language.Functions
|
|||
public Constant(Type t, object o)
|
||||
{
|
||||
Types = new List<Type> {t};
|
||||
if (o is IEnumerable<IExpression> enumerable)
|
||||
{
|
||||
if (o is IEnumerable<IExpression> enumerable) {
|
||||
o = enumerable.ToList();
|
||||
if (enumerable.Any(x => x == null))
|
||||
{
|
||||
if (enumerable.Any(x => x == null)) {
|
||||
throw new NullReferenceException("Some subexpression is null");
|
||||
}
|
||||
}
|
||||
|
@ -47,29 +42,33 @@ namespace AspectedRouting.Language.Functions
|
|||
var tps = exprs
|
||||
.SpecializeToCommonTypes(out var specializedVersions)
|
||||
.Select(t => new ListType(t));
|
||||
if(specializedVersions.Any(x => x == null))
|
||||
{
|
||||
throw new Exception("Specializing to common types failed for "+string.Join("," ,exprs.Select(e => e.ToString())));
|
||||
if (specializedVersions.Any(x => x == null)) {
|
||||
throw new Exception("Specializing to common types failed for " +
|
||||
string.Join(",", exprs.Select(e => e.ToString())));
|
||||
}
|
||||
|
||||
Types = tps.ToList();
|
||||
_o = specializedVersions;
|
||||
}
|
||||
|
||||
public Constant(List<IExpression> exprs)
|
||||
public Constant(IReadOnlyCollection<IExpression> exprs)
|
||||
{
|
||||
try
|
||||
{
|
||||
try {
|
||||
|
||||
|
||||
|
||||
Types = exprs
|
||||
.SpecializeToCommonTypes(out var specializedVersions).Select(t => new ListType(t)).ToList();
|
||||
if (specializedVersions.Any(x => x == null))
|
||||
{
|
||||
.SpecializeToCommonTypes(out var specializedVersions)
|
||||
.Select(t => new ListType(t))
|
||||
.ToList();
|
||||
if (specializedVersions.Any(x => x == null)) {
|
||||
throw new NullReferenceException("Some subexpression is null");
|
||||
}
|
||||
|
||||
_o = specializedVersions.ToList();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception($"While creating a list with members " +
|
||||
catch (Exception e) {
|
||||
throw new Exception("While creating a list with members " +
|
||||
string.Join(", ", exprs.Select(x => x.Optimize())) +
|
||||
$" {e.Message}", e);
|
||||
}
|
||||
|
@ -83,12 +82,10 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public Constant(double d)
|
||||
{
|
||||
if (d >= 0)
|
||||
{
|
||||
if (d >= 0) {
|
||||
Types = new[] {Typs.Double, Typs.PDouble};
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
Types = new[] {Typs.Double};
|
||||
}
|
||||
|
||||
|
@ -97,12 +94,10 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public Constant(int i)
|
||||
{
|
||||
if (i >= 0)
|
||||
{
|
||||
if (i >= 0) {
|
||||
Types = new[] {Typs.Double, Typs.Nat, Typs.Nat, Typs.PDouble};
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
Types = new[] {Typs.Double, Typs.Int};
|
||||
}
|
||||
|
||||
|
@ -115,23 +110,34 @@ namespace AspectedRouting.Language.Functions
|
|||
_o = tags;
|
||||
}
|
||||
|
||||
public IEnumerable<Type> Types { get; }
|
||||
|
||||
public IExpression PruneTypes(System.Func<Type, bool> allowedTypes)
|
||||
{
|
||||
var passedTypes = Types.Where(allowedTypes);
|
||||
if (!passedTypes.Any()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Constant(passedTypes, _o);
|
||||
}
|
||||
|
||||
public object Evaluate(Context c, params IExpression[] args)
|
||||
{
|
||||
if (_o is IExpression e)
|
||||
{
|
||||
if (_o is IExpression e) {
|
||||
return e.Evaluate(c).Pretty();
|
||||
}
|
||||
|
||||
|
||||
if (Types.Count() > 1) return _o;
|
||||
if (Types.Count() > 1) {
|
||||
return _o;
|
||||
}
|
||||
|
||||
var t = Types.First();
|
||||
switch (t)
|
||||
{
|
||||
switch (t) {
|
||||
case DoubleType _:
|
||||
case PDoubleType _:
|
||||
if (_o is int i)
|
||||
{
|
||||
if (_o is int i) {
|
||||
return
|
||||
(double) i; // I know, it seems absurd having to write this as it is nearly the same as the return beneath, but it _is_ needed
|
||||
}
|
||||
|
@ -145,48 +151,35 @@ namespace AspectedRouting.Language.Functions
|
|||
return _o;
|
||||
}
|
||||
|
||||
public void EvaluateAll(Context c, HashSet<IExpression> addTo)
|
||||
{
|
||||
addTo.Add(this);
|
||||
}
|
||||
|
||||
public IExpression Specialize(IEnumerable<Type> allowedTypesEnumerable)
|
||||
{
|
||||
var allowedTypes = allowedTypesEnumerable.ToList();
|
||||
var unified = Types.SpecializeTo(allowedTypes);
|
||||
if (unified == null)
|
||||
{
|
||||
if (unified == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var newO = _o;
|
||||
if (_o is IExpression e)
|
||||
{
|
||||
if (_o is IExpression e) {
|
||||
newO = e.Specialize(allowedTypes);
|
||||
}
|
||||
|
||||
if (_o is IEnumerable<IExpression> es)
|
||||
{
|
||||
if (_o is IEnumerable<IExpression> es) {
|
||||
var innerTypes = new List<Type>();
|
||||
foreach (var allowedType in allowedTypes)
|
||||
{
|
||||
if (allowedType is ListType listType)
|
||||
{
|
||||
foreach (var allowedType in allowedTypes) {
|
||||
if (allowedType is ListType listType) {
|
||||
innerTypes.Add(listType.InnerType);
|
||||
}
|
||||
}
|
||||
|
||||
var specializedExpressions = new List<IExpression>();
|
||||
foreach (var expr in es)
|
||||
{
|
||||
if (expr == null)
|
||||
{
|
||||
foreach (var expr in es) {
|
||||
if (expr == null) {
|
||||
throw new NullReferenceException("Subexpression is null");
|
||||
}
|
||||
|
||||
var specialized = expr.Specialize(innerTypes);
|
||||
if (specialized == null)
|
||||
{
|
||||
if (specialized == null) {
|
||||
// If a subexpression can not be specialized, this list cannot be specialized
|
||||
return null;
|
||||
}
|
||||
|
@ -202,26 +195,22 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public IExpression Optimize()
|
||||
{
|
||||
if (_o is IEnumerable<IExpression> exprs)
|
||||
{
|
||||
if (_o is IEnumerable<IExpression> exprs) {
|
||||
// This is a list
|
||||
var optExprs = new List<IExpression>();
|
||||
foreach (var expression in exprs)
|
||||
{
|
||||
foreach (var expression in exprs) {
|
||||
var exprOpt = expression.Optimize();
|
||||
if (exprOpt == null || exprOpt.Types.Count() == 0)
|
||||
{
|
||||
if (exprOpt == null || exprOpt.Types.Count() == 0) {
|
||||
throw new ArgumentException("Non-optimizable expression:" + expression);
|
||||
}
|
||||
|
||||
optExprs.Add(exprOpt);
|
||||
}
|
||||
|
||||
return new Constant(optExprs.ToList());
|
||||
return new Constant(optExprs);
|
||||
}
|
||||
|
||||
if (_o is IExpression expr)
|
||||
{
|
||||
if (_o is IExpression expr) {
|
||||
// This is a list
|
||||
return new Constant(expr.Types, expr.Optimize());
|
||||
}
|
||||
|
@ -229,22 +218,14 @@ namespace AspectedRouting.Language.Functions
|
|||
return this;
|
||||
}
|
||||
|
||||
public IExpression OptimizeWithArgument(IExpression argument)
|
||||
{
|
||||
return this.Apply(argument);
|
||||
}
|
||||
|
||||
public void Visit(Func<IExpression, bool> f)
|
||||
{
|
||||
if (_o is IExpression e)
|
||||
{
|
||||
if (_o is IExpression e) {
|
||||
e.Visit(f);
|
||||
}
|
||||
|
||||
if (_o is IEnumerable<IExpression> es)
|
||||
{
|
||||
foreach (var x in es)
|
||||
{
|
||||
if (_o is IEnumerable<IExpression> es) {
|
||||
foreach (var x in es) {
|
||||
x.Visit(f);
|
||||
}
|
||||
}
|
||||
|
@ -252,6 +233,16 @@ namespace AspectedRouting.Language.Functions
|
|||
f(this);
|
||||
}
|
||||
|
||||
public void EvaluateAll(Context c, HashSet<IExpression> addTo)
|
||||
{
|
||||
addTo.Add(this);
|
||||
}
|
||||
|
||||
public IExpression OptimizeWithArgument(IExpression argument)
|
||||
{
|
||||
return this.Apply(argument);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _o.Pretty();
|
||||
|
@ -262,23 +253,19 @@ namespace AspectedRouting.Language.Functions
|
|||
{
|
||||
public static string Pretty(this object o, Context context = null)
|
||||
{
|
||||
switch (o)
|
||||
{
|
||||
switch (o) {
|
||||
case Dictionary<string, string> d:
|
||||
var txt = "";
|
||||
foreach (var (k, v) in d)
|
||||
{
|
||||
foreach (var (k, v) in d) {
|
||||
txt += $"{k}={v};";
|
||||
}
|
||||
|
||||
return $"{{{txt}}}";
|
||||
case Dictionary<string, List<string>> d:
|
||||
var t = "";
|
||||
foreach (var (k, v) in d)
|
||||
{
|
||||
foreach (var (k, v) in d) {
|
||||
var values = v.Pretty();
|
||||
if (!v.Any())
|
||||
{
|
||||
if (!v.Any()) {
|
||||
values = "*";
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.Language.Typ;
|
||||
using Type = AspectedRouting.Language.Typ.Type;
|
||||
|
||||
|
@ -45,6 +46,16 @@ namespace AspectedRouting.Language.Functions
|
|||
return new Parameter(unified, ParamName);
|
||||
}
|
||||
|
||||
public IExpression PruneTypes(System.Func<Type, bool> allowedTypes)
|
||||
{
|
||||
var passedTypes = this.Types.Where(allowedTypes);
|
||||
if (!passedTypes.Any()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Parameter(passedTypes, this.ParamName);
|
||||
}
|
||||
|
||||
public IExpression Optimize()
|
||||
{
|
||||
return this;
|
||||
|
|
|
@ -13,28 +13,29 @@ namespace AspectedRouting.Language
|
|||
IEnumerable<Type> Types { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the expression.
|
||||
/// Gives null if the expression should not give a value
|
||||
/// Evaluates the expression.
|
||||
/// Gives null if the expression should not give a value
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
object Evaluate(Context c, params IExpression[] arguments);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy of this expression, but only execution paths of the given types are kept
|
||||
/// Return null if no execution paths are found
|
||||
/// Creates a copy of this expression, but only execution paths of the given types are kept
|
||||
/// Return null if no execution paths are found
|
||||
/// </summary>
|
||||
IExpression Specialize(IEnumerable<Type> allowedTypes);
|
||||
|
||||
IExpression PruneTypes(System.Func<Type, bool> allowedTypes);
|
||||
|
||||
/// <summary>
|
||||
/// Optimize a single expression, eventually recursively (e.g. a list can optimize all the contents)
|
||||
/// Optimize a single expression, eventually recursively (e.g. a list can optimize all the contents)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IExpression Optimize();
|
||||
|
||||
/// <summary>
|
||||
/// Optimize with the given argument, e.g. listdot can become a list of applied arguments.
|
||||
///
|
||||
/// By default, this should return 'this.Apply(argument)'
|
||||
/// Optimize with the given argument, e.g. listdot can become a list of applied arguments.
|
||||
/// By default, this should return 'this.Apply(argument)'
|
||||
/// </summary>
|
||||
/// <param name="argument"></param>
|
||||
/// <returns></returns>
|
||||
|
@ -46,20 +47,22 @@ namespace AspectedRouting.Language
|
|||
{
|
||||
public static object Run(this IExpression e, Context c, Dictionary<string, string> tags)
|
||||
{
|
||||
try
|
||||
{
|
||||
return e.Apply(new[] {new Constant(tags)}).Evaluate(c);
|
||||
try {
|
||||
var result = e.Apply(new Constant(tags)).Evaluate(c);
|
||||
while (result is IExpression ex) {
|
||||
result = ex.Apply(new Constant(tags)).Evaluate(c);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
catch (Exception err) {
|
||||
throw new Exception($"While evaluating the expression {e} with arguments a list of tags", err);
|
||||
}
|
||||
}
|
||||
|
||||
public static IExpression Specialize(this IExpression e, Type t)
|
||||
{
|
||||
if (t == null)
|
||||
{
|
||||
if (t == null) {
|
||||
throw new NullReferenceException("Cannot specialize to null");
|
||||
}
|
||||
|
||||
|
@ -70,19 +73,16 @@ namespace AspectedRouting.Language
|
|||
{
|
||||
var newTypes = new HashSet<Type>();
|
||||
|
||||
foreach (var oldType in e.Types)
|
||||
{
|
||||
foreach (var oldType in e.Types) {
|
||||
var newType = oldType.Substitute(substitutions);
|
||||
if (newType == null)
|
||||
{
|
||||
if (newType == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newTypes.Add(newType);
|
||||
}
|
||||
|
||||
if (!newTypes.Any())
|
||||
{
|
||||
if (!newTypes.Any()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -96,10 +96,9 @@ namespace AspectedRouting.Language
|
|||
return types;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Runs over all expresions, determines a common ground by unifications
|
||||
/// THen specializes every expression onto this common ground
|
||||
/// Runs over all expressions, determines a common ground by unifications
|
||||
/// Then specializes every expression onto this common ground
|
||||
/// </summary>
|
||||
/// <returns>The common ground of types</returns>
|
||||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||
|
@ -107,41 +106,31 @@ namespace AspectedRouting.Language
|
|||
out IEnumerable<Type> specializedTypes, out IEnumerable<IExpression> specializedExpressions)
|
||||
{
|
||||
specializedTypes = null;
|
||||
var exprsForTypes = exprs.SortWidestToSmallest();
|
||||
exprsForTypes.Reverse();
|
||||
var allExpressions = new HashSet<IExpression>();
|
||||
specializedExpressions = allExpressions;
|
||||
|
||||
foreach (var expr in exprs) {
|
||||
if (specializedTypes == null) {
|
||||
specializedTypes = expr.Types;
|
||||
}
|
||||
else {
|
||||
var newlySpecialized = Typs.WidestCommonTypes(specializedTypes, expr.Types);
|
||||
if (!newlySpecialized.Any()) {
|
||||
throw new ArgumentException("Could not find a common ground for types "+specializedTypes.Pretty()+ " and "+expr.Types.Pretty());
|
||||
}
|
||||
|
||||
foreach (var f in exprsForTypes)
|
||||
{
|
||||
if (specializedTypes == null)
|
||||
{
|
||||
specializedTypes = f.Types.ToHashSet();
|
||||
continue;
|
||||
specializedTypes = newlySpecialized;
|
||||
}
|
||||
|
||||
var specialized = f.Types.RenameVars(specializedTypes).SpecializeTo(specializedTypes, false);
|
||||
// ReSharper disable once JoinNullCheckWithUsage
|
||||
if (specialized == null)
|
||||
{
|
||||
throw new ArgumentException("Could not unify\n "
|
||||
+ "<previous items>: " + string.Join(", ", specializedTypes) +
|
||||
"\nwith\n "
|
||||
+ f.Optimize() + ": " + string.Join(", ", f.Types));
|
||||
}
|
||||
|
||||
specializedTypes = specialized;
|
||||
}
|
||||
|
||||
var tps = specializedTypes;
|
||||
|
||||
var optExprs = new List<IExpression>();
|
||||
foreach (var expr in exprs)
|
||||
{
|
||||
var exprOpt = expr.Specialize(tps);
|
||||
optExprs.Add(exprOpt);
|
||||
foreach (var expr in exprs) {
|
||||
var e = expr.Specialize(specializedTypes);
|
||||
allExpressions.Add(e);
|
||||
}
|
||||
|
||||
return specializedExpressions = optExprs;
|
||||
return specializedExpressions = allExpressions;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.Language.Functions;
|
||||
|
||||
namespace AspectedRouting.Language.Typ
|
||||
{
|
||||
|
@ -72,7 +71,13 @@ namespace AspectedRouting.Language.Typ
|
|||
return SelectSmallestUnion(subbed, t1);
|
||||
}
|
||||
|
||||
private static Type SelectSmallestUnion(this Type wider, Type smaller, bool reverse = false)
|
||||
/// <summary>
|
||||
/// Findes the smallest union type between the two types, with the assumption that 'wider' is a supertype of 'smaller'
|
||||
/// </summary>
|
||||
/// <param name="wider"></param>
|
||||
/// <param name="smaller"></param>
|
||||
/// <returns></returns>
|
||||
private static Type SelectSmallestUnion(this Type wider, Type smaller)
|
||||
{
|
||||
switch (wider) {
|
||||
case Var a:
|
||||
|
@ -80,12 +85,12 @@ namespace AspectedRouting.Language.Typ
|
|||
case ListType l when smaller is ListType lsmaller:
|
||||
return new ListType(
|
||||
l.InnerType.SelectSmallestUnion(
|
||||
l.InnerType.SelectSmallestUnion(lsmaller.InnerType, reverse)));
|
||||
l.InnerType.SelectSmallestUnion(lsmaller.InnerType)));
|
||||
case Curry cWider when smaller is Curry cSmaller:
|
||||
var arg =
|
||||
cWider.ArgType.SelectSmallestUnion(cSmaller.ArgType, !reverse);
|
||||
cWider.ArgType.SelectSmallestUnion(cSmaller.ArgType);
|
||||
var result =
|
||||
cWider.ResultType.SelectSmallestUnion(cSmaller.ResultType, reverse);
|
||||
cWider.ResultType.SelectSmallestUnion(cSmaller.ResultType);
|
||||
return new Curry(arg, result);
|
||||
default:
|
||||
if (wider.IsSuperSet(smaller) && !smaller.IsSuperSet(wider)) {
|
||||
|
@ -96,88 +101,6 @@ namespace AspectedRouting.Language.Typ
|
|||
}
|
||||
}
|
||||
|
||||
public static List<IExpression> SortWidestToSmallest(this IEnumerable<IExpression> expressions)
|
||||
{
|
||||
var all = expressions.ToHashSet();
|
||||
var sorted = new List<IExpression>();
|
||||
while (all.Any()) {
|
||||
var widest = SelectWidestType(all);
|
||||
if (widest == null) {
|
||||
throw new ArgumentException("Can not sort widest to smallest");
|
||||
}
|
||||
|
||||
all.Remove(widest);
|
||||
sorted.Add(widest);
|
||||
}
|
||||
|
||||
return sorted;
|
||||
}
|
||||
|
||||
public static IExpression SelectSmallestType(IEnumerable<IExpression> expressions)
|
||||
{
|
||||
IExpression smallest = null;
|
||||
foreach (var current in expressions) {
|
||||
if (smallest == null) {
|
||||
smallest = current;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (smallest.Types.AllAreSuperset(current.Types)) {
|
||||
smallest = current;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return smallest;
|
||||
}
|
||||
|
||||
public static IExpression SelectWidestType(IEnumerable<IExpression> expressions)
|
||||
{
|
||||
IExpression widest = null;
|
||||
foreach (var current in expressions) {
|
||||
if (widest == null) {
|
||||
widest = current;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current.Types.AllAreSuperset(widest.Types)) {
|
||||
widest = current;
|
||||
}
|
||||
}
|
||||
|
||||
return widest;
|
||||
}
|
||||
|
||||
public static bool AllAreSuperset(this IEnumerable<Type> shouldBeSuper, IEnumerable<Type> shouldBeSmaller)
|
||||
{
|
||||
foreach (var super in shouldBeSuper) {
|
||||
foreach (var smaller in shouldBeSmaller) {
|
||||
if (!super.IsSuperSet(smaller)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to unify t0 with all t1's.
|
||||
/// Every match is returned
|
||||
/// </summary>
|
||||
/// <param name="t0"></param>
|
||||
/// <param name="t1"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<Type> UnifyAny(this Type t0, IEnumerable<Type> t1)
|
||||
{
|
||||
var result = t1.Select(t => t0.Unify(t)).Where(unification => unification != null).ToHashSet();
|
||||
if (!result.Any()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to unify t0 with all t1's.
|
||||
/// Every match is returned, but only if every unification could be performed
|
||||
|
@ -325,6 +248,7 @@ namespace AspectedRouting.Language.Typ
|
|||
// This means that $a is substituted by e.g. ($a -> $x), implying an infinite and contradictory type
|
||||
return null;
|
||||
}
|
||||
|
||||
substitutionsOn0[key] = newVal;
|
||||
appliedTransitivity = true;
|
||||
}
|
||||
|
@ -426,5 +350,197 @@ namespace AspectedRouting.Language.Typ
|
|||
args.Add(t);
|
||||
return args;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// If given two sets of types, select all the WidestCommonType-Combinations possible
|
||||
/// e.g.:
|
||||
/// { Double, Tags -> PDouble} x {PDouble, a -> Double} will result in:
|
||||
/// { Double, Tags -> Double}
|
||||
/// </summary>
|
||||
/// <param name="t0"></param>
|
||||
/// <param name="t1"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<Type> WidestCommonTypes(IEnumerable<Type> t0, IEnumerable<Type> t1)
|
||||
{
|
||||
var widest = new HashSet<Type>();
|
||||
|
||||
foreach (var type0 in t0) {
|
||||
foreach (var type1 in t1) {
|
||||
var t = WidestCommonType(type0, type1);
|
||||
if (t != null) {
|
||||
var (type, subsTable) = t.Value;
|
||||
if (subsTable != null) {
|
||||
type = type.Substitute(subsTable);
|
||||
}
|
||||
widest.Add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return widest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the widest type between the two given types, without assuming which one is wider.
|
||||
/// This is used to find the Union of types, e.g. in a list of expressions
|
||||
/// e.g.:
|
||||
/// WidestCommonType(Double, PDouble) == Double
|
||||
/// WidestCommonType(a -> Double, Double) == null
|
||||
/// WidestCommonType(a -> Double, Tags -> PDouble) => Tags -> Double
|
||||
/// WidestCommonType(Double -> a, PDouble -> b) => PDouble -> a
|
||||
/// </summary>
|
||||
/// <param name="t0"></param>
|
||||
/// <param name="t1"></param>
|
||||
/// <returns></returns>
|
||||
public static (Type, Dictionary<string, Type>? substitutionTable)? WidestCommonType(Type t0, Type t1)
|
||||
{
|
||||
// First things first: we try to unify
|
||||
|
||||
if (t0 is Curry c0 && t1 is Curry c1) {
|
||||
var arg = SmallestCommonType(c0.ArgType, c1.ArgType);
|
||||
var result = WidestCommonType(c0.ResultType, c1.ResultType);
|
||||
if (arg == null) {
|
||||
return null;
|
||||
}
|
||||
var (argT, subs0) = arg.Value;
|
||||
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
var (resultT, subs1) = result.Value;
|
||||
return (new Curry(argT, resultT), MergeDicts(subs0, subs1));
|
||||
}
|
||||
|
||||
|
||||
if (t0 is Var v) {
|
||||
if (t1 is Var vx) {
|
||||
if (v.Name == vx.Name) {
|
||||
return (t0, null);
|
||||
}
|
||||
}
|
||||
return (t1, new Dictionary<string, Type> {{v.Name, t1}});
|
||||
}
|
||||
|
||||
if (t1 is Var v1) {
|
||||
return (t0, new Dictionary<string, Type> {{v1.Name, t1}});
|
||||
}
|
||||
if (t0 == t1 || t0.Equals(t1)) {
|
||||
return (t0, null);
|
||||
}
|
||||
var t0IsSuperT1 = t0.IsSuperSet(t1);
|
||||
var t1IsSuperT0 = t1.IsSuperSet(t0);
|
||||
if (t0IsSuperT1 && !t1IsSuperT0) {
|
||||
return (t0, null);
|
||||
}
|
||||
|
||||
if (t1IsSuperT0 && !t0IsSuperT1) {
|
||||
return (t1, null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Dictionary<string, Type> MergeDicts(Dictionary<string, Type> subs0,
|
||||
Dictionary<string, Type> subs1)
|
||||
{
|
||||
if (subs0 == null && subs1 == null) {
|
||||
return null;
|
||||
}
|
||||
var subsTable = new Dictionary<string, Type>();
|
||||
|
||||
void AddSubs(Dictionary<string, Type> dict)
|
||||
{
|
||||
if (dict == null) {
|
||||
return;
|
||||
}
|
||||
foreach (var kv in dict) {
|
||||
if (subsTable.TryGetValue(kv.Key, out var t)) {
|
||||
// We have seen this variable-type-name before... We should check if it matches
|
||||
if (t.Equals(kv.Value)) {
|
||||
// Ok! No problem!
|
||||
// It is already added anyway, so we continue
|
||||
continue;
|
||||
}
|
||||
|
||||
// Bruh, we have a problem!!!
|
||||
throw new Exception(t + " != " + kv.Value);
|
||||
}
|
||||
|
||||
if (kv.Value is Var v) {
|
||||
if (v.Name == kv.Key) {
|
||||
// Well, this is a useless substitution...
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
subsTable[kv.Key] = kv.Value;
|
||||
}
|
||||
}
|
||||
|
||||
AddSubs(subs0);
|
||||
AddSubs(subs1);
|
||||
|
||||
if (!subsTable.Any()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return subsTable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the smallest type between the two given types, without assuming which one is wider.
|
||||
/// This is used to find the Union of types, e.g. in a list of expressions
|
||||
/// e.g.:
|
||||
/// WidestCommonType(Double, PDouble) == PDouble
|
||||
/// WidestCommonType(a -> Double, Double) == null
|
||||
/// WidestCommonType(a -> Double, Tags -> PDouble) => Tags -> PDouble
|
||||
/// WidestCommonType(Double -> a, PDouble -> b) => Double -> a
|
||||
/// </summary>
|
||||
/// <param name="t0"></param>
|
||||
/// <param name="t1"></param>
|
||||
/// <returns></returns>
|
||||
public static (Type, Dictionary<string, Type> substitutionTable)? SmallestCommonType(Type t0, Type t1)
|
||||
{
|
||||
if (t0 is Curry c0 && t1 is Curry c1) {
|
||||
var arg = WidestCommonType(c0.ArgType, c1.ArgType);
|
||||
var result = SmallestCommonType(c0.ResultType, c1.ResultType);
|
||||
if (arg == null) {
|
||||
return null;
|
||||
}
|
||||
var (argT, subs0) = arg.Value;
|
||||
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
var (resultT, subs1) = result.Value;
|
||||
return (new Curry(argT, resultT), MergeDicts(subs0, subs1));
|
||||
}
|
||||
|
||||
|
||||
if (t0 is Var v) {
|
||||
return (t1, new Dictionary<string, Type> {{v.Name, t1}});
|
||||
}
|
||||
|
||||
if (t1 is Var v1) {
|
||||
return (t0, new Dictionary<string, Type> {{v1.Name, t1}});
|
||||
}
|
||||
|
||||
if (t0 == t1 || t0.Equals(t1)) {
|
||||
return (t0, null);
|
||||
}
|
||||
|
||||
var t0IsSuperT1 = t0.IsSuperSet(t1);
|
||||
var t1IsSuperT0 = t1.IsSuperSet(t0);
|
||||
if (t0IsSuperT1 && !t1IsSuperT0) {
|
||||
return (t0, null);
|
||||
}
|
||||
|
||||
if (t1IsSuperT0 && !t0IsSuperT1) {
|
||||
return (t1, null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue