Merge pull request from anyways-open/master

Sync branches
This commit is contained in:
Pieter Vander Vennet 2022-11-02 12:32:11 +01:00 committed by GitHub
commit 4a1d1e0f09
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 304 additions and 244 deletions

View file

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using AspectedRouting.IO.LuaSkeleton; using AspectedRouting.IO.LuaSkeleton;
using AspectedRouting.IO.LuaSnippets; using AspectedRouting.IO.LuaSnippets;
@ -94,4 +95,26 @@ public class SnippetTests
} }
[Fact]
public void ListDotWithHead_GeneratesLua()
{
// (dot head) (stringToTags (mapping speed $ parse))
var eSub = Funcs.Dot.Apply(Funcs.Head,
new Constant(new[]
{
Funcs.Head.Apply(Funcs.StringStringToTags.Apply(
new Mapping(new List<string> { "_speed" }, new[] { Funcs.Parse }))),
Funcs.Const.Apply(new Constant(Typs.Double, 42))
})
);
var condition = Funcs.Dot.Apply(Funcs.Head, Funcs.StringStringToTags.Apply(
new Mapping(new[] { "route" }, new[] { Funcs.Eq.Apply(new Constant("ferry")) })));
var e =
Funcs.IfDotted.Apply(condition, eSub);
e = e.Apply(new LuaLiteral(Typs.Tags, "tags")).Finalize().Optimize(out _);
var ctx = new Context();
var lua = new LuaSkeleton(ctx, true);
var code = IO.LuaSnippets.Snippets.Convert(lua, "varname", e);
Console.WriteLine(code);
}
} }

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Dynamic; using System.Dynamic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using AspectedRouting.IO.itinero1; using AspectedRouting.IO.itinero1;
@ -280,9 +281,9 @@ namespace AspectedRouting.IO.LuaSkeleton
case IExpression e: case IExpression e:
return ToLua(e); return ToLua(e);
case int i: case int i:
return "" + i; return i.ToString(CultureInfo.InvariantCulture);
case double d: case double d:
return "" + d; return d.ToString(CultureInfo.InvariantCulture);
case string s: case string s:
return '"' + s.Replace("\"", "\\\"") + '"'; return '"' + s.Replace("\"", "\\\"") + '"';
case null: case null:

View file

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -12,14 +13,14 @@ namespace AspectedRouting.IO.itinero1
var contents = tags.Select(kv => var contents = tags.Select(kv =>
{ {
var (key, value) = kv; var (key, value) = kv;
var left = "[\"" + key + "\"]"; var left = "[\"" + key .ToString(CultureInfo.InvariantCulture)+ "\"]";
if (Regex.IsMatch(key, "^[a-zA-Z][_a-zA-Z-9]*$")) if (Regex.IsMatch(key, "^[a-zA-Z][_a-zA-Z-9]*$"))
{ {
left = key; left = key;
} }
return $"{left} = \"{value}\""; return $"{left.ToString(CultureInfo.InvariantCulture)} = \"{value.ToString(CultureInfo.InvariantCulture)}\"";
}); });
return "{" + string.Join(", ", contents) + "}"; return "{" + string.Join(", ", contents) + "}";
} }

