More typesystem fixes, atleast now properly works

This commit is contained in:
Pieter Vander Vennet 2020-06-17 17:23:48 +02:00
parent 173756818e
commit 57704f5ee5
21 changed files with 262 additions and 88 deletions

View file

@ -329,12 +329,17 @@ namespace AspectedRouting.Test
[Fact]
public void SpecializeToCommonType()
{
var p0 = Funcs.Parse;
var p1 = Funcs.Const.Apply(new Constant(1.0));
var p0 = Funcs.Parse.Specialize(new Curry(Typs.String, Typs.PDouble));
var p1 = Funcs.Const.Apply(new Constant(1.0)).Specialize(
new Curry(new Var("a"), Typs.Double));
var exprs = new[] {p0, p1};
var newTypes = exprs.SpecializeToCommonTypes(out var specializedExpressions);
Assert.Single(newTypes);
var newTypes = exprs.SpecializeToCommonTypes(out var _);
Assert.Single( newTypes);
exprs = new[] {p1, p0};
newTypes = exprs.SpecializeToCommonTypes(out var _);
Assert.Single( newTypes);
}
}
}

View file

@ -25,7 +25,7 @@ namespace AspectedRouting.Test
{"ferry", "yes"}
};
Assert.Equal("tags -> double", string.Join(", ", aspect.Types));
Assert.Equal("tags -> pdouble", string.Join(", ", aspect.Types));
Assert.Equal(42d, new Apply(aspect, new Constant(tags)).Evaluate(null));
}

View file

@ -1,9 +1,4 @@
<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/=af5d2251_002D713d_002D473f_002D9157_002D89c9d06216e5/@EntryIndexedValue">&lt;SessionState ContinuousTestingIsOn="False" ContinuousTestingMode="0" FrameworkVersion="{x:Null}" IsLocked="False" Name="All tests from &amp;lt;AspectedRouting.Test&amp;gt;" PlatformMonoPreference="{x:Null}" PlatformType="{x:Null}" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=acd4e5d9_002D8d57_002D4495_002Da7aa_002D38598b9e98e0/@EntryIndexedValue">&lt;SessionState ContinuousTestingIsOn="False" ContinuousTestingMode="0" FrameworkVersion="{x:Null}" IsLocked="False" Name="All tests from &amp;lt;AspectedRouting.Test&amp;gt;" PlatformMonoPreference="{x:Null}" PlatformType="{x:Null}" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;
&lt;Project Location="/home/pietervdvn/werk/AspectedRouting/AspectedRouting.Test" Presentation="&amp;lt;AspectedRouting.Test&amp;gt;" /&gt;
&lt;/SessionState&gt;</s:String>
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=bad1d05d_002Df895_002D4e7d_002Da6e2_002Dede32f077b26/@EntryIndexedValue">&lt;SessionState ContinuousTestingIsOn="False" ContinuousTestingMode="0" FrameworkVersion="{x:Null}" IsLocked="False" Name="SpecializeToCommonType" PlatformMonoPreference="{x:Null}" PlatformType="{x:Null}" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;
&lt;TestAncestor&gt;
&lt;TestId&gt;xUnit::A1309041-8AAE-42D7-A886-94C9FFC6A28C::.NETCoreApp,Version=v3.1::AspectedRouting.Test.FunctionsTest.SpecializeToCommonType&lt;/TestId&gt;
&lt;/TestAncestor&gt;
&lt;/SessionState&gt;</s:String></wpf:ResourceDictionary>

View file

@ -169,6 +169,7 @@ namespace AspectedRouting.IO.LuaSkeleton
return called.Name;
}
AddDependenciesFor(called);
AddFunction(called);
return $"{fc.CalledFunctionName.AsLuaIdentifier()}(parameters, tags, result)";
case Constant c:

View file

@ -60,9 +60,8 @@ namespace AspectedRouting.IO.itinero1
var exprInLua = _skeleton.ToLua(expression.Optimize());
var subs = new Curry(Typs.Tags, new Var(("a"))).UnificationTable(expression.Types.First());
if (subs != null && subs.TryGetValue("$a", out var resultType) &&
(resultType.Equals(Typs.Bool) || resultType.Equals(Typs.String)))
var resultTypes = expression.Types.Select(t => t.Uncurry().Last());
if (resultTypes.Any(t => t.Name.Equals(Typs.Bool.Name)))
{
_skeleton. AddDep("parse");
exprInLua = "parse(" + exprInLua + ")";

View file

@ -0,0 +1,6 @@
function atleast(minimumExpected, actual, thn, els)
if (minimumExpected <= actual) then
return thn;
end
return els
end

View file

@ -197,13 +197,8 @@ namespace AspectedRouting.Language
public static string TypeBreakdown(this IExpression e)
{
var text = "";
e.Visit(x =>
{
text += $"\n\n{x}\n : {string.Join("\n : ", x.Types)}";
return true;
});
return text;
return e.ToString() + " : "+string.Join(" ; ", e.Types);
}
public static void SanityCheckProfile(this ProfileMetaData pmd, Context context)

View file

@ -83,14 +83,15 @@ namespace AspectedRouting.Language.Expression
{
try
{
_debugInfo = $"\n{f.Optimize().TypeBreakdown()}\n" +
$"is applied on an argument with types:" +
$"{string.Join(", ", argument.Optimize().Types)}";
_debugInfo = $"\n{f.Optimize().TypeBreakdown().Indent()}\n" +
$"is given the argument: " +
"(" + argument.Optimize().TypeBreakdown() + ")";
}
catch (Exception)
{
_debugInfo = $"\n{f.TypeBreakdown()}\n" +
$"{argument.TypeBreakdown()}";
_debugInfo =$"\n (NO OPT) {f.TypeBreakdown().Indent()}\n" +
$"is given the argument: " +
"(" + argument.TypeBreakdown() + ")";
}
}
}
@ -197,6 +198,7 @@ namespace AspectedRouting.Language.Expression
if (Types.Count() > 1)
{
// Too much types to optimize
var optimized = new Dictionary<Type, (IExpression f, IExpression a)>();
foreach (var (resultType, (f, a)) in FunctionApplications)
{
@ -209,6 +211,7 @@ namespace AspectedRouting.Language.Expression
}
{
// id a => a
var arg = new List<IExpression>();
if (
Deconstruct.UnApplyAny(
@ -220,6 +223,37 @@ namespace AspectedRouting.Language.Expression
}
}
{
// ifdotted fcondition fthen felse arg => if (fcondition arg) (fthen arg) (felse arg)
var fcondition = new List<IExpression>();
var fthen = new List<IExpression>();
var felse = new List<IExpression>();
var arg = new List<IExpression>();
if (
Deconstruct.UnApplyAny(
Deconstruct.UnApply(
Deconstruct.UnApply(
Deconstruct.UnApply(
Deconstruct.IsFunc(Funcs.IfDotted),
Deconstruct.Assign(fcondition)),
Deconstruct.Assign(fthen)),
Deconstruct.Assign(felse)),
Deconstruct.Assign(arg)
).Invoke(this))
{
var a = arg.First();
return
Funcs.If.Apply(
fcondition.First().Apply(a),
fthen.First().Apply(a),
felse.First().Apply(a)
);
}
}
{
var (f, a) = FunctionApplications.Values.First();

View file

@ -158,6 +158,11 @@ namespace AspectedRouting.Language.Expression
priority += aspectInfluence * aspectWeight;
}
if (priority <= 0)
{
canAccess = "no";
}
return new ProfileResult((string) canAccess, (string) oneway, speed, priority,
string.Join("\n ", weightExplanation));
}

View file

@ -45,18 +45,27 @@ namespace AspectedRouting.Language.Functions
public override object Evaluate(Context c, params IExpression[] arguments)
{
var minimum = arguments[0].Evaluate(c);
var f = (IExpression) arguments[1].Evaluate(c);
var then = arguments[2].Evaluate(c);
var @else = arguments[3].Evaluate(c);
var x = arguments[4];
var arg1 = f.Evaluate(c, x);
var arg1 = arguments[1].Evaluate(c, x);
if (minimum == null || arg1 == null)
{
return null;
}
if (minimum is IExpression e)
{
minimum = e.Evaluate(c);
}
if (arg1 is IExpression e1)
{
arg1 = e1.Evaluate(c);
}
if (minimum is int i0)
{
minimum = (double) i0;

View file

@ -18,6 +18,10 @@ namespace AspectedRouting.Language.Functions
if (o is IEnumerable<IExpression> enumerable)
{
o = enumerable.ToList();
if (enumerable.Any(x => x == null))
{
throw new NullReferenceException("Some subexpression is null");
}
}
_o = o;
@ -29,6 +33,10 @@ namespace AspectedRouting.Language.Functions
if (o is IEnumerable<IExpression> enumerable)
{
o = enumerable.ToList();
if (enumerable.Any(x => x == null))
{
throw new NullReferenceException("Some subexpression is null");
}
}
_o = o;
@ -39,6 +47,10 @@ 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())));
}
Types = tps.ToList();
_o = specializedVersions;
}
@ -49,6 +61,10 @@ namespace AspectedRouting.Language.Functions
{
Types = exprs
.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)

View file

@ -8,9 +8,10 @@ namespace AspectedRouting.Language.Functions
{
public class Dot : Function
{
public override string Description { get; } = "Higher order function: converts `f (g a)` into `(dot f g) a`. In other words, this fuses `f` and `g` in a new function, which allows the argument to be lifted out of the expression ";
public override string Description { get; } =
"Higher order function: converts `f (g a)` into `(dot f g) a`. In other words, this fuses `f` and `g` in a new function, which allows the argument to be lifted out of the expression ";
public override List<string> ArgNames { get; } = new List<string>{"f","g","a"};
public override List<string> ArgNames { get; } = new List<string> {"f", "g", "a"};
public static readonly Var A = new Var("a");
public static readonly Var B = new Var("b");
public static readonly Var C = new Var("c");
@ -33,6 +34,10 @@ namespace AspectedRouting.Language.Functions
public override object Evaluate(Context c, params IExpression[] arguments)
{
if (arguments.Count() <= 2)
{
}
var f0 = arguments[0];
var f1 = arguments[1];
var resultType = ((Curry) f1.Types.First()).ResultType;

View file

@ -39,7 +39,6 @@ namespace AspectedRouting.Language
/// <param name="argument"></param>
/// <returns></returns>
// IExpression OptimizeWithArgument(IExpression argument);
void Visit(Func<IExpression, bool> f);
}
@ -47,7 +46,7 @@ namespace AspectedRouting.Language
{
public static object Run(this IExpression e, Context c, Dictionary<string, string> tags)
{
return e.Apply(new []{new Constant(tags)}).Evaluate(c);
return e.Apply(new[] {new Constant(tags)}).Evaluate(c);
}
public static IExpression Specialize(this IExpression e, Type t)
@ -101,8 +100,11 @@ namespace AspectedRouting.Language
out IEnumerable<Type> specializedTypes, out IEnumerable<IExpression> specializedExpressions)
{
specializedTypes = null;
var expressions = exprs.ToList();
foreach (var f in expressions)
var exprsForTypes = exprs.SortWidestToSmallest();
exprsForTypes.Reverse();
foreach (var f in exprsForTypes)
{
if (specializedTypes == null)
{
@ -110,9 +112,7 @@ namespace AspectedRouting.Language
continue;
}
// TODO FIXME
// EITHER THE TYPES HAVE TO BE FROM SPECIFIC TO NON-SPECIFIC ORDER OR VICE VERSA
var specialized = f.Types.RenameVars(specializedTypes).SpecializeTo(specializedTypes);
var specialized = f.Types.RenameVars(specializedTypes).SpecializeTo(specializedTypes, false);
// ReSharper disable once JoinNullCheckWithUsage
if (specialized == null)
{
@ -126,7 +126,15 @@ namespace AspectedRouting.Language
}
var tps = specializedTypes;
return specializedExpressions = expressions.Select(expr => expr.Specialize(tps));
var optExprs = new List<IExpression>();
foreach (var expr in exprs)
{
var exprOpt = expr.Specialize(tps);
optExprs.Add(exprOpt);
}
return specializedExpressions = optExprs;
}
}
}

View file

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
@ -23,7 +24,7 @@ namespace AspectedRouting.Language.Typ
}
public static HashSet<Type> SpecializeTo(this IEnumerable<Type> types0, IEnumerable<Type> allowedTypes)
public static HashSet<Type> SpecializeTo(this IEnumerable<Type> types0, IEnumerable<Type> allowedTypes, bool reverseSuperSet = true)
{
var results = new HashSet<Type>();
@ -32,7 +33,7 @@ namespace AspectedRouting.Language.Typ
{
foreach (var allowed in allowedTypes)
{
var unified = t0.Unify(allowed, true);
var unified = t0.Unify(allowed, reverseSuperSet);
if (unified != null)
{
results.Add(unified);
@ -70,6 +71,7 @@ namespace AspectedRouting.Language.Typ
{
return SelectSmallestUnion(t1, subbed);
}
return SelectSmallestUnion(subbed, t1);
}
@ -99,6 +101,81 @@ 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
@ -136,7 +213,6 @@ namespace AspectedRouting.Language.Typ
}
public static Type Substitute(this Type t0, Dictionary<string, Type> substitutions)
{
switch (t0)
@ -161,7 +237,8 @@ namespace AspectedRouting.Language.Typ
/// <param name="t0"></param>
/// <param name="t1"></param>
/// <returns></returns>
public static Dictionary<string, Type> UnificationTable(this Type t0, Type t1, bool reverseSupersetRelation = false)
public static Dictionary<string, Type> UnificationTable(this Type t0, Type t1,
bool reverseSupersetRelation = false)
{
var substitutionsOn0 = new Dictionary<string, Type>();
@ -281,6 +358,11 @@ namespace AspectedRouting.Language.Typ
public static bool IsSuperSet(this Type t0, Type t1)
{
if (t0 is Var || t1 is Var)
{
return true;
}
switch (t0)
{
case StringType _ when t1 is BoolType _:

View file

@ -63,11 +63,6 @@ namespace AspectedRouting
var result = new List<(ProfileMetaData profile, List<BehaviourTestSuite> profileTests)>();
foreach (var jsonFile in jsonFiles)
{
if (!jsonFile.Contains("bicycle"))
{
continue;
}
try
{
var profile =
@ -227,6 +222,7 @@ namespace AspectedRouting
// With everything parsed and typechecked, time for tests
var testsOk = true;
foreach (var (aspect, t) in aspects)
{
if (t == null)
@ -235,18 +231,27 @@ namespace AspectedRouting
}
else
{
t.Run();
testsOk &= t.Run();
}
}
foreach (var (profile, profileTests) in profiles)
{
foreach (var test in profileTests)
{
test.Run(context);
testsOk &= test.Run(context);
}
}
if (!testsOk)
{
Console.WriteLine("Some tests failed, quitting now without generating output");
return;
}
foreach (var (profile, profileTests) in profiles)
{
PrintUsedTags(profile, context);
var aspectTests = aspects.Select(a => a.tests).ToList();

View file

@ -61,7 +61,7 @@ namespace AspectedRouting.Tests
public void Run()
public bool Run()
{
var failed = false;
var testCase = 0;
@ -88,12 +88,9 @@ namespace AspectedRouting.Tests
}
}
if (failed)
{
throw new ArgumentException("Some test failed");
}
Console.WriteLine($"[{FunctionToApply.Name}] {testCase} tests successful");
Console.WriteLine($"[{FunctionToApply.Name}] {testCase} tests " + (failed ? "failed" : "successful"));
return !failed;
}
}
}

View file

@ -78,7 +78,6 @@ namespace AspectedRouting.Tests
}
var expected = new ProfileResult(
testData[0],
testData[1],
@ -88,7 +87,8 @@ namespace AspectedRouting.Tests
if (expected.Priority == 0 && expected.Access != "no")
{
throw new ArgumentException("A priority of zero is interpreted as 'no access' - don't use it");
throw new ArgumentException(
"A priority of zero is interpreted as 'no access' - don't use it");
}
var vals = testData.GetRange(4, testData.Count - 4);
@ -192,11 +192,8 @@ namespace AspectedRouting.Tests
return success;
}
public void Run(Context c)
public bool Run(Context c)
{
var allOk = true;
var i = 1;
foreach (var (expected, tags) in Tests)
@ -213,14 +210,9 @@ namespace AspectedRouting.Tests
i++;
}
if (!allOk)
{
throw new ArgumentException("Some tests failed for " + BehaviourName);
}
else
{
Console.WriteLine($"[{Profile.Name}] {Tests.Count()} tests successful for behaviour {BehaviourName}");
}
Console.WriteLine($"[{Profile.Name}] {Tests.Count()} tests " + (allOk ? "successfull" : "executed, some failed") +
$" for behaviour {BehaviourName}");
return allOk;
}
}
}

