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
|
tags
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
// First the more general ones!
|
||||||
Assert.Equal(
|
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);
|
code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,27 +83,9 @@ namespace AspectedRouting.Test.Snippets
|
||||||
});
|
});
|
||||||
|
|
||||||
var expected =
|
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);
|
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,13 +1,89 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using AspectedRouting.Language;
|
using AspectedRouting.Language;
|
||||||
|
using AspectedRouting.Language.Functions;
|
||||||
using AspectedRouting.Language.Typ;
|
using AspectedRouting.Language.Typ;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using Type = AspectedRouting.Language.Typ.Type;
|
||||||
|
|
||||||
namespace AspectedRouting.Test
|
namespace AspectedRouting.Test
|
||||||
{
|
{
|
||||||
public class TypingTests
|
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]
|
[Fact]
|
||||||
public void JoinApply_Id()
|
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">
|
<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>
|
<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.Snippets.SnippetTests</TestId>
|
||||||
|
<TestId>xUnit::A1309041-8AAE-42D7-A886-94C9FFC6A28C::.NETCoreApp,Version=v3.1::AspectedRouting.Test.TypingTests.SpecializeToCommonTypes_X2PDouble_Y2Double_Gives_X2Double</TestId>
|
||||||
</TestAncestor>
|
</TestAncestor>
|
||||||
</SessionState></s:String>
|
</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">
|
<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">
|
<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;" />
|
<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>
|
</SessionState></s:String>
|
||||||
</wpf:ResourceDictionary>
|
</wpf:ResourceDictionary>
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using AspectedRouting.Language;
|
using AspectedRouting.Language;
|
||||||
using Type = AspectedRouting.Language.Typ.Type;
|
using Type = AspectedRouting.Language.Typ.Type;
|
||||||
|
|
||||||
|
@ -31,6 +32,16 @@ namespace AspectedRouting.IO.LuaSkeleton
|
||||||
return this;
|
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()
|
public IExpression Optimize()
|
||||||
{
|
{
|
||||||
return this;
|
return this;
|
||||||
|
@ -40,5 +51,6 @@ namespace AspectedRouting.IO.LuaSkeleton
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -248,6 +248,7 @@ namespace AspectedRouting.IO.jsonParser
|
||||||
var exprs = e.EnumerateArray().Select(json =>
|
var exprs = e.EnumerateArray().Select(json =>
|
||||||
Funcs.Either(Funcs.Id, Funcs.Const, json.ParseExpression(context)))
|
Funcs.Either(Funcs.Id, Funcs.Const, json.ParseExpression(context)))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var list = new Constant(exprs);
|
var list = new Constant(exprs);
|
||||||
return Funcs.Either(Funcs.Id, Funcs.ListDot, list);
|
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)
|
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 +
|
throw new ArgumentException("Not a valid profile: the declaration expression for '" + property +
|
||||||
"' is missing");
|
"' is missing");
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
var expr = ParseExpression(prop, c);
|
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");
|
throw new Exception($"Could not parse field {property}, no valid typing for expression found");
|
||||||
}
|
}
|
||||||
|
|
||||||
expr = expr.Optimize();
|
expr = expr.Optimize();
|
||||||
expr = Funcs.Either(Funcs.Id, Funcs.Const, expr);
|
expr = Funcs.Either(Funcs.Id, Funcs.Const, expr);
|
||||||
|
|
||||||
var specialized = expr.Specialize(new Curry(Typs.Tags, new Var("a")));
|
var specialized = expr.Specialize(new Curry(Typs.Tags, new Var("a")));
|
||||||
if (specialized == null)
|
|
||||||
{
|
if (specialized == null) {
|
||||||
throw new ArgumentException("The expression for " + property +
|
throw new ArgumentException("The expression for " + property +
|
||||||
" hasn't the right type of 'Tags -> a'; it has types " +
|
" hasn't the right type of 'Tags -> a'; it has types " +
|
||||||
string.Join(",", expr.Types) + "\n" + expr.TypeBreakdown());
|
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);
|
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)
|
private static Dictionary<string, IExpression> ParseParameters(this JsonElement e)
|
||||||
{
|
{
|
||||||
var ps = new Dictionary<string, IExpression>();
|
var ps = new Dictionary<string, IExpression>();
|
||||||
foreach (var obj in e.EnumerateObject())
|
foreach (var obj in e.EnumerateObject()) {
|
||||||
{
|
|
||||||
var nm = obj.Name.TrimStart('#');
|
var nm = obj.Name.TrimStart('#');
|
||||||
if (nm == "")
|
if (nm == "") {
|
||||||
{
|
|
||||||
// This is a comment - not a parameter!
|
// This is a comment - not a parameter!
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
switch (obj.Value.ValueKind)
|
|
||||||
{
|
switch (obj.Value.ValueKind) {
|
||||||
case JsonValueKind.String:
|
case JsonValueKind.String:
|
||||||
var v = obj.Value.ToString();
|
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);
|
ps[nm] = new Constant(Typs.Bool, v);
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
ps[nm] = new Constant(v);
|
ps[nm] = new Constant(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,8 +106,7 @@ namespace AspectedRouting.IO.jsonParser
|
||||||
|
|
||||||
private static string Get(this JsonElement json, string key)
|
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();
|
return p.GetString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,8 +115,7 @@ namespace AspectedRouting.IO.jsonParser
|
||||||
|
|
||||||
private static string TryGet(this JsonElement json, string key)
|
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();
|
return p.GetString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,6 +114,15 @@ namespace AspectedRouting.Language.Expression
|
||||||
return Specialize(allowedTypes);
|
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()
|
public IExpression Optimize()
|
||||||
{
|
{
|
||||||
if (Types.Count() == 0) {
|
if (Types.Count() == 0) {
|
||||||
|
|
|
@ -43,6 +43,16 @@ namespace AspectedRouting.Language.Expression
|
||||||
Name, Description, Author, Unit, Filepath);
|
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()
|
public IExpression Optimize()
|
||||||
{
|
{
|
||||||
return new AspectMetadata(ExpressionImplementation.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 object Evaluate(Context c, params IExpression[] arguments);
|
||||||
public abstract IExpression Specialize(IEnumerable<Type> allowedTypes);
|
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()
|
public virtual IExpression Optimize()
|
||||||
{
|
{
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IExpression OptimizeWithArgument(IExpression argument)
|
|
||||||
{
|
|
||||||
return this.Apply(argument);
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void Visit(Func<IExpression, bool> f)
|
public virtual void Visit(Func<IExpression, bool> f)
|
||||||
{
|
{
|
||||||
f(this);
|
f(this);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using AspectedRouting.Language.Typ;
|
using AspectedRouting.Language.Typ;
|
||||||
using Type = AspectedRouting.Language.Typ.Type;
|
using Type = AspectedRouting.Language.Typ.Type;
|
||||||
|
|
||||||
|
@ -51,6 +52,16 @@ namespace AspectedRouting.Language.Expression
|
||||||
return new FunctionCall(_name, unified);
|
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()
|
public IExpression Optimize()
|
||||||
{
|
{
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -53,6 +53,20 @@ namespace AspectedRouting.Language.Expression
|
||||||
LastChange = lastChange;
|
LastChange = lastChange;
|
||||||
DefaultParameters = defaultParameters;
|
DefaultParameters = defaultParameters;
|
||||||
Behaviours = behaviours;
|
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)
|
public List<IExpression> AllExpressions(Context ctx)
|
||||||
|
|
|
@ -8,18 +8,15 @@ namespace AspectedRouting.Language.Functions
|
||||||
{
|
{
|
||||||
public class Constant : IExpression
|
public class Constant : IExpression
|
||||||
{
|
{
|
||||||
public IEnumerable<Type> Types { get; }
|
|
||||||
private readonly object _o;
|
private readonly object _o;
|
||||||
|
|
||||||
|
|
||||||
public Constant(IEnumerable<Type> types, object o)
|
public Constant(IEnumerable<Type> types, object o)
|
||||||
{
|
{
|
||||||
Types = types.ToList();
|
Types = types.ToList();
|
||||||
if (o is IEnumerable<IExpression> enumerable)
|
if (o is IEnumerable<IExpression> enumerable) {
|
||||||
{
|
|
||||||
o = enumerable.ToList();
|
o = enumerable.ToList();
|
||||||
if (enumerable.Any(x => x == null))
|
if (enumerable.Any(x => x == null)) {
|
||||||
{
|
|
||||||
throw new NullReferenceException("Some subexpression is null");
|
throw new NullReferenceException("Some subexpression is null");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,11 +27,9 @@ namespace AspectedRouting.Language.Functions
|
||||||
public Constant(Type t, object o)
|
public Constant(Type t, object o)
|
||||||
{
|
{
|
||||||
Types = new List<Type> {t};
|
Types = new List<Type> {t};
|
||||||
if (o is IEnumerable<IExpression> enumerable)
|
if (o is IEnumerable<IExpression> enumerable) {
|
||||||
{
|
|
||||||
o = enumerable.ToList();
|
o = enumerable.ToList();
|
||||||
if (enumerable.Any(x => x == null))
|
if (enumerable.Any(x => x == null)) {
|
||||||
{
|
|
||||||
throw new NullReferenceException("Some subexpression is null");
|
throw new NullReferenceException("Some subexpression is null");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,29 +42,33 @@ namespace AspectedRouting.Language.Functions
|
||||||
var tps = exprs
|
var tps = exprs
|
||||||
.SpecializeToCommonTypes(out var specializedVersions)
|
.SpecializeToCommonTypes(out var specializedVersions)
|
||||||
.Select(t => new ListType(t));
|
.Select(t => new ListType(t));
|
||||||
if(specializedVersions.Any(x => x == null))
|
if (specializedVersions.Any(x => x == null)) {
|
||||||
{
|
throw new Exception("Specializing to common types failed for " +
|
||||||
throw new Exception("Specializing to common types failed for "+string.Join("," ,exprs.Select(e => e.ToString())));
|
string.Join(",", exprs.Select(e => e.ToString())));
|
||||||
}
|
}
|
||||||
|
|
||||||
Types = tps.ToList();
|
Types = tps.ToList();
|
||||||
_o = specializedVersions;
|
_o = specializedVersions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Constant(List<IExpression> exprs)
|
public Constant(IReadOnlyCollection<IExpression> exprs)
|
||||||
{
|
{
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
Types = exprs
|
Types = exprs
|
||||||
.SpecializeToCommonTypes(out var specializedVersions).Select(t => new ListType(t)).ToList();
|
.SpecializeToCommonTypes(out var specializedVersions)
|
||||||
if (specializedVersions.Any(x => x == null))
|
.Select(t => new ListType(t))
|
||||||
{
|
.ToList();
|
||||||
|
if (specializedVersions.Any(x => x == null)) {
|
||||||
throw new NullReferenceException("Some subexpression is null");
|
throw new NullReferenceException("Some subexpression is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
_o = specializedVersions.ToList();
|
_o = specializedVersions.ToList();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e) {
|
||||||
{
|
throw new Exception("While creating a list with members " +
|
||||||
throw new Exception($"While creating a list with members " +
|
|
||||||
string.Join(", ", exprs.Select(x => x.Optimize())) +
|
string.Join(", ", exprs.Select(x => x.Optimize())) +
|
||||||
$" {e.Message}", e);
|
$" {e.Message}", e);
|
||||||
}
|
}
|
||||||
|
@ -83,12 +82,10 @@ namespace AspectedRouting.Language.Functions
|
||||||
|
|
||||||
public Constant(double d)
|
public Constant(double d)
|
||||||
{
|
{
|
||||||
if (d >= 0)
|
if (d >= 0) {
|
||||||
{
|
|
||||||
Types = new[] {Typs.Double, Typs.PDouble};
|
Types = new[] {Typs.Double, Typs.PDouble};
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
Types = new[] {Typs.Double};
|
Types = new[] {Typs.Double};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,12 +94,10 @@ namespace AspectedRouting.Language.Functions
|
||||||
|
|
||||||
public Constant(int i)
|
public Constant(int i)
|
||||||
{
|
{
|
||||||
if (i >= 0)
|
if (i >= 0) {
|
||||||
{
|
|
||||||
Types = new[] {Typs.Double, Typs.Nat, Typs.Nat, Typs.PDouble};
|
Types = new[] {Typs.Double, Typs.Nat, Typs.Nat, Typs.PDouble};
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
Types = new[] {Typs.Double, Typs.Int};
|
Types = new[] {Typs.Double, Typs.Int};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,23 +110,34 @@ namespace AspectedRouting.Language.Functions
|
||||||
_o = tags;
|
_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)
|
public object Evaluate(Context c, params IExpression[] args)
|
||||||
{
|
{
|
||||||
if (_o is IExpression e)
|
if (_o is IExpression e) {
|
||||||
{
|
|
||||||
return e.Evaluate(c).Pretty();
|
return e.Evaluate(c).Pretty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (Types.Count() > 1) return _o;
|
if (Types.Count() > 1) {
|
||||||
|
return _o;
|
||||||
|
}
|
||||||
|
|
||||||
var t = Types.First();
|
var t = Types.First();
|
||||||
switch (t)
|
switch (t) {
|
||||||
{
|
|
||||||
case DoubleType _:
|
case DoubleType _:
|
||||||
case PDoubleType _:
|
case PDoubleType _:
|
||||||
if (_o is int i)
|
if (_o is int i) {
|
||||||
{
|
|
||||||
return
|
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
|
(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;
|
return _o;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EvaluateAll(Context c, HashSet<IExpression> addTo)
|
|
||||||
{
|
|
||||||
addTo.Add(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IExpression Specialize(IEnumerable<Type> allowedTypesEnumerable)
|
public IExpression Specialize(IEnumerable<Type> allowedTypesEnumerable)
|
||||||
{
|
{
|
||||||
var allowedTypes = allowedTypesEnumerable.ToList();
|
var allowedTypes = allowedTypesEnumerable.ToList();
|
||||||
var unified = Types.SpecializeTo(allowedTypes);
|
var unified = Types.SpecializeTo(allowedTypes);
|
||||||
if (unified == null)
|
if (unified == null) {
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newO = _o;
|
var newO = _o;
|
||||||
if (_o is IExpression e)
|
if (_o is IExpression e) {
|
||||||
{
|
|
||||||
newO = e.Specialize(allowedTypes);
|
newO = e.Specialize(allowedTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_o is IEnumerable<IExpression> es)
|
if (_o is IEnumerable<IExpression> es) {
|
||||||
{
|
|
||||||
var innerTypes = new List<Type>();
|
var innerTypes = new List<Type>();
|
||||||
foreach (var allowedType in allowedTypes)
|
foreach (var allowedType in allowedTypes) {
|
||||||
{
|
if (allowedType is ListType listType) {
|
||||||
if (allowedType is ListType listType)
|
|
||||||
{
|
|
||||||
innerTypes.Add(listType.InnerType);
|
innerTypes.Add(listType.InnerType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var specializedExpressions = new List<IExpression>();
|
var specializedExpressions = new List<IExpression>();
|
||||||
foreach (var expr in es)
|
foreach (var expr in es) {
|
||||||
{
|
if (expr == null) {
|
||||||
if (expr == null)
|
|
||||||
{
|
|
||||||
throw new NullReferenceException("Subexpression is null");
|
throw new NullReferenceException("Subexpression is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
var specialized = expr.Specialize(innerTypes);
|
var specialized = expr.Specialize(innerTypes);
|
||||||
if (specialized == null)
|
if (specialized == null) {
|
||||||
{
|
|
||||||
// If a subexpression can not be specialized, this list cannot be specialized
|
// If a subexpression can not be specialized, this list cannot be specialized
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -202,26 +195,22 @@ namespace AspectedRouting.Language.Functions
|
||||||
|
|
||||||
public IExpression Optimize()
|
public IExpression Optimize()
|
||||||
{
|
{
|
||||||
if (_o is IEnumerable<IExpression> exprs)
|
if (_o is IEnumerable<IExpression> exprs) {
|
||||||
{
|
|
||||||
// This is a list
|
// This is a list
|
||||||
var optExprs = new List<IExpression>();
|
var optExprs = new List<IExpression>();
|
||||||
foreach (var expression in exprs)
|
foreach (var expression in exprs) {
|
||||||
{
|
|
||||||
var exprOpt = expression.Optimize();
|
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);
|
throw new ArgumentException("Non-optimizable expression:" + expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
optExprs.Add(exprOpt);
|
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
|
// This is a list
|
||||||
return new Constant(expr.Types, expr.Optimize());
|
return new Constant(expr.Types, expr.Optimize());
|
||||||
}
|
}
|
||||||
|
@ -229,22 +218,14 @@ namespace AspectedRouting.Language.Functions
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IExpression OptimizeWithArgument(IExpression argument)
|
|
||||||
{
|
|
||||||
return this.Apply(argument);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Visit(Func<IExpression, bool> f)
|
public void Visit(Func<IExpression, bool> f)
|
||||||
{
|
{
|
||||||
if (_o is IExpression e)
|
if (_o is IExpression e) {
|
||||||
{
|
|
||||||
e.Visit(f);
|
e.Visit(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_o is IEnumerable<IExpression> es)
|
if (_o is IEnumerable<IExpression> es) {
|
||||||
{
|
foreach (var x in es) {
|
||||||
foreach (var x in es)
|
|
||||||
{
|
|
||||||
x.Visit(f);
|
x.Visit(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,33 +233,39 @@ namespace AspectedRouting.Language.Functions
|
||||||
f(this);
|
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()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return _o.Pretty();
|
return _o.Pretty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ObjectExtensions
|
public static class ObjectExtensions
|
||||||
{
|
{
|
||||||
public static string Pretty(this object o, Context context = null)
|
public static string Pretty(this object o, Context context = null)
|
||||||
{
|
{
|
||||||
switch (o)
|
switch (o) {
|
||||||
{
|
|
||||||
case Dictionary<string, string> d:
|
case Dictionary<string, string> d:
|
||||||
var txt = "";
|
var txt = "";
|
||||||
foreach (var (k, v) in d)
|
foreach (var (k, v) in d) {
|
||||||
{
|
|
||||||
txt += $"{k}={v};";
|
txt += $"{k}={v};";
|
||||||
}
|
}
|
||||||
|
|
||||||
return $"{{{txt}}}";
|
return $"{{{txt}}}";
|
||||||
case Dictionary<string, List<string>> d:
|
case Dictionary<string, List<string>> d:
|
||||||
var t = "";
|
var t = "";
|
||||||
foreach (var (k, v) in d)
|
foreach (var (k, v) in d) {
|
||||||
{
|
|
||||||
var values = v.Pretty();
|
var values = v.Pretty();
|
||||||
if (!v.Any())
|
if (!v.Any()) {
|
||||||
{
|
|
||||||
values = "*";
|
values = "*";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using AspectedRouting.Language.Typ;
|
using AspectedRouting.Language.Typ;
|
||||||
using Type = AspectedRouting.Language.Typ.Type;
|
using Type = AspectedRouting.Language.Typ.Type;
|
||||||
|
|
||||||
|
@ -44,6 +45,16 @@ namespace AspectedRouting.Language.Functions
|
||||||
|
|
||||||
return new Parameter(unified, ParamName);
|
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()
|
public IExpression Optimize()
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,28 +13,29 @@ namespace AspectedRouting.Language
|
||||||
IEnumerable<Type> Types { get; }
|
IEnumerable<Type> Types { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Evaluates the expression.
|
/// Evaluates the expression.
|
||||||
/// Gives null if the expression should not give a value
|
/// Gives null if the expression should not give a value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
object Evaluate(Context c, params IExpression[] arguments);
|
object Evaluate(Context c, params IExpression[] arguments);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a copy of this expression, but only execution paths of the given types are kept
|
/// Creates a copy of this expression, but only execution paths of the given types are kept
|
||||||
/// Return null if no execution paths are found
|
/// Return null if no execution paths are found
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IExpression Specialize(IEnumerable<Type> allowedTypes);
|
IExpression Specialize(IEnumerable<Type> allowedTypes);
|
||||||
|
|
||||||
|
IExpression PruneTypes(System.Func<Type, bool> allowedTypes);
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
IExpression Optimize();
|
IExpression Optimize();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Optimize with the given argument, e.g. listdot can become a list of applied arguments.
|
/// Optimize with the given argument, e.g. listdot can become a list of applied arguments.
|
||||||
///
|
/// By default, this should return 'this.Apply(argument)'
|
||||||
/// By default, this should return 'this.Apply(argument)'
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="argument"></param>
|
/// <param name="argument"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
|
@ -46,20 +47,22 @@ namespace AspectedRouting.Language
|
||||||
{
|
{
|
||||||
public static object Run(this IExpression e, Context c, Dictionary<string, string> tags)
|
public static object Run(this IExpression e, Context c, Dictionary<string, string> tags)
|
||||||
{
|
{
|
||||||
try
|
try {
|
||||||
{
|
var result = e.Apply(new Constant(tags)).Evaluate(c);
|
||||||
return e.Apply(new[] {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);
|
throw new Exception($"While evaluating the expression {e} with arguments a list of tags", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IExpression Specialize(this IExpression e, Type t)
|
public static IExpression Specialize(this IExpression e, Type t)
|
||||||
{
|
{
|
||||||
if (t == null)
|
if (t == null) {
|
||||||
{
|
|
||||||
throw new NullReferenceException("Cannot specialize to null");
|
throw new NullReferenceException("Cannot specialize to null");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,19 +73,16 @@ namespace AspectedRouting.Language
|
||||||
{
|
{
|
||||||
var newTypes = new HashSet<Type>();
|
var newTypes = new HashSet<Type>();
|
||||||
|
|
||||||
foreach (var oldType in e.Types)
|
foreach (var oldType in e.Types) {
|
||||||
{
|
|
||||||
var newType = oldType.Substitute(substitutions);
|
var newType = oldType.Substitute(substitutions);
|
||||||
if (newType == null)
|
if (newType == null) {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
newTypes.Add(newType);
|
newTypes.Add(newType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newTypes.Any())
|
if (!newTypes.Any()) {
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,10 +96,9 @@ namespace AspectedRouting.Language
|
||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Runs over all expresions, determines a common ground by unifications
|
/// Runs over all expressions, determines a common ground by unifications
|
||||||
/// THen specializes every expression onto this common ground
|
/// Then specializes every expression onto this common ground
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The common ground of types</returns>
|
/// <returns>The common ground of types</returns>
|
||||||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||||
|
@ -107,41 +106,31 @@ namespace AspectedRouting.Language
|
||||||
out IEnumerable<Type> specializedTypes, out IEnumerable<IExpression> specializedExpressions)
|
out IEnumerable<Type> specializedTypes, out IEnumerable<IExpression> specializedExpressions)
|
||||||
{
|
{
|
||||||
specializedTypes = null;
|
specializedTypes = null;
|
||||||
var exprsForTypes = exprs.SortWidestToSmallest();
|
var allExpressions = new HashSet<IExpression>();
|
||||||
exprsForTypes.Reverse();
|
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)
|
specializedTypes = newlySpecialized;
|
||||||
{
|
|
||||||
if (specializedTypes == null)
|
|
||||||
{
|
|
||||||
specializedTypes = f.Types.ToHashSet();
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
foreach (var expr in exprs) {
|
||||||
|
var e = expr.Specialize(specializedTypes);
|
||||||
var optExprs = new List<IExpression>();
|
allExpressions.Add(e);
|
||||||
foreach (var expr in exprs)
|
|
||||||
{
|
|
||||||
var exprOpt = expr.Specialize(tps);
|
|
||||||
optExprs.Add(exprOpt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return specializedExpressions = optExprs;
|
return specializedExpressions = allExpressions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using AspectedRouting.Language.Functions;
|
|
||||||
|
|
||||||
namespace AspectedRouting.Language.Typ
|
namespace AspectedRouting.Language.Typ
|
||||||
{
|
{
|
||||||
|
@ -72,7 +71,13 @@ namespace AspectedRouting.Language.Typ
|
||||||
return SelectSmallestUnion(subbed, t1);
|
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) {
|
switch (wider) {
|
||||||
case Var a:
|
case Var a:
|
||||||
|
@ -80,12 +85,12 @@ namespace AspectedRouting.Language.Typ
|
||||||
case ListType l when smaller is ListType lsmaller:
|
case ListType l when smaller is ListType lsmaller:
|
||||||
return new ListType(
|
return new ListType(
|
||||||
l.InnerType.SelectSmallestUnion(
|
l.InnerType.SelectSmallestUnion(
|
||||||
l.InnerType.SelectSmallestUnion(lsmaller.InnerType, reverse)));
|
l.InnerType.SelectSmallestUnion(lsmaller.InnerType)));
|
||||||
case Curry cWider when smaller is Curry cSmaller:
|
case Curry cWider when smaller is Curry cSmaller:
|
||||||
var arg =
|
var arg =
|
||||||
cWider.ArgType.SelectSmallestUnion(cSmaller.ArgType, !reverse);
|
cWider.ArgType.SelectSmallestUnion(cSmaller.ArgType);
|
||||||
var result =
|
var result =
|
||||||
cWider.ResultType.SelectSmallestUnion(cSmaller.ResultType, reverse);
|
cWider.ResultType.SelectSmallestUnion(cSmaller.ResultType);
|
||||||
return new Curry(arg, result);
|
return new Curry(arg, result);
|
||||||
default:
|
default:
|
||||||
if (wider.IsSuperSet(smaller) && !smaller.IsSuperSet(wider)) {
|
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>
|
/// <summary>
|
||||||
/// Tries to unify t0 with all t1's.
|
/// Tries to unify t0 with all t1's.
|
||||||
/// Every match is returned, but only if every unification could be performed
|
/// 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
|
// This means that $a is substituted by e.g. ($a -> $x), implying an infinite and contradictory type
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
substitutionsOn0[key] = newVal;
|
substitutionsOn0[key] = newVal;
|
||||||
appliedTransitivity = true;
|
appliedTransitivity = true;
|
||||||
}
|
}
|
||||||
|
@ -426,5 +350,197 @@ namespace AspectedRouting.Language.Typ
|
||||||
args.Add(t);
|
args.Add(t);
|
||||||
return args;
|
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