View file

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using AspectedRouting.Tests; using AspectedRouting.Tests;
@ -94,10 +95,10 @@ namespace AspectedRouting.IO.itinero1
tags.Remove("#" + paramName); tags.Remove("#" + paramName);
} }
var expectedPriority = "" + expected.Priority; var expectedPriority = expected.Priority.ToString(CultureInfo.InvariantCulture);
if (invertPriority) if (invertPriority)
{ {
expectedPriority = $"inv({expectedPriority})"; expectedPriority = $"inv({expectedPriority.ToString(CultureInfo.InvariantCulture)})";
} }
// Generates something like: // Generates something like:
@ -106,7 +107,7 @@ namespace AspectedRouting.IO.itinero1
$"unit_test_profile(behaviour_{testSuite.Profile.Name.AsLuaIdentifier()}_{testSuite.BehaviourName.AsLuaIdentifier()}, " + $"unit_test_profile(behaviour_{testSuite.Profile.Name.AsLuaIdentifier()}_{testSuite.BehaviourName.AsLuaIdentifier()}, " +
$"\"{testSuite.BehaviourName}\", " + $"\"{testSuite.BehaviourName}\", " +
$"{index}, " + $"{index}, " +
$"{{access = \"{D(expected.Access)}\", speed = {expected.Speed}, oneway = \"{D(expected.Oneway)}\", priority = {expectedPriority} }}, " + $"{{access = \"{D(expected.Access)}\", speed = {expected.Speed.ToString(CultureInfo.InvariantCulture)}, oneway = \"{D(expected.Oneway)}\", priority = {expectedPriority} }}, " +
tags.ToLuaTable() + tags.ToLuaTable() +
")"; ")";
} }
@ -151,8 +152,7 @@ namespace AspectedRouting.IO.itinero1
_skeleton.AddDep("unitTest"); _skeleton.AddDep("unitTest");
_skeleton.AddDep("debug_table"); _skeleton.AddDep("debug_table");
return return $"unit_test({functionToApplyName.AsLuaIdentifier()}, \"{functionToApplyName}\", {index.ToString(CultureInfo.InvariantCulture)}, \"{expected.ToString(CultureInfo.InvariantCulture)}\", {parameters.ToLuaTable()}, {tags.ToLuaTable()})";
$"unit_test({functionToApplyName.AsLuaIdentifier()}, \"{functionToApplyName}\", {index}, \"{expected}\", {parameters.ToLuaTable()}, {tags.ToLuaTable()})";
} }

View file

@ -53,7 +53,7 @@ namespace AspectedRouting.IO.LuaSnippets
var isString = cond.Types.First().Equals(Typs.String); var isString = cond.Types.First().Equals(Typs.String);
result += Snippets.Convert(lua, c, cond) + "\n"; result += Snippets.Convert(lua, c, cond) + "\n";
result += $"if ( {c} or {c} == \"yes\" ) then \n"; result += $"if ( {c} == true or {c} == \"yes\" ) then \n";
result += " " + Snippets.Convert(lua, assignTo, ifTrue).Indent(); result += " " + Snippets.Convert(lua, assignTo, ifTrue).Indent();
if (ifElse != null) if (ifElse != null)

View file