View file

@ -39,7 +39,17 @@
"#timeNeeded": "$speed",
"#distance": "$distance",
"#trespassingPenalty": "$clean_permission_score",
"#leastSafetyPenalty": 0
"#leastSafetyPenalty": {
"$multiply": [
"$speed",
{
"$atleast": "#leastSafetyRequired",
"f": "$bicycle.safety",
"then": 0,
"else": -1
}
]
}
},
"defaults": {
"#defaultSpeed": 15,
@ -56,16 +66,18 @@
"#trespassingPenalty": 15,
"#": "This is not a priority weight, but rather a kind of access restriction. If 'leastafety' is not met, a huge penalty is applied, namely #leastSafetyPenalty. Note: 0.1 is the safety level of a primary without cycle highway",
"#leastSafetyRequired": 0.11,
"#leastSafetyPenalty": 2
"#leastSafetyPenalty": 0
},
"behaviours": {
"fastest": {
"description": "The fastest route to your destination",
"#timeNeeded": 1
"#timeNeeded": 1,
"#leastSafetyPenalty": 2
},
"shortest": {
"description": "The shortest route, independent of of speed",
"#distance": 1
"#distance": 1,
"#leastSafetyPenalty": 2
},
"safety": {
"description": "A defensive route shying away from big roads with lots of cars",
@ -73,7 +85,8 @@
},
"comfort": {
"description": "A comfortable route preferring well-paved roads, smaller roads and a bit of scenery at the cost of speed",
"#comfort": 1
"#comfort": 1,
"#leastSafetyPenalty": 2
},
"comfort_safety": {
"description": "A route which aims to be both safe and comfortable at the cost of speed",
@ -84,7 +97,8 @@
"description": "A profile for a bike with an electrical motor where the engine doesn't go faster then 25km/h (thus NOT the speed-pedelec). This is a variation of the normal fastest, but with a faster default speed. As the maxspeed is 30, it'll resemble shortest a bit more on 'normal' streets and will penalize slower roads more",
"#defaultSpeed": 23,
"#maxspeed": 30,
"#timeNeeded": 1
"#timeNeeded": 1,
"#leastSafetyPenalty": 2
},
"networks": {
"description": "A recreative route following any existing cycling network. Might make a few detours",
@ -111,7 +125,8 @@
"cycle_highway": {
"description": "A route preferring the 'cycle-highways'. On non-cycleways, fastest is used (with a very low factor) in order to make sure the behaviour there is defined ",
"#cycleHighwayNetworkScore": 20,
"#timeNeeded": 0.1
"#timeNeeded": 0.1,
"#leastSafetyPenalty": 2
},
"node_network": {
"description": "A route following the recreational node network. Might make detours",

View file

@ -27,9 +27,14 @@
"speed":{
"$min":
[
{"$multiply":["#defaultSpeed", "$bicycle.speed_factor"]},
"#maxspeed",
"$legal_maxspeed_be",
"#maxspeed"
{
"$multiply": [
"#defaultSpeed",
"$bicycle.speed_factor"
]
}
]
},
"access":"$speedPedelec.access_be",

View file

@ -1,8 +1,8 @@
access,oneway,speed,priority,highway,bicycle,surface,cycleway:left,oneway,oneway:bicycle,access,maxspeed,junction
no,both,0,0,,,,,,,,,
designated,both,15,15,cycleway,,,,,,,,
yes,both,15,15,primary,,,,,,,,
yes,both,15,15,primary,yes,,,,,,,
no,both,15,-15,primary,,,,,,,,
no,both,15,-15,primary,yes,,,,,,,
yes,both,15,15,residential,,,,,,,,
yes,both,13.5,13.5,residential,yes,sett,,,,,,
dismount,both,2.25,2.25,pedestrian,,,,,,,,

1 access oneway speed priority highway bicycle surface cycleway:left oneway oneway:bicycle access maxspeed junction
2 no both 0 0
3 designated both 15 15 cycleway
4 yes no both 15 15 -15 primary
5 yes no both 15 15 -15 primary yes
6 yes both 15 15 residential
7 yes both 13.5 13.5 residential yes sett
8 dismount both 2.25 2.25 pedestrian

View file

@ -2,4 +2,4 @@ access,oneway,speed,priority,highway,surface
no,both,0,0,,
designated,both,15,1,cycleway,
yes,both,5.25,1,path,ground
yes,both,15,1,primary,
no,both,15,-29,primary,

1 access oneway speed priority highway surface
2 no both 0 0
3 designated both 15 1 cycleway
4 yes both 5.25 1 path ground
5 yes no both 15 1 -29 primary