commit
192b2b3442
16 changed files with 478 additions and 408 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,3 +4,4 @@
|
|||
.~lock.*
|
||||
output/*
|
||||
AspectedRouting.sln.DotSettings
|
||||
AspectedRouting.sln.DotSettings.user
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0"/>
|
||||
<PackageReference Include="xunit" Version="2.4.0"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0"/>
|
||||
<PackageReference Include="coverlet.collector" Version="1.0.1"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="1.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AspectedRouting\AspectedRouting.csproj"/>
|
||||
<ProjectReference Include="..\AspectedRouting\AspectedRouting.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.IO.jsonParser;
|
||||
|
@ -7,372 +8,440 @@ using AspectedRouting.Language.Functions;
|
|||
using AspectedRouting.Language.Typ;
|
||||
using Xunit;
|
||||
|
||||
namespace AspectedRouting.Test
|
||||
namespace AspectedRouting.Test;
|
||||
|
||||
public class FunctionsTest
|
||||
{
|
||||
public class FunctionsTest
|
||||
private readonly string constString = "{\"$const\": \"a\"}";
|
||||
|
||||
private readonly string IfDottedConditionJson
|
||||
= "{" +
|
||||
"\"$ifdotted\": {\"$eq\": \"yes\"}," +
|
||||
"\"then\":{\"$const\": \"a\"}," +
|
||||
"\"else\": {\"$const\": \"b\"}" +
|
||||
"}";
|
||||
|
||||
private readonly string IfSimpleConditionJson
|
||||
= "{" +
|
||||
"\"$if\": true," +
|
||||
"\"then\":\"thenResult\"," +
|
||||
"\"else\": \"elseResult\"}";
|
||||
|
||||
private IExpression MustMatchJson()
|
||||
{
|
||||
private readonly string constString = "{\"$const\": \"a\"}";
|
||||
var json = "{" +
|
||||
"\"name\":\"test\"," +
|
||||
"\"description\":\"test\"," +
|
||||
"\"$mustMatch\":{\"a\":\"b\",\"x\":\"y\"}}";
|
||||
return JsonParser.AspectFromJson(new Context(), json, "test.json");
|
||||
}
|
||||
|
||||
private readonly string IfDottedConditionJson
|
||||
= "{" +
|
||||
"\"$ifdotted\": {\"$eq\": \"yes\"}," +
|
||||
"\"then\":{\"$const\": \"a\"}," +
|
||||
"\"else\": {\"$const\": \"b\"}" +
|
||||
"}";
|
||||
private IExpression MustMatchJsonWithOr()
|
||||
{
|
||||
var json = "{" +
|
||||
"\"name\":\"test\"," +
|
||||
"\"description\":\"test\"," +
|
||||
"\"$mustMatch\":{\"a\":\"b\",\"x\":\"y\"}}";
|
||||
return JsonParser.AspectFromJson(new Context(), json, "test.json");
|
||||
}
|
||||
|
||||
private readonly string IfSimpleConditionJson
|
||||
= "{" +
|
||||
"\"$if\": true," +
|
||||
"\"then\":\"thenResult\"," +
|
||||
"\"else\": \"elseResult\"}";
|
||||
|
||||
private IExpression MustMatchJson()
|
||||
[Fact]
|
||||
public void TestAll_AllTags_Yes()
|
||||
{
|
||||
var tagsAx = new Dictionary<string, string>
|
||||
{
|
||||
var json = "{" +
|
||||
"\"name\":\"test\"," +
|
||||
"\"description\":\"test\"," +
|
||||
"\"$mustMatch\":{\"a\":\"b\",\"x\":\"y\"}}";
|
||||
return JsonParser.AspectFromJson(new Context(), json, "test.json");
|
||||
}
|
||||
{ "a", "b" },
|
||||
{ "x", "y" }
|
||||
};
|
||||
|
||||
private IExpression MustMatchJsonWithOr()
|
||||
var expr = new Apply(MustMatchJson(), new Constant(tagsAx)).Optimize();
|
||||
var result = expr.Evaluate(new Context());
|
||||
Assert.Equal("yes", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestAll_NoMatch_No()
|
||||
{
|
||||
var tagsAx = new Dictionary<string, string>
|
||||
{
|
||||
var json = "{" +
|
||||
"\"name\":\"test\"," +
|
||||
"\"description\":\"test\"," +
|
||||
"\"$mustMatch\":{\"a\":\"b\",\"x\":\"y\"}}";
|
||||
return JsonParser.AspectFromJson(new Context(), json, "test.json");
|
||||
}
|
||||
{ "a", "b" }
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void TestAll_AllTags_Yes()
|
||||
var expr = new Apply(MustMatchJson(), new Constant(tagsAx)).Optimize();
|
||||
var result = expr.Evaluate(new Context());
|
||||
Assert.Equal("no", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestAll_NoMatchDifferent_No()
|
||||
{
|
||||
var tagsAx = new Dictionary<string, string>
|
||||
{
|
||||
var tagsAx = new Dictionary<string, string> {
|
||||
{ "a", "b" },
|
||||
{ "x", "y" }
|
||||
};
|
||||
{ "a", "b" },
|
||||
{ "x", "someRandomValue" }
|
||||
};
|
||||
|
||||
var expr = new Apply(MustMatchJson(), new Constant(tagsAx)).Optimize();
|
||||
var result = expr.Evaluate(new Context());
|
||||
Assert.Equal("yes", result);
|
||||
}
|
||||
var expr = new Apply(MustMatchJson(), new Constant(tagsAx)).Optimize();
|
||||
var result = expr.Evaluate(new Context());
|
||||
Assert.Equal("no", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestAll_NoMatch_No()
|
||||
[Fact]
|
||||
public void TestParsing_SimpleIf_CorrectExpression()
|
||||
{
|
||||
var c = new Context();
|
||||
var ifExpr = JsonParser.ParseExpression(c, IfSimpleConditionJson);
|
||||
|
||||
Assert.Single(ifExpr.Types);
|
||||
Assert.Equal(ifExpr.Types.First(), Typs.String);
|
||||
|
||||
var resultT = ifExpr.Evaluate(c);
|
||||
Assert.Equal("thenResult", resultT);
|
||||
resultT = ifExpr.Optimize().Evaluate(c);
|
||||
Assert.Equal("thenResult", resultT);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestEvaluate_DottedIf_CorrectExpression()
|
||||
{
|
||||
var ifExpr = Funcs.IfDotted.Apply(
|
||||
Funcs.Eq.Apply(new Constant("abc")),
|
||||
Funcs.Const.Apply(new Constant("a")),
|
||||
Funcs.Const.Apply(new Constant("b"))
|
||||
);
|
||||
|
||||
var c = new Context();
|
||||
var ifResultMatch = ifExpr.Evaluate(c, new Constant("abc"));
|
||||
Assert.Equal("a", ifResultMatch);
|
||||
|
||||
var ifResultNoMatch = ifExpr.Evaluate(c, new Constant("def"));
|
||||
Assert.Equal("b", ifResultNoMatch);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestParsing_DottedIf_CorrectExpression()
|
||||
{
|
||||
var c = new Context();
|
||||
var ifExpr = JsonParser.ParseExpression(c, IfDottedConditionJson);
|
||||
ifExpr = ifExpr.Optimize();
|
||||
var resultT = ifExpr.Evaluate(c,
|
||||
new Constant(Typs.String, "yes"));
|
||||
var resultF = ifExpr.Evaluate(c,
|
||||
new Constant(Typs.String, "no"));
|
||||
Assert.Equal("a", resultT);
|
||||
Assert.Equal("b", resultF);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_ConstString_TypeIsFree()
|
||||
{
|
||||
var e = JsonParser.ParseExpression(new Context(), constString);
|
||||
Assert.Single(e.Types);
|
||||
Assert.Equal(new Curry(new Var("d"), Typs.String), e.Types.First());
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void TypeInference_EitherIdConstConst_CorrectType()
|
||||
{
|
||||
/*
|
||||
* id : a -> a
|
||||
* dot: (b -> c) -> (a -> b) -> a -> c
|
||||
* const - throw away b: a -> b -> a
|
||||
* eitherFunc: (a -> b) -> (c -> d) -> (a -> b)
|
||||
* eitherFunc: (a -> b) -> (c -> d) -> (c -> d)
|
||||
|
||||
*
|
||||
* All with free vars:
|
||||
* id: a -> a
|
||||
* dot: (b -> c) -> (x -> b) -> x -> c
|
||||
* const: y -> z -> y
|
||||
* eitherfunc: (d -> e) -> (f -> g) -> (d -> e)
|
||||
* (d -> e) -> (f -> g) -> (f -> g)
|
||||
*/
|
||||
|
||||
/*
|
||||
* (((eitherfunc id) dot) const)
|
||||
*
|
||||
* (eitherfunc id)
|
||||
* [(d -> e) -> (f -> g) -> (d -> e)] (a -> a)
|
||||
* [(d -> e) -> (f -> g) -> (f -> g)] (a -> a)
|
||||
*
|
||||
* Gives:
|
||||
* d ~ a
|
||||
* e ~ a
|
||||
* thus:
|
||||
* (f -> g) -> (a -> a)
|
||||
* (f -> g) -> (f -> g)
|
||||
*
|
||||
* ((eitherfunc id) dot)
|
||||
* [(f -> g) -> (a -> a)] ((b -> c) -> (x -> b) -> x -> c)
|
||||
* [(f -> g) -> (f -> g)] (b -> c) -> (x -> b) -> (x -> c)
|
||||
*
|
||||
* Thus: (f -> g) ~ (b -> c) -> ((x -> b) -> x -> c)
|
||||
* thus: f ~ (b -> c)
|
||||
* g ~ ((x -> b) -> (x -> c))
|
||||
* thus:
|
||||
* (a -> a)
|
||||
* (b -> c) -> ((x -> b) -> (x -> c))
|
||||
*
|
||||
*
|
||||
*
|
||||
* (((eitherfunc id) dot) const):
|
||||
* [(a -> a)] (y -> (z -> y))
|
||||
* [(b -> c) -> ((x -> b) -> (x -> c))] (y -> (z -> y))
|
||||
*
|
||||
* Thus: case 1:
|
||||
* a ~ (y -> (z -> y)
|
||||
* Type is: (y -> z -> y) === typeof(const)
|
||||
* case2:
|
||||
* (b -> c) ~ (y -> (z -> y))
|
||||
* thus: b ~ y
|
||||
* c ~ (z -> y)
|
||||
* ((x -> y) -> (x -> (z -> y))))
|
||||
* = ((x -> y) -> x -> z -> y === mix of dot and const
|
||||
*
|
||||
*/
|
||||
|
||||
var a = new Var("a");
|
||||
var c = new Var("c");
|
||||
var d = new Var("d");
|
||||
|
||||
|
||||
var e = Funcs.Either(Funcs.Id, Funcs.Dot, Funcs.Const);
|
||||
var types = e.Types.ToList();
|
||||
Assert.Equal(Curry.ConstructFrom(c, c, d), types[0]);
|
||||
Assert.Equal(Curry.ConstructFrom(
|
||||
c, // RESULT TYPE
|
||||
new Curry(a, c),
|
||||
a, d
|
||||
), types[1]);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void RenameVars_Constant_ConstantType()
|
||||
{
|
||||
// Funcs.Const.RenameVars(noUse: ["a","b","d","e","f"] should give something like 'c -> g -> c'
|
||||
var a = new Var("a");
|
||||
var b = new Var("b");
|
||||
|
||||
var c = new Var("c");
|
||||
var d = new Var("d");
|
||||
|
||||
var e = new Var("e");
|
||||
var f = new Var("f");
|
||||
var newTypes = Funcs.Const.Types.RenameVars(new[]
|
||||
{
|
||||
var tagsAx = new Dictionary<string, string> {
|
||||
{ "a", "b" }
|
||||
};
|
||||
new Curry(e, e),
|
||||
new Curry(new Curry(b, f), new Curry(new Curry(a, b), new Curry(a, f)))
|
||||
}).ToList();
|
||||
Assert.Single(newTypes);
|
||||
Assert.Equal(new Curry(c, new Curry(d, c)),
|
||||
newTypes[0]);
|
||||
}
|
||||
|
||||
var expr = new Apply(MustMatchJson(), new Constant(tagsAx)).Optimize();
|
||||
var result = expr.Evaluate(new Context());
|
||||
Assert.Equal("no", result);
|
||||
}
|
||||
[Fact]
|
||||
public void BuildSubstitution_TagsToStringTagsToBool_ShouldUnify()
|
||||
{
|
||||
var biggerType = new Curry(Typs.Tags, Typs.String);
|
||||
var smallerType = new Curry(Typs.Tags, Typs.Bool);
|
||||
// The expected type (biggerType) on the left, the argument type on the right (as it should be)
|
||||
var unificationTable = biggerType.UnificationTable(smallerType);
|
||||
Assert.NotNull(unificationTable);
|
||||
unificationTable = smallerType.UnificationTable(biggerType);
|
||||
Assert.Null(unificationTable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestAll_NoMatchDifferent_No()
|
||||
[Fact]
|
||||
public void BuildSubstitution_TagsToDoubleTagsToPDouble_ShouldUnify()
|
||||
{
|
||||
var biggerType = new Curry(Typs.Tags, Typs.Double);
|
||||
var smallerType = new Curry(Typs.Tags, Typs.PDouble);
|
||||
var unificationTable = biggerType.UnificationTable(smallerType);
|
||||
Assert.NotNull(unificationTable);
|
||||
unificationTable = smallerType.UnificationTable(biggerType);
|
||||
Assert.Null(unificationTable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSubstitution_DoubleToStringPDoubleToString_ShouldUnify()
|
||||
{
|
||||
var biggerType = new Curry(Typs.PDouble, Typs.Bool);
|
||||
var smallerType = new Curry(Typs.Double, Typs.Bool);
|
||||
// We expect something that is able to handle PDoubles, but it is able to handle the wider doubles - should be fine
|
||||
var unificationTable = biggerType.UnificationTable(smallerType);
|
||||
Assert.NotNull(unificationTable);
|
||||
unificationTable = smallerType.UnificationTable(biggerType);
|
||||
Assert.Null(unificationTable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Typechecker_EitherFunc_CorrectType()
|
||||
{
|
||||
var id = new Apply(Funcs.EitherFunc, Funcs.Id);
|
||||
Assert.Equal(2, id.Types.Count());
|
||||
|
||||
var idconst = new Apply(id, Funcs.Const);
|
||||
Assert.Equal(2, idconst.Types.Count());
|
||||
|
||||
var e =
|
||||
new Apply(idconst, new Constant("a"));
|
||||
Assert.Equal(2, e.Types.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpecializeToSmallest_Parse_SmallestType()
|
||||
{
|
||||
var smallest = Funcs.Parse.SpecializeToSmallestType();
|
||||
Assert.Single(smallest.Types);
|
||||
Assert.Equal(new Curry(Typs.String, Typs.PDouble), smallest.Types.First());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Unify_TwoSubtypes_DoesNotUnify()
|
||||
{
|
||||
var tags2double = new Curry(Typs.Tags, Typs.Double);
|
||||
var tags2pdouble = new Curry(Typs.Tags, Typs.PDouble);
|
||||
var unifA = tags2double.Unify(tags2pdouble, true);
|
||||
Assert.Null(unifA);
|
||||
var unifB = tags2pdouble.Unify(tags2double, true);
|
||||
Assert.NotNull(unifB);
|
||||
|
||||
var unifC = tags2double.Unify(tags2pdouble);
|
||||
Assert.NotNull(unifC);
|
||||
var unifD = tags2pdouble.Unify(tags2double);
|
||||
Assert.Null(unifD);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Specialize_WiderType_StillSmallerType()
|
||||
{
|
||||
var f = Funcs.Eq;
|
||||
var strstrb = new Curry(
|
||||
Typs.String,
|
||||
new Curry(Typs.String, Typs.Bool));
|
||||
var f0 = f.Specialize(strstrb);
|
||||
Assert.Equal(new[] { strstrb }, f0.Types);
|
||||
|
||||
var strstrstr = new Curry(
|
||||
Typs.String,
|
||||
new Curry(Typs.String, Typs.String));
|
||||
|
||||
var f1 = f.Specialize(strstrstr);
|
||||
|
||||
Assert.Equal(new[] { strstrb, strstrstr }, f1.Types);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpecializeToCommonType()
|
||||
{
|
||||
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 _);
|
||||
Assert.Single(newTypes);
|
||||
|
||||
exprs = new[] { p1, p0 };
|
||||
newTypes = exprs.SpecializeToCommonTypes(out var _);
|
||||
Assert.Single(newTypes);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void ParseFunction_InvalidInput_NullOutput()
|
||||
{
|
||||
var f = Funcs.Parse;
|
||||
var c = new Context();
|
||||
var result = f.Evaluate(c, new Constant("abc"));
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseFunction_Duration_TotalMinutes()
|
||||
{
|
||||
var f = Funcs.Parse;
|
||||
var c = new Context();
|
||||
var result = f.Evaluate(c, new Constant("01:15"));
|
||||
|
||||
Assert.Equal(75.0, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyDefaultFunctionWithId_ApplicationIsSuccessfull()
|
||||
{
|
||||
var e = new Apply(new Apply(Funcs.Default, new Constant("a")), Funcs.Id);
|
||||
Assert.Single(e.Types);
|
||||
|
||||
Assert.Equal("string -> string", e.Types.First().ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyFirstMatchOf_FirstMatchIsTaken_50()
|
||||
{
|
||||
var tags0 = new Constant(new Dictionary<string, string>
|
||||
{
|
||||
var tagsAx = new Dictionary<string, string> {
|
||||
{ "a", "b" },
|
||||
{ "x", "someRandomValue" }
|
||||
};
|
||||
{ "highway", "residential" },
|
||||
{ "maxspeed", "50" }
|
||||
});
|
||||
|
||||
var expr = new Apply(MustMatchJson(), new Constant(tagsAx)).Optimize();
|
||||
var result = expr.Evaluate(new Context());
|
||||
Assert.Equal("no", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestParsing_SimpleIf_CorrectExpression()
|
||||
var f = FirstMatchOfWithMaxspeedAndHighway();
|
||||
var o = f.Evaluate(new Context(), tags0);
|
||||
Assert.Equal(50.0, o);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void ApplyFirstMatchOf_FirstMatchIsTaken_ResidentialDefault()
|
||||
{
|
||||
var tags0 = new Constant(new Dictionary<string, string>
|
||||
{
|
||||
var c = new Context();
|
||||
var ifExpr = JsonParser.ParseExpression(c, IfSimpleConditionJson);
|
||||
{ "highway", "residential" }
|
||||
});
|
||||
|
||||
Assert.Single(ifExpr.Types);
|
||||
Assert.Equal(ifExpr.Types.First(), Typs.String);
|
||||
|
||||
var resultT = ifExpr.Evaluate(c);
|
||||
Assert.Equal("thenResult", resultT);
|
||||
resultT = ifExpr.Optimize().Evaluate(c);
|
||||
Assert.Equal("thenResult", resultT);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestEvaluate_DottedIf_CorrectExpression()
|
||||
var f = FirstMatchOfWithMaxspeedAndHighway();
|
||||
var o = f.Evaluate(new Context(), tags0);
|
||||
Assert.Equal(30, o);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyFirstMatchOf_NoMatchIfFound_Null()
|
||||
{
|
||||
var tags0 = new Constant(new Dictionary<string, string>
|
||||
{
|
||||
var ifExpr = Funcs.IfDotted.Apply(
|
||||
Funcs.Eq.Apply(new Constant("abc")),
|
||||
Funcs.Const.Apply(new Constant("a")),
|
||||
Funcs.Const.Apply(new Constant("b"))
|
||||
{ "highway", "unknown" }
|
||||
});
|
||||
|
||||
var f = FirstMatchOfWithMaxspeedAndHighway();
|
||||
var o = f.Evaluate(new Context(), tags0);
|
||||
Assert.Equal(null, o);
|
||||
}
|
||||
|
||||
public IExpression FirstMatchOfWithMaxspeedAndHighway()
|
||||
{
|
||||
var order = new Constant(new ListType(Typs.String), new List<IExpression>
|
||||
{
|
||||
new Constant("maxspeed"),
|
||||
new Constant("highway")
|
||||
});
|
||||
|
||||
var mapping =
|
||||
Funcs.StringStringToTags.Apply(
|
||||
new Mapping(
|
||||
new List<string> { "maxspeed", "highway" },
|
||||
new List<IExpression>
|
||||
{
|
||||
Funcs.Parse,
|
||||
new Mapping(
|
||||
new List<string> { "residential", "primary" },
|
||||
new List<IExpression> { new Constant(30), new Constant(90) }
|
||||
)
|
||||
})
|
||||
);
|
||||
|
||||
var c = new Context();
|
||||
var ifResultMatch = ifExpr.Evaluate(c, new Constant("abc"));
|
||||
Assert.Equal("a", ifResultMatch);
|
||||
|
||||
var ifResultNoMatch = ifExpr.Evaluate(c, new Constant("def"));
|
||||
Assert.Equal("b", ifResultNoMatch);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestParsing_DottedIf_CorrectExpression()
|
||||
{
|
||||
var c = new Context();
|
||||
var ifExpr = JsonParser.ParseExpression(c, IfDottedConditionJson);
|
||||
ifExpr = ifExpr.Optimize();
|
||||
var resultT = ifExpr.Evaluate(c,
|
||||
new Constant(Typs.String, "yes"));
|
||||
var resultF = ifExpr.Evaluate(c,
|
||||
new Constant(Typs.String, "no"));
|
||||
Assert.Equal("a", resultT);
|
||||
Assert.Equal("b", resultF);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_ConstString_TypeIsFree()
|
||||
{
|
||||
var e = JsonParser.ParseExpression(new Context(), constString);
|
||||
Assert.Single(e.Types);
|
||||
Assert.Equal(new Curry(new Var("d"), Typs.String), e.Types.First());
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void TypeInference_EitherIdConstConst_CorrectType()
|
||||
{
|
||||
/*
|
||||
* id : a -> a
|
||||
* dot: (b -> c) -> (a -> b) -> a -> c
|
||||
* const - throw away b: a -> b -> a
|
||||
* eitherFunc: (a -> b) -> (c -> d) -> (a -> b)
|
||||
* eitherFunc: (a -> b) -> (c -> d) -> (c -> d)
|
||||
|
||||
*
|
||||
* All with free vars:
|
||||
* id: a -> a
|
||||
* dot: (b -> c) -> (x -> b) -> x -> c
|
||||
* const: y -> z -> y
|
||||
* eitherfunc: (d -> e) -> (f -> g) -> (d -> e)
|
||||
* (d -> e) -> (f -> g) -> (f -> g)
|
||||
*/
|
||||
|
||||
/*
|
||||
* (((eitherfunc id) dot) const)
|
||||
*
|
||||
* (eitherfunc id)
|
||||
* [(d -> e) -> (f -> g) -> (d -> e)] (a -> a)
|
||||
* [(d -> e) -> (f -> g) -> (f -> g)] (a -> a)
|
||||
*
|
||||
* Gives:
|
||||
* d ~ a
|
||||
* e ~ a
|
||||
* thus:
|
||||
* (f -> g) -> (a -> a)
|
||||
* (f -> g) -> (f -> g)
|
||||
*
|
||||
* ((eitherfunc id) dot)
|
||||
* [(f -> g) -> (a -> a)] ((b -> c) -> (x -> b) -> x -> c)
|
||||
* [(f -> g) -> (f -> g)] (b -> c) -> (x -> b) -> (x -> c)
|
||||
*
|
||||
* Thus: (f -> g) ~ (b -> c) -> ((x -> b) -> x -> c)
|
||||
* thus: f ~ (b -> c)
|
||||
* g ~ ((x -> b) -> (x -> c))
|
||||
* thus:
|
||||
* (a -> a)
|
||||
* (b -> c) -> ((x -> b) -> (x -> c))
|
||||
*
|
||||
*
|
||||
*
|
||||
* (((eitherfunc id) dot) const):
|
||||
* [(a -> a)] (y -> (z -> y))
|
||||
* [(b -> c) -> ((x -> b) -> (x -> c))] (y -> (z -> y))
|
||||
*
|
||||
* Thus: case 1:
|
||||
* a ~ (y -> (z -> y)
|
||||
* Type is: (y -> z -> y) === typeof(const)
|
||||
* case2:
|
||||
* (b -> c) ~ (y -> (z -> y))
|
||||
* thus: b ~ y
|
||||
* c ~ (z -> y)
|
||||
* ((x -> y) -> (x -> (z -> y))))
|
||||
* = ((x -> y) -> x -> z -> y === mix of dot and const
|
||||
*
|
||||
*/
|
||||
|
||||
var a = new Var("a");
|
||||
var c = new Var("c");
|
||||
var d = new Var("d");
|
||||
|
||||
|
||||
var e = Funcs.Either(Funcs.Id, Funcs.Dot, Funcs.Const);
|
||||
var types = e.Types.ToList();
|
||||
Assert.Equal(Curry.ConstructFrom(c, c, d), types[0]);
|
||||
Assert.Equal(Curry.ConstructFrom(
|
||||
c, // RESULT TYPE
|
||||
new Curry(a, c),
|
||||
a, d
|
||||
), types[1]);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void RenameVars_Constant_ConstantType()
|
||||
{
|
||||
// Funcs.Const.RenameVars(noUse: ["a","b","d","e","f"] should give something like 'c -> g -> c'
|
||||
var a = new Var("a");
|
||||
var b = new Var("b");
|
||||
|
||||
var c = new Var("c");
|
||||
var d = new Var("d");
|
||||
|
||||
var e = new Var("e");
|
||||
var f = new Var("f");
|
||||
var newTypes = Funcs.Const.Types.RenameVars(new[] {
|
||||
new Curry(e, e),
|
||||
new Curry(new Curry(b, f), new Curry(new Curry(a, b), new Curry(a, f)))
|
||||
}).ToList();
|
||||
Assert.Single(newTypes);
|
||||
Assert.Equal(new Curry(c, new Curry(d, c)),
|
||||
newTypes[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSubstitution_TagsToStringTagsToBool_ShouldUnify()
|
||||
{
|
||||
var biggerType = new Curry(Typs.Tags, Typs.String);
|
||||
var smallerType = new Curry(Typs.Tags, Typs.Bool);
|
||||
// The expected type (biggerType) on the left, the argument type on the right (as it should be)
|
||||
var unificationTable = biggerType.UnificationTable(smallerType);
|
||||
Assert.NotNull(unificationTable);
|
||||
unificationTable = smallerType.UnificationTable(biggerType);
|
||||
Assert.Null(unificationTable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSubstitution_TagsToDoubleTagsToPDouble_ShouldUnify()
|
||||
{
|
||||
var biggerType = new Curry(Typs.Tags, Typs.Double);
|
||||
var smallerType = new Curry(Typs.Tags, Typs.PDouble);
|
||||
var unificationTable = biggerType.UnificationTable(smallerType);
|
||||
Assert.NotNull(unificationTable);
|
||||
unificationTable = smallerType.UnificationTable(biggerType);
|
||||
Assert.Null(unificationTable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSubstitution_DoubleToStringPDoubleToString_ShouldUnify()
|
||||
{
|
||||
var biggerType = new Curry(Typs.PDouble, Typs.Bool);
|
||||
var smallerType = new Curry(Typs.Double, Typs.Bool);
|
||||
// We expect something that is able to handle PDoubles, but it is able to handle the wider doubles - should be fine
|
||||
var unificationTable = biggerType.UnificationTable(smallerType);
|
||||
Assert.NotNull(unificationTable);
|
||||
unificationTable = smallerType.UnificationTable(biggerType);
|
||||
Assert.Null(unificationTable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Typechecker_EitherFunc_CorrectType()
|
||||
{
|
||||
var id = new Apply(Funcs.EitherFunc, Funcs.Id);
|
||||
Assert.Equal(2, id.Types.Count());
|
||||
|
||||
var idconst = new Apply(id, Funcs.Const);
|
||||
Assert.Equal(2, idconst.Types.Count());
|
||||
|
||||
var e =
|
||||
new Apply(idconst, new Constant("a"));
|
||||
Assert.Equal(2, e.Types.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpecializeToSmallest_Parse_SmallestType()
|
||||
{
|
||||
var smallest = Funcs.Parse.SpecializeToSmallestType();
|
||||
Assert.Single(smallest.Types);
|
||||
Assert.Equal(new Curry(Typs.String, Typs.PDouble), smallest.Types.First());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Unify_TwoSubtypes_DoesNotUnify()
|
||||
{
|
||||
var tags2double = new Curry(Typs.Tags, Typs.Double);
|
||||
var tags2pdouble = new Curry(Typs.Tags, Typs.PDouble);
|
||||
var unifA = tags2double.Unify(tags2pdouble, true);
|
||||
Assert.Null(unifA);
|
||||
var unifB = tags2pdouble.Unify(tags2double, true);
|
||||
Assert.NotNull(unifB);
|
||||
|
||||
var unifC = tags2double.Unify(tags2pdouble);
|
||||
Assert.NotNull(unifC);
|
||||
var unifD = tags2pdouble.Unify(tags2double);
|
||||
Assert.Null(unifD);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Specialize_WiderType_StillSmallerType()
|
||||
{
|
||||
var f = Funcs.Eq;
|
||||
var strstrb = new Curry(
|
||||
Typs.String,
|
||||
new Curry(Typs.String, Typs.Bool));
|
||||
var f0 = f.Specialize(strstrb);
|
||||
Assert.Equal(new[] { strstrb }, f0.Types);
|
||||
|
||||
var strstrstr = new Curry(
|
||||
Typs.String,
|
||||
new Curry(Typs.String, Typs.String));
|
||||
|
||||
var f1 = f.Specialize(strstrstr);
|
||||
|
||||
Assert.Equal(new[] { strstrb, strstrstr }, f1.Types);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpecializeToCommonType()
|
||||
{
|
||||
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 _);
|
||||
Assert.Single(newTypes);
|
||||
|
||||
exprs = new[] { p1, p0 };
|
||||
newTypes = exprs.SpecializeToCommonTypes(out var _);
|
||||
Assert.Single(newTypes);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void ParseFunction_InvalidInput_NullOutput()
|
||||
{
|
||||
var f = Funcs.Parse;
|
||||
var c = new Context();
|
||||
var result = f.Evaluate(c, new Constant("abc"));
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseFunction_Duration_TotalMinutes()
|
||||
{
|
||||
var f = Funcs.Parse;
|
||||
var c = new Context();
|
||||
var result = f.Evaluate(c, new Constant("01:15"));
|
||||
|
||||
Assert.Equal(75.0, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyDefaultFunctionWithId_ApplicationIsSuccessfull()
|
||||
{
|
||||
var e = new Apply(new Apply(Funcs.Default, new Constant("a")), Funcs.Id);
|
||||
Assert.Single(e.Types);
|
||||
|
||||
Assert.Equal("string -> string", e.Types.First().ToString());
|
||||
}
|
||||
return Funcs.FirstOf.Apply(order, mapping);
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<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/CodeInspection/PencilsConfiguration/ActualSeverity/@EntryValue">ERROR</s:String>
|
||||
<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">
|
||||
<TestAncestor>
|
||||
<TestId>xUnit::A1309041-8AAE-42D7-A886-94C9FFC6A28C::.NETCoreApp,Version=v3.1::AspectedRouting.Test.TypingTests.JoinApply_Id</TestId>
|
||||
<TestId>xUnit::A1309041-8AAE-42D7-A886-94C9FFC6A28C::.NETCoreApp,Version=v3.1::AspectedRouting.Test.FunctionsTest.ApplyDefaultFunctionWithId_ApplicationIsSuccessfull</TestId>
|
||||
</TestAncestor>
|
||||
</SessionState></s:String>
|
||||
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=a6a74f48_002D8456_002D43c7_002Dbbee_002Dd3da33a8a4be/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" 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" 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>
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<LangVersion>8</LangVersion>
|
||||
<AssemblyName>AspectedRouting</AssemblyName>
|
||||
<RootNamespace>AspectedRouting</RootNamespace>
|
||||
|
|
3
AspectedRouting/IO/itinero2/Relations.md
Normal file
3
AspectedRouting/IO/itinero2/Relations.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# What about relations in Itinero 2.0?
|
||||
|
||||
Relations have moved to the preprocessor, where they do put a tag on the members of the relation. This is done with a TagsFilter
|
|
@ -163,11 +163,9 @@ namespace AspectedRouting.IO.jsonParser
|
|||
);
|
||||
}
|
||||
|
||||
private static readonly IExpression _mconst =
|
||||
new Apply(new Apply(Funcs.EitherFunc, Funcs.Id), Funcs.Const);
|
||||
private static readonly IExpression _mconst = Funcs.EitherFunc.Apply( Funcs.Id, Funcs.Const);
|
||||
|
||||
private static readonly IExpression _mappingWrapper =
|
||||
new Apply(new Apply(Funcs.EitherFunc, Funcs.Id), Funcs.StringStringToTags);
|
||||
private static readonly IExpression _mappingWrapper = Funcs.EitherFunc.Apply( Funcs.Id, Funcs.StringStringToTags);
|
||||
|
||||
private static IExpression ParseMapping(IEnumerable<JsonProperty> allArgs, Context context)
|
||||
{
|
||||
|
@ -201,7 +199,7 @@ namespace AspectedRouting.IO.jsonParser
|
|||
{
|
||||
var simpleMapping = new Mapping(keys, exprs);
|
||||
|
||||
var wrapped = (IExpression) new Apply(_mappingWrapper, simpleMapping);
|
||||
var wrapped = _mappingWrapper.Apply(simpleMapping);
|
||||
if (keys.Count == 1)
|
||||
{
|
||||
// We can interpret this directly without going through a list
|
||||
|
|
|
@ -697,7 +697,7 @@ Consider the mapping `{'someKey':'someValue'}`. Under normal circumstances, this
|
|||
converting the string `someKey` into `someValue`, just like an ordinary dictionary would do. However, in the context
|
||||
of `mustMatch`, we would prefer this to act as a _check_, that the highway _has_ a key `someKey` which is `someValue`,
|
||||
thus acting
|
||||
as `{'someKey': {'$eq':'someValue'}}. Both behaviours are automatically supported in parsing, by parsing the string as `(
|
||||
as `{'someKey': {'$eq':'someValue'}}`. Both behaviours are automatically supported in parsing, by parsing the string as `(
|
||||
eitherFunc id eq) 'someValue'`. The type system is then able to figure out which implementation is needed.
|
||||
|
||||
Disclaimer: _you should never ever need this in your profiles_
|
||||
|
|
|
@ -245,7 +245,7 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override string ToString()
|
||||
{
|
||||
return _o.Pretty();
|
||||
return ObjectExtensions.Pretty(_o);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,6 +254,7 @@ namespace AspectedRouting.Language.Functions
|
|||
public static string Pretty(this object o, Context context = null)
|
||||
{
|
||||
switch (o) {
|
||||
case null: return "null";
|
||||
case Dictionary<string, string> d:
|
||||
var txt = "";
|
||||
foreach (var (k, v) in d) {
|
||||
|
|
|
@ -13,9 +13,9 @@ namespace AspectedRouting.Language.Functions
|
|||
"" +
|
||||
"Consider the mapping `{'someKey':'someValue'}`. Under normal circumstances, this acts as a pointwise-function, converting the string `someKey` into `someValue`, just like an ordinary dictionary would do. " +
|
||||
"However, in the context of `mustMatch`, we would prefer this to act as a _check_, that the highway _has_ a key `someKey` which is `someValue`, thus acting as " +
|
||||
"`{'someKey': {'$eq':'someValue'}}. " +
|
||||
"Both behaviours are automatically supported in parsing, by parsing the string as `(eitherFunc id eq) 'someValue'`. " +
|
||||
"The type system is then able to figure out which implementation is needed.\n\n" +
|
||||
"`{'someKey': {'$eq':'someValue'}}`. " +
|
||||
"Both behaviours are automatically supported in parsing, by parsing all string as `(eitherFunc id eq) 'someValue'`. " +
|
||||
"The type system is then able to figure out which implementation is needed an discards the unneeded implementations.\n\n" +
|
||||
"Disclaimer: _you should never ever need this in your profiles_";
|
||||
|
||||
public override List<string> ArgNames { get; } = new List<string>{"f","g","a"};
|
||||
|
|
|
@ -8,15 +8,18 @@ namespace AspectedRouting.Language.Functions
|
|||
{
|
||||
public class FirstMatchOf : Function
|
||||
{
|
||||
public override string Description { get; } = "This higherorder function takes a list of keys, a mapping (function over tags) and a collection of tags. It will try the function for the first key (and it's respective value). If the function fails (it gives null), it'll try the next key.\n\n" +
|
||||
"E.g. `$firstMatchOf ['maxspeed','highway'] {'maxspeed' --> $parse, 'highway' --> {residential --> 30, tertiary --> 50}}` applied on `{maxspeed=70, highway=tertiary}` will yield `70` as that is the first key in the list; `{highway=residential}` will yield `30`.";
|
||||
public override string Description { get; } = "This higher-order function takes a list of keys, a mapping (function over tags) and a collection of tags." +
|
||||
"It will try the function for the first key (and it's respective value). If the function fails (it gives null), it'll try the next key.\n\n" +
|
||||
"E.g. `$firstMatchOf ['maxspeed','highway'] {'maxspeed' --> $parse, 'highway' --> {residential --> 30, tertiary --> 50}}` applied on `{maxspeed=70, highway=tertiary}`" +
|
||||
" will yield `70` as that is the first key in the list; `{highway=residential}` will yield `30`.";
|
||||
public override List<string> ArgNames { get; } = new List<string> {"s"};
|
||||
|
||||
public FirstMatchOf() : base("first_match_of", true,
|
||||
new[]
|
||||
{
|
||||
// [String] -> (Tags -> [a]) -> Tags -> a
|
||||
Curry.ConstructFrom(new Var("a"), // Result type on top!
|
||||
Curry.ConstructFrom(
|
||||
new Var("a"), // Result type on top!
|
||||
new ListType(Typs.String),
|
||||
new Curry(Typs.Tags, new ListType(new Var("a"))),
|
||||
Typs.Tags
|
||||
|
|
|
@ -10,8 +10,9 @@ namespace AspectedRouting.Language.Functions
|
|||
private static Var b = new Var("b");
|
||||
|
||||
public override string Description { get; } = "Selects either one of the branches, depending on the condition." +
|
||||
" The 'then' branch is returned if the condition returns the string `yes` or `true`. Otherwise, the `else` branch is taken (including if the condition returns `null`)" +
|
||||
"If the `else` branch is not set, `null` is returned in the condition is false.";
|
||||
" The 'then' branch is returned if the condition returns the string `yes` or `true`." +
|
||||
" Otherwise, the `else` branch is taken (including if the condition returns `null`)" +
|
||||
"If the `else` branch is not set, `null` is returned if the condition evaluates to false.";
|
||||
public override List<string> ArgNames { get; } = new List<string> {"condition", "then", "else"};
|
||||
|
||||
public If() : base("if_then_else", true,
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace AspectedRouting.Language.Functions
|
|||
" a flag `_relation:<aspect_name>=\"yes\"` will be set if the aspect matches on every way for where this aspect matches.\n" +
|
||||
"However, this plays poorly with parameters (e.g.: what if we want to cycle over a highway which is part of a certain cycling network with a certain `#network_name`?) " +
|
||||
"Luckily, parameters can only be simple values. To work around this problem, an extra tag is introduced for _every single profile_:" +
|
||||
"`_relation:<profile_name>:<aspect_name>=yes'. The subfunction is thus executed `countOr(relations) * countOf(profiles)` time, yielding `countOf(profiles)` tags." +
|
||||
"`_relation:<profile_name>:<aspect_name>=yes'. The subfunction is thus executed `countOf(relations) * countOf(profiles)` time, yielding `countOf(profiles)` tags." +
|
||||
" The profile function then picks the tags for himself and strips the `<profile_name>:` away from the key.\n\n" +
|
||||
"\n\n" +
|
||||
"In the test.csv, one can simply use `_relation:<aspect_name>=yes` to mimic relations in your tests";
|
||||
|
@ -49,19 +49,23 @@ namespace AspectedRouting.Language.Functions
|
|||
// In the case of tests, relations might be added with "_relation:1:<key>"
|
||||
// So, we create this table as dictionary
|
||||
var relationTags = new Dictionary<string, Dictionary<string, string>>();
|
||||
foreach (var tag in tags) {
|
||||
if (tag.Key.StartsWith("_relation:")) {
|
||||
var keyParts = tag.Key.Split(":");
|
||||
if (keyParts.Length != 3) {
|
||||
continue;
|
||||
}
|
||||
var relationName = keyParts[1];
|
||||
if (!relationTags.ContainsKey(relationName)) {
|
||||
relationTags.Add(relationName, new Dictionary<string, string>());
|
||||
}
|
||||
|
||||
relationTags[relationName].Add(keyParts[2], tag.Value);
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
if (!tag.Key.StartsWith("_relation:"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var keyParts = tag.Key.Split(":");
|
||||
if (keyParts.Length != 3) {
|
||||
continue;
|
||||
}
|
||||
var relationName = keyParts[1];
|
||||
if (!relationTags.ContainsKey(relationName)) {
|
||||
relationTags.Add(relationName, new Dictionary<string, string>());
|
||||
}
|
||||
|
||||
relationTags[relationName].Add(keyParts[2], tag.Value);
|
||||
}
|
||||
|
||||
foreach (var relationTagging in relationTags) {
|
||||
|
|
|
@ -195,7 +195,7 @@ namespace AspectedRouting
|
|||
|
||||
private static void PrintUsedTags(ProfileMetaData profile, Context context)
|
||||
{
|
||||
Console.WriteLine("\n\n\n---------- " + profile.Name + " --------------");
|
||||
Console.WriteLine("\n\n\n---------- " + profile.Name +" : used tags and corresponding values --------------");
|
||||
foreach (var (key, values) in profile.AllExpressions(context).PossibleTags()) {
|
||||
var vs = "*";
|
||||
if (values.Any()) {
|
||||
|
@ -230,6 +230,7 @@ namespace AspectedRouting
|
|||
}
|
||||
|
||||
MdPrinter.GenerateHelpText(outputDir + "helpText.md");
|
||||
Console.WriteLine("Written helptext to "+outputDir);
|
||||
|
||||
|
||||
var files = Directory.EnumerateFiles(inputDir, "*.json", SearchOption.AllDirectories)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using AspectedRouting.Language;
|
||||
using AspectedRouting.Language.Expression;
|
||||
|
@ -106,9 +107,11 @@ namespace AspectedRouting.Tests
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!actual.ToString().Equals(test.expected) &&
|
||||
!(actual is double actualD && Math.Abs(double.Parse(test.expected) - actualD) < 0.0001)
|
||||
) {
|
||||
|
||||
var doesMatch = (actual is double d && Math.Abs(double.Parse(test.expected) - d) < 0.0001)
|
||||
|| actual.ToString().Equals(test.expected);
|
||||
|
||||
if (!doesMatch) {
|
||||
failed = true;
|
||||
Console.WriteLine(
|
||||
$"[{FunctionToApply.Name}] Line {testCase + 1} failed:\n Expected: {test.expected}\n actual: {actual}\n tags: {test.tags.Pretty()}\n");
|
||||
|
|
|
@ -183,6 +183,14 @@ namespace AspectedRouting.Tests
|
|||
success = false;
|
||||
}
|
||||
|
||||
if (actual.Priority >= 100 || actual.Priority <= -100)
|
||||
{
|
||||
Err($"priority is not within range of -100 and +100. This is needed due to a bug in Itinero2.0, see https://github.com/itinero/routing2/issues/30",
|
||||
actual.Priority + " < 100 && -100 < "+actual.Priority,
|
||||
actual.Priority);
|
||||
success = false;
|
||||
}
|
||||
|
||||
|
||||
if (!success)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue