Various improvements and bug fixes to the type system
This commit is contained in:
parent
36c1a4fd7a
commit
537fb2c613
31 changed files with 798 additions and 275 deletions
|
@ -1,8 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.IO.jsonParser;
|
||||
using AspectedRouting.Language;
|
||||
using AspectedRouting.Language.Expression;
|
||||
using AspectedRouting.Language.Functions;
|
||||
using AspectedRouting.Language.Typ;
|
||||
using Xunit;
|
||||
|
||||
namespace AspectedRouting.Test
|
||||
|
@ -59,5 +61,270 @@ namespace AspectedRouting.Test
|
|||
var result = expr.Evaluate(new Context());
|
||||
Assert.Equal("no", result);
|
||||
}
|
||||
|
||||
private string IfDottedConditionJson
|
||||
= "{" +
|
||||
"\"$ifdotted\": {\"$eq\": \"yes\"}," +
|
||||
"\"then\":{\"$const\": \"a\"}," +
|
||||
"\"else\": {\"$const\": \"b\"}" +
|
||||
"}";
|
||||
|
||||
private string IfSimpleConditionJson
|
||||
= "{" +
|
||||
"\"$if\": true," +
|
||||
"\"then\":\"thenResult\"," +
|
||||
"\"else\": \"elseResult\"}";
|
||||
|
||||
[Fact]
|
||||
public void TestParsing_SimpleIf_CorrectExpression()
|
||||
{
|
||||
var c = new Context();
|
||||
var ifExpr = JsonParser.ParseExpression(c, IfSimpleConditionJson);
|
||||
|
||||
Assert.Single(ifExpr.Types);
|
||||
Assert.Equal(ifExpr.Types.First(), Typs.String);
|
||||
|
||||
var resultT = ifExpr.Evaluate(c);
|
||||
Assert.Equal("thenResult", resultT);
|
||||
resultT = ifExpr.Optimize().Evaluate(c);
|
||||
Assert.Equal("thenResult", resultT);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestEvaluate_DottedIf_CorrectExpression()
|
||||
{
|
||||
var ifExpr = Funcs.IfDotted.Apply(
|
||||
Funcs.Eq.Apply(new Constant("abc")),
|
||||
Funcs.Const.Apply(new Constant("a")),
|
||||
Funcs.Const.Apply(new Constant("b"))
|
||||
);
|
||||
|
||||
var c = new Context();
|
||||
var ifResultMatch = ifExpr.Evaluate(c, new Constant("abc"));
|
||||
Assert.Equal("a", ifResultMatch);
|
||||
|
||||
var ifResultNoMatch = ifExpr.Evaluate(c, new Constant("def"));
|
||||
Assert.Equal("b", ifResultNoMatch);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestParsing_DottedIf_CorrectExpression()
|
||||
{
|
||||
var c = new Context();
|
||||
var ifExpr = JsonParser.ParseExpression(c, IfDottedConditionJson);
|
||||
ifExpr = ifExpr.Optimize();
|
||||
var resultT = ifExpr.Evaluate(c,
|
||||
new Constant(Typs.String, "yes"));
|
||||
var resultF = ifExpr.Evaluate(c,
|
||||
new Constant(Typs.String, "no"));
|
||||
Assert.Equal("a", resultT);
|
||||
Assert.Equal("b", resultF);
|
||||
}
|
||||
|
||||
|
||||
private string constString = "{\"$const\": \"a\"}";
|
||||
|
||||
[Fact]
|
||||
public void Parse_ConstString_TypeIsFree()
|
||||
{
|
||||
var e = JsonParser.ParseExpression(new Context(), constString);
|
||||
Assert.Single(e.Types);
|
||||
Assert.Equal(new Curry(new Var("d"), Typs.String), e.Types.First());
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void TypeInference_EitherIdConstConst_CorrectType()
|
||||
{
|
||||
/*
|
||||
* id : a -> a
|
||||
* dot: (b -> c) -> (a -> b) -> a -> c
|
||||
* const - throw away b: a -> b -> a
|
||||
* eitherFunc: (a -> b) -> (c -> d) -> (a -> b)
|
||||
* eitherFunc: (a -> b) -> (c -> d) -> (c -> d)
|
||||
|
||||
*
|
||||
* All with free vars:
|
||||
* id: a -> a
|
||||
* dot: (b -> c) -> (x -> b) -> x -> c
|
||||
* const: y -> z -> y
|
||||
* eitherfunc: (d -> e) -> (f -> g) -> (d -> e)
|
||||
* (d -> e) -> (f -> g) -> (f -> g)
|
||||
*/
|
||||
|
||||
/*
|
||||
* (((eitherfunc id) dot) const)
|
||||
*
|
||||
* (eitherfunc id)
|
||||
* [(d -> e) -> (f -> g) -> (d -> e)] (a -> a)
|
||||
* [(d -> e) -> (f -> g) -> (f -> g)] (a -> a)
|
||||
*
|
||||
* Gives:
|
||||
* d ~ a
|
||||
* e ~ a
|
||||
* thus:
|
||||
* (f -> g) -> (a -> a)
|
||||
* (f -> g) -> (f -> g)
|
||||
*
|
||||
* ((eitherfunc id) dot)
|
||||
* [(f -> g) -> (a -> a)] ((b -> c) -> (x -> b) -> x -> c)
|
||||
* [(f -> g) -> (f -> g)] (b -> c) -> (x -> b) -> (x -> c)
|
||||
*
|
||||
* Thus: (f -> g) ~ (b -> c) -> ((x -> b) -> x -> c)
|
||||
* thus: f ~ (b -> c)
|
||||
* g ~ ((x -> b) -> (x -> c))
|
||||
* thus:
|
||||
* (a -> a)
|
||||
* (b -> c) -> ((x -> b) -> (x -> c))
|
||||
*
|
||||
*
|
||||
*
|
||||
* (((eitherfunc id) dot) const):
|
||||
* [(a -> a)] (y -> (z -> y))
|
||||
* [(b -> c) -> ((x -> b) -> (x -> c))] (y -> (z -> y))
|
||||
*
|
||||
* Thus: case 1:
|
||||
* a ~ (y -> (z -> y)
|
||||
* Type is: (y -> z -> y) === typeof(const)
|
||||
* case2:
|
||||
* (b -> c) ~ (y -> (z -> y))
|
||||
* thus: b ~ y
|
||||
* c ~ (z -> y)
|
||||
* ((x -> y) -> (x -> (z -> y))))
|
||||
* = ((x -> y) -> x -> z -> y === mix of dot and const
|
||||
*
|
||||
*/
|
||||
|
||||
var a = new Var("a");
|
||||
var c = new Var("c");
|
||||
var d = new Var("d");
|
||||
|
||||
|
||||
var e = Funcs.Either(Funcs.Id, Funcs.Dot, Funcs.Const);
|
||||
var types = e.Types.ToList();
|
||||
Assert.Equal(Curry.ConstructFrom(c, c, d), types[0]);
|
||||
Assert.Equal(Curry.ConstructFrom(
|
||||
c, // RESULT TYPE
|
||||
new Curry(a, c),
|
||||
a, d
|
||||
), types[1]);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void RenameVars_Constant_ConstantType()
|
||||
{
|
||||
// Funcs.Const.RenameVars(noUse: ["a","b","d","e","f"] should give something like 'c -> g -> c'
|
||||
var a = new Var("a");
|
||||
var b = new Var("b");
|
||||
|
||||
var c = new Var("c");
|
||||
var d = new Var("d");
|
||||
|
||||
var e = new Var("e");
|
||||
var f = new Var("f");
|
||||
var newTypes = Funcs.Const.Types.RenameVars(new[]
|
||||
{
|
||||
new Curry(e, e),
|
||||
new Curry(new Curry(b, f), new Curry(new Curry(a, b), new Curry(a, f)))
|
||||
}).ToList();
|
||||
Assert.Single(newTypes);
|
||||
Assert.Equal(new Curry(c, new Curry(d, c)),
|
||||
newTypes[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSubstitution_TagsToStringTagsToBool_ShouldUnify()
|
||||
{
|
||||
var biggerType = new Curry(Typs.Tags, Typs.String);
|
||||
var smallerType = new Curry(Typs.Tags, Typs.Bool);
|
||||
// The expected type (biggerType) on the left, the argument type on the right (as it should be)
|
||||
var unificationTable = biggerType.UnificationTable(smallerType);
|
||||
Assert.NotNull(unificationTable);
|
||||
unificationTable = smallerType.UnificationTable(biggerType);
|
||||
Assert.Null(unificationTable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSubstitution_TagsToDoubleTagsToPDouble_ShouldUnify()
|
||||
{
|
||||
var biggerType = new Curry(Typs.Tags, Typs.Double);
|
||||
var smallerType = new Curry(Typs.Tags, Typs.PDouble);
|
||||
var unificationTable = biggerType.UnificationTable(smallerType);
|
||||
Assert.NotNull(unificationTable);
|
||||
unificationTable = smallerType.UnificationTable(biggerType);
|
||||
Assert.Null(unificationTable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSubstitution_DoubleToStringPDoubleToString_ShouldUnify()
|
||||
{
|
||||
var biggerType = new Curry(Typs.PDouble, Typs.Bool);
|
||||
var smallerType = new Curry(Typs.Double, Typs.Bool);
|
||||
// We expect something that is able to handle PDoubles, but it is able to handle the wider doubles - should be fine
|
||||
var unificationTable = biggerType.UnificationTable(smallerType);
|
||||
Assert.NotNull(unificationTable);
|
||||
unificationTable = smallerType.UnificationTable(biggerType);
|
||||
Assert.Null(unificationTable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Typechecker_EitherFunc_CorrectType()
|
||||
{
|
||||
var id = new Apply(Funcs.EitherFunc, Funcs.Id);
|
||||
Assert.Equal(2, id.Types.Count());
|
||||
|
||||
var idconst = new Apply(id, Funcs.Const);
|
||||
Assert.Equal(2, idconst.Types.Count());
|
||||
|
||||
var e =
|
||||
new Apply(idconst, new Constant("a"));
|
||||
Assert.Equal(2, e.Types.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpecializeToSmallest_Parse_SmallestType()
|
||||
{
|
||||
var smallest = Funcs.Parse.SpecializeToSmallestType();
|
||||
Assert.Single(smallest.Types);
|
||||
Assert.Equal(new Curry(Typs.String, Typs.PDouble), smallest.Types.First());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Unify_TwoSubtypes_DoesNotUnify()
|
||||
{
|
||||
var tags2double = new Curry(Typs.Tags, Typs.Double);
|
||||
var tags2pdouble = new Curry(Typs.Tags, Typs.PDouble);
|
||||
var unifA = tags2double.Unify(tags2pdouble, true);
|
||||
Assert.Null(unifA);
|
||||
var unifB = tags2pdouble.Unify(tags2double, true);
|
||||
Assert.NotNull(unifB);
|
||||
|
||||
var unifC = tags2double.Unify(tags2pdouble, false);
|
||||
Assert.NotNull(unifC);
|
||||
var unifD = tags2pdouble.Unify(tags2double, false);
|
||||
Assert.Null(unifD);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Specialize_WiderType_StillSmallerType()
|
||||
{
|
||||
var f = Funcs.Eq;
|
||||
var strstrb = new Curry(
|
||||
Typs.String,
|
||||
new Curry(Typs.String, Typs.Bool));
|
||||
var f0 = f.Specialize(strstrb);
|
||||
Assert.Equal(new[] {strstrb}, f0.Types);
|
||||
|
||||
var strstrstr = new Curry(
|
||||
Typs.String,
|
||||
new Curry(Typs.String, Typs.String));
|
||||
|
||||
var f1 = f.Specialize(strstrstr);
|
||||
|
||||
Assert.Equal(new[] {strstrb, strstrstr}, f1.Types);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,43 +8,6 @@ namespace AspectedRouting.Test
|
|||
{
|
||||
public class TestAnalysis
|
||||
{
|
||||
[Fact]
|
||||
public void OnAll_SmallTagSet_AllCombinations()
|
||||
{
|
||||
var possibleTags = new Dictionary<string, List<string>>
|
||||
{
|
||||
{"a", new List<string> {"x", "y"}}
|
||||
};
|
||||
|
||||
var all = possibleTags.OnAllCombinations(dict => ObjectExtensions.Pretty(dict), new List<string>()).ToList();
|
||||
Assert.Equal(3, all.Count);
|
||||
Assert.Contains("{}", all);
|
||||
Assert.Contains("{a=x;}", all);
|
||||
Assert.Contains("{a=y;}", all);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnAll_TwoTagSet_AllCombinations()
|
||||
{
|
||||
var possibleTags = new Dictionary<string, List<string>>
|
||||
{
|
||||
{"a", new List<string> {"x", "y"}},
|
||||
{"b", new List<string> {"u", "v"}}
|
||||
};
|
||||
|
||||
var all = possibleTags.OnAllCombinations(dict => dict.Pretty(), new List<string>()).ToList();
|
||||
Assert.Equal(9, all.Count);
|
||||
Assert.Contains("{}", all);
|
||||
Assert.Contains("{a=x;}", all);
|
||||
Assert.Contains("{a=y;}", all);
|
||||
|
||||
Assert.Contains("{b=u;}", all);
|
||||
Assert.Contains("{b=v;}", all);
|
||||
|
||||
Assert.Contains("{a=x;b=u;}", all);
|
||||
Assert.Contains("{a=x;b=v;}", all);
|
||||
Assert.Contains("{a=y;b=u;}", all);
|
||||
Assert.Contains("{a=y;b=v;}", all);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ namespace AspectedRouting.Test
|
|||
{"ferry", "yes"}
|
||||
};
|
||||
|
||||
Assert.Equal("tags -> pdouble", string.Join(", ", aspect.Types));
|
||||
Assert.Equal("tags -> double", string.Join(", ", aspect.Types));
|
||||
Assert.Equal(42d, new Apply(aspect, new Constant(tags)).Evaluate(null));
|
||||
}
|
||||
|
||||
|
@ -33,16 +33,18 @@ namespace AspectedRouting.Test
|
|||
public void MaxSpeed_AnalyzeTags_AllTagsReturned()
|
||||
{
|
||||
var json =
|
||||
"{\"name\": \"legal_maxspeed_be\",\"description\": \"Gives, for each type of highway, which the default legal maxspeed is in Belgium. This file is intended to be reused for in all vehicles, from pedestrian to car. In some cases, a legal maxspeed is not really defined (e.g. on footways). In that case, a socially acceptable speed should be taken (e.g.: a bicycle on a pedestrian path will go say around 12km/h)\",\"unit\": \"km/h\",\"$max\": {\"maxspeed\": \"$parse\",\"highway\": {\"residential\": 30},\"ferry\":5}}";
|
||||
"{\"name\": \"legal_maxspeed_be\",\"description\": \"Gives, for each type of highway, which the default legal maxspeed is in Belgium. This file is intended to be reused for in all vehicles, from pedestrian to car. In some cases, a legal maxspeed is not really defined (e.g. on footways). In that case, a socially acceptable speed should be taken (e.g.: a bicycle on a pedestrian path will go say around 12km/h)\"," +
|
||||
"\"unit\": \"km/h\"," +
|
||||
"\"$max\": {\"maxspeed\": \"$parse\",\"highway\": {\"residential\": 30},\"ferry\":5}}";
|
||||
|
||||
var aspect = JsonParser.AspectFromJson(null, json, null);
|
||||
|
||||
Assert.Equal(
|
||||
new Dictionary<string, List<string>>
|
||||
new Dictionary<string, HashSet<string>>
|
||||
{
|
||||
{"maxspeed", new List<string>()},
|
||||
{"highway", new List<string> {"residential"}},
|
||||
{"ferry", new List<string>()}
|
||||
{"maxspeed", new HashSet<string>()},
|
||||
{"highway", new HashSet<string> {"residential"}},
|
||||
{"ferry", new HashSet<string>()}
|
||||
},
|
||||
aspect.PossibleTags());
|
||||
}
|
||||
|
@ -178,5 +180,14 @@ namespace AspectedRouting.Test
|
|||
Assert.Equal(new List<IExpression> {Funcs.Id, Funcs.Id, a}.Select(e => e.ToString()),
|
||||
args.Select(e => e.ToString()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SpecializeToSmallest_IsSmallestType()
|
||||
{
|
||||
var app = new Apply(Funcs.Id, Funcs.Parse);
|
||||
Assert.Equal(2, app.Types.Count());
|
||||
var smaller = app.SpecializeToSmallestType();
|
||||
Assert.Single(smaller.Types);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,6 +15,9 @@
|
|||
<None Update="IO\lua\unitTestProfile2.lua">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="IO\lua\if_then_else_dotted.lua">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
44
AspectedRouting/IO/LuaSkeleton/LuaLiteral.cs
Normal file
44
AspectedRouting/IO/LuaSkeleton/LuaLiteral.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AspectedRouting.Language;
|
||||
using Type = AspectedRouting.Language.Typ.Type;
|
||||
|
||||
namespace AspectedRouting.IO.LuaSkeleton
|
||||
{
|
||||
public class LuaLiteral : IExpression
|
||||
{
|
||||
public readonly string Lua;
|
||||
public IEnumerable<Type> Types { get; }
|
||||
|
||||
public LuaLiteral(IEnumerable<Type> types, string lua)
|
||||
{
|
||||
Lua = lua;
|
||||
Types = types;
|
||||
}
|
||||
|
||||
public object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IExpression Specialize(IEnumerable<Type> allowedTypes)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public IExpression Optimize()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public IExpression OptimizeWithArgument(IExpression argument)
|
||||
{
|
||||
return this.Apply(argument);
|
||||
}
|
||||
|
||||
public void Visit(Func<IExpression, bool> f)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -143,6 +143,14 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
{
|
||||
var (f, args) = fArgs.Value;
|
||||
var baseFunc = (Function) f;
|
||||
|
||||
if (baseFunc.Name.Equals(Funcs.Id.Name) ||
|
||||
baseFunc.Name.Equals(Funcs.Dot.Name))
|
||||
{
|
||||
// This is an ugly hack
|
||||
return ToLua(args.First());
|
||||
}
|
||||
|
||||
AddDep(baseFunc.Name);
|
||||
|
||||
return baseFunc.Name + "(" + string.Join(", ", args.Select(arg => ToLua(arg, key))) + ")";
|
||||
|
@ -152,6 +160,8 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
var collected = new List<IExpression>();
|
||||
switch (bare)
|
||||
{
|
||||
case LuaLiteral lua:
|
||||
return lua.Lua;
|
||||
case FunctionCall fc:
|
||||
var called = _context.DefinedFunctions[fc.CalledFunctionName];
|
||||
if (called.ProfileInternal)
|
||||
|
@ -226,6 +236,8 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
var o = c.Evaluate(_context);
|
||||
switch (o)
|
||||
{
|
||||
case LuaLiteral lua:
|
||||
return lua.Lua;
|
||||
case IExpression e:
|
||||
return ConstantToLua(new Constant(e.Types.First(), e.Evaluate(null)));
|
||||
case int i:
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
|
||||
_alreadyAddedFunctions.Add(meta.Name);
|
||||
|
||||
var possibleTags = meta.PossibleTags() ?? new Dictionary<string, List<string>>();
|
||||
var possibleTags = meta.PossibleTags() ?? new Dictionary<string, HashSet<string>>();
|
||||
int numberOfCombinations;
|
||||
numberOfCombinations = possibleTags.Values.Select(lst => 1 + lst.Count).Multiply();
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AspectedRouting.IO.LuaSkeleton;
|
||||
using AspectedRouting.Language;
|
||||
using AspectedRouting.Language.Functions;
|
||||
using AspectedRouting.Language.Typ;
|
||||
|
@ -12,6 +13,11 @@ namespace AspectedRouting.IO.itinero1
|
|||
|
||||
private string GenerateMainProfileFunction()
|
||||
{
|
||||
|
||||
var access = _skeleton.ToLua(_profile.Access);
|
||||
var oneway = _skeleton.ToLua(_profile.Oneway);
|
||||
var speed = _skeleton.ToLua(_profile.Speed);
|
||||
|
||||
var impl = string.Join("\n",
|
||||
"",
|
||||
"",
|
||||
|
@ -33,14 +39,14 @@ namespace AspectedRouting.IO.itinero1
|
|||
" result.canstop = true",
|
||||
" result.attributes_to_keep = {}",
|
||||
"",
|
||||
" local access = " + _skeleton.ToLua(_profile.Access),
|
||||
" local access = " + access,
|
||||
" if (access == nil or access == \"no\") then",
|
||||
" return",
|
||||
" end",
|
||||
" tags.access = access",
|
||||
" local oneway = " + _skeleton.ToLua(_profile.Oneway),
|
||||
" local oneway = " + oneway,
|
||||
" tags.oneway = oneway",
|
||||
" local speed = " + _skeleton.ToLua(_profile.Speed),
|
||||
" local speed = " + speed,
|
||||
" tags.speed = speed",
|
||||
" local distance = 1 -- the weight per meter for distance travelled is, well, 1m/m",
|
||||
"");
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
"normalize = false",
|
||||
"vehicle_type = {" + string.Join(", ", _profile.VehicleTyps.Select(s => "\"" + s + "\"")) + "}",
|
||||
// meta_whitelist is defined in the profile file, these are tags that are included in the generated route, but are not relevant for determining weights
|
||||
"meta_whitelist = {\n \"cycle_network_colour\"," // cycle network colour is sneaked in here for legacy reasons
|
||||
"meta_whitelist = {\n \"cycle_network_colour\",\n " // cycle network colour is sneaked in here for legacy reasons
|
||||
+ string.Join("\n , ", _profile.Metadata.Select(s => "\"" + s + "\""))
|
||||
+ " }",
|
||||
"profile_whitelist = {\n " + string.Join("\n , ", keys) + "\n }",
|
||||
|
|
|
@ -24,6 +24,8 @@ namespace AspectedRouting.IO.jsonParser
|
|||
return null;
|
||||
}
|
||||
|
||||
Console.WriteLine("Parsing " + fileName);
|
||||
|
||||
return doc.RootElement.ParseAspect(fileName, c);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -51,6 +53,15 @@ namespace AspectedRouting.IO.jsonParser
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mostly used in the unit tests
|
||||
*/
|
||||
[Obsolete]
|
||||
public static IExpression ParseExpression(Context c, string json)
|
||||
{
|
||||
return JsonDocument.Parse(json).RootElement.ParseExpression(c);
|
||||
}
|
||||
|
||||
|
||||
private static ProfileMetaData ParseProfile(this JsonElement e, Context context, FileInfo filepath)
|
||||
{
|
||||
|
@ -191,7 +202,7 @@ namespace AspectedRouting.IO.jsonParser
|
|||
|
||||
wrapped = Funcs.Either(Funcs.Id,
|
||||
new Apply(Funcs.Dot, Funcs.Head),
|
||||
wrapped);
|
||||
wrapped);
|
||||
}
|
||||
|
||||
return wrapped;
|
||||
|
@ -229,7 +240,8 @@ namespace AspectedRouting.IO.jsonParser
|
|||
if (e.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
var exprs = e.EnumerateArray().Select(json =>
|
||||
Funcs.Either(Funcs.Id, Funcs.Const, json.ParseExpression(context)));
|
||||
Funcs.Either(Funcs.Id, Funcs.Const, json.ParseExpression(context)))
|
||||
.ToList();
|
||||
var list = new Constant(exprs);
|
||||
return Funcs.Either(Funcs.Id, Funcs.ListDot, list);
|
||||
}
|
||||
|
@ -318,6 +330,7 @@ namespace AspectedRouting.IO.jsonParser
|
|||
return func.Apply(args);
|
||||
}
|
||||
|
||||
|
||||
args.Add(firstArgument);
|
||||
|
||||
var allExprs = allArgs
|
||||
|
@ -331,7 +344,7 @@ namespace AspectedRouting.IO.jsonParser
|
|||
throw new ArgumentException("{funcName} does not specify argument names");
|
||||
|
||||
|
||||
foreach (var argName in func.ArgNames.GetRange(1, func.ArgNames.Count - 2))
|
||||
foreach (var argName in func.ArgNames.GetRange(1, func.ArgNames.Count - 1))
|
||||
// We skip the first argument, that one is already added
|
||||
{
|
||||
args.Add(allExprs[argName]);
|
||||
|
@ -363,14 +376,14 @@ namespace AspectedRouting.IO.jsonParser
|
|||
|
||||
|
||||
var f = (IExpression) Funcs.BuiltinByName(prop.Name);
|
||||
if (f == null)
|
||||
if (f == null || f.Types.Count() == 0)
|
||||
{
|
||||
throw new KeyNotFoundException($"The builtin function {prop.Name} was not found");
|
||||
}
|
||||
|
||||
var fArg = prop.Value.ParseExpression(context);
|
||||
|
||||
if (fArg == null)
|
||||
if (fArg == null || fArg.Types.Count() == 0)
|
||||
{
|
||||
throw new ArgumentException("Could not type expression " + prop);
|
||||
}
|
||||
|
@ -465,6 +478,9 @@ namespace AspectedRouting.IO.jsonParser
|
|||
throw new NullReferenceException("The finalized form of expression `" + exprSpec + "` is null");
|
||||
}
|
||||
|
||||
expr = expr.SpecializeToSmallestType();
|
||||
|
||||
|
||||
var name = e.Get("name");
|
||||
if (expr.Types.Count() > 1)
|
||||
{
|
||||
|
|
|
@ -12,15 +12,21 @@ namespace AspectedRouting.IO.jsonParser
|
|||
{
|
||||
private static IExpression ParseProfileProperty(JsonElement e, Context c, string property)
|
||||
{
|
||||
|
||||
if (!e.TryGetProperty(property, out var prop))
|
||||
{
|
||||
throw new ArgumentException("Not a valid profile: the declaration expression for '" + property +
|
||||
"' is missing");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var expr = ParseExpression(prop, c).Optimize();
|
||||
var expr = ParseExpression(prop, c);
|
||||
if (expr.Types.Count() == 0)
|
||||
{
|
||||
throw new Exception($"Could not parse field {property}, no valid typing for expression found");
|
||||
}
|
||||
|
||||
expr = expr.Optimize();
|
||||
expr = Funcs.Either(Funcs.Id, Funcs.Const, expr);
|
||||
|
||||
var specialized = expr.Specialize(new Curry(Typs.Tags, new Var("a")));
|
||||
|
@ -30,6 +36,7 @@ namespace AspectedRouting.IO.jsonParser
|
|||
" hasn't the right type of 'Tags -> a'; it has types " +
|
||||
string.Join(",", expr.Types) + "\n" + expr.TypeBreakdown());
|
||||
}
|
||||
|
||||
return specialized.Optimize();
|
||||
}
|
||||
catch (Exception exc)
|
||||
|
@ -69,7 +76,7 @@ namespace AspectedRouting.IO.jsonParser
|
|||
break;
|
||||
case JsonValueKind.Array:
|
||||
var list = obj.Value.EnumerateArray().Select(x => x.ToString()).ToList();
|
||||
ps[nm] = new Constant(new ListType(Typs.String),list);
|
||||
ps[nm] = new Constant(new ListType(Typs.String), list);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException(
|
||||
|
@ -82,8 +89,6 @@ namespace AspectedRouting.IO.jsonParser
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private static string Get(this JsonElement json, string key)
|
||||
{
|
||||
if (json.TryGetProperty(key, out var p))
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
function if_then_else(condition, thn, els)
|
||||
if (condition) then
|
||||
if (condition ~= nil and (condition == "yes" or condition == true or condition == "true") then
|
||||
return thn
|
||||
else
|
||||
return els -- if no third parameter is given, 'els' will be nil
|
||||
|
|
11
AspectedRouting/IO/lua/if_then_else_dotted.lua
Normal file
11
AspectedRouting/IO/lua/if_then_else_dotted.lua
Normal file
|
@ -0,0 +1,11 @@
|
|||
function if_then_else_dotted(conditionf, thnf, elsef, arg)
|
||||
local condition = conditionf(arg);
|
||||
if (condition) then
|
||||
return thnf(arg)
|
||||
else
|
||||
if(elsef == nil) then
|
||||
return nil
|
||||
end
|
||||
return elsef(arg) -- if no third parameter is given, 'els' will be nil
|
||||
end
|
||||
end
|
|
@ -10,121 +10,6 @@ namespace AspectedRouting.Language
|
|||
{
|
||||
public static class Analysis
|
||||
{
|
||||
public static string GenerateFullOutputCsv(Context c, IExpression e)
|
||||
{
|
||||
var possibleTags = e.PossibleTags();
|
||||
|
||||
var defaultValues = new List<string>
|
||||
{
|
||||
"0",
|
||||
"30",
|
||||
"50",
|
||||
"yes",
|
||||
"no",
|
||||
"SomeName"
|
||||
};
|
||||
|
||||
Console.WriteLine(e);
|
||||
var keys = e.PossibleTags().Keys.ToList();
|
||||
|
||||
|
||||
var results = possibleTags.OnAllCombinations(
|
||||
tags =>
|
||||
{
|
||||
Console.WriteLine(tags.Pretty());
|
||||
return (new Apply(e, new Constant(tags)).Evaluate(c), tags);
|
||||
}, defaultValues).ToList();
|
||||
|
||||
var csv = "result, " + string.Join(", ", keys) + "\n";
|
||||
|
||||
foreach (var (result, tags) in results)
|
||||
{
|
||||
csv += result + ", " +
|
||||
string.Join(", ",
|
||||
keys.Select(key =>
|
||||
{
|
||||
if (tags.ContainsKey(key))
|
||||
{
|
||||
return tags[key];
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}));
|
||||
csv += "\n";
|
||||
}
|
||||
|
||||
return csv;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> OnAllCombinations<T>(this Dictionary<string, List<string>> possibleTags,
|
||||
Func<Dictionary<string, string>, T> f, List<string> defaultValues)
|
||||
{
|
||||
var newDict = new Dictionary<string, List<string>>();
|
||||
foreach (var (key, value) in possibleTags)
|
||||
{
|
||||
if (value.Count == 0)
|
||||
{
|
||||
// This value is a list of possible values, e.g. a double
|
||||
// We replace them with various other
|
||||
newDict[key] = defaultValues;
|
||||
}
|
||||
else
|
||||
{
|
||||
newDict[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
possibleTags = newDict;
|
||||
|
||||
var keys = possibleTags.Keys.ToList();
|
||||
var currentKeyIndex = new int[possibleTags.Count];
|
||||
for (int i = 0; i < currentKeyIndex.Length; i++)
|
||||
{
|
||||
currentKeyIndex[i] = -1;
|
||||
}
|
||||
|
||||
bool SelectNext()
|
||||
{
|
||||
var j = 0;
|
||||
while (j < currentKeyIndex.Length)
|
||||
{
|
||||
currentKeyIndex[j]++;
|
||||
if (currentKeyIndex[j] ==
|
||||
possibleTags[keys[j]].Count)
|
||||
{
|
||||
// This index rolls over
|
||||
currentKeyIndex[j] = -1;
|
||||
j++;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
do
|
||||
{
|
||||
var tags = new Dictionary<string, string>();
|
||||
for (int i = 0; i < keys.Count(); i++)
|
||||
{
|
||||
var key = keys[i];
|
||||
var j = currentKeyIndex[i];
|
||||
if (j >= 0)
|
||||
{
|
||||
var value = possibleTags[key][j];
|
||||
tags.Add(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
yield return f(tags);
|
||||
} while (SelectNext());
|
||||
}
|
||||
|
||||
public static Dictionary<string, (List<Type> Types, string inFunction)> UsedParameters(
|
||||
this ProfileMetaData profile, Context context)
|
||||
|
@ -492,7 +377,7 @@ namespace AspectedRouting.Language
|
|||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
/// <returns>A dictionary containing all possible values. An entry with an empty list indicates a wildcard</returns>
|
||||
public static Dictionary<string, List<string>> PossibleTags(this IExpression e)
|
||||
public static Dictionary<string, HashSet<string>> PossibleTags(this IExpression e)
|
||||
{
|
||||
var mappings = new List<Mapping>();
|
||||
e.Visit(x =>
|
||||
|
@ -524,7 +409,7 @@ namespace AspectedRouting.Language
|
|||
|
||||
// Visit will have the main mapping at the first position
|
||||
var rootMapping = mappings[0];
|
||||
var result = new Dictionary<string, List<string>>();
|
||||
var result = new Dictionary<string, HashSet<string>>();
|
||||
|
||||
foreach (var (key, expr) in rootMapping.StringToResultFunctions)
|
||||
{
|
||||
|
@ -538,7 +423,7 @@ namespace AspectedRouting.Language
|
|||
|
||||
return true;
|
||||
});
|
||||
result[key] = values;
|
||||
result[key] = values.ToHashSet();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -38,8 +38,7 @@ namespace AspectedRouting.Language.Expression
|
|||
|
||||
FunctionApplications = new Dictionary<Type, (IExpression f, IExpression a)>();
|
||||
|
||||
var argTypesCleaned = argument.Types.RenameVars(f.Types);
|
||||
var typesCleaned = argTypesCleaned.ToList();
|
||||
var typesCleaned = argument.Types.RenameVars(f.Types).ToList();
|
||||
foreach (var funcType in f.Types)
|
||||
{
|
||||
if (!(funcType is Curry c))
|
||||
|
@ -133,19 +132,24 @@ namespace AspectedRouting.Language.Expression
|
|||
{
|
||||
foreach (var (resultType, (funExpr, argExpr)) in FunctionApplications)
|
||||
{
|
||||
var substitutions = resultType.UnificationTable(allowedType);
|
||||
var substitutions = resultType.UnificationTable(allowedType, true);
|
||||
if (substitutions == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var actualResultType = resultType.Substitute(substitutions);
|
||||
var actualResultType = allowedType.Substitute(substitutions);
|
||||
|
||||
// f : a -> b
|
||||
// actualResultType = b (b which was retrieved after a reverse substitution)
|
||||
|
||||
var actualFunction = funExpr.Specialize(substitutions);
|
||||
var actualArgument = argExpr.Specialize(substitutions);
|
||||
|
||||
if (actualFunction == null || actualArgument == null)
|
||||
{
|
||||
continue;
|
||||
// One of the subexpressions can't be optimized
|
||||
return null;
|
||||
}
|
||||
|
||||
newArgs[actualResultType] = (actualFunction, actualArgument);
|
||||
|
@ -162,6 +166,11 @@ namespace AspectedRouting.Language.Expression
|
|||
|
||||
public IExpression Optimize()
|
||||
{
|
||||
if (Types.Count() == 0)
|
||||
{
|
||||
throw new ArgumentException("This application contain no valid types, so cannot be optimized" + this);
|
||||
}
|
||||
|
||||
// (eitherfunc dot id) id
|
||||
// => (const dot _) id => dot id => id
|
||||
// or => (constRight _ id) id => id id => id
|
||||
|
@ -186,8 +195,6 @@ namespace AspectedRouting.Language.Expression
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (Types.Count() > 1)
|
||||
{
|
||||
var optimized = new Dictionary<Type, (IExpression f, IExpression a)>();
|
||||
|
@ -231,8 +238,8 @@ namespace AspectedRouting.Language.Expression
|
|||
IExpression a)
|
||||
{
|
||||
f = f.Optimize();
|
||||
a = a.Optimize();
|
||||
|
||||
a = a.Optimize();
|
||||
|
||||
switch (f)
|
||||
{
|
||||
|
@ -309,7 +316,13 @@ namespace AspectedRouting.Language.Expression
|
|||
return arg.ToString();
|
||||
}
|
||||
|
||||
return $"({f} {arg.ToString().Indent()})";
|
||||
var extra = "";
|
||||
if (FunctionApplications.Count() > 1)
|
||||
{
|
||||
extra = " [" + FunctionApplications.Count + " IMPLEMENTATIONS]";
|
||||
}
|
||||
|
||||
return $"({f} {arg.ToString().Indent()})" + extra;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,6 +40,11 @@ namespace AspectedRouting.Language.Expression
|
|||
return this;
|
||||
}
|
||||
|
||||
public IExpression OptimizeWithArgument(IExpression argument)
|
||||
{
|
||||
return this.Apply(argument);
|
||||
}
|
||||
|
||||
public virtual void Visit(Func<IExpression, bool> f)
|
||||
{
|
||||
f(this);
|
||||
|
@ -50,10 +55,6 @@ namespace AspectedRouting.Language.Expression
|
|||
return $"${Name}";
|
||||
}
|
||||
|
||||
public IExpression Apply(params IExpression[] args)
|
||||
{
|
||||
return this.Apply(args.ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gives an overview per argument what the possible types are.
|
||||
|
|
|
@ -54,6 +54,21 @@ namespace AspectedRouting.Language.Expression
|
|||
public IExpression Optimize()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public IExpression OptimizeWithArgument(IExpression argument)
|
||||
{
|
||||
|
||||
if (_name.Equals(Funcs.Id.Name))
|
||||
{
|
||||
return argument;
|
||||
}
|
||||
|
||||
return this.Apply(argument);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void Visit(Func<IExpression, bool> f)
|
||||
|
|
|
@ -91,8 +91,8 @@ namespace AspectedRouting.Language.Expression
|
|||
parameters[k.TrimStart('#')] = v;
|
||||
}
|
||||
|
||||
c = c.WithParameters(parameters);
|
||||
|
||||
c = c.WithParameters(parameters)
|
||||
.WithAspectName(this.Name);
|
||||
tags = new Dictionary<string, string>(tags);
|
||||
var canAccess = Access.Run(c, tags);
|
||||
tags["access"] = "" + canAccess;
|
||||
|
|
|
@ -38,8 +38,8 @@ namespace AspectedRouting.Language
|
|||
public static readonly Function MemberOf = new MemberOf();
|
||||
|
||||
|
||||
|
||||
public static readonly Function If = new If();
|
||||
public static readonly Function IfDotted = new IfDotted();
|
||||
|
||||
public static readonly Function Id = new Id();
|
||||
public static readonly Function Const = new Const();
|
||||
|
@ -58,7 +58,7 @@ namespace AspectedRouting.Language
|
|||
|
||||
public static IExpression Either(IExpression a, IExpression b, IExpression arg)
|
||||
{
|
||||
return new Apply(new Apply(new Apply(EitherFunc, a), b), arg);
|
||||
return EitherFunc.Apply(a, b, arg);
|
||||
}
|
||||
|
||||
public static Function BuiltinByName(string funcName)
|
||||
|
@ -78,13 +78,23 @@ namespace AspectedRouting.Language
|
|||
{
|
||||
return null;
|
||||
}
|
||||
e = e.SpecializeToSmallestType().Optimize();
|
||||
|
||||
e = e.SpecializeToSmallestType();
|
||||
// TODO FIX THIS so that it works
|
||||
// An argument 'optimizes' it's types from 'string -> bool' to 'string -> string'
|
||||
e = e.Optimize();
|
||||
//
|
||||
e.SanityCheck();
|
||||
return e;
|
||||
}
|
||||
|
||||
public static IExpression SpecializeToSmallestType(this IExpression e)
|
||||
{
|
||||
if (e.Types.Count() == 1)
|
||||
{
|
||||
return e;
|
||||
}
|
||||
|
||||
Type smallest = null;
|
||||
foreach (var t in e.Types)
|
||||
{
|
||||
|
@ -97,6 +107,7 @@ namespace AspectedRouting.Language
|
|||
var smallestIsSuperset = smallest.IsSuperSet(t);
|
||||
if (!t.IsSuperSet(smallest) && !smallestIsSuperset)
|
||||
{
|
||||
// Neither one is smaller then the other, we can not compare them
|
||||
return e;
|
||||
}
|
||||
|
||||
|
@ -114,6 +125,11 @@ namespace AspectedRouting.Language
|
|||
return function.Apply(args.ToList());
|
||||
}
|
||||
|
||||
public static IExpression Apply(this IExpression function, params IExpression[] args)
|
||||
{
|
||||
return function.Apply(args.ToList());
|
||||
}
|
||||
|
||||
public static IExpression Apply(this IExpression function, List<IExpression> args)
|
||||
{
|
||||
if (args.Count == 0)
|
||||
|
|
|
@ -14,41 +14,54 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public Constant(IEnumerable<Type> types, object o)
|
||||
{
|
||||
Types = types;
|
||||
Types = types.ToList();
|
||||
if (o is IEnumerable<IExpression> enumerable)
|
||||
{
|
||||
o = enumerable.ToList();
|
||||
}
|
||||
|
||||
_o = o;
|
||||
}
|
||||
|
||||
public Constant(Type t, object o)
|
||||
{
|
||||
Types = new[] {t};
|
||||
Types = new List<Type> {t};
|
||||
if (o is IEnumerable<IExpression> enumerable)
|
||||
{
|
||||
o = enumerable.ToList();
|
||||
}
|
||||
|
||||
_o = o;
|
||||
}
|
||||
|
||||
public Constant(IExpression[] exprs)
|
||||
{
|
||||
Types = exprs.SpecializeToCommonTypes(out var specializedVersions).Select(t => new ListType(t));
|
||||
var tps = exprs
|
||||
.SpecializeToCommonTypes(out var specializedVersions)
|
||||
.Select(t => new ListType(t));
|
||||
Types = tps.ToList();
|
||||
_o = specializedVersions;
|
||||
}
|
||||
|
||||
public Constant(IEnumerable<IExpression> xprs)
|
||||
public Constant(List<IExpression> exprs)
|
||||
{
|
||||
var exprs = xprs.ToList();
|
||||
try
|
||||
{
|
||||
Types = exprs.SpecializeToCommonTypes(out var specializedVersions).Select(t => new ListType(t));
|
||||
Types = exprs
|
||||
.SpecializeToCommonTypes(out var specializedVersions).Select(t => new ListType(t)).ToList();
|
||||
_o = specializedVersions.ToList();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception($"While creating a list with members "+
|
||||
string.Join(", ", exprs.Select(x => x.Optimize()))+
|
||||
$" {e.Message}", e);
|
||||
throw new Exception($"While creating a list with members " +
|
||||
string.Join(", ", exprs.Select(x => x.Optimize())) +
|
||||
$" {e.Message}", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Constant(string s)
|
||||
{
|
||||
Types = new[] {Typs.String};
|
||||
Types = new List<Type> {Typs.String};
|
||||
_o = s;
|
||||
}
|
||||
|
||||
|
@ -121,10 +134,10 @@ namespace AspectedRouting.Language.Functions
|
|||
addTo.Add(this);
|
||||
}
|
||||
|
||||
public IExpression Specialize(IEnumerable<Type> allowedTypes)
|
||||
public IExpression Specialize(IEnumerable<Type> allowedTypesEnumerable)
|
||||
{
|
||||
var enumerable = allowedTypes.ToList();
|
||||
var unified = Types.SpecializeTo(enumerable);
|
||||
var allowedTypes = allowedTypesEnumerable.ToList();
|
||||
var unified = Types.SpecializeTo(allowedTypes);
|
||||
if (unified == null)
|
||||
{
|
||||
return null;
|
||||
|
@ -133,15 +146,39 @@ namespace AspectedRouting.Language.Functions
|
|||
var newO = _o;
|
||||
if (_o is IExpression e)
|
||||
{
|
||||
newO = e.Specialize(enumerable);
|
||||
newO = e.Specialize(allowedTypes);
|
||||
}
|
||||
|
||||
if (_o is IEnumerable<IExpression> es)
|
||||
{
|
||||
var innerTypes = enumerable
|
||||
.Where(t => t is ListType)
|
||||
.Select(t => ((ListType) t).InnerType);
|
||||
newO = es.Select(x => x.Specialize(innerTypes)).Where(x => x != null);
|
||||
var innerTypes = new List<Type>();
|
||||
foreach (var allowedType in allowedTypes)
|
||||
{
|
||||
if (allowedType is ListType listType)
|
||||
{
|
||||
innerTypes.Add(listType.InnerType);
|
||||
}
|
||||
}
|
||||
|
||||
var specializedExpressions = new List<IExpression>();
|
||||
foreach (var expr in es)
|
||||
{
|
||||
if (expr == null)
|
||||
{
|
||||
throw new NullReferenceException("Subexpression is null");
|
||||
}
|
||||
|
||||
var specialized = expr.Specialize(innerTypes);
|
||||
if (specialized == null)
|
||||
{
|
||||
// If a subexpression can not be specialized, this list cannot be specialized
|
||||
return null;
|
||||
}
|
||||
|
||||
specializedExpressions.Add(specialized);
|
||||
}
|
||||
|
||||
newO = specializedExpressions;
|
||||
}
|
||||
|
||||
return new Constant(unified, newO);
|
||||
|
@ -151,17 +188,36 @@ namespace AspectedRouting.Language.Functions
|
|||
{
|
||||
if (_o is IEnumerable<IExpression> exprs)
|
||||
{
|
||||
return new Constant(exprs.Select(x => x.Optimize()));
|
||||
// This is a list
|
||||
var optExprs = new List<IExpression>();
|
||||
foreach (var expression in exprs)
|
||||
{
|
||||
var exprOpt = expression.Optimize();
|
||||
if (exprOpt == null || exprOpt.Types.Count() == 0)
|
||||
{
|
||||
throw new ArgumentException("Non-optimizable expression:" + expression);
|
||||
}
|
||||
|
||||
optExprs.Add(exprOpt);
|
||||
}
|
||||
|
||||
return new Constant(optExprs.ToList());
|
||||
}
|
||||
|
||||
if (_o is IExpression expr)
|
||||
{
|
||||
// This is a list
|
||||
return new Constant(expr.Types, expr.Optimize());
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public IExpression OptimizeWithArgument(IExpression argument)
|
||||
{
|
||||
return this.Apply(argument);
|
||||
}
|
||||
|
||||
public void Visit(Func<IExpression, bool> f)
|
||||
{
|
||||
if (_o is IExpression e)
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace AspectedRouting.Language.Functions
|
|||
private static Var b = new Var("b");
|
||||
|
||||
public override string Description { get; } = "Selects either one of the branches, depending on the condition." +
|
||||
" The 'then' branch is returned if the condition returns the string `yes` or `true` or the boolean `true`" +
|
||||
"If the `else` branch is not set, `null` is returned in the condition is false.";
|
||||
public override List<string> ArgNames { get; } = new List<string> {"condition", "then", "else"};
|
||||
|
||||
|
@ -18,12 +19,8 @@ namespace AspectedRouting.Language.Functions
|
|||
{
|
||||
Curry.ConstructFrom(a, Typs.Bool, a, a),
|
||||
Curry.ConstructFrom(a, Typs.Bool, a),
|
||||
|
||||
Curry.ConstructFrom(a,
|
||||
new Curry(b, Typs.Bool),
|
||||
new Curry(b, a),
|
||||
new Curry(b, a),
|
||||
b)
|
||||
Curry.ConstructFrom(a, Typs.String, a, a),
|
||||
Curry.ConstructFrom(a, Typs.String, a)
|
||||
}
|
||||
)
|
||||
{
|
||||
|
@ -38,19 +35,19 @@ namespace AspectedRouting.Language.Functions
|
|||
{
|
||||
var condition = arguments[0].Evaluate(c);
|
||||
var then = arguments[1];
|
||||
IExpression _else = null;
|
||||
IExpression @else = null;
|
||||
if (arguments.Length > 2)
|
||||
{
|
||||
_else = arguments[2];
|
||||
@else = arguments[2];
|
||||
}
|
||||
|
||||
if (condition != null && (condition.Equals("yes") || condition.Equals("true")))
|
||||
if (condition != null && (condition.Equals("yes") || condition.Equals("true") || condition.Equals(true)))
|
||||
{
|
||||
return then.Evaluate(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _else?.Evaluate(c);
|
||||
return @else?.Evaluate(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
91
AspectedRouting/Language/Functions/IfDotted.cs
Normal file
91
AspectedRouting/Language/Functions/IfDotted.cs
Normal file
|
@ -0,0 +1,91 @@
|
|||
using System.Collections.Generic;
|
||||
using AspectedRouting.Language.Expression;
|
||||
using AspectedRouting.Language.Typ;
|
||||
|
||||
namespace AspectedRouting.Language.Functions
|
||||
{
|
||||
public class IfDotted : Function
|
||||
{
|
||||
private static Var a = new Var("a");
|
||||
private static Var b = new Var("b");
|
||||
|
||||
// TODO OPTIMIZE AWAY
|
||||
|
||||
public override string Description { get; } = "An if_then_else, but one which takes an extra argument and applies it on the condition, then and else.\n" +
|
||||
"Consider `fc`, `fthen` and `felse` are all functions taking an `a`, then:\n" +
|
||||
"`(ifDotted fc fthen felse) a` === `(if (fc a) (fthen a) (felse a)" +
|
||||
"Selects either one of the branches, depending on the condition." +
|
||||
" The 'then' branch is returned if the condition returns the string `yes` or `true` or the boolean `true`" +
|
||||
"If the `else` branch is not set, `null` is returned in the condition is false.";
|
||||
public override List<string> ArgNames { get; } = new List<string> {"condition", "then", "else"};
|
||||
|
||||
public IfDotted() : base("if_then_else_dotted", true,
|
||||
new[]
|
||||
{
|
||||
|
||||
Curry.ConstructFrom(a,
|
||||
new Curry(b, Typs.Bool),
|
||||
new Curry(b, a),
|
||||
b),
|
||||
Curry.ConstructFrom(a,
|
||||
new Curry(b, Typs.String),
|
||||
new Curry(b, a),
|
||||
b),
|
||||
Curry.ConstructFrom(a,
|
||||
new Curry(b, Typs.Bool),
|
||||
new Curry(b, a),
|
||||
new Curry(b, a),
|
||||
b),
|
||||
Curry.ConstructFrom(a,
|
||||
new Curry(b, Typs.String),
|
||||
new Curry(b, a),
|
||||
new Curry(b, a),
|
||||
b)
|
||||
}
|
||||
)
|
||||
{
|
||||
Funcs.AddBuiltin(this, "ifdotted");
|
||||
Funcs.AddBuiltin(this, "ifDotted");
|
||||
}
|
||||
|
||||
private IfDotted(IEnumerable<Type> types) : base("if_then_else_dotted", types)
|
||||
{
|
||||
}
|
||||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var conditionfunc = arguments[0];
|
||||
var thenfunc = arguments[1];
|
||||
IExpression elsefunc = null;
|
||||
IExpression argument = arguments[2];
|
||||
|
||||
if (arguments.Length == 4)
|
||||
{
|
||||
elsefunc = arguments[2];
|
||||
argument = arguments[3];
|
||||
}
|
||||
|
||||
var condition = ((IExpression) conditionfunc).Apply(argument).Evaluate(c);
|
||||
|
||||
if (condition != null && (condition.Equals("yes") || condition.Equals("true") || condition.Equals(true)))
|
||||
{
|
||||
return thenfunc.Apply(argument).Evaluate(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
return elsefunc.Apply(argument).Evaluate(c);
|
||||
}
|
||||
}
|
||||
|
||||
public override IExpression Specialize(IEnumerable<Type> allowedTypes)
|
||||
{
|
||||
var unified = Types.SpecializeTo(allowedTypes);
|
||||
if (unified == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new IfDotted(unified);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -55,9 +55,13 @@ namespace AspectedRouting.Language.Functions
|
|||
var exprSpecialized = expr.Specialize(enumerable);
|
||||
if (exprSpecialized == null)
|
||||
{
|
||||
throw new Exception($"Could not specialize a mapping of type {string.Join(",", Types)}\n" +
|
||||
/*throw new Exception($"Could not specialize a mapping of type {string.Join(",", Types)}\n" +
|
||||
$"to types {string.Join(", ", allowedTypesList)};\n" +
|
||||
$"Expression {expr} could not be specialized to {functionType}");
|
||||
$"Expression {expr}:{string.Join(" ; ", expr.Types)} could not be specialized to {string.Join("; ",functionType)}");
|
||||
|
||||
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
|
||||
newFunctions[k] = exprSpecialized;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using AspectedRouting.Language.Expression;
|
||||
using AspectedRouting.Language.Typ;
|
||||
|
||||
|
@ -56,11 +57,19 @@ namespace AspectedRouting.Language.Functions
|
|||
}
|
||||
|
||||
return "no";
|
||||
case DoubleType _:
|
||||
case PDoubleType _:
|
||||
return ls.Select(o => (double) o).Max();
|
||||
default:
|
||||
return ls.Select(o => (int) o).Max();
|
||||
return ls.Select(o =>
|
||||
{
|
||||
switch (o)
|
||||
{
|
||||
case double d:
|
||||
return d;
|
||||
case int i:
|
||||
return i;
|
||||
default:
|
||||
throw new SwitchExpressionException("Unknwon type: " + o);
|
||||
}
|
||||
}).Max();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,8 +46,6 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
// var subFunction = arguments[0];
|
||||
|
||||
var tags =(Dictionary<string, string>) arguments[1].Evaluate(c);
|
||||
var name = c.AspectName.TrimStart('$');
|
||||
|
||||
|
|
|
@ -9,7 +9,9 @@ namespace AspectedRouting.Language.Functions
|
|||
public class MustMatch : Function
|
||||
{
|
||||
public override string Description { get; } =
|
||||
"Every key that is used in the subfunction must be present.\n" +
|
||||
"Checks that every specified key is present and gives a non-false value\n." +
|
||||
"" +
|
||||
"\n" +
|
||||
"If, on top, a value is present with a mapping, every key/value will be executed and must return a value that is not 'no' or 'false'\n" +
|
||||
"Note that this is a privileged builtin function, as the parser will automatically inject the keys used in the called function.";
|
||||
|
||||
|
@ -22,10 +24,10 @@ namespace AspectedRouting.Language.Functions
|
|||
public MustMatch() : base("mustMatch", true,
|
||||
new[]
|
||||
{
|
||||
// [String] -> (Tags -> [a]) -> Tags -> a
|
||||
// [String] -> (Tags -> [string]) -> Tags -> bool
|
||||
Curry.ConstructFrom(Typs.Bool, // Result type on top!
|
||||
new ListType(Typs.String), // List of keys to check for
|
||||
new Curry(Typs.Tags, new ListType(Typs.Bool)), // 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
|
||||
)
|
||||
})
|
||||
|
|
|
@ -45,6 +45,11 @@ namespace AspectedRouting.Language.Functions
|
|||
return this;
|
||||
}
|
||||
|
||||
public IExpression OptimizeWithArgument(IExpression arg)
|
||||
{
|
||||
throw new NotSupportedException("Trying to invoke a parameter");
|
||||
}
|
||||
|
||||
public void Visit(Func<IExpression, bool> f)
|
||||
{
|
||||
f(this);
|
||||
|
|
|
@ -25,8 +25,21 @@ namespace AspectedRouting.Language
|
|||
/// </summary>
|
||||
IExpression Specialize(IEnumerable<Type> allowedTypes);
|
||||
|
||||
/// <summary>
|
||||
/// Optimize a single expression, eventually recursively (e.g. a list can optimize all the contents)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IExpression Optimize();
|
||||
|
||||
/// <summary>
|
||||
/// Optimize with the given argument, e.g. listdot can become a list of applied arguments.
|
||||
///
|
||||
/// By default, this should return 'this.Apply(argument)'
|
||||
/// </summary>
|
||||
/// <param name="argument"></param>
|
||||
/// <returns></returns>
|
||||
// IExpression OptimizeWithArgument(IExpression argument);
|
||||
|
||||
void Visit(Func<IExpression, bool> f);
|
||||
}
|
||||
|
||||
|
@ -97,7 +110,9 @@ namespace AspectedRouting.Language
|
|||
continue;
|
||||
}
|
||||
|
||||
var specialized = specializedTypes.SpecializeTo(f.Types.RenameVars(specializedTypes));
|
||||
// TODO FIXME
|
||||
// EITHER THE TYPES HAVE TO BE FROM SPECIFIC TO NON-SPECIFIC ORDER OR VICE VERSA
|
||||
var specialized = f.Types.RenameVars(specializedTypes).SpecializeTo(specializedTypes);
|
||||
// ReSharper disable once JoinNullCheckWithUsage
|
||||
if (specialized == null)
|
||||
{
|
||||
|
|
|
@ -23,19 +23,19 @@ namespace AspectedRouting.Language.Typ
|
|||
}
|
||||
|
||||
|
||||
public static HashSet<Type> SpecializeTo(this IEnumerable<Type> types0, IEnumerable<Type> types1)
|
||||
public static HashSet<Type> SpecializeTo(this IEnumerable<Type> types0, IEnumerable<Type> allowedTypes)
|
||||
{
|
||||
var results = new HashSet<Type>();
|
||||
|
||||
var enumerable = types1.ToList();
|
||||
allowedTypes = allowedTypes.ToList();
|
||||
foreach (var t0 in types0)
|
||||
{
|
||||
foreach (var t1 in enumerable)
|
||||
foreach (var allowed in allowedTypes)
|
||||
{
|
||||
var unification = t0.Unify(t1);
|
||||
if (unification != null)
|
||||
var unified = t0.Unify(allowed, true);
|
||||
if (unified != null)
|
||||
{
|
||||
results.Add(unification);
|
||||
results.Add(unified);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,15 +49,54 @@ namespace AspectedRouting.Language.Typ
|
|||
}
|
||||
|
||||
|
||||
public static Type Unify(this Type t0, Type t1)
|
||||
/// <summary>
|
||||
/// Unifies the two types, where t0 is the allowed, wider type and t1 is the actual, smaller type (unless reverseSuperset is true).
|
||||
/// Unification will attempt to keep the type as small as possible
|
||||
/// </summary>
|
||||
/// <param name="t0">The expected, wider type</param>
|
||||
/// <param name="t1">The actual type</param>
|
||||
/// <param name="reverseSuperset">True if the supertyping relationship should be reversed</param>
|
||||
/// <returns></returns>
|
||||
public static Type Unify(this Type t0, Type t1, bool reverseSuperset = false)
|
||||
{
|
||||
var table = t0.UnificationTable(t1);
|
||||
var table = t0.UnificationTable(t1, reverseSuperset);
|
||||
if (table == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return t0.Substitute(table);
|
||||
var subbed = t0.Substitute(table);
|
||||
if (reverseSuperset)
|
||||
{
|
||||
return SelectSmallestUnion(t1, subbed);
|
||||
}
|
||||
return SelectSmallestUnion(subbed, t1);
|
||||
}
|
||||
|
||||
private static Type SelectSmallestUnion(this Type wider, Type smaller, bool reverse = false)
|
||||
{
|
||||
switch (wider)
|
||||
{
|
||||
case Var a:
|
||||
return a;
|
||||
case ListType l when smaller is ListType lsmaller:
|
||||
return new ListType(
|
||||
l.InnerType.SelectSmallestUnion(
|
||||
l.InnerType.SelectSmallestUnion(lsmaller.InnerType, reverse)));
|
||||
case Curry cWider when smaller is Curry cSmaller:
|
||||
var arg =
|
||||
cWider.ArgType.SelectSmallestUnion(cSmaller.ArgType, !reverse);
|
||||
var result =
|
||||
cWider.ResultType.SelectSmallestUnion(cSmaller.ResultType, reverse);
|
||||
return new Curry(arg, result);
|
||||
default:
|
||||
if (wider.IsSuperSet(smaller) && !(smaller.IsSuperSet(wider)))
|
||||
{
|
||||
return smaller;
|
||||
}
|
||||
|
||||
return wider;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -69,7 +108,7 @@ namespace AspectedRouting.Language.Typ
|
|||
/// <returns></returns>
|
||||
public static IEnumerable<Type> UnifyAny(this Type t0, IEnumerable<Type> t1)
|
||||
{
|
||||
var result = t1.Select(t0.Unify).Where(unification => unification != null).ToHashSet();
|
||||
var result = t1.Select(t => t0.Unify(t)).Where(unification => unification != null).ToHashSet();
|
||||
if (!result.Any())
|
||||
{
|
||||
return null;
|
||||
|
@ -87,7 +126,7 @@ namespace AspectedRouting.Language.Typ
|
|||
/// <returns></returns>
|
||||
public static IEnumerable<Type> UnifyAll(this Type t0, IEnumerable<Type> t1)
|
||||
{
|
||||
var result = t1.Select(t0.Unify).ToHashSet();
|
||||
var result = t1.Select(t => t0.Unify(t)).ToHashSet();
|
||||
if (result.Any(x => x == null))
|
||||
{
|
||||
return null;
|
||||
|
@ -96,6 +135,8 @@ namespace AspectedRouting.Language.Typ
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static Type Substitute(this Type t0, Dictionary<string, Type> substitutions)
|
||||
{
|
||||
switch (t0)
|
||||
|
@ -111,7 +152,16 @@ namespace AspectedRouting.Language.Typ
|
|||
}
|
||||
}
|
||||
|
||||
public static Dictionary<string, Type> UnificationTable(this Type t0, Type t1)
|
||||
/// <summary>
|
||||
/// The unification table is built when the type of an argument is introspected to see if it fits in the excpect type
|
||||
/// t0 here is the **expected** (wider) type, whereas t1 is the **actual** argument type.
|
||||
/// In other words, if we expect a `double`, a `pdouble` fits in there too.
|
||||
/// If we expect a function capable of handling pdoubles and giving strings, a function capable of handling doubles and giving bools will work just as well
|
||||
/// </summary>
|
||||
/// <param name="t0"></param>
|
||||
/// <param name="t1"></param>
|
||||
/// <returns></returns>
|
||||
public static Dictionary<string, Type> UnificationTable(this Type t0, Type t1, bool reverseSupersetRelation = false)
|
||||
{
|
||||
var substitutionsOn0 = new Dictionary<string, Type>();
|
||||
|
||||
|
@ -156,7 +206,7 @@ namespace AspectedRouting.Language.Typ
|
|||
break;
|
||||
case ListType l0 when t1 is ListType l1:
|
||||
{
|
||||
var table = l0.InnerType.UnificationTable(l1.InnerType);
|
||||
var table = l0.InnerType.UnificationTable(l1.InnerType, reverseSupersetRelation);
|
||||
if (!AddAllSubs(table))
|
||||
{
|
||||
return null;
|
||||
|
@ -167,8 +217,9 @@ namespace AspectedRouting.Language.Typ
|
|||
|
||||
case Curry curry0 when t1 is Curry curry1:
|
||||
{
|
||||
var tableA = curry0.ArgType.UnificationTable(curry1.ArgType);
|
||||
var tableB = curry0.ResultType.UnificationTable(curry1.ResultType);
|
||||
// contravariance for arguments: reversed
|
||||
var tableA = curry0.ArgType.UnificationTable(curry1.ArgType, !reverseSupersetRelation);
|
||||
var tableB = curry0.ResultType.UnificationTable(curry1.ResultType, reverseSupersetRelation);
|
||||
if (!(AddAllSubs(tableA) && AddAllSubs(tableB)))
|
||||
{
|
||||
return null;
|
||||
|
@ -182,8 +233,15 @@ namespace AspectedRouting.Language.Typ
|
|||
if (t1 is Var v)
|
||||
{
|
||||
AddSubs(v.Name, t0);
|
||||
break;
|
||||
}
|
||||
else if (!t0.Equals(t1))
|
||||
|
||||
if (!reverseSupersetRelation && !t0.IsSuperSet(t1))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (reverseSupersetRelation && !t1.IsSuperSet(t0))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
@ -197,7 +255,7 @@ namespace AspectedRouting.Language.Typ
|
|||
|
||||
public static HashSet<string> UsedVariables(this Type t0, HashSet<string> addTo = null)
|
||||
{
|
||||
addTo ??=new HashSet<string>();
|
||||
addTo ??= new HashSet<string>();
|
||||
switch (t0)
|
||||
{
|
||||
case Var a:
|
||||
|
@ -256,16 +314,23 @@ namespace AspectedRouting.Language.Typ
|
|||
|
||||
public static IEnumerable<Type> RenameVars(this IEnumerable<Type> toRename, IEnumerable<Type> noUseVar)
|
||||
{
|
||||
var usedToRename = toRename.SelectMany(t => UsedVariables(t));
|
||||
// Variables used in 'toRename'
|
||||
var usedToRename = toRename.SelectMany(t => UsedVariables(t)).ToHashSet();
|
||||
// Variables that should not be used
|
||||
var blacklist = noUseVar.SelectMany(t => UsedVariables(t)).ToHashSet();
|
||||
var alreadyUsed = blacklist.Concat(usedToRename).ToHashSet();
|
||||
// variables that should be renamed
|
||||
var variablesToRename = usedToRename.Intersect(blacklist).ToHashSet();
|
||||
|
||||
// All variables that are used and thus not free anymore, sum of 'usedToRename' and the blacklist
|
||||
var alreadyUsed = blacklist.Concat(usedToRename).ToHashSet();
|
||||
|
||||
// The substitution table
|
||||
var subsTable = new Dictionary<string, Type>();
|
||||
foreach (var v in variablesToRename)
|
||||
{
|
||||
var newValue = Var.Fresh(alreadyUsed);
|
||||
subsTable.Add(v, newValue);
|
||||
blacklist.Add(newValue.Name);
|
||||
alreadyUsed.Add(newValue.Name);
|
||||
}
|
||||
|
||||
return toRename.Select(t => t.Substitute(subsTable));
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace AspectedRouting
|
|||
foreach (var file in jsonFileNames)
|
||||
{
|
||||
var fi = new FileInfo(file);
|
||||
Console.WriteLine("Parsing " + file);
|
||||
|
||||
var aspect = JsonParser.AspectFromJson(context, File.ReadAllText(file), fi.Name);
|
||||
if (aspect == null) continue;
|
||||
|
||||
|
@ -63,6 +63,11 @@ namespace AspectedRouting
|
|||
var result = new List<(ProfileMetaData profile, List<BehaviourTestSuite> profileTests)>();
|
||||
foreach (var jsonFile in jsonFiles)
|
||||
{
|
||||
if (!jsonFile.Contains("bicycle"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var profile =
|
||||
|
@ -251,7 +256,7 @@ namespace AspectedRouting
|
|||
).ToLua();
|
||||
File.WriteAllText(outputDir + "/" + profile.Name + ".lua", luaProfile);
|
||||
|
||||
foreach (var (behaviourName, behaviourParameters) in profile.Behaviours)
|
||||
foreach (var (behaviourName, _) in profile.Behaviours)
|
||||
{
|
||||
var lua2behaviour = new LuaPrinter2(
|
||||
profile,
|
||||
|
|
|
@ -15,7 +15,15 @@
|
|||
"status",
|
||||
"network"
|
||||
],
|
||||
"access": "$bicycle.legal_access",
|
||||
"access": {
|
||||
"$ifDotted": {
|
||||
"$dot": {"$notEq": "no"},
|
||||
"f": "$bicycle.legal_access"
|
||||
},
|
||||
|
||||
"then": "$bicycle.legal_access",
|
||||
"else": "$bicycle.network_is_bicycle_network"
|
||||
},
|
||||
"oneway": "$bicycle.oneway",
|
||||
"speed": {
|
||||
"$min": [
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue