From a116fd1bdb08fdc4c9f4285ddc4710707f107cf8 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 4 Apr 2021 01:57:41 +0200 Subject: [PATCH] Fixed order of arguments, fix various issues in lua output --- .../AspectedRouting.Test.csproj | 2 +- AspectedRouting/AspectedRouting.csproj | 2 +- .../IO/LuaSkeleton/LuaSkeleton.Expressions.cs | 19 +++-- AspectedRouting/IO/LuaSkeleton/LuaSkeleton.cs | 4 ++ .../{firstMatchOf.lua => first_match_of.lua} | 5 +- .../IO/lua/{mustMatch.lua => must_match.lua} | 34 +++++++-- AspectedRouting/IO/lua/table_to_list.lua | 3 + .../Language/Functions/FirstMatchOf.cs | 5 +- AspectedRouting/Language/Functions/Mapping.cs | 8 ++- .../Language/Functions/MemberOf.cs | 69 ++++++++++++------- .../Language/Functions/MustMatch.cs | 22 ++++-- .../Functions/StringStringToTagsFunction.cs | 3 + AspectedRouting/Tests/FunctionTestSuite.cs | 2 +- 13 files changed, 128 insertions(+), 50 deletions(-) rename AspectedRouting/IO/lua/{firstMatchOf.lua => first_match_of.lua} (78%) rename AspectedRouting/IO/lua/{mustMatch.lua => must_match.lua} (64%) diff --git a/AspectedRouting.Test/AspectedRouting.Test.csproj b/AspectedRouting.Test/AspectedRouting.Test.csproj index 8fa6c58..0347b72 100644 --- a/AspectedRouting.Test/AspectedRouting.Test.csproj +++ b/AspectedRouting.Test/AspectedRouting.Test.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0 false diff --git a/AspectedRouting/AspectedRouting.csproj b/AspectedRouting/AspectedRouting.csproj index 925f715..4e00b5b 100644 --- a/AspectedRouting/AspectedRouting.csproj +++ b/AspectedRouting/AspectedRouting.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net5.0 8 AspectedRouting AspectedRouting diff --git a/AspectedRouting/IO/LuaSkeleton/LuaSkeleton.Expressions.cs b/AspectedRouting/IO/LuaSkeleton/LuaSkeleton.Expressions.cs index ced1f46..87e46e7 100644 --- a/AspectedRouting/IO/LuaSkeleton/LuaSkeleton.Expressions.cs +++ b/AspectedRouting/IO/LuaSkeleton/LuaSkeleton.Expressions.cs @@ -28,10 +28,10 @@ namespace AspectedRouting.IO.LuaSkeleton Assign(collectedMapping)) ).Invoke(bare)) { AddDep(Funcs.FirstOf.Name); - return "first_match_of(tags, result, \n" + - " " + ToLua(order.First(), key) + "," + + return "first_match_of(\n" + + " " + ToLua(order.First(), key) + ",\n" + ("\n" + MappingToLua((Mapping) collectedMapping.First())).Indent().Indent() + - ")"; + ",\n tags, result)"; } if (UnApply( @@ -43,10 +43,10 @@ namespace AspectedRouting.IO.LuaSkeleton Assign(collectedMapping)) ).Invoke(bare)) { AddDep(Funcs.MustMatch.Name); - return "must_match(tags, result, \n" + + return "must_match(" + " " + ToLua(order.First(), key) + "," + ("\n" + MappingToLua((Mapping) collectedMapping.First())).Indent().Indent() + - ")"; + ",\n tags, result)"; } if (UnApply( @@ -124,7 +124,14 @@ namespace AspectedRouting.IO.LuaSkeleton var fArgs = bare.DeconstructApply(); if (fArgs != null) { var (f, args) = fArgs.Value; - var baseFunc = (Function) f; + + if(f is Constant constant) { + return ConstantToLua(constant); + } + + if (!(f is Function baseFunc)) { + throw new ArgumentException("Not a function: " + f); + } if (baseFunc.Name.Equals(Funcs.Id.Name)) { // This is an ugly hack diff --git a/AspectedRouting/IO/LuaSkeleton/LuaSkeleton.cs b/AspectedRouting/IO/LuaSkeleton/LuaSkeleton.cs index fa6e7f7..3a29c18 100644 --- a/AspectedRouting/IO/LuaSkeleton/LuaSkeleton.cs +++ b/AspectedRouting/IO/LuaSkeleton/LuaSkeleton.cs @@ -45,6 +45,10 @@ namespace AspectedRouting.IO.LuaSkeleton if (name.StartsWith("mapping")) { throw new Exception("A mapping was added as dependency - this is a bug"); } + + if (name.Contains("stringToTags")) { + AddDep("table_to_list"); + } _dependencies.Add(name); } diff --git a/AspectedRouting/IO/lua/firstMatchOf.lua b/AspectedRouting/IO/lua/first_match_of.lua similarity index 78% rename from AspectedRouting/IO/lua/firstMatchOf.lua rename to AspectedRouting/IO/lua/first_match_of.lua index 68bbc89..946b5bc 100644 --- a/AspectedRouting/IO/lua/firstMatchOf.lua +++ b/AspectedRouting/IO/lua/first_match_of.lua @@ -1,4 +1,7 @@ -function first_match_of(tags, result, order_of_keys, table) +function first_match_of(order_of_keys, table, tags, result) + if (result == nil) then + result = { attributes_to_keep = {} } + end for _, key in pairs(order_of_keys) do local v = tags[key] if (v ~= nil) then diff --git a/AspectedRouting/IO/lua/mustMatch.lua b/AspectedRouting/IO/lua/must_match.lua similarity index 64% rename from AspectedRouting/IO/lua/mustMatch.lua rename to AspectedRouting/IO/lua/must_match.lua index 66ab3a4..f9e4f7b 100644 --- a/AspectedRouting/IO/lua/mustMatch.lua +++ b/AspectedRouting/IO/lua/must_match.lua @@ -22,16 +22,34 @@ Arguments: - table which is the table to match ]] -function must_match(tags, result, needed_keys, table) +function must_match(needed_keys, table, tags, result) for _, key in ipairs(needed_keys) do local v = tags[key] if (v == nil) then -- a key is missing... - return false + + -- this probably means that we must return false... unless the mapping returns something for null! + local mappng = table[key] + if (mappng ~= nil) then + -- there is a mapping! We might be in luck... + local resultValue = mappng[v] + if (resultValue == nil or resultValue == false) then + -- nope, no luck after all + return false + end + if (resultValue == true or resultValue == "yes") then + return true + end + else + return false + end end local mapping = table[key] - if (type(mapping) == "table") then + if (mapping == nil) then + -- the mapping is nil! That is fine, the key is present anyway + -- we ignore + elseif (type(mapping) == "table") then -- we have to map the value with a function: local resultValue = mapping[v] if (resultValue ~= nil or -- actually, having nil for a mapping is fine for this function!. @@ -50,15 +68,19 @@ function must_match(tags, result, needed_keys, table) error("MustMatch got a string value it can't handle: " .. bool) end elseif (type(mapping) == "boolean") then - if(not mapping) then + if (not mapping) then return false end else - error("The mapping is not a table. This is not supported. We got " .. tostring(mapping) .. " (" .. type(mapping)..")") + error("The mapping is not a table. This is not supported. We got " .. tostring(mapping) .. " (" .. type(mapping) .. ")") end end - -- Now that we know for sure that every key matches, we add them all + -- Now that we know for sure that every key matches, we add them all to the 'attributes_to_keep' + if (result == nil) then + -- euhm, well, seems like we don't are about the attributes_to_keep; early return! + return true + end for _, key in ipairs(needed_keys) do local v = tags[key] -- this is the only place where we use the original tags if (v ~= nil) then diff --git a/AspectedRouting/IO/lua/table_to_list.lua b/AspectedRouting/IO/lua/table_to_list.lua index b094c13..8729b88 100644 --- a/AspectedRouting/IO/lua/table_to_list.lua +++ b/AspectedRouting/IO/lua/table_to_list.lua @@ -1,5 +1,8 @@ function table_to_list(tags, result, factor_table) local list = {} + if(tags == nil) then + return list + end for key, mapping in pairs(factor_table) do local v = tags[key] if (v ~= nil) then diff --git a/AspectedRouting/Language/Functions/FirstMatchOf.cs b/AspectedRouting/Language/Functions/FirstMatchOf.cs index 38e5df5..136e407 100644 --- a/AspectedRouting/Language/Functions/FirstMatchOf.cs +++ b/AspectedRouting/Language/Functions/FirstMatchOf.cs @@ -12,7 +12,7 @@ namespace AspectedRouting.Language.Functions "E.g. `$firstMatchOf ['maxspeed','highway'] {'maxspeed' --> $parse, 'highway' --> {residential --> 30, tertiary --> 50}}` applied on `{maxspeed=70, highway=tertiary}` will yield `70` as that is the first key in the list; `{highway=residential}` will yield `30`."; public override List ArgNames { get; } = new List {"s"}; - public FirstMatchOf() : base("firstMatchOf", true, + public FirstMatchOf() : base("first_match_of", true, new[] { // [String] -> (Tags -> [a]) -> Tags -> a @@ -23,9 +23,10 @@ namespace AspectedRouting.Language.Functions ) }) { + Funcs.AddBuiltin( this,"firstMatchOf"); } - private FirstMatchOf(IEnumerable types) : base("firstMatchOf", types) + private FirstMatchOf(IEnumerable types) : base("first_match_of", types) { } diff --git a/AspectedRouting/Language/Functions/Mapping.cs b/AspectedRouting/Language/Functions/Mapping.cs index de745d6..d5e1b34 100644 --- a/AspectedRouting/Language/Functions/Mapping.cs +++ b/AspectedRouting/Language/Functions/Mapping.cs @@ -89,8 +89,12 @@ namespace AspectedRouting.Language.Functions var key = (string) s; var otherARgs = arguments.ToList().GetRange(1, arguments.Length - 1); - if (!StringToResultFunctions.TryGetValue(key, out var resultFunction)) - { + if (!StringToResultFunctions.ContainsKey(key)) { // This is really roundabout, but it has to be + return null; + } + + var resultFunction = StringToResultFunctions[key]; + if (resultFunction == null) { return null; } diff --git a/AspectedRouting/Language/Functions/MemberOf.cs b/AspectedRouting/Language/Functions/MemberOf.cs index 7832cd4..28d6c50 100644 --- a/AspectedRouting/Language/Functions/MemberOf.cs +++ b/AspectedRouting/Language/Functions/MemberOf.cs @@ -1,12 +1,22 @@ using System.Collections.Generic; using AspectedRouting.Language.Expression; using AspectedRouting.Language.Typ; -using Type = AspectedRouting.Language.Typ.Type; namespace AspectedRouting.Language.Functions { public class MemberOf : Function { + public MemberOf() : base( + "memberOf", true, + new[] { + new Curry( + new Curry(Typs.Tags, Typs.Bool), + new Curry(Typs.Tags, Typs.Bool)) + } + ) { } + + public MemberOf(IEnumerable types) : base("memberOf", types) { } + public override string Description { get; } = "This function returns true, if the way is member of a relation matching the specified function.\n" + "\n" + @@ -23,44 +33,51 @@ namespace AspectedRouting.Language.Functions "\n\n" + "In the test.csv, one can simply use `_relation:=yes` to mimic relations in your tests"; - public override List ArgNames { get; } = new List - { - "f","tags" + public override List ArgNames { get; } = new List { + "f", "tags" }; - public MemberOf() : base( - "memberOf", true, - new[] - { - new Curry( - new Curry(Typs.Tags, Typs.Bool), - new Curry(Typs.Tags, Typs.Bool)) - } - ) - { - } - - public MemberOf(IEnumerable types) : base("memberOf", types) - { - } - public override object Evaluate(Context c, params IExpression[] arguments) { - var tags =(Dictionary) arguments[1].Evaluate(c); + var tags = (Dictionary) arguments[1].Evaluate(c); var name = c.AspectName.TrimStart('$'); - - if(tags.TryGetValue("_relation:"+name, out var v)) - { + + if (tags.TryGetValue("_relation:" + name, out var v)) { return v; } + + // In the case of tests, relations might be added with "_relation:1:" + // So, we create this table as dictionary + var relationTags = new Dictionary>(); + foreach (var tag in tags) { + if (tag.Key.StartsWith("_relation:")) { + var keyParts = tag.Key.Split(":"); + if (keyParts.Length != 3) { + continue; + } + var relationName = keyParts[1]; + if (!relationTags.ContainsKey(relationName)) { + relationTags.Add(relationName, new Dictionary()); + } + + relationTags[relationName].Add(keyParts[2], tag.Value); + } + } + + foreach (var relationTagging in relationTags) { + var result = arguments[0].Evaluate(c, new Constant(relationTagging.Value)); + if (result.Equals("yes")) { + return "yes"; + } + } + return "no"; } public override IExpression Specialize(IEnumerable allowedTypes) { var unified = Types.SpecializeTo(allowedTypes); - if (unified == null) - { + if (unified == null) { return null; } diff --git a/AspectedRouting/Language/Functions/MustMatch.cs b/AspectedRouting/Language/Functions/MustMatch.cs index 29668d5..8e887e0 100644 --- a/AspectedRouting/Language/Functions/MustMatch.cs +++ b/AspectedRouting/Language/Functions/MustMatch.cs @@ -7,7 +7,7 @@ namespace AspectedRouting.Language.Functions { public class MustMatch : Function { - public MustMatch() : base("mustMatch", true, + public MustMatch() : base("must_match", true, new[] { // [String] -> (Tags -> [string]) -> Tags -> bool Curry.ConstructFrom(Typs.Bool, // Result type on top! @@ -15,9 +15,12 @@ namespace AspectedRouting.Language.Functions new Curry(Typs.Tags, new ListType(Typs.String)), // The function to execute on every key Typs.Tags // The tags to apply this on ) - }) { } + }) + { + Funcs.AddBuiltin(this, "mustMatch"); + } - private MustMatch(IEnumerable types) : base("mustMatch", types) { } + private MustMatch(IEnumerable types) : base("must_match", types) { } public override string Description { get; } = Utils.Lines( "Checks that every specified key is present and gives a non-false value.\n", @@ -64,7 +67,18 @@ namespace AspectedRouting.Language.Functions } if (!tags.ContainsKey(tagKey)) { - // A required key is missing: return 'no' + // A required key is missing + // Normally, we return no; but there is a second chance + // IF the mapping returns 'yes' on null, we make an exception and ignore it + var applied = function.Evaluate(c, new Constant(new Dictionary { + {tagKey, ""} + })); + if (applied == null) { + return "no"; + } + if (applied.Equals("yes") || (applied is IEnumerable l && l.Count() > 0 && l.ToList()[0].Equals("yes")) ) { + continue; // We ignore the absence of the key + } return "no"; } } diff --git a/AspectedRouting/Language/Functions/StringStringToTagsFunction.cs b/AspectedRouting/Language/Functions/StringStringToTagsFunction.cs index 5c9e0bf..f627c19 100644 --- a/AspectedRouting/Language/Functions/StringStringToTagsFunction.cs +++ b/AspectedRouting/Language/Functions/StringStringToTagsFunction.cs @@ -34,6 +34,9 @@ namespace AspectedRouting.Language.Functions { var f = arguments[0]; var tags = (Dictionary) arguments[1].Evaluate(c); + if (tags == null) { + return null; + } var result = new List(); foreach (var (k, v) in tags) { diff --git a/AspectedRouting/Tests/FunctionTestSuite.cs b/AspectedRouting/Tests/FunctionTestSuite.cs index 3bcfd19..1cc5160 100644 --- a/AspectedRouting/Tests/FunctionTestSuite.cs +++ b/AspectedRouting/Tests/FunctionTestSuite.cs @@ -60,7 +60,7 @@ namespace AspectedRouting.Tests var testCase = 0; foreach (var test in Tests) { testCase++; - var context = new Context(); + var context = new Context().WithAspectName("unittest"); foreach (var (key, value) in test.tags) { if (key.StartsWith("#")) { context.AddParameter(key, value);