Fixed order of arguments, fix various issues in lua output

This commit is contained in:
Pieter Vander Vennet 2021-04-04 01:57:41 +02:00
parent e2cd6caa70
commit a116fd1bdb
13 changed files with 128 additions and 50 deletions

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>

View file

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<LangVersion>8</LangVersion> <LangVersion>8</LangVersion>
<AssemblyName>AspectedRouting</AssemblyName> <AssemblyName>AspectedRouting</AssemblyName>
<RootNamespace>AspectedRouting</RootNamespace> <RootNamespace>AspectedRouting</RootNamespace>

View file

@ -28,10 +28,10 @@ namespace AspectedRouting.IO.LuaSkeleton
Assign(collectedMapping)) Assign(collectedMapping))
).Invoke(bare)) { ).Invoke(bare)) {
AddDep(Funcs.FirstOf.Name); AddDep(Funcs.FirstOf.Name);
return "first_match_of(tags, result, \n" + return "first_match_of(\n" +
" " + ToLua(order.First(), key) + "," + " " + ToLua(order.First(), key) + ",\n" +
("\n" + MappingToLua((Mapping) collectedMapping.First())).Indent().Indent() + ("\n" + MappingToLua((Mapping) collectedMapping.First())).Indent().Indent() +
")"; ",\n tags, result)";
} }
if (UnApply( if (UnApply(
@ -43,10 +43,10 @@ namespace AspectedRouting.IO.LuaSkeleton
Assign(collectedMapping)) Assign(collectedMapping))
).Invoke(bare)) { ).Invoke(bare)) {
AddDep(Funcs.MustMatch.Name); AddDep(Funcs.MustMatch.Name);
return "must_match(tags, result, \n" + return "must_match(" +
" " + ToLua(order.First(), key) + "," + " " + ToLua(order.First(), key) + "," +
("\n" + MappingToLua((Mapping) collectedMapping.First())).Indent().Indent() + ("\n" + MappingToLua((Mapping) collectedMapping.First())).Indent().Indent() +
")"; ",\n tags, result)";
} }
if (UnApply( if (UnApply(
@ -124,7 +124,14 @@ namespace AspectedRouting.IO.LuaSkeleton
var fArgs = bare.DeconstructApply(); var fArgs = bare.DeconstructApply();
if (fArgs != null) { if (fArgs != null) {
var (f, args) = fArgs.Value; 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)) { if (baseFunc.Name.Equals(Funcs.Id.Name)) {
// This is an ugly hack // This is an ugly hack

View file

@ -45,6 +45,10 @@ namespace AspectedRouting.IO.LuaSkeleton
if (name.StartsWith("mapping")) { if (name.StartsWith("mapping")) {
throw new Exception("A mapping was added as dependency - this is a bug"); throw new Exception("A mapping was added as dependency - this is a bug");
} }
if (name.Contains("stringToTags")) {
AddDep("table_to_list");
}
_dependencies.Add(name); _dependencies.Add(name);
} }

View file

@ -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 for _, key in pairs(order_of_keys) do
local v = tags[key] local v = tags[key]
if (v ~= nil) then if (v ~= nil) then

View file

@ -22,16 +22,34 @@ Arguments:
- table which is the table to match - 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 for _, key in ipairs(needed_keys) do
local v = tags[key] local v = tags[key]
if (v == nil) then if (v == nil) then
-- a key is missing... -- 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 end
local mapping = table[key] 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: -- we have to map the value with a function:
local resultValue = mapping[v] local resultValue = mapping[v]
if (resultValue ~= nil or -- actually, having nil for a mapping is fine for this function!. 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) error("MustMatch got a string value it can't handle: " .. bool)
end end
elseif (type(mapping) == "boolean") then elseif (type(mapping) == "boolean") then
if(not mapping) then if (not mapping) then
return false return false
end end
else 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
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 for _, key in ipairs(needed_keys) do
local v = tags[key] -- this is the only place where we use the original tags local v = tags[key] -- this is the only place where we use the original tags
if (v ~= nil) then if (v ~= nil) then

View file

@ -1,5 +1,8 @@
function table_to_list(tags, result, factor_table) function table_to_list(tags, result, factor_table)
local list = {} local list = {}
if(tags == nil) then
return list
end
for key, mapping in pairs(factor_table) do for key, mapping in pairs(factor_table) do
local v = tags[key] local v = tags[key]
if (v ~= nil) then if (v ~= nil) then

View file

@ -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`."; "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<string> ArgNames { get; } = new List<string> {"s"}; public override List<string> ArgNames { get; } = new List<string> {"s"};
public FirstMatchOf() : base("firstMatchOf", true, public FirstMatchOf() : base("first_match_of", true,
new[] new[]
{ {
// [String] -> (Tags -> [a]) -> Tags -> a // [String] -> (Tags -> [a]) -> Tags -> a
@ -23,9 +23,10 @@ namespace AspectedRouting.Language.Functions
) )
}) })
{ {
Funcs.AddBuiltin( this,"firstMatchOf");
} }
private FirstMatchOf(IEnumerable<Type> types) : base("firstMatchOf", types) private FirstMatchOf(IEnumerable<Type> types) : base("first_match_of", types)
{ {
} }

View file

@ -89,8 +89,12 @@ namespace AspectedRouting.Language.Functions
var key = (string) s; var key = (string) s;
var otherARgs = arguments.ToList().GetRange(1, arguments.Length - 1); 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; return null;
} }

View file

@ -1,12 +1,22 @@
using System.Collections.Generic; using System.Collections.Generic;
using AspectedRouting.Language.Expression; using AspectedRouting.Language.Expression;
using AspectedRouting.Language.Typ; using AspectedRouting.Language.Typ;
using Type = AspectedRouting.Language.Typ.Type;
namespace AspectedRouting.Language.Functions namespace AspectedRouting.Language.Functions
{ {
public class MemberOf : Function 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<Type> types) : base("memberOf", types) { }
public override string Description { get; } = public override string Description { get; } =
"This function returns true, if the way is member of a relation matching the specified function.\n" + "This function returns true, if the way is member of a relation matching the specified function.\n" +
"\n" + "\n" +
@ -23,44 +33,51 @@ namespace AspectedRouting.Language.Functions
"\n\n" + "\n\n" +
"In the test.csv, one can simply use `_relation:<aspect_name>=yes` to mimic relations in your tests"; "In the test.csv, one can simply use `_relation:<aspect_name>=yes` to mimic relations in your tests";
public override List<string> ArgNames { get; } = new List<string> public override List<string> ArgNames { get; } = new List<string> {
{ "f", "tags"
"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<Type> types) : base("memberOf", types)
{
}
public override object Evaluate(Context c, params IExpression[] arguments) public override object Evaluate(Context c, params IExpression[] arguments)
{ {
var tags =(Dictionary<string, string>) arguments[1].Evaluate(c); var tags = (Dictionary<string, string>) arguments[1].Evaluate(c);
var name = c.AspectName.TrimStart('$'); var name = c.AspectName.TrimStart('$');
if(tags.TryGetValue("_relation:"+name, out var v)) if (tags.TryGetValue("_relation:" + name, out var v)) {
{
return v; return v;
} }
// In the case of tests, relations might be added with "_relation:1:<key>"
// So, we create this table as dictionary
var relationTags = new Dictionary<string, Dictionary<string, string>>();
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<string, string>());
}
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"; return "no";
} }
public override IExpression Specialize(IEnumerable<Type> allowedTypes) public override IExpression Specialize(IEnumerable<Type> allowedTypes)
{ {
var unified = Types.SpecializeTo(allowedTypes); var unified = Types.SpecializeTo(allowedTypes);
if (unified == null) if (unified == null) {
{
return null; return null;
} }

View file

@ -7,7 +7,7 @@ namespace AspectedRouting.Language.Functions
{ {
public class MustMatch : Function public class MustMatch : Function
{ {
public MustMatch() : base("mustMatch", true, public MustMatch() : base("must_match", true,
new[] { new[] {
// [String] -> (Tags -> [string]) -> Tags -> bool // [String] -> (Tags -> [string]) -> Tags -> bool
Curry.ConstructFrom(Typs.Bool, // Result type on top! 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 new Curry(Typs.Tags, new ListType(Typs.String)), // The function to execute on every key
Typs.Tags // The tags to apply this on Typs.Tags // The tags to apply this on
) )
}) { } })
{
Funcs.AddBuiltin(this, "mustMatch");
}
private MustMatch(IEnumerable<Type> types) : base("mustMatch", types) { } private MustMatch(IEnumerable<Type> types) : base("must_match", types) { }
public override string Description { get; } = Utils.Lines( public override string Description { get; } = Utils.Lines(
"Checks that every specified key is present and gives a non-false value.\n", "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)) { 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<string, string> {
{tagKey, ""}
}));
if (applied == null) {
return "no";
}
if (applied.Equals("yes") || (applied is IEnumerable<object> l && l.Count() > 0 && l.ToList()[0].Equals("yes")) ) {
continue; // We ignore the absence of the key
}
return "no"; return "no";
} }
} }

View file

@ -34,6 +34,9 @@ namespace AspectedRouting.Language.Functions
{ {
var f = arguments[0]; var f = arguments[0];
var tags = (Dictionary<string, string>) arguments[1].Evaluate(c); var tags = (Dictionary<string, string>) arguments[1].Evaluate(c);
if (tags == null) {
return null;
}
var result = new List<object>(); var result = new List<object>();
foreach (var (k, v) in tags) foreach (var (k, v) in tags)
{ {

View file

@ -60,7 +60,7 @@ namespace AspectedRouting.Tests
var testCase = 0; var testCase = 0;
foreach (var test in Tests) { foreach (var test in Tests) {
testCase++; testCase++;
var context = new Context(); var context = new Context().WithAspectName("unittest");
foreach (var (key, value) in test.tags) { foreach (var (key, value) in test.tags) {
if (key.StartsWith("#")) { if (key.StartsWith("#")) {
context.AddParameter(key, value); context.AddParameter(key, value);