@ -151,6 +151,8 @@ namespace AspectedRouting.IO.itinero1
"test_all()", "test_all()",
"if (not failed_tests and not failed_profile_tests) then", "if (not failed_tests and not failed_profile_tests) then",
" print(\"Tests OK\")", " print(\"Tests OK\")",
"else",
" error(\"Some tests failed\")",
"end" "end"
); );
} }

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using AspectedRouting.IO.LuaSkeleton; using AspectedRouting.IO.LuaSkeleton;
using AspectedRouting.IO.LuaSnippets; using AspectedRouting.IO.LuaSnippets;
@ -45,7 +46,7 @@ namespace AspectedRouting.IO.itinero2
var exprInLua = _skeleton.ToLua(exprSpecialized); var exprInLua = _skeleton.ToLua(exprSpecialized);
if (exprInLua.Contains("constRight") || exprInLua.Contains("firstArg")) if (exprInLua.Contains("constRight") || exprInLua.Contains("firstArg"))
throw new Exception("Not optimized properly:" + exprSpecialized.Repr()); throw new Exception("Not optimized properly:" + exprSpecialized.Repr());
aspects.Add(weight + " * " + exprInLua); aspects.Add(weight.ToString(CultureInfo.InvariantCulture) + " * " + exprInLua.ToString(CultureInfo.InvariantCulture));
} }
var scalingFactor = Funcs.Default.Apply(new Constant(Typs.Double, 1.0), _profile.ScalingFactor, tags) var scalingFactor = Funcs.Default.Apply(new Constant(Typs.Double, 1.0), _profile.ScalingFactor, tags)
@ -90,14 +91,15 @@ If result.factor is positive, that is the cost.
There is no forward or backward, so this should always be the same for the same attributes There is no forward or backward, so this should always be the same for the same attributes
*/ */
var parameters = _profile.Behaviours[_behaviourName];
var tags = new LuaLiteral(Typs.Tags, "attributes"); var tags = new LuaLiteral(Typs.Tags, "attributes");
var hasAccess = _profile.ObstacleAccess.Apply(tags).SpecializeToSmallestType().Optimize(out _); var hasAccess = _profile.ObstacleAccess.Apply(tags).SpecializeToSmallestType().Optimize(out _);
var code = new List<string> var code = new List<string>
{ {
"--[[ Function called by itinero2 on every turn restriction relation", " ]]", "--[[ Function called by itinero2 on every turn restriction relation", " ]]",
"function turn_cost_factor(attributes, result)", "function turn_cost_factor(attributes, result)",
" local parameters = default_parameters()",
_parameterPrinter.DeclareParametersFor(parameters),
"local has_access", "local has_access",
Snippets.Convert(_skeleton, "has_access", hasAccess), Snippets.Convert(_skeleton, "has_access", hasAccess),
"if ( has_access == \"no\" or has_access == \"false\") then", "if ( has_access == \"no\" or has_access == \"false\") then",

View file

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using AspectedRouting.IO.itinero1; using AspectedRouting.IO.itinero1;
@ -29,7 +28,7 @@ namespace AspectedRouting.IO.itinero2
private readonly string _behaviourName; private readonly string _behaviourName;
private readonly IEnumerable<BehaviourTestSuite> _behaviourTestSuite; private readonly IEnumerable<BehaviourTestSuite> _behaviourTestSuite;
private readonly Context _context; private readonly Context _context;
private readonly DateTime _lastChangeTime; private readonly bool _includeTests;
private readonly LuaParameterPrinter _parameterPrinter; private readonly LuaParameterPrinter _parameterPrinter;
private readonly ProfileMetaData _profile; private readonly ProfileMetaData _profile;
@ -39,7 +38,7 @@ namespace AspectedRouting.IO.itinero2
public LuaPrinter2(ProfileMetaData profile, string behaviourName, public LuaPrinter2(ProfileMetaData profile, string behaviourName,
Context context, Context context,
List<AspectTestSuite> aspectTests, IEnumerable<BehaviourTestSuite> behaviourTestSuite, List<AspectTestSuite> aspectTests, IEnumerable<BehaviourTestSuite> behaviourTestSuite,
DateTime lastChangeTime) bool includeTests = true)
{ {
_skeleton = new LuaSkeleton.LuaSkeleton(context, true); _skeleton = new LuaSkeleton.LuaSkeleton(context, true);
_profile = profile; _profile = profile;
@ -47,29 +46,61 @@ namespace AspectedRouting.IO.itinero2
_context = context; _context = context;
_aspectTests = aspectTests; _aspectTests = aspectTests;
_behaviourTestSuite = behaviourTestSuite; _behaviourTestSuite = behaviourTestSuite;
_lastChangeTime = lastChangeTime; _includeTests = includeTests;
_parameterPrinter = new LuaParameterPrinter(_profile, _skeleton); _parameterPrinter = new LuaParameterPrinter(_profile, _skeleton);
} }
private string TestRunner()
{
return new List<string>
{
"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 ({_profile.Name}.{_behaviourName})\")",
"else",
" error(\"Some tests failed\")",
"end"
}.Lined();
}
public string ToLua() public string ToLua()
{ {
var profileDescr = _profile.Behaviours[_behaviourName]["description"].Evaluate(_context).ToString(); var profileDescr = _profile.Behaviours[_behaviourName]["description"].Evaluate(_context).ToString();
var header = var header =
new List<string> { new List<string>
{
$"name = \"{_profile.Name}.{_behaviourName}\"", $"name = \"{_profile.Name}.{_behaviourName}\"",
$"description = \"{profileDescr} ({_profile.Description})\"", $"description = \"{profileDescr} ({_profile.Description})\"",
"", "",
"-- The hierarchy of types that this vehicle is; mostly used to check access restrictions", "-- The hierarchy of types that this vehicle is; mostly used to check access restrictions",
$"vehicle_types = "+_skeleton.ToLua(new Constant( _profile.VehicleTyps.Select(v => new Constant(v)).ToArray())) "vehicle_types = " +
_skeleton.ToLua(new Constant(_profile.VehicleTyps.Select(v => new Constant(v)).ToArray()))
}; };
var testPrinter = new LuaTestPrinter(_skeleton, var tests = "";
new List<string> { "unitTestProfile2" }); if (_includeTests) {
var tests = testPrinter.GenerateFullTestSuite( var testPrinter = new LuaTestPrinter(_skeleton,
_behaviourTestSuite.ToList(), new List<string> { "unitTestProfile2" });
new List<AspectTestSuite>(), tests = testPrinter.GenerateFullTestSuite(
true); _behaviourTestSuite.ToList(),
var all = new List<string> { new List<AspectTestSuite>(),
true) + "\n\n" + TestRunner();
}
var all = new List<string>
{
header.Lined(), header.Lined(),
"", "",
GenerateMainFunction(), GenerateMainFunction(),
@ -87,25 +118,7 @@ namespace AspectedRouting.IO.itinero2
"", "",
string.Join("\n\n", _skeleton.GenerateConstants()), string.Join("\n\n", _skeleton.GenerateConstants()),
"", "",
tests, 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 ({_profile.Name}.{_behaviourName})\")",
"end"
}; };
return all.Lined(); return all.Lined();

View file

@ -14,6 +14,6 @@ function if_then_else_dotted(conditionf, thnf, elsef, arg)
if (condition) then if (condition) then
return applyIfNeeded(thnf, arg) return applyIfNeeded(thnf, arg)
else else
return applyIfNeeded(elsef, arg) -- if no third parameter is given, 'els' will be nil return applyIfNeeded(elsef, arg) -- if no third parameter is given, 'else' will be nil
end end
end end

View file

@ -45,6 +45,8 @@ namespace AspectedRouting.Language
AddParams(profile.Access, "profile definition for " + profile.Name + ".access"); AddParams(profile.Access, "profile definition for " + profile.Name + ".access");
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");
AddParams(profile.ObstacleAccess, "profile definition for " + profile.Name + ".obstacleaccess");
AddParams(profile.ObstacleCost, "profile definition for " + profile.Name + ".obstaclecost");
foreach (var (key, expr) in profile.Priority) foreach (var (key, expr) in profile.Priority)
{ {

148
AspectedRouting/Printer.cs Normal file
View file

@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using AspectedRouting.IO.itinero1;
using AspectedRouting.IO.itinero2;
using AspectedRouting.IO.md;
using AspectedRouting.Language;
using AspectedRouting.Language.Expression;
using AspectedRouting.Tests;
namespace AspectedRouting
{
/**
* Prints to the specified location
*/
public class Printer
{
private readonly List<(AspectMetadata aspect, AspectTestSuite tests)> _aspects;
private readonly Context _context;
private readonly string _outputDirectory;
private readonly ProfileMetaData _profile;
private readonly List<BehaviourTestSuite> _profileTests;
private readonly bool _includeTests;
public Printer(string outputDirectory, ProfileMetaData profile, Context context,
List<(AspectMetadata aspect, AspectTestSuite tests)> aspects,
List<BehaviourTestSuite> profileTests, bool includeTests)
{
_outputDirectory = outputDirectory;
_profile = profile;
_context = context;
_aspects = aspects;
_profileTests = profileTests;
_includeTests = includeTests;
if (!Directory.Exists($"{outputDirectory}/profile-documentation/")) {
Directory.CreateDirectory($"{outputDirectory}/profile-documentation/");
}
if (!Directory.Exists($"{outputDirectory}/itinero1/")) {
Directory.CreateDirectory($"{outputDirectory}/itinero1/");
}
if (!Directory.Exists($"{outputDirectory}/itinero2/")) {
Directory.CreateDirectory($"{outputDirectory}/itinero2/");
}
}
public void PrintUsedTags()
{
var profile = _profile;
var context = _context;
Console.WriteLine("\n\n\n---------- " + profile.Name +
" : used tags and corresponding values --------------");
foreach (var (key, values) in profile.AllExpressions(context).PossibleTags()) {
var vs = "*";
if (values.Any()) {
vs = string.Join(", ", values);
}
Console.WriteLine(key + ": " + vs);
}
Console.WriteLine("\n\n\n------------------------");
}
public void WriteProfile1()
{
var aspectTests = _aspects.Select(a => a.tests).ToList();
var luaProfile = new LuaPrinter1(_profile, _context,
aspectTests,
_profileTests
).ToLua();
var itinero1ProfileFile = Path.Combine($"{_outputDirectory}/itinero1/" + _profile.Name + ".lua");
File.WriteAllText(itinero1ProfileFile, luaProfile);
Console.WriteLine($"Written {new FileInfo(itinero1ProfileFile).FullName}");
}
public void WriteAllProfile2()
{
foreach (var (behaviourName,_) in _profile.Behaviours) {
WriteProfile2(behaviourName);
}
}
public void WriteProfile2(string behaviourName)
{
var aspectTests = _aspects.Select(a => a.tests).ToList();
var lua2behaviour = new LuaPrinter2(
_profile,
behaviourName,
_context,
aspectTests,
_profileTests.Where(testsSuite => testsSuite.BehaviourName == behaviourName),
_includeTests
).ToLua();
var itinero2ProfileFile = Path.Combine($"{_outputDirectory}/itinero2/{_profile.Name}.{behaviourName}.lua");
File.WriteAllText(
itinero2ProfileFile,
lua2behaviour);
Console.WriteLine($"Written {new FileInfo(itinero2ProfileFile).FullName}");
}
public void PrintMdInfo()
{
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 behaviourMd = new ProfileToMD(_profile, behaviourName, _context);
File.WriteAllText(
$"{_outputDirectory}/profile-documentation/{_profile.Name}.{behaviourName}.md",
behaviourMd.ToString());
profileMd.AddTitle($"[{behaviourName}](./{_profile.Name}.{behaviourName}.md)", 2);
profileMd.Add(vars["description"].Evaluate(_context).ToString());
profileMd.Add(behaviourMd.MainFormula());
}
File.WriteAllText(
$"{_outputDirectory}/profile-documentation/{_profile.Name}.md",
profileMd.ToString());
}
}
}

View file

@ -1,12 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using AspectedRouting.IO; using AspectedRouting.IO;
using AspectedRouting.IO.itinero1;
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;
@ -19,13 +18,11 @@ 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) if (aspect == null) {
{
continue; continue;
} }
@ -33,8 +30,7 @@ namespace AspectedRouting
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));
} }
@ -47,13 +43,11 @@ 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) if (testPaths.Count > 0) {
{
return testPaths.First(); return testPaths.First();
} }
@ -66,43 +60,35 @@ namespace AspectedRouting
{ {
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) if (profile == null) {
{
continue; 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;
} }
@ -111,28 +97,23 @@ 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) if (read == null) {
{
return; // End of stream has been reached 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")) if (read.Equals("quit")) {
{
return; return;
} }
if (read.Equals("help")) if (read.Equals("help")) {
{
Console.WriteLine( Console.WriteLine(
Utils.Lines( Utils.Lines(
"select <behaviourName> to change behaviour or <vehicle.behaviourName> to change vehicle", "select <behaviourName> to change behaviour or <vehicle.behaviourName> to change vehicle",
@ -140,40 +121,31 @@ namespace AspectedRouting
continue; continue;
} }
if (read.Equals("clear")) if (read.Equals("clear")) {
{ for (var i = 0; i < 80; i++) Console.WriteLine();
for (var i = 0; i < 80; i++)
{
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;
} }
profile = newProfile; profile = newProfile;
beh = beh.Substring(beh.IndexOf(".") + 1); beh = beh.Substring(beh.IndexOf(".", StringComparison.Ordinal) + 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));
} }
@ -184,34 +156,27 @@ 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;
} }
try try {
{
var strSplit = str.Split("="); var strSplit = str.Split("=");
var k = strSplit[0].Trim(); var k = strSplit[0].Trim();
var v = strSplit[1].Trim(); var v = strSplit[1].Trim();
tags[k] = v; tags[k] = v;
} }
catch (Exception) catch (Exception) {
{
Console.Error.WriteLine("Could not parse tag: " + str); Console.Error.WriteLine("Could not parse tag: " + str);
} }
} }
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);
} }
@ -221,8 +186,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;
} }
@ -230,113 +194,29 @@ namespace AspectedRouting
Console.WriteLine($"Error in the file {file}:\n {msg}"); Console.WriteLine($"Error in the file {file}:\n {msg}");
} }
private static void PrintUsedTags(ProfileMetaData profile, Context context)
{
Console.WriteLine("\n\n\n---------- " + profile.Name + " : used tags and corresponding values --------------");
foreach (var (key, values) in profile.AllExpressions(context).PossibleTags())
{
var vs = "*";
if (values.Any())
{
vs = string.Join(", ", values);
}
Console.WriteLine(key + ": " + vs);
}
Console.WriteLine("\n\n\n------------------------");
}
private static int Main(string[] args) private static int Main(string[] args)
{ {
var errMessage = MainWithError(args); var errMessage = MainWithError(args);
if (errMessage == null) return 0; if (errMessage == null) {
return 0;
}
Console.WriteLine(errMessage); Console.WriteLine(errMessage);
return 1; return 1;
} }
private static void WriteOutputFiles(Context context, private static void WriteOutputFiles(Context context,
List<(AspectMetadata aspect, AspectTestSuite tests)> aspects, string outputDir, List<(AspectMetadata aspect, AspectTestSuite tests)> aspects, string outputDir,
List<(ProfileMetaData profile, List<BehaviourTestSuite> profileTests)> profiles, DateTime lastChange) List<(ProfileMetaData profile, List<BehaviourTestSuite> profileTests)> profiles, bool includeTests)
{ {
if (!Directory.Exists($"{outputDir}/profile-documentation/")) foreach (var (profile, profileTests) in profiles) {
{ var printer = new Printer(outputDir, profile, context, aspects, profileTests, includeTests);
Directory.CreateDirectory($"{outputDir}/profile-documentation/"); printer.PrintUsedTags();
} printer.WriteProfile1();
printer.PrintMdInfo();
if (!Directory.Exists($"{outputDir}/itinero1/")) printer.WriteAllProfile2();
{
Directory.CreateDirectory($"{outputDir}/itinero1/");
}
if (!Directory.Exists($"{outputDir}/itinero2/"))
{
Directory.CreateDirectory($"{outputDir}/itinero2/");
}
foreach (var (profile, profileTests) in profiles)
{
PrintUsedTags(profile, context);
var aspectTests = aspects.Select(a => a.tests).ToList();
var luaProfile = new LuaPrinter1(profile, context,
aspectTests,
profileTests
).ToLua();
var itinero1ProfileFile = Path.Combine($"{outputDir}/itinero1/" + profile.Name + ".lua");
File.WriteAllText(itinero1ProfileFile, luaProfile);
Console.WriteLine($"Written {(new FileInfo(itinero1ProfileFile)).FullName}");
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(
profile,
behaviourName,
context,
aspectTests,
profileTests.Where(testsSuite => testsSuite.BehaviourName == behaviourName),
lastChange
).ToLua();
var itinero2ProfileFile = Path.Combine($"{outputDir}/itinero2/{profile.Name}.{behaviourName}.lua");
File.WriteAllText(
itinero2ProfileFile,
lua2behaviour);
Console.WriteLine($"Written {(new FileInfo(itinero2ProfileFile)).FullName}");
var behaviourMd = new ProfileToMD(profile, behaviourName, context);
File.WriteAllText(
$"{outputDir}/profile-documentation/{profile.Name}.{behaviourName}.md",
behaviourMd.ToString());
profileMd.AddTitle($"[{behaviourName}](./{profile.Name}.{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",
@ -349,16 +229,19 @@ namespace AspectedRouting
public static string MainWithError(string[] args) public static string MainWithError(string[] args)
{ {
if (args.Length < 2) Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US", false);
{ 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> [--include-tests]\n" +
"The flag '--include-tests' will append some self-tests in the lua files";
} }
var inputDir = args[0]; var inputDir = args[0];
var outputDir = args[1]; var outputDir = args[1];
var includeTests = args.Contains("--include-tests");
var runRepl = !args.Contains("--no-repl");
if (!Directory.Exists(outputDir)) if (!Directory.Exists(outputDir)) {
{
Directory.CreateDirectory(outputDir); Directory.CreateDirectory(outputDir);
} }
@ -373,10 +256,8 @@ 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;
} }
@ -388,17 +269,12 @@ namespace AspectedRouting
var aspects = ParseAspects(files, tests, context); var aspects = ParseAspects(files, tests, context);
foreach (var (aspect, _) in aspects) foreach (var (aspect, _) in aspects) context.AddFunction(aspect.Name, aspect);
{
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;
} }
} }
@ -409,40 +285,32 @@ 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) if (testsOk) {
{ WriteOutputFiles(context, aspects, outputDir, profiles, includeTests);
WriteOutputFiles(context, aspects, outputDir, profiles, lastChange);
} }
if (!args.Contains("--no-repl")) if (runRepl) {
{
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 !testsOk ? "Some tests failed, quitting now without generating output" : null; return !testsOk ? "Some tests failed, quitting now without generating output" : null;
} }
} }