Refactoring of lua printing, adds generation of itinero 2.0 profiles
This commit is contained in:
parent
8bf77cbc69
commit
5268d4ee81
25 changed files with 1191 additions and 580 deletions
|
@ -12,6 +12,9 @@
|
|||
<None Update="IO\lua\**">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="IO\lua\unitTestProfile2.lua">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
using System.Collections.Generic;
|
||||
using AspectedRouting.Language;
|
||||
using AspectedRouting.Language.Expression;
|
||||
|
||||
namespace AspectedRouting.IO.itinero1
|
||||
{
|
||||
public class LuaParameterPrinter
|
||||
{
|
||||
private ProfileMetaData _profile;
|
||||
private LuaSkeleton.LuaSkeleton _skeleton;
|
||||
|
||||
public LuaParameterPrinter(ProfileMetaData profile, LuaSkeleton.LuaSkeleton skeleton)
|
||||
{
|
||||
_profile = profile;
|
||||
_skeleton = skeleton;
|
||||
}
|
||||
|
||||
|
||||
public string GenerateDefaultParameters()
|
||||
{
|
||||
var impl = new List<string>()
|
||||
{
|
||||
"function default_parameters()",
|
||||
" local parameters = {}",
|
||||
DeclareParametersFor(_profile.DefaultParameters),
|
||||
" return parameters",
|
||||
"end"
|
||||
};
|
||||
|
||||
return string.Join("\n",impl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a piece of code of the following format:
|
||||
///
|
||||
/// parameters["x"] = a;
|
||||
/// parameters["y"] = b:
|
||||
/// ...
|
||||
///
|
||||
/// Where x=a and y=b are defined in the profile
|
||||
///
|
||||
/// Dependencies are added.
|
||||
///
|
||||
/// Note that the caller should still add `local paramaters = default_parameters()`
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="behaviour"></param>
|
||||
/// <returns></returns>
|
||||
public string DeclareParametersFor(Dictionary<string, IExpression> subParams)
|
||||
{
|
||||
var impl = "";
|
||||
foreach (var (paramName, value) in subParams)
|
||||
{
|
||||
if (paramName.Equals("description"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
impl += $" parameters.{paramName.TrimStart('#').AsLuaIdentifier()} = {_skeleton.ToLua(value)}\n";
|
||||
}
|
||||
|
||||
return impl;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,18 +2,20 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using AspectedRouting.IO.itinero1;
|
||||
using AspectedRouting.Language;
|
||||
using AspectedRouting.Language.Expression;
|
||||
using AspectedRouting.Language.Functions;
|
||||
using AspectedRouting.Language.Typ;
|
||||
using static AspectedRouting.Language.Deconstruct;
|
||||
|
||||
namespace AspectedRouting.IO.itinero1
|
||||
namespace AspectedRouting.IO.LuaSkeleton
|
||||
{
|
||||
public partial class LuaPrinter
|
||||
public partial class LuaSkeleton
|
||||
{
|
||||
private string ToLua(IExpression bare, string key = "nil")
|
||||
internal string ToLua(IExpression bare, string key = "nil")
|
||||
{
|
||||
|
||||
var collectedMapping = new List<IExpression>();
|
||||
var order = new List<IExpression>();
|
||||
|
||||
|
@ -63,6 +65,9 @@ namespace AspectedRouting.IO.itinero1
|
|||
var func = new List<IExpression>();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (
|
||||
UnApply(
|
||||
UnApply(IsFunc(Funcs.Dot), Assign(func)),
|
||||
|
@ -155,7 +160,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
}
|
||||
|
||||
AddFunction(called);
|
||||
return $"{fc.CalledFunctionName.FunctionName()}(parameters, tags, result)";
|
||||
return $"{fc.CalledFunctionName.AsLuaIdentifier()}(parameters, tags, result)";
|
||||
case Constant c:
|
||||
return ConstantToLua(c);
|
||||
case Mapping m:
|
||||
|
@ -184,7 +189,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
return ToLua(collected.First(), key);
|
||||
|
||||
case Parameter p:
|
||||
return $"parameters[\"{p.ParamName.FunctionName()}\"]";
|
||||
return $"parameters[\"{p.ParamName.AsLuaIdentifier()}\"]";
|
||||
default:
|
||||
throw new Exception("Could not convert " + bare + " to a lua expression");
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.IO.itinero1;
|
||||
using AspectedRouting.Language;
|
||||
using AspectedRouting.Language.Expression;
|
||||
|
||||
namespace AspectedRouting.IO.itinero1
|
||||
namespace AspectedRouting.IO.LuaSkeleton
|
||||
{
|
||||
public partial class LuaPrinter
|
||||
public partial class LuaSkeleton
|
||||
{
|
||||
private readonly HashSet<string> _alreadyAddedFunctions = new HashSet<string>();
|
||||
|
||||
public void AddFunction(AspectMetadata meta)
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
{
|
||||
if (e is Function f && f.Name.Equals(Funcs.MemberOf.Name))
|
||||
{
|
||||
funcNameDeclaration = $"\n local funcName = \"{meta.Name.FunctionName()}\"";
|
||||
funcNameDeclaration = $"\n local funcName = \"{meta.Name.AsLuaIdentifier()}\"";
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -49,16 +49,12 @@ namespace AspectedRouting.IO.itinero1
|
|||
"Number of combintations: " + numberOfCombinations,
|
||||
"Returns values: ",
|
||||
"]]",
|
||||
"function " + meta.Name.FunctionName() + "(parameters, tags, result)" + funcNameDeclaration,
|
||||
"function " + meta.Name.AsLuaIdentifier() + "(parameters, tags, result)" + funcNameDeclaration,
|
||||
" return " + ToLua(meta.ExpressionImplementation),
|
||||
"end"
|
||||
);
|
||||
|
||||
_code.Add(impl);
|
||||
foreach (var k in possibleTags.Keys)
|
||||
{
|
||||
_neededKeys.Add(k); // To generate a whitelist of OSM-keys that should be kept
|
||||
}
|
||||
_functionImplementations.Add(impl);
|
||||
}
|
||||
}
|
||||
}
|
78
AspectedRouting/IO/LuaSkeleton/LuaSkeleton.cs
Normal file
78
AspectedRouting/IO/LuaSkeleton/LuaSkeleton.cs
Normal file
|
@ -0,0 +1,78 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using AspectedRouting.Language;
|
||||
|
||||
namespace AspectedRouting.IO.LuaSkeleton
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The 'LuaSkeleton' is a class which is used in Lua generation of profiles.
|
||||
///
|
||||
/// The lua skeleton basically keeps track of dependencies, and added functions.
|
||||
/// Once done, all these can be retrieved as code.
|
||||
///
|
||||
/// E.g. if an expression is turned into lua with 'ToExpression', then the dependencies will be automatically added.
|
||||
///
|
||||
/// </summary>
|
||||
public partial class LuaSkeleton
|
||||
{
|
||||
private readonly Context _context;
|
||||
|
||||
private readonly HashSet<string> _dependencies = new HashSet<string>();
|
||||
private readonly List<string> _functionImplementations = new List<string>();
|
||||
private readonly HashSet<string> _alreadyAddedFunctions = new HashSet<string>();
|
||||
|
||||
public LuaSkeleton(Context context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
internal void AddDep(string name)
|
||||
{
|
||||
_dependencies.Add(name);
|
||||
}
|
||||
|
||||
public List<string> GenerateFunctions()
|
||||
{
|
||||
return _functionImplementations;
|
||||
}
|
||||
|
||||
public void AddDependenciesFor(IExpression e)
|
||||
{
|
||||
var (_, functionNames) = e.InList().DirectlyAndInderectlyCalled(_context);
|
||||
foreach (var functionName in functionNames)
|
||||
{
|
||||
|
||||
if (_context.DefinedFunctions.TryGetValue(functionName, out var aspectemeta))
|
||||
{
|
||||
AddFunction(aspectemeta);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddDep(functionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> GenerateDependencies()
|
||||
{
|
||||
var imps = new List<string>();
|
||||
|
||||
foreach (var name in _dependencies)
|
||||
{
|
||||
var path = $"IO/lua/{name}.lua";
|
||||
if (File.Exists(path))
|
||||
{
|
||||
imps.Add(File.ReadAllText(path));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FileNotFoundException(path);
|
||||
}
|
||||
}
|
||||
|
||||
return imps;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
return "{" + string.Join(", ", contents) + "}";
|
||||
}
|
||||
|
||||
public static string FunctionName(this string s)
|
||||
public static string AsLuaIdentifier(this string s)
|
||||
{
|
||||
return s.Replace("$", "")
|
||||
.Replace("#", "")
|
151
AspectedRouting/IO/LuaSkeleton/LuaTestPrinter.cs
Normal file
151
AspectedRouting/IO/LuaSkeleton/LuaTestPrinter.cs
Normal file
|
@ -0,0 +1,151 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.Tests;
|
||||
|
||||
namespace AspectedRouting.IO.itinero1
|
||||
{
|
||||
public class LuaTestPrinter
|
||||
{
|
||||
private LuaSkeleton.LuaSkeleton _skeleton;
|
||||
|
||||
public LuaTestPrinter(LuaSkeleton.LuaSkeleton skeleton, List<string> unitTestRunners)
|
||||
{
|
||||
_skeleton = skeleton;
|
||||
unitTestRunners.ForEach(_skeleton.AddDep);
|
||||
}
|
||||
|
||||
|
||||
public string GenerateFullTestSuite(List<BehaviourTestSuite> profileTests, List<AspectTestSuite> aspectTests)
|
||||
{
|
||||
|
||||
_skeleton.AddDep("inv");
|
||||
_skeleton.AddDep("double_compare");
|
||||
|
||||
|
||||
var aspectTestSuite =
|
||||
string.Join("\n\n", aspectTests
|
||||
.Where(x => x != null)
|
||||
.Select(
|
||||
GenerateAspectTestSuite
|
||||
));
|
||||
|
||||
var profileTestSuite =
|
||||
string.Join("\n\n", profileTests
|
||||
.Where(x => x != null)
|
||||
.Select(
|
||||
GenerateProfileTestSuite
|
||||
));
|
||||
|
||||
return string.Join("\n\n\n", new List<string>
|
||||
{
|
||||
"function test_all()",
|
||||
" " + aspectTestSuite.Indent(),
|
||||
" -- Behaviour tests --",
|
||||
" " + profileTestSuite.Indent(),
|
||||
"end"
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private string GenerateProfileTestSuite(BehaviourTestSuite testSuite)
|
||||
{
|
||||
return string.Join("\n",
|
||||
testSuite.Tests.Select((test, i) => GenerateProfileUnitTestCall(testSuite, i, test.Item1, test.tags))
|
||||
.ToList());
|
||||
}
|
||||
|
||||
private string GenerateProfileUnitTestCall(BehaviourTestSuite testSuite, int index, ProfileResult expected,
|
||||
Dictionary<string, string> tags)
|
||||
{
|
||||
_skeleton.AddDep("debug_table");
|
||||
var parameters = new Dictionary<string, string>();
|
||||
|
||||
|
||||
var keysToCheck = new List<string>();
|
||||
foreach (var (key, value) in tags)
|
||||
{
|
||||
if (key.StartsWith("#"))
|
||||
{
|
||||
parameters[key.TrimStart('#')] = value;
|
||||
}
|
||||
|
||||
if (key.StartsWith("_relation:"))
|
||||
{
|
||||
keysToCheck.Add(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach (var key in keysToCheck)
|
||||
{
|
||||
var newKey = key.Replace(".", "_");
|
||||
tags[newKey] = tags[key];
|
||||
tags.Remove(key);
|
||||
}
|
||||
|
||||
foreach (var (paramName, _) in parameters)
|
||||
{
|
||||
tags.Remove("#" + paramName);
|
||||
}
|
||||
|
||||
// Generates something like:
|
||||
// function unit_test_profile(profile_function, profile_name, index, expected, tags)
|
||||
return
|
||||
$"unit_test_profile(behaviour_{testSuite.Profile.Name.AsLuaIdentifier()}_{testSuite.BehaviourName.AsLuaIdentifier()}, " +
|
||||
$"\"{testSuite.BehaviourName}\", " +
|
||||
$"{index}, " +
|
||||
$"{{access = \"{D(expected.Access)}\", speed = {expected.Speed}, oneway = \"{D(expected.Oneway)}\", priority = {expected.Priority} }}, " +
|
||||
tags.ToLuaTable() +
|
||||
")";
|
||||
}
|
||||
|
||||
|
||||
private string GenerateAspectTestSuite(AspectTestSuite testSuite)
|
||||
{
|
||||
var fName = testSuite.FunctionToApply.Name;
|
||||
var tests =
|
||||
testSuite.Tests
|
||||
.Select((test, i) => GenerateAspectUnitTestCall(fName, i, test.expected, test.tags))
|
||||
.ToList();
|
||||
return string.Join("\n", tests);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a unit test call
|
||||
/// </summary>
|
||||
private string GenerateAspectUnitTestCall(string functionToApplyName, int index, string expected,
|
||||
Dictionary<string, string> tags)
|
||||
{
|
||||
var parameters = new Dictionary<string, string>();
|
||||
|
||||
foreach (var (key, value) in tags)
|
||||
{
|
||||
if (key.StartsWith("#"))
|
||||
{
|
||||
parameters[key.TrimStart('#')] = value;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (paramName, _) in parameters)
|
||||
{
|
||||
tags.Remove("#" + paramName);
|
||||
}
|
||||
|
||||
_skeleton.AddDep("unitTest");
|
||||
_skeleton.AddDep("debug_table");
|
||||
return
|
||||
$"unit_test({functionToApplyName.AsLuaIdentifier()}, \"{functionToApplyName}\", {index}, \"{expected}\", {parameters.ToLuaTable()}, {tags.ToLuaTable()})";
|
||||
}
|
||||
|
||||
|
||||
private string D(string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s))
|
||||
{
|
||||
return "0";
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace AspectedRouting.IO.itinero1
|
||||
{
|
||||
public partial class LuaPrinter
|
||||
{
|
||||
private readonly HashSet<string> _dependencies = new HashSet<string>();
|
||||
private readonly HashSet<string> _neededKeys = new HashSet<string>();
|
||||
|
||||
private readonly List<string> _code = new List<string>();
|
||||
private readonly List<string> _tests = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// A dictionary containing the implementation of basic functions
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<string> LoadFunctions(IEnumerable<string> names)
|
||||
{
|
||||
var imps = new List<string>();
|
||||
|
||||
foreach (var name in names)
|
||||
{
|
||||
var path = $"IO/lua/{name}.lua";
|
||||
if (File.Exists(path))
|
||||
{
|
||||
imps.Add(File.ReadAllText(path));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FileNotFoundException(path);
|
||||
}
|
||||
}
|
||||
|
||||
return imps;
|
||||
}
|
||||
|
||||
private void AddDep(string name)
|
||||
{
|
||||
_dependencies.Add(name);
|
||||
}
|
||||
|
||||
|
||||
public string ToLua()
|
||||
{
|
||||
var deps = _dependencies.ToList();
|
||||
deps.Add("unitTestProfile");
|
||||
deps.Add("inv");
|
||||
deps.Add("double_compare");
|
||||
deps.Add("spoken_instructions");
|
||||
|
||||
var code = new List<string>();
|
||||
|
||||
code.Add($"-- Itinero 1.0-profile, generated on {DateTime.Now:s}");
|
||||
code.Add("\n\n----------------------------- UTILS ---------------------------");
|
||||
code.AddRange(LoadFunctions(deps).ToList());
|
||||
|
||||
code.Add("\n\n----------------------------- PROFILE ---------------------------");
|
||||
var keys = _neededKeys.Select(key => "\"" + key + "\"");
|
||||
code.Add("\n\nprofile_whitelist = {\n " + string.Join("\n , ", keys) + "}");
|
||||
|
||||
code.AddRange(_code);
|
||||
|
||||
code.Add("\n\n ------------------------------- TESTS -------------------------");
|
||||
|
||||
code.Add("function test_all()");
|
||||
code.Add(string.Join("\n",_tests).Indent());
|
||||
code.Add("end");
|
||||
|
||||
var compatibility = string.Join("\n",
|
||||
|
||||
|
||||
"",
|
||||
"if (itinero == nil) then",
|
||||
" itinero = {}",
|
||||
" itinero.log = print",
|
||||
"",
|
||||
" -- Itinero is not defined -> we are running from a lua interpreter -> the tests are intended",
|
||||
" runTests = true",
|
||||
"",
|
||||
"",
|
||||
"else",
|
||||
" print = itinero.log",
|
||||
"end",
|
||||
"",
|
||||
"test_all()",
|
||||
"if (not failed_tests and not failed_profile_tests) then",
|
||||
" print(\"Tests OK\")",
|
||||
"end"
|
||||
);
|
||||
code.Add(compatibility);
|
||||
|
||||
return string.Join("\n\n\n", code);
|
||||
}
|
||||
}
|
||||
}
|
130
AspectedRouting/IO/itinero1/LuaPrinter1.MainFunction.cs
Normal file
130
AspectedRouting/IO/itinero1/LuaPrinter1.MainFunction.cs
Normal file
|
@ -0,0 +1,130 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.Language;
|
||||
using AspectedRouting.Language.Functions;
|
||||
using AspectedRouting.Language.Typ;
|
||||
|
||||
namespace AspectedRouting.IO.itinero1
|
||||
{
|
||||
public partial class LuaPrinter1
|
||||
{
|
||||
|
||||
|
||||
private string GenerateMainProfileFunction()
|
||||
{
|
||||
var impl = string.Join("\n",
|
||||
"",
|
||||
"",
|
||||
"--[[",
|
||||
_profile.Name,
|
||||
"This is the main function called to calculate the access, oneway and speed.",
|
||||
"Comfort is calculated as well, based on the parameters which are padded in",
|
||||
"",
|
||||
"Created by " + _profile.Author,
|
||||
"Originally defined in " + _profile.Filename,
|
||||
"]]",
|
||||
"function " + _profile.Name + "(parameters, tags, result)",
|
||||
"",
|
||||
" -- initialize the result table on the default values",
|
||||
" result.access = 0",
|
||||
" result.speed = 0",
|
||||
" result.factor = 1",
|
||||
" result.direction = 0",
|
||||
" result.canstop = true",
|
||||
" result.attributes_to_keep = {}",
|
||||
"",
|
||||
" local access = " + _skeleton.ToLua(_profile.Access),
|
||||
" if (access == nil or access == \"no\") then",
|
||||
" return",
|
||||
" end",
|
||||
" tags.access = access",
|
||||
" local oneway = " + _skeleton.ToLua(_profile.Oneway),
|
||||
" tags.oneway = oneway",
|
||||
" local speed = " + _skeleton.ToLua(_profile.Speed),
|
||||
" tags.speed = speed",
|
||||
" local distance = 1 -- the weight per meter for distance travelled is, well, 1m/m",
|
||||
"");
|
||||
|
||||
impl +=
|
||||
"\n local priority = 0\n ";
|
||||
|
||||
foreach (var (parameterName, expression) in _profile.Priority)
|
||||
{
|
||||
var paramInLua = _skeleton.ToLua(new Parameter(parameterName));
|
||||
|
||||
|
||||
var exprInLua = _skeleton.ToLua(expression.Optimize());
|
||||
var subs = new Curry(Typs.Tags, new Var(("a"))).UnificationTable(expression.Types.First());
|
||||
if (subs != null && subs.TryGetValue("$a", out var resultType) &&
|
||||
(resultType.Equals(Typs.Bool) || resultType.Equals(Typs.String)))
|
||||
{
|
||||
_skeleton. AddDep("parse");
|
||||
exprInLua = "parse(" + exprInLua + ")";
|
||||
}
|
||||
|
||||
impl += "\n " + string.Join("\n ",
|
||||
$"if({paramInLua} ~= 0) then",
|
||||
$" priority = priority + {paramInLua} * {exprInLua}",
|
||||
"end"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
impl += string.Join("\n",
|
||||
"",
|
||||
"",
|
||||
" if (priority <= 0) then",
|
||||
" result.access = 0",
|
||||
" return",
|
||||
" end",
|
||||
"",
|
||||
" result.access = 1",
|
||||
" result.speed = speed",
|
||||
" result.factor = 1 / priority",
|
||||
"",
|
||||
" if (oneway == \"both\") then",
|
||||
" result.direction = 0",
|
||||
" elseif (oneway == \"with\") then",
|
||||
" result.direction = 1",
|
||||
" elseif (oneway == \"against\") then",
|
||||
" result.direction = 2",
|
||||
" else",
|
||||
" error(\"Unexpected value for oneway: \"..oneway)",
|
||||
" end",
|
||||
"",
|
||||
"end"
|
||||
);
|
||||
|
||||
return impl;
|
||||
}
|
||||
|
||||
|
||||
private (string functionName, string implementation) GenerateBehaviourFunction(
|
||||
string behaviourName,
|
||||
Dictionary<string, IExpression> behaviourParameters)
|
||||
{
|
||||
var referenceName = _profile.Name + "_" + behaviourName;
|
||||
var functionName = referenceName.AsLuaIdentifier();
|
||||
behaviourParameters.TryGetValue("description", out var description);
|
||||
|
||||
_skeleton.AddDep("remove_relation_prefix");
|
||||
var impl = string.Join("\n",
|
||||
"",
|
||||
"--[[",
|
||||
description,
|
||||
"]]",
|
||||
"function behaviour_" + functionName + "(tags, result)",
|
||||
$" tags = remove_relation_prefix(tags, \"{behaviourName.AsLuaIdentifier()}\")",
|
||||
" local parameters = default_parameters()",
|
||||
" parameters.name = \"" + referenceName + "\"",
|
||||
""
|
||||
);
|
||||
|
||||
impl += _parameterPrinter.DeclareParametersFor(behaviourParameters);
|
||||
|
||||
impl += " " + _profile.Name + "(parameters, tags, result)\n";
|
||||
impl += "end\n";
|
||||
return (functionName, impl);
|
||||
}
|
||||
}
|
||||
}
|
108
AspectedRouting/IO/itinero1/LuaPrinter1.RelationPreprocessor.cs
Normal file
108
AspectedRouting/IO/itinero1/LuaPrinter1.RelationPreprocessor.cs
Normal file
|
@ -0,0 +1,108 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.Language;
|
||||
using AspectedRouting.Language.Expression;
|
||||
|
||||
namespace AspectedRouting.IO.itinero1
|
||||
{
|
||||
public partial class LuaPrinter1
|
||||
{
|
||||
|
||||
private (string implementation, HashSet<string> extraKeys) GenerateMembershipPreprocessor()
|
||||
{
|
||||
// Extra keys are the names of introduced tag-keys, e.g. '_relation:bicycle_fastest:cycle_highway'
|
||||
var extraKeys = new HashSet<string>();
|
||||
var memberships = Analysis.MembershipMappingsFor(_profile, _context);
|
||||
|
||||
foreach (var (calledInFunction, membership) in memberships)
|
||||
{
|
||||
var funcMetaData = new AspectMetadata(
|
||||
membership,
|
||||
"relation_preprocessing_for_" + calledInFunction.AsLuaIdentifier(),
|
||||
"Function preprocessing needed for aspect " + calledInFunction +
|
||||
", called by the relation preprocessor",
|
||||
"Generator", "", "NA"
|
||||
);
|
||||
|
||||
|
||||
_skeleton.AddFunction(funcMetaData);
|
||||
}
|
||||
|
||||
|
||||
var func = new List<string>
|
||||
{
|
||||
"",
|
||||
"",
|
||||
"-- Processes the relation. All tags which are added to result.attributes_to_keep will be copied to 'attributes' of each individual way",
|
||||
"function relation_tag_processor(relation_tags, result)",
|
||||
" local parameters = {}",
|
||||
" local subresult = {}",
|
||||
" local matched = false",
|
||||
" result.attributes_to_keep = {}",
|
||||
" ",
|
||||
" -- Legacy to add colours to the bike networks",
|
||||
" legacy_relation_preprocessor(relation_tags, result)"
|
||||
};
|
||||
_skeleton.AddDep("legacy");
|
||||
|
||||
foreach (var (calledInFunction, expr) in memberships)
|
||||
{
|
||||
func.Add($"\n\n -- {calledInFunction} ---");
|
||||
|
||||
var usedParameters = expr.UsedParameters().Select(param => param.ParamName.TrimStart('#')).ToHashSet();
|
||||
|
||||
// First, we calculate the value for the default parameters
|
||||
var preProcName = "relation_preprocessing_for_" + calledInFunction.AsLuaIdentifier();
|
||||
func.Add("");
|
||||
func.Add("");
|
||||
func.Add(" subresult.attributes_to_keep = {}");
|
||||
func.Add(" parameters = default_parameters()");
|
||||
func.Add($" matched = {preProcName}(parameters, relation_tags, subresult)");
|
||||
func.Add(" if (matched) then");
|
||||
var tagKey = "_relation:" + calledInFunction.AsLuaIdentifier();
|
||||
extraKeys.Add(tagKey);
|
||||
func.Add(
|
||||
" -- " + tagKey +
|
||||
" is the default value, which will be overwritten in 'remove_relation_prefix' for behaviours having a different parameter settign");
|
||||
func.Add($" result.attributes_to_keep[\"{tagKey}\"] = \"yes\"");
|
||||
func.Add(" end");
|
||||
|
||||
|
||||
if (usedParameters.Count() == 0)
|
||||
{
|
||||
// Every behaviour uses the default parameters for this one
|
||||
func.Add(" -- No parameter dependence for aspect " + calledInFunction);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var (behaviourName, parameters) in _profile.Behaviours)
|
||||
{
|
||||
if (usedParameters.Except(parameters.Keys.ToHashSet()).Any())
|
||||
{
|
||||
// The parameters where the membership depends on, are not used here
|
||||
// This is thus the same as the default. We don't have to calculate it
|
||||
continue;
|
||||
}
|
||||
|
||||
func.Add("");
|
||||
func.Add("");
|
||||
func.Add(" subresult.attributes_to_keep = {}");
|
||||
func.Add(" parameters = default_parameters()");
|
||||
func.Add(_parameterPrinter.DeclareParametersFor(parameters.Where(kv => usedParameters.Contains(kv.Key))
|
||||
.ToDictionary(kv => kv.Key, kv => kv.Value)));
|
||||
func.Add($" matched = {preProcName}(parameters, relation_tags, subresult)");
|
||||
func.Add(" if (matched) then");
|
||||
tagKey = "_relation:" + behaviourName.AsLuaIdentifier() + ":" + calledInFunction.AsLuaIdentifier();
|
||||
extraKeys.Add(tagKey);
|
||||
func.Add($" result.attributes_to_keep[\"{tagKey}\"] = \"yes\"");
|
||||
func.Add(" end");
|
||||
}
|
||||
}
|
||||
|
||||
func.Add("end");
|
||||
|
||||
return (string.Join("\n", func), extraKeys);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
148
AspectedRouting/IO/itinero1/LuaPrinter1.cs
Normal file
148
AspectedRouting/IO/itinero1/LuaPrinter1.cs
Normal file
|
@ -0,0 +1,148 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.Language;
|
||||
using AspectedRouting.Language.Expression;
|
||||
using AspectedRouting.Tests;
|
||||
|
||||
namespace AspectedRouting.IO.itinero1
|
||||
{
|
||||
public partial class LuaPrinter1
|
||||
{
|
||||
private readonly ProfileMetaData _profile;
|
||||
private readonly Context _context;
|
||||
private readonly List<AspectTestSuite> _aspectTestSuites;
|
||||
private readonly List<BehaviourTestSuite> _profileTests;
|
||||
private readonly LuaSkeleton.LuaSkeleton _skeleton;
|
||||
|
||||
|
||||
private readonly LuaParameterPrinter _parameterPrinter;
|
||||
|
||||
|
||||
public LuaPrinter1(ProfileMetaData profile, Context context,
|
||||
List<AspectTestSuite> aspectTestSuites,
|
||||
List<BehaviourTestSuite> profileTests)
|
||||
{
|
||||
_profile = profile;
|
||||
_context = context;
|
||||
_aspectTestSuites = aspectTestSuites;
|
||||
_profileTests = profileTests;
|
||||
_skeleton = new LuaSkeleton.LuaSkeleton(context);
|
||||
_parameterPrinter = new LuaParameterPrinter(profile, _skeleton);
|
||||
}
|
||||
|
||||
public string ToLua()
|
||||
{
|
||||
_skeleton.AddDep("spoken_instructions");
|
||||
|
||||
var (membershipFunction, extraKeys) = GenerateMembershipPreprocessor();
|
||||
var (profileOverview, behaviourFunctions) = GenerateProfileFunctions();
|
||||
var mainFunction = GenerateMainProfileFunction();
|
||||
var tests = new LuaTestPrinter(_skeleton, new List<string>{"unitTest","unitTestProfile"}).GenerateFullTestSuite(_profileTests, _aspectTestSuites);
|
||||
|
||||
|
||||
var keys = _profile.AllExpressions(_context).PossibleTags().Keys
|
||||
.Concat(extraKeys)
|
||||
.Select(key => "\"" + key + "\"")
|
||||
.ToHashSet();
|
||||
|
||||
var header = new List<string>
|
||||
{
|
||||
$"-- Itinero 1.0-profile, generated by AspectedRouting on {DateTime.Now:s}",
|
||||
$"name = \"{_profile.Name}\"",
|
||||
"normalize = false",
|
||||
"vehicle_type = {" + string.Join(", ", _profile.VehicleTyps.Select(s => "\"" + s + "\"")) + "}",
|
||||
// meta_whitelist is defined in the profile file, these are tags that are included in the generated route, but are not relevant for determining weights
|
||||
"meta_whitelist = {\n \"cycle_network_colour\"," // cycle network colour is sneaked in here for legacy reasons
|
||||
+ string.Join("\n , ", _profile.Metadata.Select(s => "\"" + s + "\""))
|
||||
+ " }",
|
||||
"profile_whitelist = {\n " + string.Join("\n , ", keys) + "\n }",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
profileOverview,
|
||||
"",
|
||||
_parameterPrinter.GenerateDefaultParameters()
|
||||
};
|
||||
|
||||
|
||||
// Add the aspect functions to the skeleton
|
||||
var usedFunctions = _profile.CalledFunctionsRecursive(_context).Values.SelectMany(v => v).ToHashSet();
|
||||
foreach (var functionName in usedFunctions)
|
||||
{
|
||||
_skeleton.AddFunction(_context.GetAspect(functionName));
|
||||
}
|
||||
|
||||
|
||||
// The dependencies should be generated after all the other functions are generated, to make sure all are added
|
||||
var dependencies = _skeleton.GenerateDependencies();
|
||||
var functions = _skeleton.GenerateFunctions();
|
||||
|
||||
var allCode = new List<List<string>>
|
||||
{
|
||||
header,
|
||||
behaviourFunctions,
|
||||
mainFunction.InList(),
|
||||
membershipFunction.InList(),
|
||||
"---------------------- ASPECTS ----------------------".InList(),
|
||||
functions,
|
||||
"---------------------- UTILS ------------------------".InList(),
|
||||
dependencies,
|
||||
"----------------------- TESTS ------------------------".InList(),
|
||||
tests.InList(),
|
||||
GenerateLegacyTail().InList()
|
||||
};
|
||||
|
||||
|
||||
return string.Join("\n\n\n", allCode.Select(code => string.Join("\n", code)));
|
||||
}
|
||||
|
||||
|
||||
private (string profilesOverview, List<string> behaviourImplementations) GenerateProfileFunctions()
|
||||
{
|
||||
// Profiles gets the contents for the 'profiles' field, part of the profile spec
|
||||
var profiles = new List<string>();
|
||||
var behaviourImplementations = new List<string>();
|
||||
foreach (var (behaviourName, behaviourParameters) in _profile.Behaviours)
|
||||
{
|
||||
var (functionName, implementation ) = GenerateBehaviourFunction(behaviourName, behaviourParameters);
|
||||
behaviourImplementations.Add(implementation);
|
||||
profiles.Add(
|
||||
string.Join(",\n ",
|
||||
$" name = \"{behaviourName.AsLuaIdentifier()}\"",
|
||||
" function_name = \"behaviour_" + functionName + "\"",
|
||||
" metric = \"custom\""
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
var profilesOverview = "profiles = {\n {\n" +
|
||||
string.Join("\n },\n {\n ", profiles) + "\n }\n}";
|
||||
return (profilesOverview, behaviourImplementations);
|
||||
}
|
||||
|
||||
|
||||
private string GenerateLegacyTail()
|
||||
{
|
||||
return string.Join("\n",
|
||||
"",
|
||||
"if (itinero == nil) then",
|
||||
" itinero = {}",
|
||||
" itinero.log = print",
|
||||
"",
|
||||
" -- Itinero is not defined -> we are running from a lua interpreter -> the tests are intended",
|
||||
" runTests = true",
|
||||
"",
|
||||
"",
|
||||
"else",
|
||||
" print = itinero.log",
|
||||
"end",
|
||||
"",
|
||||
"test_all()",
|
||||
"if (not failed_tests and not failed_profile_tests) then",
|
||||
" print(\"Tests OK\")",
|
||||
"end"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,297 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.Language;
|
||||
using AspectedRouting.Language.Expression;
|
||||
using AspectedRouting.Language.Functions;
|
||||
using AspectedRouting.Language.Typ;
|
||||
|
||||
namespace AspectedRouting.IO.itinero1
|
||||
{
|
||||
public partial class LuaPrinter
|
||||
{
|
||||
private readonly Context _context;
|
||||
|
||||
public LuaPrinter(Context context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
private void CreateMembershipPreprocessor(ProfileMetaData profile)
|
||||
{
|
||||
var memberships = Analysis.MembershipMappingsFor(profile, _context);
|
||||
|
||||
foreach (var (calledInFunction, membership) in memberships)
|
||||
{
|
||||
var funcMetaData = new AspectMetadata(
|
||||
membership,
|
||||
"relation_preprocessing_for_" + calledInFunction.FunctionName(),
|
||||
"Function preprocessing needed for aspect " + calledInFunction +
|
||||
", called by the relation preprocessor",
|
||||
"Generator", "", "NA"
|
||||
);
|
||||
|
||||
|
||||
AddFunction(funcMetaData);
|
||||
}
|
||||
|
||||
|
||||
var func = new List<string>
|
||||
{
|
||||
"",
|
||||
"",
|
||||
"-- Processes the relation. All tags which are added to result.attributes_to_keep will be copied to 'attributes' of each individual way",
|
||||
"function relation_tag_processor(relation_tags, result)",
|
||||
" local parameters = {}",
|
||||
" local subresult = {}",
|
||||
" local matched = false",
|
||||
" result.attributes_to_keep = {}"
|
||||
};
|
||||
|
||||
foreach (var (calledInFunction, expr) in memberships)
|
||||
{
|
||||
func.Add($"\n\n -- {calledInFunction} ---");
|
||||
|
||||
var usedParameters = expr.UsedParameters().Select(param => param.ParamName.TrimStart('#')).ToHashSet();
|
||||
|
||||
// First, we calculate the value for the default parameters
|
||||
var preProcName = "relation_preprocessing_for_" + calledInFunction.FunctionName();
|
||||
func.Add("");
|
||||
func.Add("");
|
||||
func.Add(" subresult.attributes_to_keep = {}");
|
||||
func.Add(" parameters = default_parameters()");
|
||||
func.Add($" matched = {preProcName}(parameters, relation_tags, subresult)");
|
||||
func.Add(" if (matched) then");
|
||||
var tagKey = "_relation:" + calledInFunction.FunctionName();
|
||||
_neededKeys.Add(tagKey);
|
||||
func.Add(
|
||||
" -- " + tagKey +
|
||||
" is the default value, which will be overwritten in 'remove_relation_prefix' for behaviours having a different parameter settign");
|
||||
func.Add($" result.attributes_to_keep[\"{tagKey}\"] = \"yes\"");
|
||||
func.Add(" end");
|
||||
|
||||
|
||||
if (usedParameters.Count() == 0)
|
||||
{
|
||||
// Every behaviour uses the default parameters for this one
|
||||
func.Add(" -- No parameter dependence for aspect " + calledInFunction);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var (behaviourName, parameters) in profile.Behaviours)
|
||||
{
|
||||
if (usedParameters.Except(parameters.Keys.ToHashSet()).Any())
|
||||
{
|
||||
// The parameters where the membership depends on, are not used here
|
||||
// This is thus the same as the default. We don't have to calculate it
|
||||
continue;
|
||||
}
|
||||
|
||||
func.Add("");
|
||||
func.Add("");
|
||||
func.Add(" subresult.attributes_to_keep = {}");
|
||||
func.Add(" parameters = default_parameters()");
|
||||
func.Add(ParametersToLua(parameters.Where(kv => usedParameters.Contains(kv.Key))
|
||||
.ToDictionary(kv => kv.Key, kv => kv.Value)));
|
||||
func.Add($" matched = {preProcName}(parameters, relation_tags, subresult)");
|
||||
func.Add(" if (matched) then");
|
||||
tagKey = "_relation:" + behaviourName.FunctionName() + ":" + calledInFunction.FunctionName();
|
||||
_neededKeys.Add(tagKey);
|
||||
func.Add($" result.attributes_to_keep[\"{tagKey}\"] = \"yes\"");
|
||||
func.Add(" end");
|
||||
}
|
||||
}
|
||||
|
||||
func.Add("end");
|
||||
|
||||
|
||||
_code.Add(string.Join("\n", func));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the necessary called functions and the profile main entry point
|
||||
/// </summary>
|
||||
public void AddProfile(ProfileMetaData profile)
|
||||
{
|
||||
var defaultParameters = "\n";
|
||||
foreach (var (name, (types, inFunction)) in profile.UsedParameters(_context))
|
||||
{
|
||||
defaultParameters += $"{name}: {string.Join(", ", types)}\n" +
|
||||
$" Used in {inFunction}\n";
|
||||
}
|
||||
|
||||
CreateMembershipPreprocessor(profile);
|
||||
|
||||
|
||||
var impl = string.Join("\n",
|
||||
"",
|
||||
"",
|
||||
$"name = \"{profile.Name}\"",
|
||||
"normalize = false",
|
||||
"vehicle_type = {" + string.Join(", ", profile.VehicleTyps.Select(s => "\"" + s + "\"")) + "}",
|
||||
"meta_whitelist = {\n " + string.Join("\n , ", profile.Metadata.Select(s => "\"" + s + "\"")) +
|
||||
"}",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"--[[",
|
||||
profile.Name,
|
||||
"This is the main function called to calculate the access, oneway and speed.",
|
||||
"Comfort is calculated as well, based on the parameters which are padded in",
|
||||
"",
|
||||
"Created by " + profile.Author,
|
||||
"Originally defined in " + profile.Filename,
|
||||
"Used parameters: " + defaultParameters.Indent(),
|
||||
"]]",
|
||||
"function " + profile.Name + "(parameters, tags, result)",
|
||||
"",
|
||||
" -- initialize the result table on the default values",
|
||||
" result.access = 0",
|
||||
" result.speed = 0",
|
||||
" result.factor = 1",
|
||||
" result.direction = 0",
|
||||
" result.canstop = true",
|
||||
" result.attributes_to_keep = {}",
|
||||
"",
|
||||
" local access = " + ToLua(profile.Access),
|
||||
" if (access == nil or access == \"no\") then",
|
||||
" return",
|
||||
" end",
|
||||
" tags.access = access",
|
||||
" local oneway = " + ToLua(profile.Oneway),
|
||||
" tags.oneway = oneway",
|
||||
" local speed = " + ToLua(profile.Speed),
|
||||
" tags.speed = speed",
|
||||
" local distance = 1 -- the weight per meter for distance travelled is, well, 1m/m",
|
||||
"");
|
||||
|
||||
impl +=
|
||||
"\n local priority = 0\n ";
|
||||
|
||||
foreach (var (parameterName, expression) in profile.Priority)
|
||||
{
|
||||
var paramInLua = ToLua(new Parameter(parameterName));
|
||||
|
||||
|
||||
var exprInLua = ToLua(expression);
|
||||
var subs = new Curry(Typs.Tags, new Var(("a"))).UnificationTable(expression.Types.First());
|
||||
if (subs != null && subs.TryGetValue("$a", out var resultType) &&
|
||||
(resultType.Equals(Typs.Bool) || resultType.Equals(Typs.String)))
|
||||
{
|
||||
AddDep("parse");
|
||||
exprInLua = "parse(" + exprInLua + ")";
|
||||
}
|
||||
|
||||
impl += "\n " + string.Join("\n ",
|
||||
$"if({paramInLua} ~= 0) then",
|
||||
$" priority = priority + {paramInLua} * {exprInLua}",
|
||||
"end"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
impl += string.Join("\n",
|
||||
"",
|
||||
"",
|
||||
" if (priority <= 0) then",
|
||||
" result.access = 0",
|
||||
" return",
|
||||
" end",
|
||||
"",
|
||||
" result.access = 1",
|
||||
" result.speed = speed",
|
||||
" result.factor = 1 / priority",
|
||||
"",
|
||||
" if (oneway == \"both\") then",
|
||||
" result.direction = 0",
|
||||
" elseif (oneway == \"with\") then",
|
||||
" result.direction = 1",
|
||||
" elseif (oneway == \"against\") then",
|
||||
" result.direction = 2",
|
||||
" else",
|
||||
" error(\"Unexpected value for oneway: \"..oneway)",
|
||||
" end",
|
||||
"",
|
||||
"end",
|
||||
"",
|
||||
"",
|
||||
"function default_parameters()",
|
||||
" local parameters = {}",
|
||||
ParametersToLua(profile.DefaultParameters),
|
||||
" return parameters",
|
||||
"end",
|
||||
"",
|
||||
""
|
||||
);
|
||||
|
||||
|
||||
var profiles = new List<string>();
|
||||
foreach (var (name, subParams) in profile.Behaviours)
|
||||
{
|
||||
impl += BehaviourFunction(profile, name, subParams, profiles);
|
||||
}
|
||||
|
||||
impl += "\n\n\n";
|
||||
impl += "profiles = {\n {\n" +
|
||||
string.Join("\n },\n {\n ", profiles) + "\n }\n}";
|
||||
|
||||
_code.Add(impl);
|
||||
}
|
||||
|
||||
private string BehaviourFunction(ProfileMetaData profile,
|
||||
string name,
|
||||
Dictionary<string, IExpression> subParams, List<string> profiles)
|
||||
{
|
||||
var functionName = profile.Name + "_" + name;
|
||||
|
||||
subParams.TryGetValue("description", out var description);
|
||||
profiles.Add(
|
||||
string.Join(",\n ",
|
||||
$" name = \"{name.FunctionName()}\"",
|
||||
" function_name = \"behaviour_" + functionName.FunctionName() + "\"",
|
||||
" metric = \"custom\""
|
||||
)
|
||||
);
|
||||
|
||||
AddDep("remove_relation_prefix");
|
||||
var impl = string.Join("\n",
|
||||
"",
|
||||
"--[[",
|
||||
description,
|
||||
"]]",
|
||||
"function behaviour_" + functionName.FunctionName() + "(tags, result)",
|
||||
$" tags = remove_relation_prefix(tags, \"{name.FunctionName()}\")",
|
||||
" local parameters = default_parameters()",
|
||||
" parameters.name = \"" + functionName + "\"",
|
||||
""
|
||||
);
|
||||
|
||||
impl += ParametersToLua(subParams);
|
||||
|
||||
impl += " " + profile.Name + "(parameters, tags, result)\n";
|
||||
impl += "end\n";
|
||||
return impl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// `local parameters = default_parameters()` must still be invoked by caller!
|
||||
/// </summary>
|
||||
/// <param name="subParams"></param>
|
||||
/// <returns></returns>
|
||||
private string ParametersToLua(Dictionary<string, IExpression> subParams)
|
||||
{
|
||||
var impl = "";
|
||||
foreach (var (paramName, value) in subParams)
|
||||
{
|
||||
if (paramName.Equals("description"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
impl += $" parameters.{paramName.TrimStart('#').FunctionName()} = {ToLua(value)}\n";
|
||||
}
|
||||
|
||||
return impl;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.Tests;
|
||||
|
||||
namespace AspectedRouting.IO.itinero1
|
||||
{
|
||||
public partial class LuaPrinter
|
||||
{
|
||||
public void AddTestSuite(ProfileTestSuite testSuite)
|
||||
{
|
||||
var tests = string.Join("\n",
|
||||
testSuite.Tests.Select((test, i) => ToLua(testSuite, i, test.Item1, test.tags)));
|
||||
_tests.Add(tests);
|
||||
}
|
||||
|
||||
private string ToLua(ProfileTestSuite testSuite, int index, ProfileResult expected, Dictionary<string, string> tags)
|
||||
{
|
||||
AddDep("debug_table");
|
||||
var parameters = new Dictionary<string, string>();
|
||||
|
||||
|
||||
var keysToCheck = new List<string>();
|
||||
foreach (var (key, value) in tags)
|
||||
{
|
||||
if (key.StartsWith("#"))
|
||||
{
|
||||
parameters[key.TrimStart('#')] = value;
|
||||
}
|
||||
if(key.StartsWith("_relation:"))
|
||||
{
|
||||
keysToCheck.Add(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach (var key in keysToCheck)
|
||||
{
|
||||
var newKey = key.Replace(".", "_");
|
||||
tags[newKey] = tags[key];
|
||||
tags.Remove(key);
|
||||
}
|
||||
|
||||
foreach (var (paramName, _) in parameters)
|
||||
{
|
||||
tags.Remove("#" + paramName);
|
||||
}
|
||||
// function unit_test_profile(profile_function, profile_name, index, expected, tags)
|
||||
|
||||
return $"unit_test_profile(behaviour_{testSuite.Profile.Name.FunctionName()}_{testSuite.BehaviourName.FunctionName()}, " +
|
||||
$"\"{testSuite.BehaviourName}\", " +
|
||||
$"{index}, " +
|
||||
$"{{access = \"{D(expected.Access)}\", speed = {expected.Speed}, oneway = \"{D(expected.Oneway)}\", weight = {expected.Priority} }}, " +
|
||||
tags.ToLuaTable() +
|
||||
")";
|
||||
}
|
||||
|
||||
private string D(string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s))
|
||||
{
|
||||
return "0";
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
public void AddTestSuite(AspectTestSuite testSuite)
|
||||
{
|
||||
var fName = testSuite.FunctionToApply.Name;
|
||||
var tests = string.Join("\n",
|
||||
testSuite.Tests.Select((test, i) => ToLua(fName, i, test.expected, test.tags)));
|
||||
_tests.Add(tests);
|
||||
}
|
||||
|
||||
private string ToLua(string functionToApplyName, int index, string expected, Dictionary<string, string> tags)
|
||||
{
|
||||
var parameters = new Dictionary<string, string>();
|
||||
|
||||
|
||||
foreach (var (key, value) in tags)
|
||||
{
|
||||
if (key.StartsWith("#"))
|
||||
{
|
||||
parameters[key.TrimStart('#')] = value;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (paramName, _) in parameters)
|
||||
{
|
||||
tags.Remove("#" + paramName);
|
||||
}
|
||||
|
||||
AddDep("unitTest");
|
||||
AddDep("debug_table");
|
||||
var funcName = functionToApplyName.Replace(" ", "_").Replace(".", "_");
|
||||
return
|
||||
$"unit_test({funcName}, \"{functionToApplyName}\", {index}, \"{expected}\", {parameters.ToLuaTable()}, {tags.ToLuaTable()})";
|
||||
}
|
||||
}
|
||||
}
|
145
AspectedRouting/IO/itinero2/LuaPrinter2.MainFunction.cs
Normal file
145
AspectedRouting/IO/itinero2/LuaPrinter2.MainFunction.cs
Normal file
|
@ -0,0 +1,145 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.Language;
|
||||
using AspectedRouting.Language.Typ;
|
||||
|
||||
namespace AspectedRouting.IO.itinero2
|
||||
{
|
||||
public partial class LuaPrinter2
|
||||
{
|
||||
private string GenerateFactorFunction()
|
||||
{
|
||||
var parameters = new Dictionary<string, IExpression>();
|
||||
foreach (var (name, value) in _profile.DefaultParameters)
|
||||
{
|
||||
parameters[name] = value;
|
||||
}
|
||||
|
||||
foreach (var (name, value) in _profile.Behaviours[_behaviourName])
|
||||
{
|
||||
parameters[name] = value;
|
||||
}
|
||||
|
||||
|
||||
var aspects = new List<string>();
|
||||
|
||||
foreach (var (paramName, expr) in _profile.Priority)
|
||||
{
|
||||
var paramExpr = parameters[paramName].Evaluate(_context);
|
||||
if (!(paramExpr is double weight))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (weight == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var expression = _profile.Priority[paramName];
|
||||
var exprInLua = _skeleton.ToLua(expression);
|
||||
var subs = new Curry(Typs.Tags, new Var(("a"))).UnificationTable(expression.Types.First());
|
||||
if (subs != null && subs.TryGetValue("$a", out var resultType) &&
|
||||
(resultType.Equals(Typs.Bool) || resultType.Equals(Typs.String)))
|
||||
{
|
||||
_skeleton.AddDep("parse");
|
||||
exprInLua = "parse(" + exprInLua + ")";
|
||||
}
|
||||
|
||||
aspects.Add(weight + " * " + exprInLua);
|
||||
}
|
||||
|
||||
Console.WriteLine(aspects.Lined());
|
||||
var code = new List<string>()
|
||||
{
|
||||
"--[[",
|
||||
"Generates the factor according to the priorities and the parameters for this behaviour",
|
||||
"Note: 'result' is not actually used",
|
||||
"]]",
|
||||
"function calculate_priority(parameters, tags, result, access, oneway, speed)",
|
||||
" local distance = 1",
|
||||
" local priority = \n " + string.Join(" +\n ", aspects),
|
||||
" return priority",
|
||||
"end"
|
||||
};
|
||||
return code.Lined();
|
||||
}
|
||||
|
||||
private string GenerateMainFunction()
|
||||
{
|
||||
var parameters = _profile.Behaviours[_behaviourName];
|
||||
|
||||
_skeleton.AddDependenciesFor(_profile.Access);
|
||||
_skeleton.AddDependenciesFor(_profile.Oneway);
|
||||
_skeleton.AddDependenciesFor(_profile.Speed);
|
||||
|
||||
_skeleton.AddDep("eq");
|
||||
var code = new List<string>
|
||||
{
|
||||
"--[[",
|
||||
_profile.Behaviours[_behaviourName]["description"].Evaluate(_context).ToString(),
|
||||
"]]",
|
||||
"function factor(tags, result)",
|
||||
" ",
|
||||
" -- initialize the result table on the default values",
|
||||
" result.forward_speed = 0",
|
||||
" result.backward_speed = 0",
|
||||
" result.forward = 0",
|
||||
" result.backward = 0",
|
||||
" result.canstop = true",
|
||||
" result.attributes_to_keep = {} -- not actually used anymore, but the code generation still uses this",
|
||||
|
||||
"",
|
||||
"",
|
||||
" local parameters = default_parameters()",
|
||||
_parameterPrinter.DeclareParametersFor(parameters),
|
||||
"",
|
||||
" local oneway = " + _skeleton.ToLua(_profile.Oneway),
|
||||
" tags.oneway = oneway",
|
||||
" -- forward calculation",
|
||||
" tags[\"_direction\"] = \"with\"",
|
||||
" local access_forward = " + _skeleton.ToLua(_profile.Access),
|
||||
" if(oneway == \"against\") then",
|
||||
" access_forward = \"no\"",
|
||||
" end",
|
||||
|
||||
" if(access_forward ~= nil and access_forward ~= \"no\") then",
|
||||
" tags.access = access_forward",
|
||||
" result.forward_speed = " + _skeleton.ToLua(_profile.Speed).Indent(),
|
||||
" tags.speed = result.forward_speed",
|
||||
" local priority = calculate_priority(parameters, tags, result, access_forward, oneway, result.forward_speed)",
|
||||
" if (priority <= 0) then",
|
||||
" result.forward_speed = 0",
|
||||
" else",
|
||||
" result.forward = 1 / priority",
|
||||
" end",
|
||||
" end",
|
||||
"",
|
||||
" -- backward calculation",
|
||||
" tags[\"_direction\"] = \"against\"",
|
||||
" local access_backward = " + _skeleton.ToLua(_profile.Access),
|
||||
"",
|
||||
" if(oneway == \"with\") then",
|
||||
" access_backward = \"no\"",
|
||||
" end",
|
||||
"",
|
||||
" if(access_backward ~= nil and access_backward ~= \"no\") then",
|
||||
" tags.access = access_backward" +
|
||||
" result.backward_speed = " + _skeleton.ToLua(_profile.Speed).Indent(),
|
||||
" tags.speed = result.backward_speed",
|
||||
" local priority = calculate_priority(parameters, tags, result, access_backward, oneway, result.backward_speed)",
|
||||
" if (priority <= 0) then",
|
||||
" result.backward_speed = 0",
|
||||
" else",
|
||||
" result.backward = 1 / priority",
|
||||
" end",
|
||||
" end",
|
||||
|
||||
"end"
|
||||
};
|
||||
|
||||
return code.Lined();
|
||||
}
|
||||
}
|
||||
}
|
104
AspectedRouting/IO/itinero2/LuaPrinter2.cs
Normal file
104
AspectedRouting/IO/itinero2/LuaPrinter2.cs
Normal file
|
@ -0,0 +1,104 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.IO.itinero1;
|
||||
using AspectedRouting.Language;
|
||||
using AspectedRouting.Language.Expression;
|
||||
using AspectedRouting.Tests;
|
||||
|
||||
namespace AspectedRouting.IO.itinero2
|
||||
{
|
||||
/// <summary>
|
||||
/// Lua printer for itinero2-lua format
|
||||
///
|
||||
/// The itinero 2.0 lua profile is a whole lot simpler then the 1.0 format,
|
||||
/// as a single profile there only describes a single behaviour of a vehicle:
|
||||
///
|
||||
/// It has:
|
||||
/// - name: string, e.g. 'bicycle.fastest'
|
||||
/// - factor(attributes, result): void, where 'attributes' are all the tags of the way,
|
||||
/// and result must contain (after calling):
|
||||
/// - 'forward_speed', a double describing the forward speed (in km/h)
|
||||
/// - 'backward_speed', the speed when travelling in the opposite direction (0 if not possible)
|
||||
/// - 'forward', a double describing the forwardfactor
|
||||
/// - 'backward', the backward factor
|
||||
/// - 'canstop', a boolean indicating if stopping along the road is possible
|
||||
///
|
||||
/// </summary>
|
||||
public partial class LuaPrinter2
|
||||
{
|
||||
private readonly ProfileMetaData _profile;
|
||||
private readonly string _behaviourName;
|
||||
private readonly Context _context;
|
||||
private readonly List<AspectTestSuite> _aspectTests;
|
||||
private readonly IEnumerable<BehaviourTestSuite> _behaviourTestSuite;
|
||||
|
||||
private readonly LuaSkeleton.LuaSkeleton _skeleton;
|
||||
private readonly LuaParameterPrinter _parameterPrinter;
|
||||
|
||||
|
||||
public LuaPrinter2(ProfileMetaData profile, string behaviourName,
|
||||
Context context,
|
||||
List<AspectTestSuite> aspectTests, IEnumerable<BehaviourTestSuite> behaviourTestSuite)
|
||||
{
|
||||
_skeleton = new LuaSkeleton.LuaSkeleton(context);
|
||||
_profile = profile;
|
||||
_behaviourName = behaviourName;
|
||||
_context = context;
|
||||
_aspectTests = aspectTests;
|
||||
_behaviourTestSuite = behaviourTestSuite;
|
||||
_parameterPrinter = new LuaParameterPrinter(_profile, _skeleton);
|
||||
}
|
||||
|
||||
public string ToLua()
|
||||
{
|
||||
var header =
|
||||
new List<string>
|
||||
{
|
||||
$"name = \"{_profile.Name}.{_behaviourName}\"",
|
||||
$"generationDate = \"{DateTime.Now:s}\"",
|
||||
$"description = \"{_profile.Description}\""
|
||||
};
|
||||
|
||||
var tests = new LuaTestPrinter(_skeleton, new List<string>() {"unitTestProfile2"}).GenerateFullTestSuite(
|
||||
_behaviourTestSuite.ToList(), new List<AspectTestSuite>());
|
||||
var all = new List<string>
|
||||
{
|
||||
header.Lined(),
|
||||
"",
|
||||
GenerateMainFunction(),
|
||||
"",
|
||||
GenerateFactorFunction(),
|
||||
"",
|
||||
_parameterPrinter.GenerateDefaultParameters(),
|
||||
"",
|
||||
"",
|
||||
string.Join("\n\n", _skeleton.GenerateFunctions()),
|
||||
"",
|
||||
string.Join("\n\n", _skeleton.GenerateDependencies()), // Should be AFTER generating the main function!
|
||||
"",
|
||||
tests,
|
||||
"",
|
||||
|
||||
"if (itinero == nil) then",
|
||||
" itinero = {}",
|
||||
" itinero.log = print",
|
||||
"",
|
||||
" -- Itinero is not defined -> we are running from a lua interpreter -> the tests are intended",
|
||||
" runTests = true",
|
||||
"",
|
||||
"",
|
||||
"else",
|
||||
" print = itinero.log",
|
||||
"end",
|
||||
"",
|
||||
"test_all()",
|
||||
"if (not failed_tests and not failed_profile_tests and print ~= nil) then",
|
||||
" print(\"Tests OK\")",
|
||||
"end"
|
||||
};
|
||||
|
||||
return all.Lined();
|
||||
}
|
||||
}
|
||||
}
|
17
AspectedRouting/IO/lua/legacy.lua
Normal file
17
AspectedRouting/IO/lua/legacy.lua
Normal file
|
@ -0,0 +1,17 @@
|
|||
--[[
|
||||
Legacy function to add cycle_colour
|
||||
]]
|
||||
|
||||
function legacy_relation_preprocessor(attributes, result)
|
||||
if (attributes.route == "bicycle") then
|
||||
-- This is a cycling network, the colour is copied
|
||||
if (attributes.colour ~= nil) then
|
||||
result.attributes_to_keep.cycle_network_colour = attributes.colour
|
||||
end
|
||||
|
||||
if (attributes.color ~= nil) then
|
||||
-- for the americans!
|
||||
result.attributes_to_keep.cycle_network_colour = attributes.color
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,20 +1,20 @@
|
|||
failed_profile_tests = false
|
||||
--[[
|
||||
expected should be a table containing 'access', 'speed' and 'weight'
|
||||
expected should be a table containing 'access', 'speed' and 'priority'
|
||||
]]
|
||||
function unit_test_profile(profile_function, profile_name, index, expected, tags)
|
||||
local result = { attributes_to_keep = {} }
|
||||
local profile_failed = false
|
||||
profile_function(tags, result)
|
||||
|
||||
local accessCorrect = (result.access == 0 and (expected.access == "no" or expected.weight <= 0)) or result.access == 1
|
||||
local accessCorrect = (result.access == 0 and (expected.access == "no" or expected.priority <= 0)) or result.access == 1
|
||||
if (not accessCorrect) then
|
||||
print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".access: expected " .. expected.access .. " but got " .. result.access)
|
||||
profile_failed = true
|
||||
failed_profile_tests = true
|
||||
end
|
||||
|
||||
if (expected.access == "no" or expected.weight <= 0) then
|
||||
if (expected.access == "no" or expected.priority <= 0) then
|
||||
-- we cannot access this road, the other results are irrelevant
|
||||
if (profile_failed) then
|
||||
print("The used tags for test " .. tostring(index) .. " are:")
|
||||
|
@ -46,8 +46,8 @@ function unit_test_profile(profile_function, profile_name, index, expected, tags
|
|||
end
|
||||
|
||||
|
||||
if (not double_compare(result.factor, 1/expected.weight)) then
|
||||
print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".factor: expected " .. expected.weight .. " but got " .. 1/result.factor)
|
||||
if (not double_compare(result.factor, 1/expected.priority)) then
|
||||
print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".factor: expected " .. expected.priority .. " but got " .. 1/result.factor)
|
||||
failed_profile_tests = true
|
||||
profile_failed = true
|
||||
end
|
||||
|
|
67
AspectedRouting/IO/lua/unitTestProfile2.lua
Normal file
67
AspectedRouting/IO/lua/unitTestProfile2.lua
Normal file
|
@ -0,0 +1,67 @@
|
|||
failed_profile_tests = false
|
||||
--[[
|
||||
Unit test of a behaviour function for an itinero 2.0 profile
|
||||
]]
|
||||
|
||||
function unit_test_profile(profile_function, profile_name, index, expected, tags)
|
||||
-- Note: we don't actually use 'profile_function'
|
||||
|
||||
local result = {}
|
||||
local profile_failed = false
|
||||
factor(tags, result)
|
||||
|
||||
|
||||
local forward_access = result.forward_speed > 0 and result.forward > 0;
|
||||
local backward_access = result.backward_speed > 0 and result.backward > 0;
|
||||
|
||||
if (not forward_access and not backward_access) then
|
||||
|
||||
if (expected.access == "no" or expected.speed <= 0 or expected.priority <= 0) then
|
||||
-- All is fine, we can't access this thing anyway
|
||||
return
|
||||
end
|
||||
|
||||
profile_failed = true
|
||||
print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".access: expected " .. expected.access .. " but forward and backward are 0 (for either speed or factor)")
|
||||
end
|
||||
|
||||
if (expected.oneway == "with") then
|
||||
if (backward_access) then
|
||||
-- we can go against the direction, not good
|
||||
print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going against the direction is possible")
|
||||
profile_failed = true;
|
||||
end
|
||||
if (not forward_access) then
|
||||
print("Test " .. tostring(index) .. " warning for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going with the direction is not possible")
|
||||
end
|
||||
end
|
||||
|
||||
if (expected.oneway == "against") then
|
||||
if (forward_access) then
|
||||
-- we can go against the direction, not good
|
||||
print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going with the direction is possible")
|
||||
end
|
||||
if (not backward_access) then
|
||||
print("Test " .. tostring(index) .. " warning for " .. profile_name .. ".oneway: expected " .. expected.oneway .. " but going against the direction is not possible")
|
||||
end
|
||||
end
|
||||
|
||||
if (result.forward_speed ~= expected.speed and result.backward_speed ~= expected.speed) then
|
||||
print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".speed: expected " .. expected.speed .. " but got " .. result.forward_speed .. " forward and " .. result.backward_speed .. " backward")
|
||||
profile_failed = true;
|
||||
end
|
||||
|
||||
|
||||
if (result.forward ~= expected.priority and result.backward ~= expected.priority) then
|
||||
print("Test " .. tostring(index) .. " failed for " .. profile_name .. ".priority: expected " .. expected.priority .. " but got " .. result.forward .. " forward and " .. result.backward .. " backward")
|
||||
profile_failed = true;
|
||||
end
|
||||
|
||||
if(profile_failed) then
|
||||
failed_profile_tests = true;
|
||||
debug_table(tags, "tags: ")
|
||||
debug_table(expected, "expected: ")
|
||||
debug_table(result, "result: ")
|
||||
end
|
||||
|
||||
end
|
|
@ -207,6 +207,8 @@ namespace AspectedRouting.Language
|
|||
{
|
||||
// Read as: this function calls the value-function
|
||||
var result = new Dictionary<string, List<string>>();
|
||||
|
||||
|
||||
var calledFunctions = new Queue<string>();
|
||||
|
||||
void ScanExpression(IExpression e, string inFunction)
|
||||
|
@ -252,6 +254,62 @@ namespace AspectedRouting.Language
|
|||
return result;
|
||||
}
|
||||
|
||||
public static (HashSet<string> parameterName, HashSet<string> calledFunctionNames) DirectlyAndInderectlyCalled(
|
||||
this List<IExpression> exprs, Context ctx)
|
||||
{
|
||||
var parameters = new HashSet<string>();
|
||||
var dependencies = new HashSet<string>();
|
||||
|
||||
var queue = new Queue<IExpression>();
|
||||
exprs.ForEach(queue.Enqueue);
|
||||
|
||||
while (queue.TryDequeue(out var next))
|
||||
{
|
||||
var (p, deps) = next.DirectlyCalled();
|
||||
parameters.UnionWith(p);
|
||||
var toCheck = deps.Except(dependencies);
|
||||
dependencies.UnionWith(deps);
|
||||
|
||||
foreach (var fName in toCheck)
|
||||
{
|
||||
queue.Enqueue(ctx.GetFunction(fName));
|
||||
}
|
||||
}
|
||||
|
||||
return (parameters, dependencies);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Generates an overview of the dependencies of the expression, both which parameters it needs and what other functions (builtin or defined) it needs.
|
||||
/// </summary>
|
||||
/// <param name="expr"></param>
|
||||
/// <returns></returns>
|
||||
public static (HashSet<string> parameterName, HashSet<string> calledFunctionNames) DirectlyCalled(
|
||||
this IExpression expr)
|
||||
{
|
||||
var parameters = new HashSet<string>();
|
||||
var dependencies = new HashSet<string>();
|
||||
|
||||
expr.Visit(e =>
|
||||
{
|
||||
if (e is FunctionCall fc)
|
||||
{
|
||||
dependencies.Add(fc.CalledFunctionName);
|
||||
}
|
||||
|
||||
if (e is Parameter p)
|
||||
{
|
||||
parameters.Add(p.ParamName);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
return (parameters, dependencies);
|
||||
}
|
||||
|
||||
public static string TypeBreakdown(this IExpression e)
|
||||
{
|
||||
var text = "";
|
||||
|
@ -404,12 +462,12 @@ namespace AspectedRouting.Language
|
|||
var usedTags = new Dictionary<string, HashSet<string>>();
|
||||
foreach (var expr in exprs)
|
||||
{
|
||||
|
||||
var possible = expr.PossibleTags();
|
||||
if (possible == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var (key, values) in possible)
|
||||
{
|
||||
if (!usedTags.TryGetValue(key, out var collection))
|
||||
|
|
|
@ -8,6 +8,7 @@ namespace AspectedRouting.Language
|
|||
public class Context
|
||||
{
|
||||
public readonly Dictionary<string, IExpression> Parameters = new Dictionary<string, IExpression>();
|
||||
|
||||
public readonly Dictionary<string, AspectMetadata> DefinedFunctions = new Dictionary<string, AspectMetadata>();
|
||||
|
||||
public readonly string AspectName;
|
||||
|
@ -48,6 +49,23 @@ namespace AspectedRouting.Language
|
|||
DefinedFunctions[name] = function;
|
||||
}
|
||||
|
||||
public AspectMetadata GetAspect(string name)
|
||||
{
|
||||
if (name.StartsWith("$"))
|
||||
{
|
||||
name = name.Substring(1);
|
||||
}
|
||||
|
||||
if (DefinedFunctions.ContainsKey(name))
|
||||
{
|
||||
return DefinedFunctions[name];
|
||||
}
|
||||
|
||||
throw new ArgumentException(
|
||||
$"The aspect {name} is not a defined function. Known functions are " +
|
||||
string.Join(", ", DefinedFunctions.Keys));
|
||||
}
|
||||
|
||||
public IExpression GetFunction(string name)
|
||||
{
|
||||
if (name.StartsWith("$"))
|
||||
|
|
|
@ -185,6 +185,8 @@ namespace AspectedRouting.Language.Expression
|
|||
return Funcs.Id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (Types.Count() > 1)
|
||||
{
|
||||
|
@ -198,6 +200,18 @@ namespace AspectedRouting.Language.Expression
|
|||
|
||||
return new Apply(_debugInfo, optimized);
|
||||
}
|
||||
|
||||
{
|
||||
var arg = new List<IExpression>();
|
||||
if (
|
||||
Deconstruct.UnApplyAny(
|
||||
Deconstruct.IsFunc(Funcs.Id),
|
||||
Deconstruct.Assign(arg)
|
||||
).Invoke(this))
|
||||
{
|
||||
return arg.First();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var (f, a) = FunctionApplications.Values.First();
|
||||
|
@ -274,6 +288,7 @@ namespace AspectedRouting.Language.Expression
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (_, (f, a)) in FunctionApplications)
|
||||
{
|
||||
f.Visit(visitor);
|
||||
|
|
|
@ -35,9 +35,9 @@ namespace AspectedRouting.Language.Expression
|
|||
Author = author;
|
||||
Filename = filename;
|
||||
VehicleTyps = vehicleTyps;
|
||||
Access = access;
|
||||
Oneway = oneway;
|
||||
Speed = speed;
|
||||
Access = access.Optimize();
|
||||
Oneway = oneway.Optimize();
|
||||
Speed = speed.Optimize();
|
||||
Priority = priority;
|
||||
Metadata = metadata;
|
||||
DefaultParameters = defaultParameters;
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using AspectedRouting.IO;
|
||||
using AspectedRouting.IO.itinero1;
|
||||
using AspectedRouting.IO.itinero2;
|
||||
using AspectedRouting.IO.jsonParser;
|
||||
using AspectedRouting.Language;
|
||||
using AspectedRouting.Language.Expression;
|
||||
|
@ -13,7 +14,7 @@ namespace AspectedRouting
|
|||
{
|
||||
static class Program
|
||||
{
|
||||
public static IEnumerable<(AspectMetadata aspect, AspectTestSuite tests)> ParseAspects(
|
||||
public static List<(AspectMetadata aspect, AspectTestSuite tests)> ParseAspects(
|
||||
this IEnumerable<string> jsonFileNames, List<string> testFileNames, Context context)
|
||||
{
|
||||
var aspects = new List<(AspectMetadata aspect, AspectTestSuite tests)>();
|
||||
|
@ -55,42 +56,11 @@ namespace AspectedRouting
|
|||
return null;
|
||||
}
|
||||
|
||||
private static LuaPrinter GenerateLua(Context context,
|
||||
IEnumerable<(AspectMetadata aspect, AspectTestSuite tests)> aspects,
|
||||
ProfileMetaData profile, List<ProfileTestSuite> profileTests)
|
||||
{
|
||||
var luaPrinter = new LuaPrinter(context);
|
||||
|
||||
var usedFunctions = profile.CalledFunctionsRecursive(context).Values.SelectMany(v => v).ToHashSet();
|
||||
|
||||
foreach (var (aspect, tests) in aspects)
|
||||
{
|
||||
if (!usedFunctions.Contains(aspect.Name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
luaPrinter.AddFunction(aspect);
|
||||
if (tests != null)
|
||||
{
|
||||
luaPrinter.AddTestSuite(tests);
|
||||
}
|
||||
}
|
||||
|
||||
luaPrinter.AddProfile(profile);
|
||||
foreach (var testSuite in profileTests)
|
||||
{
|
||||
luaPrinter.AddTestSuite(testSuite);
|
||||
}
|
||||
|
||||
|
||||
return luaPrinter;
|
||||
}
|
||||
|
||||
private static IEnumerable<(ProfileMetaData profile, List<ProfileTestSuite> profileTests)> ParseProfiles(
|
||||
private static List<(ProfileMetaData profile, List<BehaviourTestSuite> profileTests)> ParseProfiles(
|
||||
IEnumerable<string> jsonFiles, IReadOnlyCollection<string> testFiles, Context context)
|
||||
{
|
||||
var result = new List<(ProfileMetaData profile, List<ProfileTestSuite> profileTests)>();
|
||||
var result = new List<(ProfileMetaData profile, List<BehaviourTestSuite> profileTests)>();
|
||||
foreach (var jsonFile in jsonFiles)
|
||||
{
|
||||
try
|
||||
|
@ -104,13 +74,13 @@ namespace AspectedRouting
|
|||
|
||||
profile.SanityCheckProfile(context);
|
||||
|
||||
var profileTests = new List<ProfileTestSuite>();
|
||||
var profileTests = new List<BehaviourTestSuite>();
|
||||
foreach (var behaviourName in profile.Behaviours.Keys)
|
||||
{
|
||||
var path = testFiles.FindTest($"{profile.Name}.{behaviourName}.behaviour_test.csv");
|
||||
if (path != null && File.Exists(path))
|
||||
{
|
||||
var test = ProfileTestSuite.FromString(context, profile, behaviourName,
|
||||
var test = BehaviourTestSuite.FromString(context, profile, behaviourName,
|
||||
File.ReadAllText(path));
|
||||
profileTests.Add(test);
|
||||
}
|
||||
|
@ -201,6 +171,23 @@ namespace AspectedRouting
|
|||
Console.WriteLine($"Error in the file {file}:\n {msg}");
|
||||
}
|
||||
|
||||
private static void PrintUsedTags(ProfileMetaData profile, Context context)
|
||||
{
|
||||
Console.WriteLine("\n\n\n---------- " + profile.Name + " --------------");
|
||||
foreach (var (key, values) in profile.AllExpressions(context).PossibleTags())
|
||||
{
|
||||
var vs = "*";
|
||||
if (values.Any())
|
||||
{
|
||||
vs = string.Join(", ", values);
|
||||
}
|
||||
|
||||
Console.WriteLine(key + ": " + vs);
|
||||
}
|
||||
|
||||
Console.WriteLine("\n\n\n------------------------");
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
|
@ -255,24 +242,26 @@ namespace AspectedRouting
|
|||
}
|
||||
|
||||
|
||||
Console.WriteLine("\n\n\n---------- " + profile.Name + " --------------");
|
||||
foreach (var (key, values) in profile.AllExpressions(context).PossibleTags())
|
||||
PrintUsedTags(profile, context);
|
||||
|
||||
var aspectTests = aspects.Select(a => a.tests).ToList();
|
||||
var luaProfile = new LuaPrinter1(profile, context,
|
||||
aspectTests,
|
||||
profileTests
|
||||
).ToLua();
|
||||
File.WriteAllText(outputDir + "/" + profile.Name + ".lua", luaProfile);
|
||||
|
||||
foreach (var (behaviourName, behaviourParameters) in profile.Behaviours)
|
||||
{
|
||||
var vs = "*";
|
||||
if (values.Any())
|
||||
{
|
||||
vs = string.Join(", ", values);
|
||||
}
|
||||
|
||||
Console.WriteLine(key + ": " + vs);
|
||||
var lua2behaviour = new LuaPrinter2(
|
||||
profile,
|
||||
behaviourName,
|
||||
context,
|
||||
aspectTests,
|
||||
profileTests.Where(testsSuite => testsSuite.BehaviourName == behaviourName)
|
||||
).ToLua();
|
||||
File.WriteAllText($"{outputDir}/itinero2/{profile.Name}.{behaviourName}.lua", lua2behaviour);
|
||||
}
|
||||
|
||||
Console.WriteLine("\n\n\n------------------------");
|
||||
|
||||
var luaPrinter = GenerateLua(context, aspects, profile, profileTests);
|
||||
|
||||
|
||||
File.WriteAllText(outputDir + "/" + profile.Name + ".lua", luaPrinter.ToLua());
|
||||
}
|
||||
|
||||
Repl(context,
|
||||
|
|
|
@ -8,13 +8,13 @@ using AspectedRouting.Language.Typ;
|
|||
|
||||
namespace AspectedRouting.Tests
|
||||
{
|
||||
public class ProfileTestSuite
|
||||
public class BehaviourTestSuite
|
||||
{
|
||||
public readonly ProfileMetaData Profile;
|
||||
public readonly string BehaviourName;
|
||||
public readonly IEnumerable<(ProfileResult, Dictionary<string, string> tags)> Tests;
|
||||
|
||||
public static ProfileTestSuite FromString(Context c, ProfileMetaData function, string profileName,
|
||||
public static BehaviourTestSuite FromString(Context c, ProfileMetaData function, string behaviourName,
|
||||
string csvContents)
|
||||
{
|
||||
try
|
||||
|
@ -109,21 +109,21 @@ namespace AspectedRouting.Tests
|
|||
}
|
||||
}
|
||||
|
||||
return new ProfileTestSuite(function, profileName, tests);
|
||||
return new BehaviourTestSuite(function, behaviourName, tests);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("In the profile test file for " + profileName, e);
|
||||
throw new Exception("In the profile test file for " + behaviourName, e);
|
||||
}
|
||||
}
|
||||
|
||||
public ProfileTestSuite(
|
||||
public BehaviourTestSuite(
|
||||
ProfileMetaData profile,
|
||||
string profileName,
|
||||
string behaviourName,
|
||||
IEnumerable<(ProfileResult, Dictionary<string, string> tags)> tests)
|
||||
{
|
||||
Profile = profile;
|
||||
BehaviourName = profileName;
|
||||
BehaviourName = behaviourName;
|
||||
Tests = tests;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,16 @@ namespace AspectedRouting
|
|||
return s.Replace("\n", "\n ");
|
||||
}
|
||||
|
||||
public static List<T> InList<T>(this T t)
|
||||
{
|
||||
return new List<T> {t};
|
||||
}
|
||||
|
||||
public static string Lined(this IEnumerable<string> lines)
|
||||
{
|
||||
return string.Join("\n", lines);
|
||||
}
|
||||
|
||||
public static int Multiply(this IEnumerable<int> ints)
|
||||
{
|
||||
var factor = 1;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue