From d990597b55bcde8defe9e713bc6425cd089d9cbe Mon Sep 17 00:00:00 2001
From: Pieter Vander Vennet <pietervdvn@posteo.net>
Date: Thu, 27 Oct 2022 11:03:36 +0200
Subject: [PATCH 01/10] Expose parameters in obstacleAccess

---
 AspectedRouting/IO/itinero2/LuaPrinter2.MainFunction.cs | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/AspectedRouting/IO/itinero2/LuaPrinter2.MainFunction.cs b/AspectedRouting/IO/itinero2/LuaPrinter2.MainFunction.cs
index bcc318b..fcbbd51 100644
--- a/AspectedRouting/IO/itinero2/LuaPrinter2.MainFunction.cs
+++ b/AspectedRouting/IO/itinero2/LuaPrinter2.MainFunction.cs
@@ -90,14 +90,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
              */
-
+            var parameters = _profile.Behaviours[_behaviourName];
             var tags = new LuaLiteral(Typs.Tags, "attributes");
             var hasAccess = _profile.ObstacleAccess.Apply(tags).SpecializeToSmallestType().Optimize(out _);
             var code = new List<string>
             {
                 "--[[ Function called by itinero2 on every turn restriction relation", " ]]",
                 "function turn_cost_factor(attributes, result)",
-
+                "    local parameters = default_parameters()",
+                _parameterPrinter.DeclareParametersFor(parameters),
                 "local has_access",
                 Snippets.Convert(_skeleton, "has_access", hasAccess),
                 "if ( has_access == \"no\" or has_access == \"false\") then",

From 86c62e520a5399fb721ad8caac4ba6008a1e400e Mon Sep 17 00:00:00 2001
From: Pieter Vander Vennet <pietervdvn@posteo.net>
Date: Thu, 27 Oct 2022 13:34:00 +0200
Subject: [PATCH 02/10] Add obstacleAccess & obstracleCost to parameter checks

---
 AspectedRouting/Language/Analysis.cs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/AspectedRouting/Language/Analysis.cs b/AspectedRouting/Language/Analysis.cs
index 2609ae6..1a903b5 100644
--- a/AspectedRouting/Language/Analysis.cs
+++ b/AspectedRouting/Language/Analysis.cs
@@ -45,6 +45,8 @@ namespace AspectedRouting.Language
             AddParams(profile.Access, "profile definition for " + profile.Name + ".access");
             AddParams(profile.Oneway, "profile definition for " + profile.Name + ".oneway");
             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)
             {

From 717d0a951405d4dc6e99e6ce7c833c9aac566b54 Mon Sep 17 00:00:00 2001
From: Pieter Vander Vennet <pietervdvn@posteo.net>
Date: Thu, 27 Oct 2022 13:35:01 +0200
Subject: [PATCH 03/10] Fix: IfThenElseSnippted would also trigger if any
 string was specified as condition; this should only be if 'true' or '"yes"'

---
 AspectedRouting/IO/LuaSnippets/IfThenElseSnippet.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/AspectedRouting/IO/LuaSnippets/IfThenElseSnippet.cs b/AspectedRouting/IO/LuaSnippets/IfThenElseSnippet.cs
index cebd784..1f447e4 100644
--- a/AspectedRouting/IO/LuaSnippets/IfThenElseSnippet.cs
+++ b/AspectedRouting/IO/LuaSnippets/IfThenElseSnippet.cs
@@ -53,7 +53,7 @@ namespace AspectedRouting.IO.LuaSnippets
 
                 var isString = cond.Types.First().Equals(Typs.String);
                 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();
 
                 if (ifElse != null)

From e9e53ac8b68c395260f768dea6c45683dcebec6c Mon Sep 17 00:00:00 2001
From: Pieter Vander Vennet <pietervdvn@posteo.net>
Date: Thu, 27 Oct 2022 13:35:17 +0200
Subject: [PATCH 04/10] Fix typo in comments

---
 AspectedRouting/IO/lua/if_then_else_dotted.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/AspectedRouting/IO/lua/if_then_else_dotted.lua b/AspectedRouting/IO/lua/if_then_else_dotted.lua
index 018254e..4e56a0f 100644
--- a/AspectedRouting/IO/lua/if_then_else_dotted.lua
+++ b/AspectedRouting/IO/lua/if_then_else_dotted.lua
@@ -14,6 +14,6 @@ function if_then_else_dotted(conditionf, thnf, elsef, arg)
     if (condition) then
         return applyIfNeeded(thnf, arg)
     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
\ No newline at end of file

From 1eb4139174b60ebfe6ad9579b9de0812dabeca2b Mon Sep 17 00:00:00 2001
From: Pieter Vander Vennet <pietervdvn@posteo.net>
Date: Thu, 27 Oct 2022 13:36:38 +0200
Subject: [PATCH 05/10] Selftests are only emitted (for Itinero2.0) if the
 approriate flag is specified, fix
 https://github.com/anyways-open/routing-profiles-aspects/issues/15

---
 AspectedRouting/IO/itinero2/LuaPrinter2.cs | 75 +++++++++++++---------
 AspectedRouting/Program.cs                 | 15 +++--
 2 files changed, 52 insertions(+), 38 deletions(-)

diff --git a/AspectedRouting/IO/itinero2/LuaPrinter2.cs b/AspectedRouting/IO/itinero2/LuaPrinter2.cs
index dc3d50c..c7dbf46 100644
--- a/AspectedRouting/IO/itinero2/LuaPrinter2.cs
+++ b/AspectedRouting/IO/itinero2/LuaPrinter2.cs
@@ -1,4 +1,3 @@
-using System;
 using System.Collections.Generic;
 using System.Linq;
 using AspectedRouting.IO.itinero1;
@@ -29,7 +28,7 @@ namespace AspectedRouting.IO.itinero2
         private readonly string _behaviourName;
         private readonly IEnumerable<BehaviourTestSuite> _behaviourTestSuite;
         private readonly Context _context;
-        private readonly DateTime _lastChangeTime;
+        private readonly bool _includeTests;
         private readonly LuaParameterPrinter _parameterPrinter;
         private readonly ProfileMetaData _profile;
 
@@ -39,7 +38,7 @@ namespace AspectedRouting.IO.itinero2
         public LuaPrinter2(ProfileMetaData profile, string behaviourName,
             Context context,
             List<AspectTestSuite> aspectTests, IEnumerable<BehaviourTestSuite> behaviourTestSuite,
-            DateTime lastChangeTime)
+            bool includeTests = true)
         {
             _skeleton = new LuaSkeleton.LuaSkeleton(context, true);
             _profile = profile;
@@ -47,29 +46,59 @@ namespace AspectedRouting.IO.itinero2
             _context = context;
             _aspectTests = aspectTests;
             _behaviourTestSuite = behaviourTestSuite;
-            _lastChangeTime = lastChangeTime;
+            _includeTests = includeTests;
             _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})\")",
+                "end"
+            }.Lined();
+        }
+
         public string ToLua()
         {
             var profileDescr = _profile.Behaviours[_behaviourName]["description"].Evaluate(_context).ToString();
             var header =
-                new List<string> {
+                new List<string>
+                {
                     $"name = \"{_profile.Name}.{_behaviourName}\"",
                     $"description = \"{profileDescr} ({_profile.Description})\"",
                     "",
                     "-- 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,
-                new List<string> { "unitTestProfile2" });
-            var tests = testPrinter.GenerateFullTestSuite(
-                _behaviourTestSuite.ToList(),
-                new List<AspectTestSuite>(),
-                true);
-            var all = new List<string> {
+            var tests = "";
+            if (_includeTests) {
+                var testPrinter = new LuaTestPrinter(_skeleton,
+                    new List<string> { "unitTestProfile2" });
+                tests = testPrinter.GenerateFullTestSuite(
+                    _behaviourTestSuite.ToList(),
+                    new List<AspectTestSuite>(),
+                    true) + "\n\n" + TestRunner();
+            }
+
+            var all = new List<string>
+            {
                 header.Lined(),
                 "",
                 GenerateMainFunction(),
@@ -87,25 +116,7 @@ namespace AspectedRouting.IO.itinero2
                 "",
                 string.Join("\n\n", _skeleton.GenerateConstants()),
                 "",
-                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"
+                tests
             };
 
             return all.Lined();
diff --git a/AspectedRouting/Program.cs b/AspectedRouting/Program.cs
index 898dc1f..34b580b 100644
--- a/AspectedRouting/Program.cs
+++ b/AspectedRouting/Program.cs
@@ -258,7 +258,7 @@ namespace AspectedRouting
 
         private static void WriteOutputFiles(Context context,
             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/"))
             {
@@ -315,7 +315,7 @@ namespace AspectedRouting
                         context,
                         aspectTests,
                         profileTests.Where(testsSuite => testsSuite.BehaviourName == behaviourName),
-                        lastChange
+                        includeTests
                     ).ToLua();
 
                     var itinero2ProfileFile = Path.Combine($"{outputDir}/itinero2/{profile.Name}.{behaviourName}.lua");
@@ -351,12 +351,15 @@ namespace AspectedRouting
         {
             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 outputDir = args[1];
-
+            var includeTests = args.Contains("--include-tests");
+            var runRepl = !args.Contains("--no-repl");
+            
             if (!Directory.Exists(outputDir))
             {
                 Directory.CreateDirectory(outputDir);
@@ -429,11 +432,11 @@ namespace AspectedRouting
 
             if (testsOk)
             {
-                WriteOutputFiles(context, aspects, outputDir, profiles, lastChange);
+                WriteOutputFiles(context, aspects, outputDir, profiles, includeTests);
             }
 
 
-            if (!args.Contains("--no-repl"))
+            if (runRepl)
             {
                 Repl(context, profiles
                     .Select(p => p.profile)

From 59bc7fef63ca67021bb38f5e667d12c7e69937b3 Mon Sep 17 00:00:00 2001
From: Pieter Vander Vennet <pietervdvn@posteo.net>
Date: Tue, 1 Nov 2022 18:05:30 +0100
Subject: [PATCH 06/10] Split of printing code

---
 AspectedRouting.Test/Snippets/SnippetTests.cs |  23 ++
 AspectedRouting/Printer.cs                    | 148 ++++++++++
 AspectedRouting/Program.cs                    | 257 +++++-------------
 3 files changed, 232 insertions(+), 196 deletions(-)
 create mode 100644 AspectedRouting/Printer.cs

diff --git a/AspectedRouting.Test/Snippets/SnippetTests.cs b/AspectedRouting.Test/Snippets/SnippetTests.cs
index cbfabe4..a54eaaf 100644
--- a/AspectedRouting.Test/Snippets/SnippetTests.cs
+++ b/AspectedRouting.Test/Snippets/SnippetTests.cs
@@ -1,3 +1,4 @@
+using System;
 using System.Collections.Generic;
 using AspectedRouting.IO.LuaSkeleton;
 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);
+    }
 }
\ No newline at end of file
diff --git a/AspectedRouting/Printer.cs b/AspectedRouting/Printer.cs
new file mode 100644
index 0000000..000f85c
--- /dev/null
+++ b/AspectedRouting/Printer.cs
@@ -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());
+        }
+    }
+}
\ No newline at end of file
diff --git a/AspectedRouting/Program.cs b/AspectedRouting/Program.cs
index 34b580b..60aee5b 100644
--- a/AspectedRouting/Program.cs
+++ b/AspectedRouting/Program.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using AspectedRouting.IO;
-using AspectedRouting.IO.itinero1;
 using AspectedRouting.IO.itinero2;
 using AspectedRouting.IO.jsonParser;
 using AspectedRouting.IO.md;
@@ -19,13 +18,11 @@ namespace AspectedRouting
             this IEnumerable<string> jsonFileNames, List<string> testFileNames, Context context)
         {
             var aspects = new List<(AspectMetadata aspect, AspectTestSuite tests)>();
-            foreach (var file in jsonFileNames)
-            {
+            foreach (var file in jsonFileNames) {
                 var fi = new FileInfo(file);
 
                 var aspect = JsonParser.AspectFromJson(context, File.ReadAllText(file), fi.Name);
-                if (aspect == null)
-                {
+                if (aspect == null) {
                     continue;
                 }
 
@@ -33,8 +30,7 @@ namespace AspectedRouting
                 var testName = aspect.Name + ".test.csv";
                 var testPath = testFileNames.FindTest(testName);
                 AspectTestSuite tests = null;
-                if (!string.IsNullOrEmpty(testPath) && File.Exists(testPath))
-                {
+                if (!string.IsNullOrEmpty(testPath) && File.Exists(testPath)) {
                     tests = AspectTestSuite.FromString(aspect, File.ReadAllText(testPath));
                 }
 
@@ -47,13 +43,11 @@ namespace AspectedRouting
         private static string FindTest(this IEnumerable<string> testFileNames, string testName)
         {
             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");
             }
 
-            if (testPaths.Count > 0)
-            {
+            if (testPaths.Count > 0) {
                 return testPaths.First();
             }
 
@@ -66,43 +60,35 @@ namespace AspectedRouting
         {
             var result = new List<(ProfileMetaData profile, List<BehaviourTestSuite> profileTests)>();
             foreach (var jsonFile in jsonFiles)
-            {
-                try
-                {
+                try {
                     var profile =
                         JsonParser.ProfileFromJson(context, File.ReadAllText(jsonFile), new FileInfo(jsonFile),
                             lastChange);
-                    if (profile == null)
-                    {
+                    if (profile == null) {
                         continue;
                     }
 
                     profile.SanityCheckProfile(context);
 
                     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");
-                        if (path != null && File.Exists(path))
-                        {
+                        if (path != null && File.Exists(path)) {
                             var test = BehaviourTestSuite.FromString(context, profile, behaviourName,
                                 File.ReadAllText(path));
                             profileTests.Add(test);
                         }
-                        else
-                        {
+                        else {
                             Console.WriteLine($"[{profile.Name}] WARNING: no test found for behaviour {behaviourName}");
                         }
                     }
 
                     result.Add((profile, profileTests));
                 }
-                catch (Exception e)
-                {
+                catch (Exception e) {
                     // PrintError(jsonFile, e);
                     throw new Exception("In the file " + jsonFile, e);
                 }
-            }
 
             return result;
         }
@@ -111,28 +97,23 @@ namespace AspectedRouting
         {
             var profile = profiles["emergency_vehicle"];
             var behaviour = profile.Behaviours.Keys.First();
-            do
-            {
+            do {
                 Console.Write(profile.Name + "." + behaviour + " > ");
                 var read = Console.ReadLine();
-                if (read == null)
-                {
+                if (read == null) {
                     return; // End of stream has been reached
                 }
 
-                if (read == "")
-                {
+                if (read == "") {
                     Console.WriteLine("looƆ sᴉ dɐWʇǝǝɹʇSuǝdO");
                     continue;
                 }
 
-                if (read.Equals("quit"))
-                {
+                if (read.Equals("quit")) {
                     return;
                 }
 
-                if (read.Equals("help"))
-                {
+                if (read.Equals("help")) {
                     Console.WriteLine(
                         Utils.Lines(
                             "select <behaviourName> to change behaviour or <vehicle.behaviourName> to change vehicle",
@@ -140,40 +121,31 @@ namespace AspectedRouting
                     continue;
                 }
 
-                if (read.Equals("clear"))
-                {
-                    for (var i = 0; i < 80; i++)
-                    {
-                        Console.WriteLine();
-                    }
+                if (read.Equals("clear")) {
+                    for (var i = 0; i < 80; i++) Console.WriteLine();
 
                     continue;
                 }
 
-                if (read.StartsWith("select"))
-                {
+                if (read.StartsWith("select")) {
                     var beh = read.Substring("select".Length + 1).Trim();
 
-                    if (beh.Contains("."))
-                    {
+                    if (beh.Contains(".")) {
                         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");
                             continue;
                         }
 
                         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;
                         Console.WriteLine("Switched to " + beh);
                     }
-                    else
-                    {
+                    else {
                         Console.WriteLine("Behaviour not found. Known behaviours are:\n   " +
                                           string.Join("\n   ", profile.Behaviours.Keys));
                     }
@@ -184,34 +156,27 @@ namespace AspectedRouting
 
                 var tagsRaw = read.Split(";").Select(s => s.Trim());
                 var tags = new Dictionary<string, string>();
-                foreach (var str in tagsRaw)
-                {
-                    if (str == "")
-                    {
+                foreach (var str in tagsRaw) {
+                    if (str == "") {
                         continue;
                     }
 
-                    try
-                    {
-
+                    try {
                         var strSplit = str.Split("=");
                         var k = strSplit[0].Trim();
                         var v = strSplit[1].Trim();
                         tags[k] = v;
                     }
-                    catch (Exception)
-                    {
+                    catch (Exception) {
                         Console.Error.WriteLine("Could not parse tag: " + str);
                     }
                 }
 
-                try
-                {
+                try {
                     var result = profile.Run(c, behaviour, tags);
                     Console.WriteLine(result);
                 }
-                catch (Exception e)
-                {
+                catch (Exception e) {
                     Console.WriteLine(e);
                     Console.WriteLine(e.Message);
                 }
@@ -221,8 +186,7 @@ namespace AspectedRouting
         private static void PrintError(string file, Exception exception)
         {
             var msg = exception.Message;
-            while (exception.InnerException != null)
-            {
+            while (exception.InnerException != null) {
                 exception = exception.InnerException;
                 msg += "\n    " + exception.Message;
             }
@@ -230,113 +194,30 @@ 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 + " : 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)
         {
             var errMessage = MainWithError(args);
-            if (errMessage == null) return 0;
+            if (errMessage == null) {
+                return 0;
+            }
 
             Console.WriteLine(errMessage);
             return 1;
         }
 
+
         private static void WriteOutputFiles(Context context,
             List<(AspectMetadata aspect, AspectTestSuite tests)> aspects, string outputDir,
             List<(ProfileMetaData profile, List<BehaviourTestSuite> profileTests)> profiles, bool includeTests)
         {
-            if (!Directory.Exists($"{outputDir}/profile-documentation/"))
-            {
-                Directory.CreateDirectory($"{outputDir}/profile-documentation/");
-            }
-
-            if (!Directory.Exists($"{outputDir}/itinero1/"))
-            {
-                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),
-                        includeTests
-                    ).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());
+            foreach (var (profile, profileTests) in profiles) {
+                var printer = new Printer(outputDir, profile, context, aspects, profileTests, includeTests);
+                printer.PrintUsedTags();
+                printer.WriteProfile1();
+                printer.PrintMdInfo();
+                printer.WriteAllProfile2();
+                printer.WriteProfile2("short");
             }
 
             File.WriteAllText($"{outputDir}/ProfileMetadata.json",
@@ -349,19 +230,18 @@ namespace AspectedRouting
 
         public static string MainWithError(string[] args)
         {
-            if (args.Length < 2)
-            {
-                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";
+            if (args.Length < 2) {
+                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 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);
             }
 
@@ -376,10 +256,8 @@ namespace AspectedRouting
                 .ToList();
             tests.Sort();
 
-            foreach (var test in tests)
-            {
-                if (test.EndsWith(".test.csv") || test.EndsWith(".behaviour_test.csv"))
-                {
+            foreach (var test in tests) {
+                if (test.EndsWith(".test.csv") || test.EndsWith(".behaviour_test.csv")) {
                     continue;
                 }
 
@@ -391,17 +269,12 @@ namespace AspectedRouting
 
             var aspects = ParseAspects(files, tests, context);
 
-            foreach (var (aspect, _) in aspects)
-            {
-                context.AddFunction(aspect.Name, aspect);
-            }
+            foreach (var (aspect, _) in aspects) context.AddFunction(aspect.Name, aspect);
 
             var lastChange = DateTime.UnixEpoch;
-            foreach (var file in files)
-            {
+            foreach (var file in files) {
                 var time = new FileInfo(file).LastWriteTimeUtc;
-                if (lastChange < time)
-                {
+                if (lastChange < time) {
                     lastChange = time;
                 }
             }
@@ -412,40 +285,32 @@ namespace AspectedRouting
             // With everything parsed and typechecked, time for tests
             var testsOk = true;
             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");
                 }
-                else
-                {
+                else {
                     testsOk &= t.Run();
                 }
-            }
 
 
             foreach (var (profile, profileTests) in profiles)
-                foreach (var test in profileTests)
-                {
-                    testsOk &= test.Run(context);
-                }
+            foreach (var test in profileTests)
+                testsOk &= test.Run(context);
 
-            if (testsOk)
-            {
+            if (testsOk) {
                 WriteOutputFiles(context, aspects, outputDir, profiles, includeTests);
             }
 
 
-            if (runRepl)
-            {
+            if (runRepl) {
                 Repl(context, profiles
                     .Select(p => p.profile)
                     .ToDictionary(p => p.Name, p => p));
             }
-            else
-            {
+            else {
                 Console.WriteLine("Not starting REPL as --no-repl is specified");
             }
+
             return !testsOk ? "Some tests failed, quitting now without generating output" : null;
         }
     }

From 54fa859aee72f815ba3e4591f1e4a22f3025726b Mon Sep 17 00:00:00 2001
From: Pieter Vander Vennet <pietervdvn@posteo.net>
Date: Tue, 1 Nov 2022 18:06:47 +0100
Subject: [PATCH 07/10] Set Locale before starting actual program, should
 mitigate #22

---
 AspectedRouting/Program.cs | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/AspectedRouting/Program.cs b/AspectedRouting/Program.cs
index 60aee5b..cb96f87 100644
--- a/AspectedRouting/Program.cs
+++ b/AspectedRouting/Program.cs
@@ -1,11 +1,11 @@
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.Linq;
+using System.Threading;
 using AspectedRouting.IO;
-using AspectedRouting.IO.itinero2;
 using AspectedRouting.IO.jsonParser;
-using AspectedRouting.IO.md;
 using AspectedRouting.Language;
 using AspectedRouting.Language.Expression;
 using AspectedRouting.Tests;
@@ -230,6 +230,7 @@ namespace AspectedRouting
 
         public static string MainWithError(string[] args)
         {
+            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US", false); 
             if (args.Length < 2) {
                 return
                     "Usage: <directory where all aspects and profiles can be found> <outputdirectory> [--include-tests]\n" +

From ba177dd0cd6ea8005a0bf54594650a4cdc17ceab Mon Sep 17 00:00:00 2001
From: Pieter Vander Vennet <pietervdvn@posteo.net>
Date: Tue, 1 Nov 2022 18:48:53 +0100
Subject: [PATCH 08/10] Remove obsolete testing code

---
 AspectedRouting/Program.cs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/AspectedRouting/Program.cs b/AspectedRouting/Program.cs
index cb96f87..b94fe52 100644
--- a/AspectedRouting/Program.cs
+++ b/AspectedRouting/Program.cs
@@ -217,7 +217,6 @@ namespace AspectedRouting
                 printer.WriteProfile1();
                 printer.PrintMdInfo();
                 printer.WriteAllProfile2();
-                printer.WriteProfile2("short");
             }
 
             File.WriteAllText($"{outputDir}/ProfileMetadata.json",

From cb90b4c57c34749ec133fc03c839c50f983c5d0b Mon Sep 17 00:00:00 2001
From: Pieter Vander Vennet <pietervdvn@posteo.net>
Date: Tue, 1 Nov 2022 18:49:29 +0100
Subject: [PATCH 09/10] Crash on failing self test; add some mitigation for
 locale variant strings (WIP)

---
 .../IO/LuaSkeleton/LuaSkeleton.Expressions.cs          |  5 +++--
 AspectedRouting/IO/LuaSkeleton/LuaStringExtensions.cs  |  5 +++--
 AspectedRouting/IO/LuaSkeleton/LuaTestPrinter.cs       | 10 +++++-----
 AspectedRouting/IO/itinero1/LuaPrinter1.cs             |  2 ++
 AspectedRouting/IO/itinero2/LuaPrinter2.cs             |  2 ++
 5 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/AspectedRouting/IO/LuaSkeleton/LuaSkeleton.Expressions.cs b/AspectedRouting/IO/LuaSkeleton/LuaSkeleton.Expressions.cs
index b34dbe1..d8f09b0 100644
--- a/AspectedRouting/IO/LuaSkeleton/LuaSkeleton.Expressions.cs
+++ b/AspectedRouting/IO/LuaSkeleton/LuaSkeleton.Expressions.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Dynamic;
+using System.Globalization;
 using System.Linq;
 using System.Text.RegularExpressions;
 using AspectedRouting.IO.itinero1;
@@ -280,9 +281,9 @@ namespace AspectedRouting.IO.LuaSkeleton
                 case IExpression e:
                     return ToLua(e);
                 case int i:
-                    return "" + i;
+                    return i.ToString(CultureInfo.InvariantCulture);
                 case double d:
-                    return "" + d;
+                    return d.ToString(CultureInfo.InvariantCulture);
                 case string s:
                     return '"' + s.Replace("\"", "\\\"") + '"';
                 case null:
diff --git a/AspectedRouting/IO/LuaSkeleton/LuaStringExtensions.cs b/AspectedRouting/IO/LuaSkeleton/LuaStringExtensions.cs
index b172e4d..4f46d89 100644
--- a/AspectedRouting/IO/LuaSkeleton/LuaStringExtensions.cs
+++ b/AspectedRouting/IO/LuaSkeleton/LuaStringExtensions.cs
@@ -1,4 +1,5 @@
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Text.RegularExpressions;
 
@@ -12,14 +13,14 @@ namespace AspectedRouting.IO.itinero1
             var contents = tags.Select(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]*$"))
                 {
                     left = key;
                 }
 
-                return $"{left} = \"{value}\"";
+                return $"{left.ToString(CultureInfo.InvariantCulture)} = \"{value.ToString(CultureInfo.InvariantCulture)}\"";
             });
             return "{" + string.Join(", ", contents) + "}";
         }
diff --git a/AspectedRouting/IO/LuaSkeleton/LuaTestPrinter.cs b/AspectedRouting/IO/LuaSkeleton/LuaTestPrinter.cs
index cdd684a..733bbf7 100644
--- a/AspectedRouting/IO/LuaSkeleton/LuaTestPrinter.cs
+++ b/AspectedRouting/IO/LuaSkeleton/LuaTestPrinter.cs
@@ -1,4 +1,5 @@
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using AspectedRouting.Tests;
 
@@ -94,10 +95,10 @@ namespace AspectedRouting.IO.itinero1
                 tags.Remove("#" + paramName);
             }
 
-            var expectedPriority = "" + expected.Priority;
+            var expectedPriority = expected.Priority.ToString(CultureInfo.InvariantCulture);
             if (invertPriority)
             {
-                expectedPriority = $"inv({expectedPriority})";
+                expectedPriority = $"inv({expectedPriority.ToString(CultureInfo.InvariantCulture)})";
             }
 
             // Generates something like:
@@ -106,7 +107,7 @@ namespace AspectedRouting.IO.itinero1
                 $"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 = {expectedPriority} }}, " +
+                $"{{access = \"{D(expected.Access)}\", speed = {expected.Speed.ToString(CultureInfo.InvariantCulture)}, oneway = \"{D(expected.Oneway)}\", priority = {expectedPriority} }}, " +
                 tags.ToLuaTable() +
                 ")";
         }
@@ -151,8 +152,7 @@ namespace AspectedRouting.IO.itinero1
 
             _skeleton.AddDep("unitTest");
             _skeleton.AddDep("debug_table");
-            return
-                $"unit_test({functionToApplyName.AsLuaIdentifier()}, \"{functionToApplyName}\", {index}, \"{expected}\", {parameters.ToLuaTable()}, {tags.ToLuaTable()})";
+            return $"unit_test({functionToApplyName.AsLuaIdentifier()}, \"{functionToApplyName}\", {index.ToString(CultureInfo.InvariantCulture)}, \"{expected.ToString(CultureInfo.InvariantCulture)}\", {parameters.ToLuaTable()}, {tags.ToLuaTable()})";
         }
 
 
diff --git a/AspectedRouting/IO/itinero1/LuaPrinter1.cs b/AspectedRouting/IO/itinero1/LuaPrinter1.cs
index 05290d6..1526367 100644
--- a/AspectedRouting/IO/itinero1/LuaPrinter1.cs
+++ b/AspectedRouting/IO/itinero1/LuaPrinter1.cs
@@ -151,6 +151,8 @@ namespace AspectedRouting.IO.itinero1
                 "test_all()",
                 "if (not failed_tests and not failed_profile_tests) then",
                 "    print(\"Tests OK\")",
+                "else",
+                "    error(\"Some tests failed\")",
                 "end"
             );
         }
diff --git a/AspectedRouting/IO/itinero2/LuaPrinter2.cs b/AspectedRouting/IO/itinero2/LuaPrinter2.cs
index c7dbf46..1e16d81 100644
--- a/AspectedRouting/IO/itinero2/LuaPrinter2.cs
+++ b/AspectedRouting/IO/itinero2/LuaPrinter2.cs
@@ -69,6 +69,8 @@ namespace AspectedRouting.IO.itinero2
                 "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();
         }

From 499ede6ae332799c17c3f6e28fd2e8768ba15cfb Mon Sep 17 00:00:00 2001
From: Pieter Vander Vennet <pietervdvn@posteo.net>
Date: Wed, 2 Nov 2022 12:30:45 +0100
Subject: [PATCH 10/10] More invariant-cultures in toString, tests now work,
 fix #22

---
 AspectedRouting/IO/itinero2/LuaPrinter2.MainFunction.cs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/AspectedRouting/IO/itinero2/LuaPrinter2.MainFunction.cs b/AspectedRouting/IO/itinero2/LuaPrinter2.MainFunction.cs
index fcbbd51..a095dc0 100644
--- a/AspectedRouting/IO/itinero2/LuaPrinter2.MainFunction.cs
+++ b/AspectedRouting/IO/itinero2/LuaPrinter2.MainFunction.cs
@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using AspectedRouting.IO.LuaSkeleton;
 using AspectedRouting.IO.LuaSnippets;
@@ -45,7 +46,7 @@ namespace AspectedRouting.IO.itinero2
                 var exprInLua = _skeleton.ToLua(exprSpecialized);
                 if (exprInLua.Contains("constRight") || exprInLua.Contains("firstArg"))
                     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)