Add documentation output
This commit is contained in:
parent
df63111009
commit
b877fff2b5
19 changed files with 643 additions and 441 deletions
|
@ -7,14 +7,14 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0"/>
|
||||||
<PackageReference Include="xunit" Version="2.4.0" />
|
<PackageReference Include="xunit" Version="2.4.0"/>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0"/>
|
||||||
<PackageReference Include="coverlet.collector" Version="1.0.1" />
|
<PackageReference Include="coverlet.collector" Version="1.0.1"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\AspectedRouting\AspectedRouting.csproj" />
|
<ProjectReference Include="..\AspectedRouting\AspectedRouting.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
|
@ -11,6 +11,21 @@ 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 IExpression MustMatchJson()
|
||||||
{
|
{
|
||||||
var json = "{" +
|
var json = "{" +
|
||||||
|
@ -32,10 +47,9 @@ namespace AspectedRouting.Test
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TestAll_AllTags_Yes()
|
public void TestAll_AllTags_Yes()
|
||||||
{
|
{
|
||||||
var tagsAx = new Dictionary<string, string>
|
var tagsAx = new Dictionary<string, string> {
|
||||||
{
|
{ "a", "b" },
|
||||||
{"a", "b"},
|
{ "x", "y" }
|
||||||
{"x", "y"}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var expr = new Apply(MustMatchJson(), new Constant(tagsAx)).Optimize();
|
var expr = new Apply(MustMatchJson(), new Constant(tagsAx)).Optimize();
|
||||||
|
@ -46,9 +60,8 @@ namespace AspectedRouting.Test
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TestAll_NoMatch_No()
|
public void TestAll_NoMatch_No()
|
||||||
{
|
{
|
||||||
var tagsAx = new Dictionary<string, string>
|
var tagsAx = new Dictionary<string, string> {
|
||||||
{
|
{ "a", "b" }
|
||||||
{"a", "b"},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var expr = new Apply(MustMatchJson(), new Constant(tagsAx)).Optimize();
|
var expr = new Apply(MustMatchJson(), new Constant(tagsAx)).Optimize();
|
||||||
|
@ -59,10 +72,9 @@ namespace AspectedRouting.Test
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TestAll_NoMatchDifferent_No()
|
public void TestAll_NoMatchDifferent_No()
|
||||||
{
|
{
|
||||||
var tagsAx = new Dictionary<string, string>
|
var tagsAx = new Dictionary<string, string> {
|
||||||
{
|
{ "a", "b" },
|
||||||
{"a", "b"},
|
{ "x", "someRandomValue" }
|
||||||
{"x", "someRandomValue"}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var expr = new Apply(MustMatchJson(), new Constant(tagsAx)).Optimize();
|
var expr = new Apply(MustMatchJson(), new Constant(tagsAx)).Optimize();
|
||||||
|
@ -70,19 +82,6 @@ namespace AspectedRouting.Test
|
||||||
Assert.Equal("no", result);
|
Assert.Equal("no", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string IfDottedConditionJson
|
|
||||||
= "{" +
|
|
||||||
"\"$ifdotted\": {\"$eq\": \"yes\"}," +
|
|
||||||
"\"then\":{\"$const\": \"a\"}," +
|
|
||||||
"\"else\": {\"$const\": \"b\"}" +
|
|
||||||
"}";
|
|
||||||
|
|
||||||
private string IfSimpleConditionJson
|
|
||||||
= "{" +
|
|
||||||
"\"$if\": true," +
|
|
||||||
"\"then\":\"thenResult\"," +
|
|
||||||
"\"else\": \"elseResult\"}";
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TestParsing_SimpleIf_CorrectExpression()
|
public void TestParsing_SimpleIf_CorrectExpression()
|
||||||
{
|
{
|
||||||
|
@ -129,9 +128,6 @@ namespace AspectedRouting.Test
|
||||||
Assert.Equal("b", resultF);
|
Assert.Equal("b", resultF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private string constString = "{\"$const\": \"a\"}";
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_ConstString_TypeIsFree()
|
public void Parse_ConstString_TypeIsFree()
|
||||||
{
|
{
|
||||||
|
@ -231,8 +227,7 @@ namespace AspectedRouting.Test
|
||||||
|
|
||||||
var e = new Var("e");
|
var e = new Var("e");
|
||||||
var f = new Var("f");
|
var f = new Var("f");
|
||||||
var newTypes = Funcs.Const.Types.RenameVars(new[]
|
var newTypes = Funcs.Const.Types.RenameVars(new[] {
|
||||||
{
|
|
||||||
new Curry(e, e),
|
new Curry(e, e),
|
||||||
new Curry(new Curry(b, f), new Curry(new Curry(a, b), new Curry(a, f)))
|
new Curry(new Curry(b, f), new Curry(new Curry(a, b), new Curry(a, f)))
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
@ -308,9 +303,9 @@ namespace AspectedRouting.Test
|
||||||
var unifB = tags2pdouble.Unify(tags2double, true);
|
var unifB = tags2pdouble.Unify(tags2double, true);
|
||||||
Assert.NotNull(unifB);
|
Assert.NotNull(unifB);
|
||||||
|
|
||||||
var unifC = tags2double.Unify(tags2pdouble, false);
|
var unifC = tags2double.Unify(tags2pdouble);
|
||||||
Assert.NotNull(unifC);
|
Assert.NotNull(unifC);
|
||||||
var unifD = tags2pdouble.Unify(tags2double, false);
|
var unifD = tags2pdouble.Unify(tags2double);
|
||||||
Assert.Null(unifD);
|
Assert.Null(unifD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,7 +318,7 @@ namespace AspectedRouting.Test
|
||||||
Typs.String,
|
Typs.String,
|
||||||
new Curry(Typs.String, Typs.Bool));
|
new Curry(Typs.String, Typs.Bool));
|
||||||
var f0 = f.Specialize(strstrb);
|
var f0 = f.Specialize(strstrb);
|
||||||
Assert.Equal(new[] {strstrb}, f0.Types);
|
Assert.Equal(new[] { strstrb }, f0.Types);
|
||||||
|
|
||||||
var strstrstr = new Curry(
|
var strstrstr = new Curry(
|
||||||
Typs.String,
|
Typs.String,
|
||||||
|
@ -331,7 +326,7 @@ namespace AspectedRouting.Test
|
||||||
|
|
||||||
var f1 = f.Specialize(strstrstr);
|
var f1 = f.Specialize(strstrstr);
|
||||||
|
|
||||||
Assert.Equal(new[] {strstrb, strstrstr}, f1.Types);
|
Assert.Equal(new[] { strstrb, strstrstr }, f1.Types);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -341,11 +336,11 @@ namespace AspectedRouting.Test
|
||||||
var p1 = Funcs.Const.Apply(new Constant(1.0)).Specialize(
|
var p1 = Funcs.Const.Apply(new Constant(1.0)).Specialize(
|
||||||
new Curry(new Var("a"), Typs.Double));
|
new Curry(new Var("a"), Typs.Double));
|
||||||
|
|
||||||
var exprs = new[] {p0, p1};
|
var exprs = new[] { p0, p1 };
|
||||||
var newTypes = exprs.SpecializeToCommonTypes(out var _);
|
var newTypes = exprs.SpecializeToCommonTypes(out var _);
|
||||||
Assert.Single(newTypes);
|
Assert.Single(newTypes);
|
||||||
|
|
||||||
exprs = new[] {p1, p0};
|
exprs = new[] { p1, p0 };
|
||||||
newTypes = exprs.SpecializeToCommonTypes(out var _);
|
newTypes = exprs.SpecializeToCommonTypes(out var _);
|
||||||
Assert.Single(newTypes);
|
Assert.Single(newTypes);
|
||||||
}
|
}
|
||||||
|
@ -360,7 +355,7 @@ namespace AspectedRouting.Test
|
||||||
|
|
||||||
Assert.Null(result);
|
Assert.Null(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ParseFunction_Duration_TotalMinutes()
|
public void ParseFunction_Duration_TotalMinutes()
|
||||||
{
|
{
|
||||||
|
@ -376,10 +371,8 @@ namespace AspectedRouting.Test
|
||||||
{
|
{
|
||||||
var e = new Apply(new Apply(Funcs.Default, new Constant("a")), Funcs.Id);
|
var e = new Apply(new Apply(Funcs.Default, new Constant("a")), Funcs.Id);
|
||||||
Assert.Single(e.Types);
|
Assert.Single(e.Types);
|
||||||
|
|
||||||
Assert.Equal("string -> string", e.Types.First().ToString());
|
Assert.Equal("string -> string", e.Types.First().ToString());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using AspectedRouting.IO.itinero1;
|
|
||||||
using AspectedRouting.IO.LuaSkeleton;
|
using AspectedRouting.IO.LuaSkeleton;
|
||||||
using AspectedRouting.Language;
|
using AspectedRouting.Language;
|
||||||
using AspectedRouting.Language.Functions;
|
using AspectedRouting.Language.Functions;
|
||||||
|
@ -13,12 +12,11 @@ namespace AspectedRouting.Test
|
||||||
public void ToLua_SimpleMapping_Table()
|
public void ToLua_SimpleMapping_Table()
|
||||||
{
|
{
|
||||||
var mapping = new Mapping(
|
var mapping = new Mapping(
|
||||||
new[] {"a", "b", "c"},
|
new[] { "a", "b", "c" },
|
||||||
new[]
|
new[] {
|
||||||
{
|
|
||||||
new Constant(5),
|
new Constant(5),
|
||||||
new Constant(6),
|
new Constant(6),
|
||||||
new Constant(7),
|
new Constant(7)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -34,13 +32,11 @@ namespace AspectedRouting.Test
|
||||||
public void ToLua_NestedMapping_Table()
|
public void ToLua_NestedMapping_Table()
|
||||||
{
|
{
|
||||||
var mapping = new Mapping(
|
var mapping = new Mapping(
|
||||||
new[] {"a"},
|
new[] { "a" },
|
||||||
new[]
|
new[] {
|
||||||
{
|
new Mapping(new[] { "b" },
|
||||||
new Mapping(new[] {"b"},
|
new[] {
|
||||||
new[]
|
new Constant(42)
|
||||||
{
|
|
||||||
new Constant(42),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -54,10 +50,8 @@ namespace AspectedRouting.Test
|
||||||
public void Sanity_EveryBasicFunction_HasDescription()
|
public void Sanity_EveryBasicFunction_HasDescription()
|
||||||
{
|
{
|
||||||
var missing = new List<string>();
|
var missing = new List<string>();
|
||||||
foreach (var (_, f) in Funcs.Builtins)
|
foreach (var (_, f) in Funcs.Builtins) {
|
||||||
{
|
if (string.IsNullOrEmpty(f.Description)) {
|
||||||
if (string.IsNullOrEmpty(f.Description))
|
|
||||||
{
|
|
||||||
missing.Add(f.Name);
|
missing.Add(f.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,15 +59,13 @@ namespace AspectedRouting.Test
|
||||||
Assert.True(0 == missing.Count,
|
Assert.True(0 == missing.Count,
|
||||||
"These functions do not have a description: " + string.Join(", ", missing));
|
"These functions do not have a description: " + string.Join(", ", missing));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Sanity_EveryBasicFunction_HasArgNames()
|
public void Sanity_EveryBasicFunction_HasArgNames()
|
||||||
{
|
{
|
||||||
var missing = new List<string>();
|
var missing = new List<string>();
|
||||||
foreach (var (_, f) in Funcs.Builtins)
|
foreach (var (_, f) in Funcs.Builtins) {
|
||||||
{
|
if (f.ArgNames == null) {
|
||||||
if (f.ArgNames == null)
|
|
||||||
{
|
|
||||||
missing.Add(f.Name);
|
missing.Add(f.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,18 +9,18 @@ namespace AspectedRouting.Test
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void SimpleMapping_SimpleHighway_GivesResult()
|
public static void SimpleMapping_SimpleHighway_GivesResult()
|
||||||
{
|
{
|
||||||
var maxspeed = new Mapping(new[] {"residential", "living_street"},
|
var maxspeed = new Mapping(new[] { "residential", "living_street" },
|
||||||
new[] {
|
new[] {
|
||||||
new Constant(30),
|
new Constant(30),
|
||||||
new Constant(20)
|
new Constant(20)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
var resMaxspeed= maxspeed.Evaluate(new Context(), new Constant("residential"));
|
var resMaxspeed = maxspeed.Evaluate(new Context(), new Constant("residential"));
|
||||||
Assert.Equal(30, resMaxspeed);
|
Assert.Equal(30, resMaxspeed);
|
||||||
var livingStreetMaxspeed= maxspeed.Evaluate(new Context(), new Constant("living_street"));
|
var livingStreetMaxspeed = maxspeed.Evaluate(new Context(), new Constant("living_street"));
|
||||||
Assert.Equal(20, livingStreetMaxspeed);
|
Assert.Equal(20, livingStreetMaxspeed);
|
||||||
var undefinedSpeed = maxspeed.Evaluate(new Context(), new Constant("some_unknown_highway_type"));
|
var undefinedSpeed = maxspeed.Evaluate(new Context(), new Constant("some_unknown_highway_type"));
|
||||||
Assert.Null(undefinedSpeed);
|
Assert.Null(undefinedSpeed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,37 +10,37 @@ namespace AspectedRouting.Test
|
||||||
[Fact]
|
[Fact]
|
||||||
public void MustMatch_SimpleInput()
|
public void MustMatch_SimpleInput()
|
||||||
{
|
{
|
||||||
var mapValue = new Mapping(new[] {"residential", "living_street"},
|
var mapValue = new Mapping(new[] { "residential", "living_street" },
|
||||||
new[] {
|
new[] {
|
||||||
new Constant("yes"),
|
new Constant("yes"),
|
||||||
new Constant("no")
|
new Constant("no")
|
||||||
});
|
});
|
||||||
var mapTag = new Mapping(new[] {"highway"}, new[] {mapValue});
|
var mapTag = new Mapping(new[] { "highway" }, new[] { mapValue });
|
||||||
var mm = Funcs.MustMatch
|
var mm = Funcs.MustMatch
|
||||||
.Apply(
|
.Apply(
|
||||||
new Constant(new[] {new Constant("highway")}),
|
new Constant(new[] { new Constant("highway") }),
|
||||||
Funcs.StringStringToTags.Apply(mapTag)
|
Funcs.StringStringToTags.Apply(mapTag)
|
||||||
)
|
)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
var residential = mm.Apply(new Constant(new Dictionary<string, string> {
|
var residential = mm.Apply(new Constant(new Dictionary<string, string> {
|
||||||
{"highway", "residential"}
|
{ "highway", "residential" }
|
||||||
})).Evaluate(new Context());
|
})).Evaluate(new Context());
|
||||||
Assert.Equal("yes", residential);
|
Assert.Equal("yes", residential);
|
||||||
|
|
||||||
var living = mm.Apply(new Constant(new Dictionary<string, string> {
|
var living = mm.Apply(new Constant(new Dictionary<string, string> {
|
||||||
{"highway", "living_street"}
|
{ "highway", "living_street" }
|
||||||
})).Evaluate(new Context());
|
})).Evaluate(new Context());
|
||||||
Assert.Equal("no", living);
|
Assert.Equal("no", living);
|
||||||
|
|
||||||
var unknown = mm.Apply(new Constant(new Dictionary<string, string> {
|
var unknown = mm.Apply(new Constant(new Dictionary<string, string> {
|
||||||
{"highway", "unknown_type"}
|
{ "highway", "unknown_type" }
|
||||||
})).Evaluate(new Context());
|
})).Evaluate(new Context());
|
||||||
Assert.Equal("yes", unknown);
|
Assert.Equal("yes", unknown);
|
||||||
|
|
||||||
var missing = mm.Apply(new Constant(new Dictionary<string, string> {
|
var missing = mm.Apply(new Constant(new Dictionary<string, string> {
|
||||||
{"proposed:highway", "unknown_type"}
|
{ "proposed:highway", "unknown_type" }
|
||||||
})).Evaluate(new Context());
|
})).Evaluate(new Context());
|
||||||
Assert.Equal("no", missing);
|
Assert.Equal("no", missing);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ namespace AspectedRouting.Test.Snippets
|
||||||
var func = new Apply(
|
var func = new Apply(
|
||||||
Funcs.StringStringToTags,
|
Funcs.StringStringToTags,
|
||||||
new Mapping(
|
new Mapping(
|
||||||
new[] {"bicycle", "access"},
|
new[] { "bicycle", "access" },
|
||||||
new IExpression[] {
|
new IExpression[] {
|
||||||
Funcs.Id,
|
Funcs.Id,
|
||||||
Funcs.Id
|
Funcs.Id
|
||||||
|
@ -51,7 +51,7 @@ namespace AspectedRouting.Test.Snippets
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
var tags = new LuaLiteral(new[] {Typs.Tags}, "tags");
|
var tags = new LuaLiteral(new[] { Typs.Tags }, "tags");
|
||||||
|
|
||||||
var code = gen.Convert(lua, "result",
|
var code = gen.Convert(lua, "result",
|
||||||
new List<IExpression> {
|
new List<IExpression> {
|
||||||
|
@ -71,7 +71,7 @@ namespace AspectedRouting.Test.Snippets
|
||||||
public void SimpleMappingSnippet_SimpleMapping_GeneratesLua()
|
public void SimpleMappingSnippet_SimpleMapping_GeneratesLua()
|
||||||
{
|
{
|
||||||
var mapping = new Mapping(
|
var mapping = new Mapping(
|
||||||
new[] {"1", "-1"},
|
new[] { "1", "-1" },
|
||||||
new IExpression[] {
|
new IExpression[] {
|
||||||
new Constant("with"),
|
new Constant("with"),
|
||||||
new Constant("against")
|
new Constant("against")
|
||||||
|
@ -86,6 +86,5 @@ namespace AspectedRouting.Test.Snippets
|
||||||
"local v\nv = tags.oneway\n\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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -18,11 +18,10 @@ namespace AspectedRouting.Test
|
||||||
"{\"name\": \"legal_maxspeed_be\",\"description\": \"Gives, for each type of highway, which the default legal maxspeed is in Belgium. This file is intended to be reused for in all vehicles, from pedestrian to car. In some cases, a legal maxspeed is not really defined (e.g. on footways). In that case, a socially acceptable speed should be taken (e.g.: a bicycle on a pedestrian path will go say around 12km/h)\",\"unit\": \"km/h\",\"$max\": {\"maxspeed\": \"$parse\",\"highway\": {\"residential\": 30},\"ferry\":5}}";
|
"{\"name\": \"legal_maxspeed_be\",\"description\": \"Gives, for each type of highway, which the default legal maxspeed is in Belgium. This file is intended to be reused for in all vehicles, from pedestrian to car. In some cases, a legal maxspeed is not really defined (e.g. on footways). In that case, a socially acceptable speed should be taken (e.g.: a bicycle on a pedestrian path will go say around 12km/h)\",\"unit\": \"km/h\",\"$max\": {\"maxspeed\": \"$parse\",\"highway\": {\"residential\": 30},\"ferry\":5}}";
|
||||||
|
|
||||||
var aspect = JsonParser.AspectFromJson(null, json, null);
|
var aspect = JsonParser.AspectFromJson(null, json, null);
|
||||||
var tags = new Dictionary<string, string>
|
var tags = new Dictionary<string, string> {
|
||||||
{
|
{ "maxspeed", "42" },
|
||||||
{"maxspeed", "42"},
|
{ "highway", "residential" },
|
||||||
{"highway", "residential"},
|
{ "ferry", "yes" }
|
||||||
{"ferry", "yes"}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Assert.Equal("tags -> pdouble", string.Join(", ", aspect.Types));
|
Assert.Equal("tags -> pdouble", string.Join(", ", aspect.Types));
|
||||||
|
@ -40,11 +39,10 @@ namespace AspectedRouting.Test
|
||||||
var aspect = JsonParser.AspectFromJson(null, json, null);
|
var aspect = JsonParser.AspectFromJson(null, json, null);
|
||||||
|
|
||||||
Assert.Equal(
|
Assert.Equal(
|
||||||
new Dictionary<string, HashSet<string>>
|
new Dictionary<string, HashSet<string>> {
|
||||||
{
|
{ "maxspeed", new HashSet<string>() },
|
||||||
{"maxspeed", new HashSet<string>()},
|
{ "highway", new HashSet<string> { "residential" } },
|
||||||
{"highway", new HashSet<string> {"residential"}},
|
{ "ferry", new HashSet<string>() }
|
||||||
{"ferry", new HashSet<string>()}
|
|
||||||
},
|
},
|
||||||
aspect.PossibleTags());
|
aspect.PossibleTags());
|
||||||
}
|
}
|
||||||
|
@ -79,7 +77,7 @@ namespace AspectedRouting.Test
|
||||||
public void EitherFunc_SpecializeToString_Const()
|
public void EitherFunc_SpecializeToString_Const()
|
||||||
{
|
{
|
||||||
var a = new Constant("a");
|
var a = new Constant("a");
|
||||||
|
|
||||||
var mconst = new Apply(new Apply(Funcs.EitherFunc, Funcs.Id), Funcs.Const);
|
var mconst = new Apply(new Apply(Funcs.EitherFunc, Funcs.Id), Funcs.Const);
|
||||||
var specialized = new Apply(mconst, a).Specialize(Typs.String);
|
var specialized = new Apply(mconst, a).Specialize(Typs.String);
|
||||||
|
|
||||||
|
@ -125,7 +123,7 @@ namespace AspectedRouting.Test
|
||||||
public void MaxTest()
|
public void MaxTest()
|
||||||
{
|
{
|
||||||
var ls = new Constant(new ListType(Typs.Double),
|
var ls = new Constant(new ListType(Typs.Double),
|
||||||
new[] {1.1, 2.0, 3.0}.Select(d => (object) d));
|
new[] { 1.1, 2.0, 3.0 }.Select(d => (object)d));
|
||||||
Assert.Equal("[1.1, 2, 3] : list (double)",
|
Assert.Equal("[1.1, 2, 3] : list (double)",
|
||||||
ls.Evaluate(null).Pretty() + " : " + string.Join(", ", ls.Types));
|
ls.Evaluate(null).Pretty() + " : " + string.Join(", ", ls.Types));
|
||||||
var mx = Funcs.Max.Apply(ls);
|
var mx = Funcs.Max.Apply(ls);
|
||||||
|
@ -164,7 +162,7 @@ namespace AspectedRouting.Test
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TestStringGeneration()
|
public void TestStringGeneration()
|
||||||
{
|
{
|
||||||
var v = Var.Fresh(new HashSet<string> {"$a", "$b"});
|
var v = Var.Fresh(new HashSet<string> { "$a", "$b" });
|
||||||
Assert.Equal("$c", v.Name);
|
Assert.Equal("$c", v.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,9 +174,9 @@ namespace AspectedRouting.Test
|
||||||
var app = new Apply(
|
var app = new Apply(
|
||||||
new Apply(
|
new Apply(
|
||||||
new Apply(Funcs.Id, Funcs.Id), Funcs.Id), a);
|
new Apply(Funcs.Id, Funcs.Id), Funcs.Id), a);
|
||||||
var (f, args ) = app.DeconstructApply().Value;
|
var (f, args) = app.DeconstructApply().Value;
|
||||||
Assert.Equal(Funcs.Id.Name, ((Function) f).Name);
|
Assert.Equal(Funcs.Id.Name, ((Function)f).Name);
|
||||||
Assert.Equal(new List<IExpression> {Funcs.Id, Funcs.Id, a}.Select(e => e.ToString()),
|
Assert.Equal(new List<IExpression> { Funcs.Id, Funcs.Id, a }.Select(e => e.ToString()),
|
||||||
args.Select(e => e.ToString()));
|
args.Select(e => e.ToString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,8 +52,8 @@ namespace AspectedRouting.Test
|
||||||
new Curry(Typs.Tags, Typs.Double), x
|
new Curry(Typs.Tags, Typs.Double), x
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void WidestCommonGround_StringAndString_String()
|
public void WidestCommonGround_StringAndString_String()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<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/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">
|
<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>
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="Examples\**">
|
<None Update="Examples\**">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -9,19 +9,17 @@ namespace AspectedRouting.IO.LuaSkeleton
|
||||||
public class LuaLiteral : IExpression
|
public class LuaLiteral : IExpression
|
||||||
{
|
{
|
||||||
public readonly string Lua;
|
public readonly string Lua;
|
||||||
public IEnumerable<Type> Types { get; }
|
|
||||||
|
|
||||||
public LuaLiteral(Type type, string lua):this(new [] {type}, lua)
|
public LuaLiteral(Type type, string lua) : this(new[] { type }, lua) { }
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public LuaLiteral(IEnumerable<Type> types, string lua)
|
public LuaLiteral(IEnumerable<Type> types, string lua)
|
||||||
{
|
{
|
||||||
Lua = lua;
|
Lua = lua;
|
||||||
Types = types;
|
Types = types;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Type> Types { get; }
|
||||||
|
|
||||||
public object Evaluate(Context c, params IExpression[] arguments)
|
public object Evaluate(Context c, params IExpression[] arguments)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
@ -32,11 +30,11 @@ namespace AspectedRouting.IO.LuaSkeleton
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IExpression PruneTypes(Func<Type, bool> allowedTypes)
|
public IExpression PruneTypes(System.Func<Type, bool> allowedTypes)
|
||||||
{
|
{
|
||||||
var passed = this.Types.Where(allowedTypes);
|
var passed = Types.Where(allowedTypes);
|
||||||
if (passed.Any()) {
|
if (passed.Any()) {
|
||||||
return new LuaLiteral(passed, this.Lua);
|
return new LuaLiteral(passed, Lua);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -44,13 +42,12 @@ namespace AspectedRouting.IO.LuaSkeleton
|
||||||
|
|
||||||
public IExpression Optimize()
|
public IExpression Optimize()
|
||||||
{
|
{
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Visit(Func<IExpression, bool> f)
|
public void Visit(Func<IExpression, bool> f)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,8 +6,8 @@ namespace AspectedRouting.IO.itinero1
|
||||||
{
|
{
|
||||||
public class LuaParameterPrinter
|
public class LuaParameterPrinter
|
||||||
{
|
{
|
||||||
private ProfileMetaData _profile;
|
private readonly ProfileMetaData _profile;
|
||||||
private LuaSkeleton.LuaSkeleton _skeleton;
|
private readonly LuaSkeleton.LuaSkeleton _skeleton;
|
||||||
|
|
||||||
public LuaParameterPrinter(ProfileMetaData profile, LuaSkeleton.LuaSkeleton skeleton)
|
public LuaParameterPrinter(ProfileMetaData profile, LuaSkeleton.LuaSkeleton skeleton)
|
||||||
{
|
{
|
||||||
|
@ -18,8 +18,7 @@ namespace AspectedRouting.IO.itinero1
|
||||||
|
|
||||||
public string GenerateDefaultParameters()
|
public string GenerateDefaultParameters()
|
||||||
{
|
{
|
||||||
var impl = new List<string>()
|
var impl = new List<string> {
|
||||||
{
|
|
||||||
"function default_parameters()",
|
"function default_parameters()",
|
||||||
" local parameters = {}",
|
" local parameters = {}",
|
||||||
DeclareParametersFor(_profile.DefaultParameters),
|
DeclareParametersFor(_profile.DefaultParameters),
|
||||||
|
@ -31,34 +30,26 @@ namespace AspectedRouting.IO.itinero1
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a piece of code of the following format:
|
/// Generates a piece of code of the following format:
|
||||||
///
|
/// parameters["x"] = a;
|
||||||
/// parameters["x"] = a;
|
/// parameters["y"] = b:
|
||||||
/// parameters["y"] = b:
|
/// ...
|
||||||
/// ...
|
/// Where x=a and y=b are defined in the profile
|
||||||
///
|
/// Dependencies are added.
|
||||||
/// Where x=a and y=b are defined in the profile
|
/// Note that the caller should still add `local paramaters = default_parameters()`
|
||||||
///
|
|
||||||
/// Dependencies are added.
|
|
||||||
///
|
|
||||||
/// Note that the caller should still add `local paramaters = default_parameters()`
|
|
||||||
///
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="behaviour"></param>
|
/// <param name="behaviour"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public string DeclareParametersFor(Dictionary<string, IExpression> subParams)
|
public string DeclareParametersFor(Dictionary<string, IExpression> subParams)
|
||||||
{
|
{
|
||||||
var impl = "";
|
var impl = "";
|
||||||
foreach (var (paramName, value) in subParams)
|
foreach (var (paramName, value) in subParams) {
|
||||||
{
|
if (paramName.Equals("description")) {
|
||||||
if (paramName.Equals("description"))
|
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var paramNameTrimmed = paramName.TrimStart('#').AsLuaIdentifier();
|
var paramNameTrimmed = paramName.TrimStart('#').AsLuaIdentifier();
|
||||||
if (!string.IsNullOrEmpty(paramNameTrimmed))
|
if (!string.IsNullOrEmpty(paramNameTrimmed)) {
|
||||||
{
|
|
||||||
impl += $" parameters.{paramNameTrimmed} = {_skeleton.ToLua(value)}\n";
|
impl += $" parameters.{paramNameTrimmed} = {_skeleton.ToLua(value)}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
225
AspectedRouting/IO/md/ProfileToMD.cs
Normal file
225
AspectedRouting/IO/md/ProfileToMD.cs
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using AspectedRouting.Language;
|
||||||
|
using AspectedRouting.Language.Expression;
|
||||||
|
using AspectedRouting.Tests;
|
||||||
|
|
||||||
|
namespace AspectedRouting.IO.md
|
||||||
|
{
|
||||||
|
internal class MarkDownSection
|
||||||
|
{
|
||||||
|
private readonly List<string> parts = new List<string>();
|
||||||
|
|
||||||
|
public string ToString()
|
||||||
|
{
|
||||||
|
return string.Join("\n\n", parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddTitle(string title, int level)
|
||||||
|
{
|
||||||
|
var str = "";
|
||||||
|
for (var i = 0; i < level; i++) {
|
||||||
|
str += "#";
|
||||||
|
}
|
||||||
|
|
||||||
|
str += " " + title;
|
||||||
|
parts.Add(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(params string[] paragraph)
|
||||||
|
{
|
||||||
|
parts.Add(string.Join("\n", paragraph));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddList(List<string> items)
|
||||||
|
{
|
||||||
|
parts.Add(string.Join("\n", items.Select(i => " - " + i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class ProfileToMD
|
||||||
|
{
|
||||||
|
private readonly string _behaviour;
|
||||||
|
private readonly Context _c;
|
||||||
|
private readonly ProfileMetaData _profile;
|
||||||
|
private readonly MarkDownSection md = new MarkDownSection();
|
||||||
|
|
||||||
|
public ProfileToMD(ProfileMetaData profile, string behaviour, Context c)
|
||||||
|
{
|
||||||
|
_profile = profile;
|
||||||
|
_behaviour = behaviour;
|
||||||
|
_c = c.WithAspectName(behaviour);
|
||||||
|
_c.DefinedFunctions["speed"] = new AspectMetadata(profile.Speed, "speed", "The speed this vehicle is going",
|
||||||
|
"", "km/h", "", true);
|
||||||
|
if (!profile.Behaviours.ContainsKey(behaviour)) {
|
||||||
|
throw new ArgumentException("Profile does not contain behaviour " + behaviour);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private decimal R(double d)
|
||||||
|
{
|
||||||
|
return Math.Round((decimal)d, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates an entry with `speed`, `priority` for the profile
|
||||||
|
*/
|
||||||
|
public string TableEntry(string msg, Dictionary<string, string> tags, ProfileResult? reference,
|
||||||
|
bool nullOnSame = false)
|
||||||
|
{
|
||||||
|
var profile = _profile.Run(_c, _behaviour, tags);
|
||||||
|
if (!reference.HasValue) {
|
||||||
|
return "| " + msg + " | " + profile.Speed + " | " + profile.Priority + " | ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reference.Equals(profile) && nullOnSame) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "| " + msg + " | " + R(profile.Speed) + " | " +
|
||||||
|
R(profile.Speed / reference.Value.Speed) + " | " +
|
||||||
|
R(profile.Priority) + " | " + R(profile.Priority / reference.Value.Priority) + " | " +
|
||||||
|
profile.Access + " | " + profile.Oneway;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTagsTable(ProfileResult reference, Dictionary<string, HashSet<string>> usedTags)
|
||||||
|
{
|
||||||
|
var p = _profile;
|
||||||
|
var b = _profile.Behaviours[_behaviour];
|
||||||
|
|
||||||
|
var tableEntries = new List<string>();
|
||||||
|
foreach (var (key, vals) in usedTags) {
|
||||||
|
var values = vals;
|
||||||
|
if (values.Count == 0 && key == "maxspeed") {
|
||||||
|
tableEntries.Add($" | {key}=* (example values below)");
|
||||||
|
values = new HashSet<string> {
|
||||||
|
"20", "30", "50", "70", "90", "120", "150"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.Count == 0) {
|
||||||
|
tableEntries.Add($" | {key}=*");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.Count > 0) {
|
||||||
|
foreach (var value in values) {
|
||||||
|
var tags = new Dictionary<string, string> {
|
||||||
|
[key] = value
|
||||||
|
};
|
||||||
|
var entry = TableEntry($"{key}={value} ", tags, reference);
|
||||||
|
if (entry == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
tableEntries.Add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
md.Add("| Tags | Speed (km/h) | speedfactor | Priority | priorityfactor | access | oneway | ",
|
||||||
|
"| ---- | ------------ | ----------- | -------- | --------------- | ----- | ------ |",
|
||||||
|
string.Join("\n", tableEntries));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, IExpression> TagsWithPriorityInfluence()
|
||||||
|
{
|
||||||
|
|
||||||
|
var p = _profile;
|
||||||
|
var parameters = _profile.ParametersFor(_behaviour);
|
||||||
|
var withInfluence = new Dictionary<string, IExpression>();
|
||||||
|
|
||||||
|
foreach (var kv in p.Priority) {
|
||||||
|
if (parameters[kv.Key].Equals(0.0) || parameters[kv.Key].Equals(0)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
withInfluence[kv.Key] = kv.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return withInfluence;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public string MainFormula()
|
||||||
|
{
|
||||||
|
var p = _profile;
|
||||||
|
var b = _profile.Behaviours[_behaviour];
|
||||||
|
|
||||||
|
var overridenParams = new HashSet<string>();
|
||||||
|
var paramValues = new Dictionary<string, object>();
|
||||||
|
foreach (var kv in p.DefaultParameters) {
|
||||||
|
paramValues[kv.Key] = kv.Value.Evaluate(_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var kv in b) {
|
||||||
|
paramValues[kv.Key] = kv.Value.Evaluate(_c);
|
||||||
|
overridenParams.Add(kv.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
var mainFormulaParts = p.Priority.Select(delegate(KeyValuePair<string, IExpression> kv) {
|
||||||
|
var key = kv.Key;
|
||||||
|
var param = paramValues[key];
|
||||||
|
if (param.Equals(0) || param.Equals(0.0)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overridenParams.Contains(key)) {
|
||||||
|
param = "**" + param + "**";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var called = kv.Value.DirectlyCalled();
|
||||||
|
return param + " * `" + string.Join("", called.calledFunctionNames) + "`";
|
||||||
|
});
|
||||||
|
|
||||||
|
var mainFormula = string.Join(" + ", mainFormulaParts.Where(p => p != ""));
|
||||||
|
return mainFormula;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var p = _profile;
|
||||||
|
var b = _profile.Behaviours[_behaviour];
|
||||||
|
md.AddTitle(_profile.Name + "." + _behaviour, 1);
|
||||||
|
|
||||||
|
md.Add(p.Description);
|
||||||
|
|
||||||
|
if (b.ContainsKey("description")) {
|
||||||
|
md.Add(b["description"].Evaluate(_c).ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
md.Add("This profile is calculated as following (non-default keys are bold):", MainFormula());
|
||||||
|
|
||||||
|
var residentialTags = new Dictionary<string, string> {
|
||||||
|
["highway"] = "residential"
|
||||||
|
};
|
||||||
|
|
||||||
|
md.Add("| Tags | Speed (km/h) | Priority",
|
||||||
|
"| ---- | ----- | ---------- | ",
|
||||||
|
TableEntry("Residential highway (reference)", residentialTags, null));
|
||||||
|
var reference = _profile.Run(_c, _behaviour, residentialTags);
|
||||||
|
md.AddTitle("Tags influencing priority", 2);
|
||||||
|
md.Add(
|
||||||
|
"Priority is what influences which road to take. The routeplanner will search a way where `1/priority` is minimal.");
|
||||||
|
addTagsTable(reference, TagsWithPriorityInfluence().Values.PossibleTagsRecursive(_c));
|
||||||
|
|
||||||
|
md.AddTitle("Tags influencing speed", 2);
|
||||||
|
md.Add(
|
||||||
|
"Speed is used to calculate how long the trip will take, but does _not_ influence which route is taken. Some profiles do use speed as a factor in priority too - in this case, these tags will be mentioned above too.");
|
||||||
|
addTagsTable(reference, _profile.Speed.PossibleTagsRecursive(_c));
|
||||||
|
|
||||||
|
md.AddTitle("Tags influencing access", 2);
|
||||||
|
md.Add("These tags influence whether or not this road can be taken with this vehicle or behaviour");
|
||||||
|
addTagsTable(reference, _profile.Access.PossibleTagsRecursive(_c));
|
||||||
|
md.AddTitle("Tags influencing oneway", 2);
|
||||||
|
md.Add("These tags influence whether or not this road can be taken in all directions or not");
|
||||||
|
addTagsTable(reference, _profile.Oneway.PossibleTagsRecursive(_c));
|
||||||
|
|
||||||
|
|
||||||
|
return md.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
Generating a good route for travellers is hard; especially for cyclists. They can be very picky and the driving style and purposes are diverse. Think about:
|
Generating a good route for travellers is hard; especially for cyclists. They can be very picky and the driving style
|
||||||
|
and purposes are diverse. Think about:
|
||||||
|
|
||||||
- A lightweight food delivery driver, who wants to be at their destination as soon as possible
|
- A lightweight food delivery driver, who wants to be at their destination as soon as possible
|
||||||
- A cargo bike, possibly with a cart or electrically supported, doing heavy delivery
|
- A cargo bike, possibly with a cart or electrically supported, doing heavy delivery
|
||||||
|
@ -10,13 +11,18 @@ Generating a good route for travellers is hard; especially for cyclists. They ca
|
||||||
- Grandma cycling along a canal on sunday afternoon
|
- Grandma cycling along a canal on sunday afternoon
|
||||||
- Someone bringing their kids to school
|
- Someone bringing their kids to school
|
||||||
|
|
||||||
It is clear that these persona's on top have very different wishes for their route. A road with a high car pressure won't pose a problem for the food delivery, whereas grandma wouldn't even think about going there. And this is without mentioning the speed these cyclists drive, where they are allowed to drive, ...
|
It is clear that these persona's on top have very different wishes for their route. A road with a high car pressure
|
||||||
|
won't pose a problem for the food delivery, whereas grandma wouldn't even think about going there. And this is without
|
||||||
|
mentioning the speed these cyclists drive, where they are allowed to drive, ...
|
||||||
|
|
||||||
Generating a cycle route for these persons is thus clearly far from simply picking the shortest possible path. On top of that, a consumer expects the route calculations to be both customizable and to be blazingly fast.
|
Generating a cycle route for these persons is thus clearly far from simply picking the shortest possible path. On top of
|
||||||
|
that, a consumer expects the route calculations to be both customizable and to be blazingly fast.
|
||||||
|
|
||||||
In order to simplify the generation of these routing profiles, this repository introduces _aspected routing_.
|
In order to simplify the generation of these routing profiles, this repository introduces _aspected routing_.
|
||||||
|
|
||||||
In _aspected routing_, one does not try to tackle the routing problem all at once, but one tries to dissassemble the preferences of the travellers into multiple, orthogonal aspects. These aspects can then be combined in a linear way, giving a fast and flexible system.
|
In _aspected routing_, one does not try to tackle the routing problem all at once, but one tries to dissassemble the
|
||||||
|
preferences of the travellers into multiple, orthogonal aspects. These aspects can then be combined in a linear way,
|
||||||
|
giving a fast and flexible system.
|
||||||
|
|
||||||
Some aspects can be:
|
Some aspects can be:
|
||||||
|
|
||||||
|
@ -38,9 +44,12 @@ Even though this repository is heavily inspired on OpenStreetMap, it can be gene
|
||||||
|
|
||||||
## Road network assumptions
|
## Road network assumptions
|
||||||
|
|
||||||
The only assumptions made are that roads have a **length** and a collection of **tags**, this is a dictionary mapping strings onto strings. These tags encode the properties of the road (e.g. road classification, name, surface, ...)
|
The only assumptions made are that roads have a **length** and a collection of **tags**, this is a dictionary mapping
|
||||||
|
strings onto strings. These tags encode the properties of the road (e.g. road classification, name, surface, ...)
|
||||||
|
|
||||||
OpenStreetMap also has a concept of **relations**. A special function is available for that. However, in a preprocessing step, the relations that a road is a member of, are converted into tags on every way with a `_network:i:key=value` format, where `i` is the number of the relation, and `key`=`value` is a tag present on the relation.
|
OpenStreetMap also has a concept of **relations**. A special function is available for that. However, in a preprocessing
|
||||||
|
step, the relations that a road is a member of, are converted into tags on every way with a `_network:i:key=value`
|
||||||
|
format, where `i` is the number of the relation, and `key`=`value` is a tag present on the relation.
|
||||||
|
|
||||||
## Describing an aspect
|
## Describing an aspect
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
- string
|
- string
|
||||||
- tags
|
- tags
|
||||||
- bool
|
- bool
|
||||||
|
|
||||||
## Builtin functions
|
## Builtin functions
|
||||||
|
|
||||||
- eq
|
- eq
|
||||||
|
@ -37,7 +38,6 @@
|
||||||
- eitherFunc
|
- eitherFunc
|
||||||
- stringToTags
|
- stringToTags
|
||||||
|
|
||||||
|
|
||||||
### Function overview
|
### Function overview
|
||||||
|
|
||||||
#### eq
|
#### eq
|
||||||
|
@ -49,8 +49,6 @@ $a | $a | string |
|
||||||
|
|
||||||
Returns 'yes' if both values _are_ the same
|
Returns 'yes' if both values _are_ the same
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -64,7 +62,6 @@ end
|
||||||
|
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### notEq
|
#### notEq
|
||||||
|
|
||||||
a | b | returns |
|
a | b | returns |
|
||||||
|
@ -75,8 +72,6 @@ bool | bool |
|
||||||
|
|
||||||
OVerloaded function, either boolean not or returns 'yes' if the two passed in values are _not_ the same;
|
OVerloaded function, either boolean not or returns 'yes' if the two passed in values are _not_ the same;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -93,7 +88,6 @@ function notEq(a, b)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### not
|
#### not
|
||||||
|
|
||||||
a | b | returns |
|
a | b | returns |
|
||||||
|
@ -104,8 +98,6 @@ bool | bool |
|
||||||
|
|
||||||
OVerloaded function, either boolean not or returns 'yes' if the two passed in values are _not_ the same;
|
OVerloaded function, either boolean not or returns 'yes' if the two passed in values are _not_ the same;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -122,7 +114,6 @@ function notEq(a, b)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### inv
|
#### inv
|
||||||
|
|
||||||
d | returns |
|
d | returns |
|
||||||
|
@ -132,8 +123,6 @@ double | double |
|
||||||
|
|
||||||
Calculates `1/d`
|
Calculates `1/d`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -142,7 +131,6 @@ function inv(n)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### default
|
#### default
|
||||||
|
|
||||||
defaultValue | f | returns |
|
defaultValue | f | returns |
|
||||||
|
@ -151,8 +139,6 @@ $a | $b -> $a | $b | $a |
|
||||||
|
|
||||||
Calculates function `f` for the given argument. If the result is `null`, the default value is returned instead
|
Calculates function `f` for the given argument. If the result is `null`, the default value is returned instead
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -164,7 +150,6 @@ function default(defaultValue, realValue)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### parse
|
#### parse
|
||||||
|
|
||||||
s | returns |
|
s | returns |
|
||||||
|
@ -174,8 +159,6 @@ string | pdouble |
|
||||||
|
|
||||||
Parses a string into a numerical value
|
Parses a string into a numerical value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -208,7 +191,6 @@ function parse(string)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### to_string
|
#### to_string
|
||||||
|
|
||||||
obj | returns |
|
obj | returns |
|
||||||
|
@ -217,8 +199,6 @@ $a | string |
|
||||||
|
|
||||||
Converts a value into a human readable string
|
Converts a value into a human readable string
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -227,7 +207,6 @@ function to_string(o)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### concat
|
#### concat
|
||||||
|
|
||||||
a | b | returns |
|
a | b | returns |
|
||||||
|
@ -236,8 +215,6 @@ string | string | string |
|
||||||
|
|
||||||
Concatenates two strings
|
Concatenates two strings
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -246,7 +223,6 @@ function concat(a, b)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### containedIn
|
#### containedIn
|
||||||
|
|
||||||
list | a | returns |
|
list | a | returns |
|
||||||
|
@ -255,8 +231,6 @@ list ($a) | $a | bool |
|
||||||
|
|
||||||
Given a list of values, checks if the argument is contained in the list.
|
Given a list of values, checks if the argument is contained in the list.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -271,7 +245,6 @@ function containedIn(list, a)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### min
|
#### min
|
||||||
|
|
||||||
list | returns |
|
list | returns |
|
||||||
|
@ -284,8 +257,6 @@ list (bool) | bool |
|
||||||
|
|
||||||
Out of a list of values, gets the smallest value. IN case of a list of bools, this acts as `and`
|
Out of a list of values, gets the smallest value. IN case of a list of bools, this acts as `and`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -303,7 +274,6 @@ function min(list)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### and
|
#### and
|
||||||
|
|
||||||
list | returns |
|
list | returns |
|
||||||
|
@ -316,8 +286,6 @@ list (bool) | bool |
|
||||||
|
|
||||||
Out of a list of values, gets the smallest value. IN case of a list of bools, this acts as `and`
|
Out of a list of values, gets the smallest value. IN case of a list of bools, this acts as `and`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -335,7 +303,6 @@ function min(list)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### max
|
#### max
|
||||||
|
|
||||||
list | returns |
|
list | returns |
|
||||||
|
@ -348,8 +315,6 @@ list (bool) | bool |
|
||||||
|
|
||||||
Returns the biggest value in the list. For a list of booleans, this acts as 'or'
|
Returns the biggest value in the list. For a list of booleans, this acts as 'or'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -367,7 +332,6 @@ function max(list)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### or
|
#### or
|
||||||
|
|
||||||
list | returns |
|
list | returns |
|
||||||
|
@ -380,8 +344,6 @@ list (bool) | bool |
|
||||||
|
|
||||||
Returns the biggest value in the list. For a list of booleans, this acts as 'or'
|
Returns the biggest value in the list. For a list of booleans, this acts as 'or'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -399,7 +361,6 @@ function max(list)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### sum
|
#### sum
|
||||||
|
|
||||||
list | returns |
|
list | returns |
|
||||||
|
@ -412,8 +373,6 @@ list (bool) | int |
|
||||||
|
|
||||||
Sums all the numbers in the given list. If the list contains bool, `yes` or `true` will be considered to equal `1`
|
Sums all the numbers in the given list. If the list contains bool, `yes` or `true` will be considered to equal `1`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -429,7 +388,6 @@ function sum(list)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### multiply
|
#### multiply
|
||||||
|
|
||||||
list | returns |
|
list | returns |
|
||||||
|
@ -442,8 +400,6 @@ list (bool) | bool |
|
||||||
|
|
||||||
Multiplies all the values in a given list. On a list of booleans, this acts as 'and' or 'all'
|
Multiplies all the values in a given list. On a list of booleans, this acts as 'and' or 'all'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -456,7 +412,6 @@ function multiply(list)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### firstMatchOf
|
#### firstMatchOf
|
||||||
|
|
||||||
s | returns |
|
s | returns |
|
||||||
|
@ -465,8 +420,6 @@ list (string) | tags -> list ($a) | tags | $a |
|
||||||
|
|
||||||
Parses a string into a numerical value
|
Parses a string into a numerical value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -492,18 +445,15 @@ function first_match_of(tags, result, order_of_keys, table)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### mustMatch
|
#### mustMatch
|
||||||
|
|
||||||
neededKeys (filled in by parser) | f | returns |
|
neededKeys (filled in by parser) | f | returns |
|
||||||
--- | --- | --- |
|
--- | --- | --- |
|
||||||
list (string) | tags -> list (bool) | tags | bool |
|
list (string) | tags -> list (bool) | tags | bool |
|
||||||
|
|
||||||
Every key that is used in the subfunction must be present.
|
Every key that is used in the subfunction must be present. If, on top, a value is present with a mapping, every
|
||||||
If, on top, a value is present with a mapping, every key/value will be executed and must return a value that is not 'no' or 'false'
|
key/value will be executed and must return a value that is not 'no' or 'false' Note that this is a privileged builtin
|
||||||
Note that this is a privileged builtin function, as the parser will automatically inject the keys used in the called function.
|
function, as the parser will automatically inject the keys used in the called function.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
|
@ -572,7 +522,6 @@ function must_match(tags, result, needed_keys, table)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### memberOf
|
#### memberOf
|
||||||
|
|
||||||
f | tags | returns |
|
f | tags | returns |
|
||||||
|
@ -586,15 +535,18 @@ In order to use this for itinero 1.0, the membership _must_ be the top level exp
|
||||||
Conceptually, when the aspect is executed for a way, every relation will be used as argument in the subfunction `f`
|
Conceptually, when the aspect is executed for a way, every relation will be used as argument in the subfunction `f`
|
||||||
If this subfunction returns 'true', the entire aspect will return true.
|
If this subfunction returns 'true', the entire aspect will return true.
|
||||||
|
|
||||||
In the lua implementation for itinero 1.0, this is implemented slightly different: a flag `_relation:<aspect_name>="yes"` will be set if the aspect matches on every way for where this aspect matches.
|
In the lua implementation for itinero 1.0, this is implemented slightly different: a
|
||||||
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. The profile function then picks the tags for himself and strips the `<profile_name>:` away from the key.
|
flag `_relation:<aspect_name>="yes"` will be set if the aspect matches on every way for where this aspect matches.
|
||||||
|
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. The profile function then picks the tags for himself and strips the `<profile_name>:` away from the
|
||||||
|
key.
|
||||||
|
|
||||||
In the test.csv, one can simply use `_relation:<aspect_name>=yes` to mimic relations in your tests
|
In the test.csv, one can simply use `_relation:<aspect_name>=yes` to mimic relations in your tests
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -610,7 +562,6 @@ function member_of(calledIn, parameters, tags, result)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### if_then_else
|
#### if_then_else
|
||||||
|
|
||||||
condition | then | else | returns |
|
condition | then | else | returns |
|
||||||
|
@ -618,9 +569,8 @@ condition | then | else | returns |
|
||||||
bool | $a | $a | $a |
|
bool | $a | $a | $a |
|
||||||
bool | $a | $a |
|
bool | $a | $a |
|
||||||
|
|
||||||
Selects either one of the branches, depending on the condition.If the `else` branch is not set, `null` is returned in the condition is false.
|
Selects either one of the branches, depending on the condition.If the `else` branch is not set, `null` is returned in
|
||||||
|
the condition is false.
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
|
@ -634,7 +584,6 @@ function if_then_else(condition, thn, els)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### if
|
#### if
|
||||||
|
|
||||||
condition | then | else | returns |
|
condition | then | else | returns |
|
||||||
|
@ -642,9 +591,8 @@ condition | then | else | returns |
|
||||||
bool | $a | $a | $a |
|
bool | $a | $a | $a |
|
||||||
bool | $a | $a |
|
bool | $a | $a |
|
||||||
|
|
||||||
Selects either one of the branches, depending on the condition.If the `else` branch is not set, `null` is returned in the condition is false.
|
Selects either one of the branches, depending on the condition.If the `else` branch is not set, `null` is returned in
|
||||||
|
the condition is false.
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
|
@ -658,7 +606,6 @@ function if_then_else(condition, thn, els)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### id
|
#### id
|
||||||
|
|
||||||
a | returns |
|
a | returns |
|
||||||
|
@ -667,8 +614,6 @@ $a | $a |
|
||||||
|
|
||||||
Returns the argument unchanged - the identity function. Seems useless at first sight, but useful in parsing
|
Returns the argument unchanged - the identity function. Seems useless at first sight, but useful in parsing
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -677,7 +622,6 @@ function id(v)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### const
|
#### const
|
||||||
|
|
||||||
a | b | returns |
|
a | b | returns |
|
||||||
|
@ -686,8 +630,6 @@ $a | $b | $a |
|
||||||
|
|
||||||
Small utility function, which takes two arguments `a` and `b` and returns `a`. Used extensively to insert freedom
|
Small utility function, which takes two arguments `a` and `b` and returns `a`. Used extensively to insert freedom
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
@ -696,7 +638,6 @@ function const(a, b)
|
||||||
end
|
end
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### constRight
|
#### constRight
|
||||||
|
|
||||||
a | b | returns |
|
a | b | returns |
|
||||||
|
@ -705,24 +646,20 @@ $a | $b | $b |
|
||||||
|
|
||||||
Small utility function, which takes two arguments `a` and `b` and returns `b`. Used extensively to insert freedom
|
Small utility function, which takes two arguments `a` and `b` and returns `b`. Used extensively to insert freedom
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### dot
|
#### dot
|
||||||
|
|
||||||
f | g | a | returns |
|
f | g | a | returns |
|
||||||
--- | --- | --- | --- |
|
--- | --- | --- | --- |
|
||||||
$b -> $c | $a -> $b | $a | $c |
|
$b -> $c | $a -> $b | $a | $c |
|
||||||
|
|
||||||
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
|
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
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
|
@ -730,16 +667,14 @@ Lua implementation:
|
||||||
|
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### listDot
|
#### listDot
|
||||||
|
|
||||||
list | a | returns |
|
list | a | returns |
|
||||||
--- | --- | --- |
|
--- | --- | --- |
|
||||||
list ($a -> $b) | $a | list ($b) |
|
list ($a -> $b) | $a | list ($b) |
|
||||||
|
|
||||||
Listdot takes a list of functions `[f, g, h]` and and an argument `a`. It applies the argument on every single function.It conveniently lifts the argument out of the list.
|
Listdot takes a list of functions `[f, g, h]` and and an argument `a`. It applies the argument on every single
|
||||||
|
function.It conveniently lifts the argument out of the list.
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
|
@ -748,7 +683,6 @@ Lua implementation:
|
||||||
-- listDot
|
-- listDot
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### eitherFunc
|
#### eitherFunc
|
||||||
|
|
||||||
f | g | a | returns |
|
f | g | a | returns |
|
||||||
|
@ -756,21 +690,24 @@ f | g | a | returns |
|
||||||
$a -> $b | $c -> $d | $a | $b |
|
$a -> $b | $c -> $d | $a | $b |
|
||||||
$a -> $b | $c -> $d | $c | $d |
|
$a -> $b | $c -> $d | $c | $d |
|
||||||
|
|
||||||
EitherFunc is a small utility function, mostly used in the parser. It allows the compiler to choose a function, based on the types.
|
EitherFunc is a small utility function, mostly used in the parser. It allows the compiler to choose a function, based on
|
||||||
|
the types.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
Disclaimer: _you should never ever need this in your profiles_
|
Disclaimer: _you should never ever need this in your profiles_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
#### stringToTags
|
#### stringToTags
|
||||||
|
|
||||||
f | tags | returns |
|
f | tags | returns |
|
||||||
|
@ -779,8 +716,6 @@ string -> string -> $a | tags | list ($a) |
|
||||||
|
|
||||||
stringToTags converts a function `string -> string -> a` into a function `tags -> [a]`
|
stringToTags converts a function `string -> string -> a` into a function `tags -> [a]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lua implementation:
|
Lua implementation:
|
||||||
|
|
||||||
````lua
|
````lua
|
||||||
|
|
|
@ -10,7 +10,6 @@ namespace AspectedRouting.Language
|
||||||
{
|
{
|
||||||
public static class Analysis
|
public static class Analysis
|
||||||
{
|
{
|
||||||
|
|
||||||
public static Dictionary<string, (List<Type> Types, string inFunction)> UsedParameters(
|
public static Dictionary<string, (List<Type> Types, string inFunction)> UsedParameters(
|
||||||
this ProfileMetaData profile, Context context)
|
this ProfileMetaData profile, Context context)
|
||||||
{
|
{
|
||||||
|
@ -20,23 +19,19 @@ namespace AspectedRouting.Language
|
||||||
void AddParams(IExpression e, string inFunction)
|
void AddParams(IExpression e, string inFunction)
|
||||||
{
|
{
|
||||||
var parms = e.UsedParameters();
|
var parms = e.UsedParameters();
|
||||||
foreach (var param in parms)
|
foreach (var param in parms) {
|
||||||
{
|
if (parameters.TryGetValue(param.ParamName, out var typesOldUsage)) {
|
||||||
if (parameters.TryGetValue(param.ParamName, out var typesOldUsage))
|
|
||||||
{
|
|
||||||
var (types, oldUsage) = typesOldUsage;
|
var (types, oldUsage) = typesOldUsage;
|
||||||
var unified = types.SpecializeTo(param.Types);
|
var unified = types.SpecializeTo(param.Types);
|
||||||
if (unified == null)
|
if (unified == null) {
|
||||||
{
|
|
||||||
throw new ArgumentException("Inconsistent parameter usage: the paremeter " +
|
throw new ArgumentException("Inconsistent parameter usage: the paremeter " +
|
||||||
param.ParamName + " is used\n" +
|
param.ParamName + " is used\n" +
|
||||||
$" in {oldUsage} as {string.Join(",", types)}\n" +
|
$" in {oldUsage} as {string.Join(",", types)}\n" +
|
||||||
$" in {inFunction} as {string.Join(",", param.Types)}\n" +
|
$" in {inFunction} as {string.Join(",", param.Types)}\n" +
|
||||||
$"which can not be unified");
|
"which can not be unified");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
parameters[param.ParamName] = (param.Types.ToList(), inFunction);
|
parameters[param.ParamName] = (param.Types.ToList(), inFunction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,19 +42,16 @@ namespace AspectedRouting.Language
|
||||||
AddParams(profile.Oneway, "profile definition for " + profile.Name + ".oneway");
|
AddParams(profile.Oneway, "profile definition for " + profile.Name + ".oneway");
|
||||||
AddParams(profile.Speed, "profile definition for " + profile.Name + ".speed");
|
AddParams(profile.Speed, "profile definition for " + profile.Name + ".speed");
|
||||||
|
|
||||||
foreach (var (key, expr) in profile.Priority)
|
foreach (var (key, expr) in profile.Priority) {
|
||||||
{
|
|
||||||
AddParams(new Parameter(key), profile.Name + ".priority.lefthand");
|
AddParams(new Parameter(key), profile.Name + ".priority.lefthand");
|
||||||
AddParams(expr, profile.Name + ".priority");
|
AddParams(expr, profile.Name + ".priority");
|
||||||
}
|
}
|
||||||
|
|
||||||
var calledFunctions = profile.CalledFunctionsRecursive(context).Values
|
var calledFunctions = profile.CalledFunctionsRecursive(context).Values
|
||||||
.SelectMany(ls => ls).ToHashSet();
|
.SelectMany(ls => ls).ToHashSet();
|
||||||
foreach (var calledFunction in calledFunctions)
|
foreach (var calledFunction in calledFunctions) {
|
||||||
{
|
|
||||||
var func = context.GetFunction(calledFunction);
|
var func = context.GetFunction(calledFunction);
|
||||||
if (func is AspectMetadata meta && meta.ProfileInternal)
|
if (func is AspectMetadata meta && meta.ProfileInternal) {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,10 +66,8 @@ namespace AspectedRouting.Language
|
||||||
public static HashSet<Parameter> UsedParameters(this IExpression e)
|
public static HashSet<Parameter> UsedParameters(this IExpression e)
|
||||||
{
|
{
|
||||||
var result = new HashSet<Parameter>();
|
var result = new HashSet<Parameter>();
|
||||||
e.Visit(expr =>
|
e.Visit(expr => {
|
||||||
{
|
if (expr is Parameter p) {
|
||||||
if (expr is Parameter p)
|
|
||||||
{
|
|
||||||
result.Add(p);
|
result.Add(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,18 +88,14 @@ namespace AspectedRouting.Language
|
||||||
|
|
||||||
void ScanExpression(IExpression e, string inFunction)
|
void ScanExpression(IExpression e, string inFunction)
|
||||||
{
|
{
|
||||||
if (!result.ContainsKey(inFunction))
|
if (!result.ContainsKey(inFunction)) {
|
||||||
{
|
|
||||||
result.Add(inFunction, new List<string>());
|
result.Add(inFunction, new List<string>());
|
||||||
}
|
}
|
||||||
|
|
||||||
e.Visit(x =>
|
e.Visit(x => {
|
||||||
{
|
if (x is FunctionCall fc) {
|
||||||
if (x is FunctionCall fc)
|
|
||||||
{
|
|
||||||
result[inFunction].Add(fc.CalledFunctionName);
|
result[inFunction].Add(fc.CalledFunctionName);
|
||||||
if (!result.ContainsKey(fc.CalledFunctionName))
|
if (!result.ContainsKey(fc.CalledFunctionName)) {
|
||||||
{
|
|
||||||
calledFunctions.Enqueue(fc.CalledFunctionName);
|
calledFunctions.Enqueue(fc.CalledFunctionName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,14 +109,12 @@ namespace AspectedRouting.Language
|
||||||
ScanExpression(profile.Oneway, profile.Name + ".oneway");
|
ScanExpression(profile.Oneway, profile.Name + ".oneway");
|
||||||
ScanExpression(profile.Speed, profile.Name + ".speed");
|
ScanExpression(profile.Speed, profile.Name + ".speed");
|
||||||
|
|
||||||
foreach (var (key, expr) in profile.Priority)
|
foreach (var (key, expr) in profile.Priority) {
|
||||||
{
|
|
||||||
ScanExpression(new Parameter(key), $"{profile.Name}.priority.{key}.lefthand");
|
ScanExpression(new Parameter(key), $"{profile.Name}.priority.{key}.lefthand");
|
||||||
ScanExpression(expr, $"{profile.Name}.priority.{key}");
|
ScanExpression(expr, $"{profile.Name}.priority.{key}");
|
||||||
}
|
}
|
||||||
|
|
||||||
while (calledFunctions.TryDequeue(out var calledFunction))
|
while (calledFunctions.TryDequeue(out var calledFunction)) {
|
||||||
{
|
|
||||||
var func = c.GetFunction(calledFunction);
|
var func = c.GetFunction(calledFunction);
|
||||||
ScanExpression(func, calledFunction);
|
ScanExpression(func, calledFunction);
|
||||||
}
|
}
|
||||||
|
@ -148,16 +132,14 @@ namespace AspectedRouting.Language
|
||||||
var queue = new Queue<IExpression>();
|
var queue = new Queue<IExpression>();
|
||||||
exprs.ForEach(queue.Enqueue);
|
exprs.ForEach(queue.Enqueue);
|
||||||
|
|
||||||
while (queue.TryDequeue(out var next))
|
while (queue.TryDequeue(out var next)) {
|
||||||
{
|
|
||||||
var (p, deps) = next.DirectlyCalled();
|
var (p, deps) = next.DirectlyCalled();
|
||||||
parameters.UnionWith(p);
|
parameters.UnionWith(p);
|
||||||
var toCheck = deps.Except(dependencies);
|
var toCheck = deps.Except(dependencies);
|
||||||
dependencies.UnionWith(deps);
|
dependencies.UnionWith(deps);
|
||||||
|
|
||||||
foreach (var fName in toCheck)
|
foreach (var fName in toCheck) {
|
||||||
{
|
queue.Enqueue(ctx.GetFunction(fName));
|
||||||
queue.Enqueue(ctx.GetFunction(fName));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +148,8 @@ namespace AspectedRouting.Language
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates an overview of the dependencies of the expression, both which parameters it needs and what other functions (builtin or defined) it needs.
|
/// Generates an overview of the dependencies of the expression, both which parameters it needs and what other
|
||||||
|
/// functions (builtin or defined) it needs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="expr"></param>
|
/// <param name="expr"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
|
@ -176,15 +159,12 @@ namespace AspectedRouting.Language
|
||||||
var parameters = new HashSet<string>();
|
var parameters = new HashSet<string>();
|
||||||
var dependencies = new HashSet<string>();
|
var dependencies = new HashSet<string>();
|
||||||
|
|
||||||
expr.Visit(e =>
|
expr.Visit(e => {
|
||||||
{
|
if (e is FunctionCall fc) {
|
||||||
if (e is FunctionCall fc)
|
|
||||||
{
|
|
||||||
dependencies.Add(fc.CalledFunctionName);
|
dependencies.Add(fc.CalledFunctionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e is Parameter p)
|
if (e is Parameter p) {
|
||||||
{
|
|
||||||
parameters.Add(p.ParamName);
|
parameters.Add(p.ParamName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,8 +177,7 @@ namespace AspectedRouting.Language
|
||||||
|
|
||||||
public static string TypeBreakdown(this IExpression e)
|
public static string TypeBreakdown(this IExpression e)
|
||||||
{
|
{
|
||||||
|
return e + " : " + string.Join(" ; ", e.Types);
|
||||||
return e.ToString() + " : "+string.Join(" ; ", e.Types);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SanityCheckProfile(this ProfileMetaData pmd, Context context)
|
public static void SanityCheckProfile(this ProfileMetaData pmd, Context context)
|
||||||
|
@ -212,8 +191,7 @@ namespace AspectedRouting.Language
|
||||||
string MetaList(IEnumerable<string> paramNames)
|
string MetaList(IEnumerable<string> paramNames)
|
||||||
{
|
{
|
||||||
var metaInfo = "";
|
var metaInfo = "";
|
||||||
foreach (var paramName in paramNames)
|
foreach (var paramName in paramNames) {
|
||||||
{
|
|
||||||
var _ = usedMetadata.TryGetValue(paramName, out var inFunction) ||
|
var _ = usedMetadata.TryGetValue(paramName, out var inFunction) ||
|
||||||
usedMetadata.TryGetValue('#' + paramName, out inFunction);
|
usedMetadata.TryGetValue('#' + paramName, out inFunction);
|
||||||
metaInfo += $"\n - {paramName} (used in {inFunction.inFunction})";
|
metaInfo += $"\n - {paramName} (used in {inFunction.inFunction})";
|
||||||
|
@ -225,43 +203,36 @@ namespace AspectedRouting.Language
|
||||||
var usedParameters = usedMetadata.Keys.Select(key => key.TrimStart('#')).ToList();
|
var usedParameters = usedMetadata.Keys.Select(key => key.TrimStart('#')).ToList();
|
||||||
|
|
||||||
var diff = usedParameters.ToHashSet().Except(defaultParameters).ToList();
|
var diff = usedParameters.ToHashSet().Except(defaultParameters).ToList();
|
||||||
if (diff.Any())
|
if (diff.Any()) {
|
||||||
{
|
|
||||||
throw new ArgumentException("No default value set for parameter: " + MetaList(diff));
|
throw new ArgumentException("No default value set for parameter: " + MetaList(diff));
|
||||||
}
|
}
|
||||||
|
|
||||||
var unused = defaultParameters.Except(usedParameters);
|
var unused = defaultParameters.Except(usedParameters);
|
||||||
if (unused.Any())
|
if (unused.Any()) {
|
||||||
{
|
|
||||||
Console.WriteLine("[WARNING] A default value is set for parameter, but it is unused: " +
|
Console.WriteLine("[WARNING] A default value is set for parameter, but it is unused: " +
|
||||||
string.Join(", ", unused));
|
string.Join(", ", unused));
|
||||||
}
|
}
|
||||||
|
|
||||||
var paramsUsedInBehaviour = new HashSet<string>();
|
var paramsUsedInBehaviour = new HashSet<string>();
|
||||||
|
|
||||||
foreach (var (behaviourName, behaviourParams) in pmd.Behaviours)
|
foreach (var (behaviourName, behaviourParams) in pmd.Behaviours) {
|
||||||
{
|
|
||||||
var sum = 0.0;
|
var sum = 0.0;
|
||||||
var explanation = "";
|
var explanation = "";
|
||||||
paramsUsedInBehaviour.UnionWith(behaviourParams.Keys.Select(k => k.Trim('#')));
|
paramsUsedInBehaviour.UnionWith(behaviourParams.Keys.Select(k => k.Trim('#')));
|
||||||
foreach (var (paramName, _) in pmd.Priority)
|
foreach (var (paramName, _) in pmd.Priority) {
|
||||||
{
|
if (!pmd.DefaultParameters.ContainsKey(paramName)) {
|
||||||
if (!pmd.DefaultParameters.ContainsKey(paramName))
|
|
||||||
{
|
|
||||||
throw new ArgumentException(
|
throw new ArgumentException(
|
||||||
$"The behaviour {behaviourName} uses a parameter for which no default is set: {paramName}");
|
$"The behaviour {behaviourName} uses a parameter for which no default is set: {paramName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!behaviourParams.TryGetValue(paramName, out var weight))
|
if (!behaviourParams.TryGetValue(paramName, out var weight)) {
|
||||||
{
|
|
||||||
explanation += $"\n - {paramName} = default (not set)";
|
explanation += $"\n - {paramName} = default (not set)";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var weightObj = weight.Evaluate(context);
|
var weightObj = weight.Evaluate(context);
|
||||||
|
|
||||||
if (!(weightObj is double d))
|
if (!(weightObj is double d)) {
|
||||||
{
|
|
||||||
throw new ArgumentException(
|
throw new ArgumentException(
|
||||||
$"The parameter {paramName} is not a numeric value in profile {behaviourName}");
|
$"The parameter {paramName} is not a numeric value in profile {behaviourName}");
|
||||||
}
|
}
|
||||||
|
@ -270,8 +241,7 @@ namespace AspectedRouting.Language
|
||||||
explanation += $"\n - {paramName} = {d}";
|
explanation += $"\n - {paramName} = {d}";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Math.Abs(sum) < 0.0001)
|
if (Math.Abs(sum) < 0.0001) {
|
||||||
{
|
|
||||||
throw new ArgumentException("Profile " + behaviourName +
|
throw new ArgumentException("Profile " + behaviourName +
|
||||||
": the summed parameters to calculate the weight are zero or very low:" +
|
": the summed parameters to calculate the weight are zero or very low:" +
|
||||||
explanation);
|
explanation);
|
||||||
|
@ -280,8 +250,7 @@ namespace AspectedRouting.Language
|
||||||
|
|
||||||
|
|
||||||
var defaultOnly = defaultParameters.Except(paramsUsedInBehaviour).ToList();
|
var defaultOnly = defaultParameters.Except(paramsUsedInBehaviour).ToList();
|
||||||
if (defaultOnly.Any())
|
if (defaultOnly.Any()) {
|
||||||
{
|
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"[{pmd.Name}] WARNING: Some parameters only have a default value: {string.Join(", ", defaultOnly)}");
|
$"[{pmd.Name}] WARNING: Some parameters only have a default value: {string.Join(", ", defaultOnly)}");
|
||||||
}
|
}
|
||||||
|
@ -289,39 +258,32 @@ namespace AspectedRouting.Language
|
||||||
|
|
||||||
public static void SanityCheck(this IExpression e)
|
public static void SanityCheck(this IExpression e)
|
||||||
{
|
{
|
||||||
e.Visit(expr =>
|
e.Visit(expr => {
|
||||||
{
|
|
||||||
var order = new List<IExpression>();
|
var order = new List<IExpression>();
|
||||||
var mapping = new List<IExpression>();
|
var mapping = new List<IExpression>();
|
||||||
if (Deconstruct.UnApply(
|
if (Deconstruct.UnApply(
|
||||||
Deconstruct.UnApply(Deconstruct.IsFunc(Funcs.FirstOf), Deconstruct.Assign(order)),
|
Deconstruct.UnApply(Deconstruct.IsFunc(Funcs.FirstOf), Deconstruct.Assign(order)),
|
||||||
Deconstruct.Assign(mapping)
|
Deconstruct.Assign(mapping)
|
||||||
).Invoke(expr))
|
).Invoke(expr)) {
|
||||||
{
|
var expectedKeys = ((IEnumerable<object>)order.First().Evaluate(null)).Select(o => {
|
||||||
var expectedKeys = ((IEnumerable<object>) order.First().Evaluate(null)).Select(o =>
|
if (o is IExpression x) {
|
||||||
{
|
return (string)x.Evaluate(null);
|
||||||
if (o is IExpression x)
|
|
||||||
{
|
|
||||||
return (string) x.Evaluate(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (string) o;
|
return (string)o;
|
||||||
})
|
})
|
||||||
.ToHashSet();
|
.ToHashSet();
|
||||||
var actualKeys = mapping.First().PossibleTags().Keys;
|
var actualKeys = mapping.First().PossibleTags().Keys;
|
||||||
var missingInOrder = actualKeys.Where(key => !expectedKeys.Contains(key)).ToList();
|
var missingInOrder = actualKeys.Where(key => !expectedKeys.Contains(key)).ToList();
|
||||||
var missingInMapping = expectedKeys.Where(key => !actualKeys.Contains(key)).ToList();
|
var missingInMapping = expectedKeys.Where(key => !actualKeys.Contains(key)).ToList();
|
||||||
if (missingInOrder.Any() || missingInMapping.Any())
|
if (missingInOrder.Any() || missingInMapping.Any()) {
|
||||||
{
|
|
||||||
var missingInOrderMsg = "";
|
var missingInOrderMsg = "";
|
||||||
if (missingInOrder.Any())
|
if (missingInOrder.Any()) {
|
||||||
{
|
|
||||||
missingInOrderMsg = $"The order misses keys {string.Join(",", missingInOrder)}\n";
|
missingInOrderMsg = $"The order misses keys {string.Join(",", missingInOrder)}\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
var missingInMappingMsg = "";
|
var missingInMappingMsg = "";
|
||||||
if (missingInMapping.Any())
|
if (missingInMapping.Any()) {
|
||||||
{
|
|
||||||
missingInMappingMsg =
|
missingInMappingMsg =
|
||||||
$"The mapping misses mappings for keys {string.Join(", ", missingInMapping)}\n";
|
$"The mapping misses mappings for keys {string.Join(", ", missingInMapping)}\n";
|
||||||
}
|
}
|
||||||
|
@ -347,49 +309,95 @@ namespace AspectedRouting.Language
|
||||||
public static Dictionary<string, HashSet<string>> PossibleTags(this IEnumerable<IExpression> exprs)
|
public static Dictionary<string, HashSet<string>> PossibleTags(this IEnumerable<IExpression> exprs)
|
||||||
{
|
{
|
||||||
var usedTags = new Dictionary<string, HashSet<string>>();
|
var usedTags = new Dictionary<string, HashSet<string>>();
|
||||||
foreach (var expr in exprs)
|
foreach (var expr in exprs) {
|
||||||
{
|
|
||||||
var possible = expr.PossibleTags();
|
var possible = expr.PossibleTags();
|
||||||
if (possible == null)
|
if (possible == null) {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var (key, values) in possible)
|
foreach (var (key, values) in possible) {
|
||||||
{
|
if (!usedTags.TryGetValue(key, out var collection)) {
|
||||||
if (!usedTags.TryGetValue(key, out var collection))
|
|
||||||
{
|
|
||||||
// This is the first time we see this collection
|
// This is the first time we see this collection
|
||||||
collection = new HashSet<string>();
|
collection = new HashSet<string>();
|
||||||
usedTags[key] = collection;
|
usedTags[key] = collection;
|
||||||
|
|
||||||
}
|
}
|
||||||
foreach (var v in values)
|
|
||||||
{
|
foreach (var v in values) {
|
||||||
collection.Add(v);
|
collection.Add(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values.Count == 0) {
|
if (values.Count == 0) {
|
||||||
collection.Add("*");
|
collection.Add("*");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return usedTags;
|
return usedTags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Dictionary<string, HashSet<string>> PossibleTagsRecursive(this IEnumerable<IExpression> exprs, Context c)
|
||||||
|
{ var usedTags = new Dictionary<string, HashSet<string>>();
|
||||||
|
foreach (var e in exprs) {
|
||||||
|
var possibleTags = e.PossibleTagsRecursive(c);
|
||||||
|
|
||||||
|
if (possibleTags != null) {
|
||||||
|
foreach (var tag in possibleTags) {
|
||||||
|
usedTags[tag.Key] = tag.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return usedTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<string, HashSet<string>> PossibleTagsRecursive(this IExpression e, Context c)
|
||||||
|
{
|
||||||
|
var allExpr = new List<IExpression>();
|
||||||
|
var queue = new Queue<IExpression>();
|
||||||
|
queue.Enqueue(e);
|
||||||
|
do {
|
||||||
|
var next = queue.Dequeue();
|
||||||
|
allExpr.Add(next);
|
||||||
|
next.Visit(expression => {
|
||||||
|
if (expression is FunctionCall fc) {
|
||||||
|
var called = c.GetFunction(fc.CalledFunctionName);
|
||||||
|
queue.Enqueue(called);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
} while (queue.Any());
|
||||||
|
|
||||||
|
var result = new Dictionary<string, HashSet<string>>();
|
||||||
|
|
||||||
|
foreach (var expression in allExpr) {
|
||||||
|
var subTags = expression.PossibleTags();
|
||||||
|
if (subTags == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var kv in subTags) {
|
||||||
|
if (!result.ContainsKey(kv.Key)) {
|
||||||
|
result[kv.Key] = new HashSet<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var val in kv.Value) {
|
||||||
|
result[kv.Key].Add(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns which tags are used in this calculation
|
/// Returns which tags are used in this calculation
|
||||||
///
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="e"></param>
|
/// <param name="e"></param>
|
||||||
/// <returns>A dictionary containing all possible values. An entry with an empty list indicates a wildcard</returns>
|
/// <returns>A dictionary containing all possible values. An entry with an empty list indicates a wildcard</returns>
|
||||||
public static Dictionary<string, HashSet<string>> PossibleTags(this IExpression e)
|
public static Dictionary<string, HashSet<string>> PossibleTags(this IExpression e)
|
||||||
{
|
{
|
||||||
var mappings = new List<Mapping>();
|
var mappings = new List<Mapping>();
|
||||||
e.Visit(x =>
|
e.Visit(x => {
|
||||||
{
|
|
||||||
/*
|
/*
|
||||||
var networkMapping = new List<IExpression>();
|
var networkMapping = new List<IExpression>();
|
||||||
if (Deconstruct.UnApply(
|
if (Deconstruct.UnApply(
|
||||||
|
@ -402,16 +410,14 @@ namespace AspectedRouting.Language
|
||||||
return false;
|
return false;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
if (x is Mapping m)
|
if (x is Mapping m) {
|
||||||
{
|
|
||||||
mappings.Add(m);
|
mappings.Add(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (mappings.Count == 0)
|
if (mappings.Count == 0) {
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,13 +425,10 @@ namespace AspectedRouting.Language
|
||||||
var rootMapping = mappings[0];
|
var rootMapping = mappings[0];
|
||||||
var result = new Dictionary<string, HashSet<string>>();
|
var result = new Dictionary<string, HashSet<string>>();
|
||||||
|
|
||||||
foreach (var (key, expr) in rootMapping.StringToResultFunctions)
|
foreach (var (key, expr) in rootMapping.StringToResultFunctions) {
|
||||||
{
|
|
||||||
var values = new List<string>();
|
var values = new List<string>();
|
||||||
expr.Visit(x =>
|
expr.Visit(x => {
|
||||||
{
|
if (x is Mapping m) {
|
||||||
if (x is Mapping m)
|
|
||||||
{
|
|
||||||
values.AddRange(m.StringToResultFunctions.Keys);
|
values.AddRange(m.StringToResultFunctions.Keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,19 +454,16 @@ namespace AspectedRouting.Language
|
||||||
|
|
||||||
void HandleExpression(IExpression e, string calledIn)
|
void HandleExpression(IExpression e, string calledIn)
|
||||||
{
|
{
|
||||||
e.Visit(f =>
|
e.Visit(f => {
|
||||||
{
|
|
||||||
var mapping = new List<IExpression>();
|
var mapping = new List<IExpression>();
|
||||||
if (Deconstruct.UnApply(Deconstruct.IsFunc(Funcs.MemberOf),
|
if (Deconstruct.UnApply(Deconstruct.IsFunc(Funcs.MemberOf),
|
||||||
Deconstruct.Assign(mapping)
|
Deconstruct.Assign(mapping)
|
||||||
).Invoke(f))
|
).Invoke(f)) {
|
||||||
{
|
|
||||||
memberships.Add(calledIn, mapping.First());
|
memberships.Add(calledIn, mapping.First());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f is FunctionCall fc)
|
if (f is FunctionCall fc) {
|
||||||
{
|
|
||||||
calledFunctionQueue.Enqueue(fc.CalledFunctionName);
|
calledFunctionQueue.Enqueue(fc.CalledFunctionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,15 +471,12 @@ namespace AspectedRouting.Language
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var e in calledFunctions)
|
foreach (var e in calledFunctions) {
|
||||||
{
|
|
||||||
HandleExpression(e, "profile_root");
|
HandleExpression(e, "profile_root");
|
||||||
}
|
}
|
||||||
|
|
||||||
while (calledFunctionQueue.TryDequeue(out var functionName))
|
while (calledFunctionQueue.TryDequeue(out var functionName)) {
|
||||||
{
|
if (alreadyAnalysedFunctions.Contains(functionName)) {
|
||||||
if (alreadyAnalysedFunctions.Contains(functionName))
|
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,14 +96,8 @@ namespace AspectedRouting.Language.Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ProfileResult Run(Context c, string behaviour, Dictionary<string, string> tags)
|
public Dictionary<string, IExpression> ParametersFor(string behaviour)
|
||||||
{
|
{
|
||||||
if (!Behaviours.ContainsKey(behaviour))
|
|
||||||
{
|
|
||||||
throw new ArgumentException(
|
|
||||||
$"Profile {Name} does not contain the behaviour {behaviour}\nTry one of {string.Join(",", Behaviours.Keys)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
var parameters = new Dictionary<string, IExpression>();
|
var parameters = new Dictionary<string, IExpression>();
|
||||||
|
|
||||||
foreach (var (k, v) in DefaultParameters)
|
foreach (var (k, v) in DefaultParameters)
|
||||||
|
@ -116,7 +110,18 @@ namespace AspectedRouting.Language.Expression
|
||||||
parameters[k.TrimStart('#')] = v;
|
parameters[k.TrimStart('#')] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
c = c.WithParameters(parameters)
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProfileResult Run(Context c, string behaviour, Dictionary<string, string> tags)
|
||||||
|
{
|
||||||
|
if (!Behaviours.ContainsKey(behaviour))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(
|
||||||
|
$"Profile {Name} does not contain the behaviour {behaviour}\nTry one of {string.Join(",", Behaviours.Keys)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
c = c.WithParameters(ParametersFor(behaviour))
|
||||||
.WithAspectName(this.Name);
|
.WithAspectName(this.Name);
|
||||||
tags = new Dictionary<string, string>(tags);
|
tags = new Dictionary<string, string>(tags);
|
||||||
var canAccess = Access.Run(c, tags);
|
var canAccess = Access.Run(c, tags);
|
||||||
|
|
|
@ -6,6 +6,7 @@ using AspectedRouting.IO;
|
||||||
using AspectedRouting.IO.itinero1;
|
using AspectedRouting.IO.itinero1;
|
||||||
using AspectedRouting.IO.itinero2;
|
using AspectedRouting.IO.itinero2;
|
||||||
using AspectedRouting.IO.jsonParser;
|
using AspectedRouting.IO.jsonParser;
|
||||||
|
using AspectedRouting.IO.md;
|
||||||
using AspectedRouting.Language;
|
using AspectedRouting.Language;
|
||||||
using AspectedRouting.Language.Expression;
|
using AspectedRouting.Language.Expression;
|
||||||
using AspectedRouting.Tests;
|
using AspectedRouting.Tests;
|
||||||
|
@ -18,19 +19,21 @@ namespace AspectedRouting
|
||||||
this IEnumerable<string> jsonFileNames, List<string> testFileNames, Context context)
|
this IEnumerable<string> jsonFileNames, List<string> testFileNames, Context context)
|
||||||
{
|
{
|
||||||
var aspects = new List<(AspectMetadata aspect, AspectTestSuite tests)>();
|
var aspects = new List<(AspectMetadata aspect, AspectTestSuite tests)>();
|
||||||
foreach (var file in jsonFileNames)
|
foreach (var file in jsonFileNames) {
|
||||||
{
|
|
||||||
var fi = new FileInfo(file);
|
var fi = new FileInfo(file);
|
||||||
|
|
||||||
var aspect = JsonParser.AspectFromJson(context, File.ReadAllText(file), fi.Name);
|
var aspect = JsonParser.AspectFromJson(context, File.ReadAllText(file), fi.Name);
|
||||||
if (aspect == null) continue;
|
if (aspect == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var testName = aspect.Name + ".test.csv";
|
var testName = aspect.Name + ".test.csv";
|
||||||
var testPath = testFileNames.FindTest(testName);
|
var testPath = testFileNames.FindTest(testName);
|
||||||
AspectTestSuite tests = null;
|
AspectTestSuite tests = null;
|
||||||
if (!string.IsNullOrEmpty(testPath) && File.Exists(testPath))
|
if (!string.IsNullOrEmpty(testPath) && File.Exists(testPath)) {
|
||||||
tests = AspectTestSuite.FromString(aspect, File.ReadAllText(testPath));
|
tests = AspectTestSuite.FromString(aspect, File.ReadAllText(testPath));
|
||||||
|
}
|
||||||
|
|
||||||
aspects.Add((aspect, tests));
|
aspects.Add((aspect, tests));
|
||||||
}
|
}
|
||||||
|
@ -41,10 +44,13 @@ namespace AspectedRouting
|
||||||
private static string FindTest(this IEnumerable<string> testFileNames, string testName)
|
private static string FindTest(this IEnumerable<string> testFileNames, string testName)
|
||||||
{
|
{
|
||||||
var testPaths = testFileNames.Where(nm => nm.EndsWith(testName)).ToList();
|
var testPaths = testFileNames.Where(nm => nm.EndsWith(testName)).ToList();
|
||||||
if (testPaths.Count > 1)
|
if (testPaths.Count > 1) {
|
||||||
Console.WriteLine("[WARNING] Multiple tests found for " + testName + ", using only one arbitrarily");
|
Console.WriteLine("[WARNING] Multiple tests found for " + testName + ", using only one arbitrarily");
|
||||||
|
}
|
||||||
|
|
||||||
if (testPaths.Count > 0) return testPaths.First();
|
if (testPaths.Count > 0) {
|
||||||
|
return testPaths.First();
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -54,39 +60,37 @@ namespace AspectedRouting
|
||||||
IEnumerable<string> jsonFiles, IReadOnlyCollection<string> testFiles, Context context, DateTime lastChange)
|
IEnumerable<string> jsonFiles, IReadOnlyCollection<string> testFiles, Context context, DateTime lastChange)
|
||||||
{
|
{
|
||||||
var result = new List<(ProfileMetaData profile, List<BehaviourTestSuite> profileTests)>();
|
var result = new List<(ProfileMetaData profile, List<BehaviourTestSuite> profileTests)>();
|
||||||
foreach (var jsonFile in jsonFiles)
|
foreach (var jsonFile in jsonFiles) {
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
var profile =
|
var profile =
|
||||||
JsonParser.ProfileFromJson(context, File.ReadAllText(jsonFile), new FileInfo(jsonFile),
|
JsonParser.ProfileFromJson(context, File.ReadAllText(jsonFile), new FileInfo(jsonFile),
|
||||||
lastChange);
|
lastChange);
|
||||||
if (profile == null) continue;
|
if (profile == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
profile.SanityCheckProfile(context);
|
profile.SanityCheckProfile(context);
|
||||||
|
|
||||||
var profileTests = new List<BehaviourTestSuite>();
|
var profileTests = new List<BehaviourTestSuite>();
|
||||||
foreach (var behaviourName in profile.Behaviours.Keys)
|
foreach (var behaviourName in profile.Behaviours.Keys) {
|
||||||
{
|
|
||||||
var path = testFiles.FindTest($"{profile.Name}.{behaviourName}.behaviour_test.csv");
|
var path = testFiles.FindTest($"{profile.Name}.{behaviourName}.behaviour_test.csv");
|
||||||
if (path != null && File.Exists(path))
|
if (path != null && File.Exists(path)) {
|
||||||
{
|
|
||||||
var test = BehaviourTestSuite.FromString(context, profile, behaviourName,
|
var test = BehaviourTestSuite.FromString(context, profile, behaviourName,
|
||||||
File.ReadAllText(path));
|
File.ReadAllText(path));
|
||||||
profileTests.Add(test);
|
profileTests.Add(test);
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
Console.WriteLine($"[{profile.Name}] WARNING: no test found for behaviour {behaviourName}");
|
Console.WriteLine($"[{profile.Name}] WARNING: no test found for behaviour {behaviourName}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Add((profile, profileTests));
|
result.Add((profile, profileTests));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e) {
|
||||||
{
|
|
||||||
// PrintError(jsonFile, e);
|
// PrintError(jsonFile, e);
|
||||||
throw new Exception("In the file " + jsonFile, e);
|
throw new Exception("In the file " + jsonFile, e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -95,43 +99,44 @@ namespace AspectedRouting
|
||||||
{
|
{
|
||||||
var profile = profiles["emergency_vehicle"];
|
var profile = profiles["emergency_vehicle"];
|
||||||
var behaviour = profile.Behaviours.Keys.First();
|
var behaviour = profile.Behaviours.Keys.First();
|
||||||
do
|
do {
|
||||||
{
|
|
||||||
Console.Write(profile.Name + "." + behaviour + " > ");
|
Console.Write(profile.Name + "." + behaviour + " > ");
|
||||||
var read = Console.ReadLine();
|
var read = Console.ReadLine();
|
||||||
if (read == null) return; // End of stream has been reached
|
if (read == null) {
|
||||||
|
return; // End of stream has been reached
|
||||||
|
}
|
||||||
|
|
||||||
if (read == "")
|
if (read == "") {
|
||||||
{
|
|
||||||
Console.WriteLine("looƆ sᴉ dɐWʇǝǝɹʇSuǝdO");
|
Console.WriteLine("looƆ sᴉ dɐWʇǝǝɹʇSuǝdO");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read.Equals("quit")) return;
|
if (read.Equals("quit")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (read.Equals("help")) {
|
if (read.Equals("help")) {
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
Utils.Lines("select <behaviourName> to change behaviour or <vehicle.behaviourName> to change vehicle",
|
Utils.Lines(
|
||||||
|
"select <behaviourName> to change behaviour or <vehicle.behaviourName> to change vehicle",
|
||||||
""));
|
""));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read.Equals("clear"))
|
if (read.Equals("clear")) {
|
||||||
{
|
for (var i = 0; i < 80; i++) {
|
||||||
for (var i = 0; i < 80; i++) Console.WriteLine();
|
Console.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read.StartsWith("select"))
|
if (read.StartsWith("select")) {
|
||||||
{
|
|
||||||
var beh = read.Substring("select".Length + 1).Trim();
|
var beh = read.Substring("select".Length + 1).Trim();
|
||||||
|
|
||||||
if (beh.Contains("."))
|
if (beh.Contains(".")) {
|
||||||
{
|
|
||||||
var profileName = beh.Split(".")[0];
|
var profileName = beh.Split(".")[0];
|
||||||
if (!profiles.TryGetValue(profileName, out var newProfile))
|
if (!profiles.TryGetValue(profileName, out var newProfile)) {
|
||||||
{
|
|
||||||
Console.Error.WriteLine("Profile " + profileName + " not found, ignoring");
|
Console.Error.WriteLine("Profile " + profileName + " not found, ignoring");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -140,13 +145,11 @@ namespace AspectedRouting
|
||||||
beh = beh.Substring(beh.IndexOf(".") + 1);
|
beh = beh.Substring(beh.IndexOf(".") + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (profile.Behaviours.ContainsKey(beh))
|
if (profile.Behaviours.ContainsKey(beh)) {
|
||||||
{
|
|
||||||
behaviour = beh;
|
behaviour = beh;
|
||||||
Console.WriteLine("Switched to " + beh);
|
Console.WriteLine("Switched to " + beh);
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
Console.WriteLine("Behaviour not found. Known behaviours are:\n " +
|
Console.WriteLine("Behaviour not found. Known behaviours are:\n " +
|
||||||
string.Join("\n ", profile.Behaviours.Keys));
|
string.Join("\n ", profile.Behaviours.Keys));
|
||||||
}
|
}
|
||||||
|
@ -157,9 +160,10 @@ namespace AspectedRouting
|
||||||
|
|
||||||
var tagsRaw = read.Split(";").Select(s => s.Trim());
|
var tagsRaw = read.Split(";").Select(s => s.Trim());
|
||||||
var tags = new Dictionary<string, string>();
|
var tags = new Dictionary<string, string>();
|
||||||
foreach (var str in tagsRaw)
|
foreach (var str in tagsRaw) {
|
||||||
{
|
if (str == "") {
|
||||||
if (str == "") continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var strSplit = str.Split("=");
|
var strSplit = str.Split("=");
|
||||||
var k = strSplit[0].Trim();
|
var k = strSplit[0].Trim();
|
||||||
|
@ -167,13 +171,11 @@ namespace AspectedRouting
|
||||||
tags[k] = v;
|
tags[k] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
var result = profile.Run(c, behaviour, tags);
|
var result = profile.Run(c, behaviour, tags);
|
||||||
Console.WriteLine(result);
|
Console.WriteLine(result);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e) {
|
||||||
{
|
|
||||||
Console.WriteLine(e);
|
Console.WriteLine(e);
|
||||||
Console.WriteLine(e.Message);
|
Console.WriteLine(e.Message);
|
||||||
}
|
}
|
||||||
|
@ -183,8 +185,7 @@ namespace AspectedRouting
|
||||||
private static void PrintError(string file, Exception exception)
|
private static void PrintError(string file, Exception exception)
|
||||||
{
|
{
|
||||||
var msg = exception.Message;
|
var msg = exception.Message;
|
||||||
while (exception.InnerException != null)
|
while (exception.InnerException != null) {
|
||||||
{
|
|
||||||
exception = exception.InnerException;
|
exception = exception.InnerException;
|
||||||
msg += "\n " + exception.Message;
|
msg += "\n " + exception.Message;
|
||||||
}
|
}
|
||||||
|
@ -195,10 +196,11 @@ namespace AspectedRouting
|
||||||
private static void PrintUsedTags(ProfileMetaData profile, Context context)
|
private static void PrintUsedTags(ProfileMetaData profile, Context context)
|
||||||
{
|
{
|
||||||
Console.WriteLine("\n\n\n---------- " + profile.Name + " --------------");
|
Console.WriteLine("\n\n\n---------- " + profile.Name + " --------------");
|
||||||
foreach (var (key, values) in profile.AllExpressions(context).PossibleTags())
|
foreach (var (key, values) in profile.AllExpressions(context).PossibleTags()) {
|
||||||
{
|
|
||||||
var vs = "*";
|
var vs = "*";
|
||||||
if (values.Any()) vs = string.Join(", ", values);
|
if (values.Any()) {
|
||||||
|
vs = string.Join(", ", values);
|
||||||
|
}
|
||||||
|
|
||||||
Console.WriteLine(key + ": " + vs);
|
Console.WriteLine(key + ": " + vs);
|
||||||
}
|
}
|
||||||
|
@ -209,18 +211,23 @@ namespace AspectedRouting
|
||||||
private static void Main(string[] args)
|
private static void Main(string[] args)
|
||||||
{
|
{
|
||||||
var errMessage = MainWithError(args);
|
var errMessage = MainWithError(args);
|
||||||
if (errMessage != null) Console.WriteLine(errMessage);
|
if (errMessage != null) {
|
||||||
|
Console.WriteLine(errMessage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string MainWithError(string[] args)
|
public static string MainWithError(string[] args)
|
||||||
{
|
{
|
||||||
if (args.Length < 2)
|
if (args.Length < 2) {
|
||||||
return "Usage: <directory where all aspects and profiles can be found> <outputdirectory>";
|
return "Usage: <directory where all aspects and profiles can be found> <outputdirectory>";
|
||||||
|
}
|
||||||
|
|
||||||
var inputDir = args[0];
|
var inputDir = args[0];
|
||||||
var outputDir = args[1];
|
var outputDir = args[1];
|
||||||
|
|
||||||
if (!Directory.Exists(outputDir)) Directory.CreateDirectory(outputDir);
|
if (!Directory.Exists(outputDir)) {
|
||||||
|
Directory.CreateDirectory(outputDir);
|
||||||
|
}
|
||||||
|
|
||||||
MdPrinter.GenerateHelpText(outputDir + "helpText.md");
|
MdPrinter.GenerateHelpText(outputDir + "helpText.md");
|
||||||
|
|
||||||
|
@ -232,9 +239,10 @@ namespace AspectedRouting
|
||||||
.ToList();
|
.ToList();
|
||||||
tests.Sort();
|
tests.Sort();
|
||||||
|
|
||||||
foreach (var test in tests)
|
foreach (var test in tests) {
|
||||||
{
|
if (test.EndsWith(".test.csv") || test.EndsWith(".behaviour_test.csv")) {
|
||||||
if (test.EndsWith(".test.csv") || test.EndsWith(".behaviour_test.csv")) continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
throw new ArgumentException(
|
throw new ArgumentException(
|
||||||
$"Invalid name for csv file ${test}, should end with either '.behaviour_test.csv' or '.test.csv'");
|
$"Invalid name for csv file ${test}, should end with either '.behaviour_test.csv' or '.test.csv'");
|
||||||
|
@ -244,14 +252,14 @@ namespace AspectedRouting
|
||||||
|
|
||||||
var aspects = ParseAspects(files, tests, context);
|
var aspects = ParseAspects(files, tests, context);
|
||||||
|
|
||||||
foreach (var (aspect, _) in aspects) context.AddFunction(aspect.Name, aspect);
|
foreach (var (aspect, _) in aspects) {
|
||||||
|
context.AddFunction(aspect.Name, aspect);
|
||||||
|
}
|
||||||
|
|
||||||
var lastChange = DateTime.UnixEpoch;
|
var lastChange = DateTime.UnixEpoch;
|
||||||
foreach (var file in files)
|
foreach (var file in files) {
|
||||||
{
|
|
||||||
var time = new FileInfo(file).LastWriteTimeUtc;
|
var time = new FileInfo(file).LastWriteTimeUtc;
|
||||||
if (lastChange < time)
|
if (lastChange < time) {
|
||||||
{
|
|
||||||
lastChange = time;
|
lastChange = time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -261,21 +269,34 @@ namespace AspectedRouting
|
||||||
|
|
||||||
// With everything parsed and typechecked, time for tests
|
// With everything parsed and typechecked, time for tests
|
||||||
var testsOk = true;
|
var testsOk = true;
|
||||||
foreach (var (aspect, t) in aspects)
|
foreach (var (aspect, t) in aspects) {
|
||||||
if (t == null)
|
if (t == null) {
|
||||||
Console.WriteLine($"[{aspect.Name}] WARNING: no tests found: please add {aspect.Name}.test.csv");
|
Console.WriteLine($"[{aspect.Name}] WARNING: no tests found: please add {aspect.Name}.test.csv");
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
testsOk &= t.Run();
|
testsOk &= t.Run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
foreach (var (profile, profileTests) in profiles)
|
foreach (var (profile, profileTests) in profiles)
|
||||||
foreach (var test in profileTests)
|
foreach (var test in profileTests) {
|
||||||
testsOk &= test.Run(context);
|
testsOk &= test.Run(context);
|
||||||
|
}
|
||||||
|
|
||||||
if (!testsOk) return "Some tests failed, quitting now without generating output";
|
if (!testsOk) {
|
||||||
|
return "Some tests failed, quitting now without generating output";
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var (profile, profileTests) in profiles)
|
if (!Directory.Exists($"{outputDir}/profile-documentation/")) {
|
||||||
{
|
Directory.CreateDirectory($"{outputDir}/profile-documentation/");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Directory.Exists($"{outputDir}/itinero2/")) {
|
||||||
|
Directory.CreateDirectory($"{outputDir}/itinero2/");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (profile, profileTests) in profiles) {
|
||||||
PrintUsedTags(profile, context);
|
PrintUsedTags(profile, context);
|
||||||
|
|
||||||
var aspectTests = aspects.Select(a => a.tests).ToList();
|
var aspectTests = aspects.Select(a => a.tests).ToList();
|
||||||
|
@ -285,8 +306,23 @@ namespace AspectedRouting
|
||||||
).ToLua();
|
).ToLua();
|
||||||
File.WriteAllText(outputDir + "/" + profile.Name + ".lua", luaProfile);
|
File.WriteAllText(outputDir + "/" + profile.Name + ".lua", luaProfile);
|
||||||
|
|
||||||
foreach (var (behaviourName, _) in profile.Behaviours)
|
var profileMd = new MarkDownSection();
|
||||||
{
|
profileMd.AddTitle(profile.Name, 1);
|
||||||
|
|
||||||
|
profileMd.Add(profile.Description);
|
||||||
|
profileMd.AddTitle("Default parameters", 4);
|
||||||
|
profileMd.Add("| name | value | ", "| ---- | ---- | ",
|
||||||
|
string.Join("\n",
|
||||||
|
profile.DefaultParameters.Select(delegate(KeyValuePair<string, IExpression> kv) {
|
||||||
|
var v = kv.Value.Evaluate(context);
|
||||||
|
if (!(v is string || v is int || v is double)) {
|
||||||
|
v = "_special value_";
|
||||||
|
}
|
||||||
|
return $" | {kv.Key} | {v} |";
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach (var (behaviourName, vars) in profile.Behaviours) {
|
||||||
var lua2behaviour = new LuaPrinter2(
|
var lua2behaviour = new LuaPrinter2(
|
||||||
profile,
|
profile,
|
||||||
behaviourName,
|
behaviourName,
|
||||||
|
@ -295,12 +331,24 @@ namespace AspectedRouting
|
||||||
profileTests.Where(testsSuite => testsSuite.BehaviourName == behaviourName),
|
profileTests.Where(testsSuite => testsSuite.BehaviourName == behaviourName),
|
||||||
lastChange
|
lastChange
|
||||||
).ToLua();
|
).ToLua();
|
||||||
if (!Directory.Exists($"{outputDir}/itinero2/"))
|
|
||||||
Directory.CreateDirectory($"{outputDir}/itinero2/");
|
|
||||||
File.WriteAllText(
|
File.WriteAllText(
|
||||||
$"{outputDir}/itinero2/{profile.Name}.{behaviourName}.lua",
|
$"{outputDir}/itinero2/{profile.Name}.{behaviourName}.lua",
|
||||||
lua2behaviour);
|
lua2behaviour);
|
||||||
|
|
||||||
|
var behaviourMd = new ProfileToMD(profile, behaviourName, context);
|
||||||
|
|
||||||
|
File.WriteAllText(
|
||||||
|
$"{outputDir}/profile-documentation/{profile.Name}.{behaviourName}.md",
|
||||||
|
behaviourMd.ToString());
|
||||||
|
profileMd.AddTitle($"[{behaviourName}](./{behaviourName}.md)", 2);
|
||||||
|
profileMd.Add(vars["description"].Evaluate(context).ToString());
|
||||||
|
profileMd.Add(behaviourMd.MainFormula());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File.WriteAllText(
|
||||||
|
$"{outputDir}/profile-documentation/{profile.Name}.md",
|
||||||
|
profileMd.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
File.WriteAllText($"{outputDir}/ProfileMetadata.json",
|
File.WriteAllText($"{outputDir}/ProfileMetadata.json",
|
||||||
|
@ -310,12 +358,16 @@ namespace AspectedRouting
|
||||||
Utils.GenerateTagsOverview(profiles.Select(p => p.profile), context)
|
Utils.GenerateTagsOverview(profiles.Select(p => p.profile), context)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!args.Contains("--no-repl"))
|
|
||||||
|
if (!args.Contains("--no-repl")) {
|
||||||
Repl(context, profiles
|
Repl(context, profiles
|
||||||
.Select(p => p.profile)
|
.Select(p => p.profile)
|
||||||
.ToDictionary(p => p.Name, p => p));
|
.ToDictionary(p => p.Name, p => p));
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
Console.WriteLine("Not starting REPL as --no-repl is specified");
|
Console.WriteLine("Not starting REPL as --no-repl is specified");
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,5 +42,13 @@ namespace AspectedRouting.Tests
|
||||||
"because \n "+str(PriorityExplanation)
|
"because \n "+str(PriorityExplanation)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
if (!(obj is ProfileResult other)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return other.Access == this.Access && other.Oneway == this.Oneway && other.Priority == this.Priority && other.Speed == this.Speed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue