dotnet format
This commit is contained in:
parent
d304a80e7b
commit
43590ad69f
70 changed files with 1040 additions and 675 deletions
|
@ -34,7 +34,8 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
public IExpression PruneTypes(System.Func<Type, bool> allowedTypes)
|
||||
{
|
||||
var passed = Types.Where(allowedTypes);
|
||||
if (passed.Any()) {
|
||||
if (passed.Any())
|
||||
{
|
||||
return new LuaLiteral(passed, Lua);
|
||||
}
|
||||
|
||||
|
@ -51,7 +52,7 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
{
|
||||
f(this);
|
||||
}
|
||||
|
||||
|
||||
public bool Equals(IExpression other)
|
||||
{
|
||||
if (other is LuaLiteral ll)
|
||||
|
@ -66,9 +67,9 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
{
|
||||
if (this.Types.Count() == 1 && this.Types.First() == Typs.Tags)
|
||||
{
|
||||
return $"new LuaLiteral(Typs.Tags, \"{this.Lua}\")";
|
||||
}
|
||||
|
||||
return $"new LuaLiteral(Typs.Tags, \"{this.Lua}\")";
|
||||
}
|
||||
|
||||
return $"new LuaLiteral(\"{this.Lua}\")";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,13 +43,16 @@ namespace AspectedRouting.IO.itinero1
|
|||
public string DeclareParametersFor(Dictionary<string, IExpression> subParams)
|
||||
{
|
||||
var impl = "";
|
||||
foreach (var (paramName, value) in subParams) {
|
||||
if (paramName.Equals("description")) {
|
||||
foreach (var (paramName, value) in subParams)
|
||||
{
|
||||
if (paramName.Equals("description"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var paramNameTrimmed = paramName.TrimStart('#').AsLuaIdentifier();
|
||||
if (!string.IsNullOrEmpty(paramNameTrimmed)) {
|
||||
if (!string.IsNullOrEmpty(paramNameTrimmed))
|
||||
{
|
||||
impl += $" parameters.{paramNameTrimmed} = {_skeleton.ToLua(value)}\n";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,11 +37,12 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
, UnApply(
|
||||
IsFunc(Funcs.StringStringToTags),
|
||||
Assign(collectedMapping))
|
||||
).Invoke(bare)) {
|
||||
).Invoke(bare))
|
||||
{
|
||||
AddDep(Funcs.FirstOf.Name);
|
||||
return "first_match_of(\n" +
|
||||
" " + ToLua(order.First(), key) + ",\n" +
|
||||
("\n" + MappingToLua((Mapping) collectedMapping.First())).Indent().Indent() +
|
||||
("\n" + MappingToLua((Mapping)collectedMapping.First())).Indent().Indent() +
|
||||
",\n tags, result)";
|
||||
}
|
||||
|
||||
|
@ -52,18 +53,20 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
, UnApply(
|
||||
IsFunc(Funcs.StringStringToTags),
|
||||
Assign(collectedMapping))
|
||||
).Invoke(bare)) {
|
||||
).Invoke(bare))
|
||||
{
|
||||
AddDep(Funcs.MustMatch.Name);
|
||||
return "must_match(" +
|
||||
" " + ToLua(order.First(), key) + "," +
|
||||
("\n" + MappingToLua((Mapping) collectedMapping.First())).Indent().Indent() +
|
||||
("\n" + MappingToLua((Mapping)collectedMapping.First())).Indent().Indent() +
|
||||
",\n tags, result)";
|
||||
}
|
||||
|
||||
if (UnApply(
|
||||
IsFunc(Funcs.MemberOf),
|
||||
Any
|
||||
).Invoke(bare)) {
|
||||
).Invoke(bare))
|
||||
{
|
||||
AddDep("memberOf");
|
||||
return "memberOf(funcName, parameters, tags, result)";
|
||||
}
|
||||
|
@ -77,7 +80,8 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
).Invoke(bare))
|
||||
{
|
||||
var called = _context.DefinedFunctions[name.First()];
|
||||
if (called.ProfileInternal) {
|
||||
if (called.ProfileInternal)
|
||||
{
|
||||
return called.Name;
|
||||
}
|
||||
|
||||
|
@ -86,9 +90,9 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
var usesParams = called.ExpressionImplementation.UsedParameters().Any();
|
||||
if (usesParams)
|
||||
{
|
||||
return $"{name.First().Replace(".","_")}({ToLua(arg.First())}, parameters)";
|
||||
return $"{name.First().Replace(".", "_")}({ToLua(arg.First())}, parameters)";
|
||||
}
|
||||
return $"{name.First().Replace(".","_")}({ToLua(arg.First())})";
|
||||
return $"{name.First().Replace(".", "_")}({ToLua(arg.First())})";
|
||||
}
|
||||
}
|
||||
collectedMapping.Clear();
|
||||
|
@ -102,9 +106,10 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
UnApply(
|
||||
IsFunc(Funcs.StringStringToTags),
|
||||
Assign(collectedMapping))).Invoke(bare)
|
||||
) {
|
||||
var mapping = (Mapping) collectedMapping.First();
|
||||
var baseFunc = (Function) dottedFunction.First();
|
||||
)
|
||||
{
|
||||
var mapping = (Mapping)collectedMapping.First();
|
||||
var baseFunc = (Function)dottedFunction.First();
|
||||
AddDep(baseFunc.Name);
|
||||
AddDep("table_to_list");
|
||||
|
||||
|
@ -118,7 +123,8 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
// The expression might be a function which still expects a string (the value from the tag) as argument
|
||||
if (!(bare is Mapping) &&
|
||||
bare.Types.First() is Curry curr &&
|
||||
curr.ArgType.Equals(Typs.String)) {
|
||||
curr.ArgType.Equals(Typs.String))
|
||||
{
|
||||
var applied = new Apply(bare, new Constant(curr.ArgType, ("tags", "\"" + key + "\"")));
|
||||
return ToLua(applied.Optimize(out _), key);
|
||||
}
|
||||
|
@ -126,28 +132,33 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
|
||||
// The expression might consist of multiple nested functions
|
||||
var fArgs = bare.DeconstructApply();
|
||||
if (fArgs != null) {
|
||||
if (fArgs != null)
|
||||
{
|
||||
var (f, args) = fArgs.Value;
|
||||
|
||||
if(f is Constant constant) {
|
||||
|
||||
if (f is Constant constant)
|
||||
{
|
||||
return ConstantToLua(constant);
|
||||
}
|
||||
|
||||
if (!(f is Function baseFunc)) {
|
||||
|
||||
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
|
||||
return ToLua(args.First());
|
||||
}
|
||||
|
||||
if (baseFunc.Name.Equals(Funcs.Dot.Name)) {
|
||||
if (args.Count == 1 )
|
||||
if (baseFunc.Name.Equals(Funcs.Dot.Name))
|
||||
{
|
||||
if (args.Count == 1)
|
||||
{
|
||||
return ToLua(args[0]);
|
||||
}
|
||||
|
||||
|
||||
if (forceFirstArgInDot)
|
||||
{
|
||||
return ToLua(args[0]);
|
||||
|
@ -163,7 +174,8 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
AddDep(baseFunc.Name);
|
||||
|
||||
var argExpressions = new List<string>();
|
||||
foreach (var arg in args) {
|
||||
foreach (var arg in args)
|
||||
{
|
||||
argExpressions.Add(ToLua(arg, key));
|
||||
}
|
||||
|
||||
|
@ -172,12 +184,14 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
|
||||
|
||||
var collected = new List<IExpression>();
|
||||
switch (bare) {
|
||||
switch (bare)
|
||||
{
|
||||
case LuaLiteral lua:
|
||||
return lua.Lua;
|
||||
case FunctionCall fc:
|
||||
var called = _context.DefinedFunctions[fc.CalledFunctionName];
|
||||
if (called.ProfileInternal) {
|
||||
if (called.ProfileInternal)
|
||||
{
|
||||
return called.Name;
|
||||
}
|
||||
|
||||
|
@ -190,12 +204,15 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
return MappingToLua(m).Indent();
|
||||
case Function f:
|
||||
var fName = f.Name.TrimStart('$');
|
||||
if (Funcs.Builtins.ContainsKey(fName)) {
|
||||
if (Funcs.Builtins.ContainsKey(fName))
|
||||
{
|
||||
AddDep(f.Name);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
var definedFunc = _context.DefinedFunctions[fName];
|
||||
if (definedFunc.ProfileInternal) {
|
||||
if (definedFunc.ProfileInternal)
|
||||
{
|
||||
return f.Name;
|
||||
}
|
||||
|
||||
|
@ -217,27 +234,31 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
public string MappingToLua(Mapping m)
|
||||
{
|
||||
var isConstant = true;
|
||||
var contents = m.StringToResultFunctions.Select(kv => {
|
||||
var (key, expr) = kv;
|
||||
var left = "[\"" + key + "\"]";
|
||||
var contents = m.StringToResultFunctions.Select(kv =>
|
||||
{
|
||||
var (key, expr) = kv;
|
||||
var left = "[\"" + key + "\"]";
|
||||
|
||||
if (Regex.IsMatch(key, "^[a-zA-Z][_a-zA-Z-9]*$")) {
|
||||
left = key;
|
||||
}
|
||||
|
||||
var luaExpr = ToLua(expr, key);
|
||||
if (luaExpr.Contains("tags")) {
|
||||
isConstant = false;
|
||||
}
|
||||
|
||||
return left + " = " + luaExpr;
|
||||
if (Regex.IsMatch(key, "^[a-zA-Z][_a-zA-Z-9]*$"))
|
||||
{
|
||||
left = key;
|
||||
}
|
||||
|
||||
var luaExpr = ToLua(expr, key);
|
||||
if (luaExpr.Contains("tags"))
|
||||
{
|
||||
isConstant = false;
|
||||
}
|
||||
|
||||
return left + " = " + luaExpr;
|
||||
}
|
||||
);
|
||||
var mapping =
|
||||
"{\n " +
|
||||
string.Join(",\n ", contents) +
|
||||
"\n}";
|
||||
if (_staticTables && isConstant) {
|
||||
if (_staticTables && isConstant)
|
||||
{
|
||||
return AddConstant(mapping);
|
||||
}
|
||||
|
||||
|
@ -252,7 +273,8 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
private string ConstantToLua(Constant c)
|
||||
{
|
||||
var o = c.Get();
|
||||
switch (o) {
|
||||
switch (o)
|
||||
{
|
||||
case LuaLiteral lua:
|
||||
return lua.Lua;
|
||||
case IExpression e:
|
||||
|
@ -268,10 +290,12 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
case ValueTuple<string, string> unpack:
|
||||
return unpack.Item1 + "[" + unpack.Item2 + "]";
|
||||
case IEnumerable<object> ls:
|
||||
var t = ((ListType) c.Types.First()).InnerType;
|
||||
return "{" + string.Join(", ", ls.Select(obj => {
|
||||
var t = ((ListType)c.Types.First()).InnerType;
|
||||
return "{" + string.Join(", ", ls.Select(obj =>
|
||||
{
|
||||
var objInConstant = new Constant(t, obj);
|
||||
if (obj is Constant asConstant) {
|
||||
if (obj is Constant asConstant)
|
||||
{
|
||||
objInConstant = asConstant;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
{
|
||||
public void AddFunction(AspectMetadata meta)
|
||||
{
|
||||
if (_alreadyAddedFunctions.Contains(meta.Name)) {
|
||||
if (_alreadyAddedFunctions.Contains(meta.Name))
|
||||
{
|
||||
// already added
|
||||
return;
|
||||
}
|
||||
|
@ -28,8 +29,10 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
|
||||
var funcNameDeclaration = "";
|
||||
|
||||
meta.Visit(e => {
|
||||
if (e is Function f && f.Name.Equals(Funcs.MemberOf.Name)) {
|
||||
meta.Visit(e =>
|
||||
{
|
||||
if (e is Function f && f.Name.Equals(Funcs.MemberOf.Name))
|
||||
{
|
||||
funcNameDeclaration = $"\n local funcName = \"{meta.Name.AsLuaIdentifier()}\"";
|
||||
}
|
||||
|
||||
|
@ -50,14 +53,16 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
_context = _context.WithAspectName(meta.Name);
|
||||
|
||||
var body = "";
|
||||
if (_useSnippets) {
|
||||
if (_useSnippets)
|
||||
{
|
||||
body = Utils.Lines(
|
||||
" local r = nil",
|
||||
" " + Snippets.Convert(this, "r", expression).Indent(),
|
||||
" return r"
|
||||
);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
body = " return " + ToLua(expression);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
|
||||
public Context Context => _context;
|
||||
|
||||
public LuaSkeleton(Context context, bool useSnippets, bool staticTables = false)
|
||||
public LuaSkeleton(Context context, bool useSnippets, bool staticTables = false)
|
||||
{
|
||||
_context = context;
|
||||
_useSnippets = useSnippets;
|
||||
|
@ -42,12 +42,14 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
|
||||
internal void AddDep(string name)
|
||||
{
|
||||
if (name.StartsWith("mapping")) {
|
||||
if (name.StartsWith("mapping"))
|
||||
{
|
||||
Console.Error.WriteLine(">>>");
|
||||
throw new Exception("A mapping was added as dependency - this is a bug");
|
||||
}
|
||||
|
||||
if (name.Contains("stringToTags")) {
|
||||
if (name.Contains("stringToTags"))
|
||||
{
|
||||
AddDep("table_to_list");
|
||||
}
|
||||
_dependencies.Add(name);
|
||||
|
@ -66,11 +68,14 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
public void AddDependenciesFor(IExpression e)
|
||||
{
|
||||
var (_, functionNames) = e.InList().DirectlyAndInderectlyCalled(_context);
|
||||
foreach (var functionName in functionNames) {
|
||||
if (_context.DefinedFunctions.TryGetValue(functionName, out var aspectMeta)) {
|
||||
foreach (var functionName in functionNames)
|
||||
{
|
||||
if (_context.DefinedFunctions.TryGetValue(functionName, out var aspectMeta))
|
||||
{
|
||||
AddFunction(aspectMeta);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
AddDep(functionName);
|
||||
}
|
||||
}
|
||||
|
@ -80,12 +85,15 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
{
|
||||
var imps = new List<string>();
|
||||
|
||||
foreach (var name in _dependencies) {
|
||||
foreach (var name in _dependencies)
|
||||
{
|
||||
var path = $"IO/lua/{name}.lua";
|
||||
if (File.Exists(path)) {
|
||||
if (File.Exists(path))
|
||||
{
|
||||
imps.Add(File.ReadAllText(path));
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
throw new FileNotFoundException(path);
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +115,8 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
private readonly Dictionary<string, uint> counters = new Dictionary<string, uint>();
|
||||
public string FreeVar(string key)
|
||||
{
|
||||
if (!counters.ContainsKey(key)) {
|
||||
if (!counters.ContainsKey(key))
|
||||
{
|
||||
counters[key] = 0;
|
||||
return key;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
{
|
||||
public static class LuaStringExtensions
|
||||
{
|
||||
|
||||
|
||||
public static string ToLuaTable(this Dictionary<string, string> tags)
|
||||
{
|
||||
var contents = tags.Select(kv =>
|
||||
|
|
|
@ -13,17 +13,17 @@ namespace AspectedRouting.IO.itinero1
|
|||
_skeleton = skeleton;
|
||||
unitTestRunners.ForEach(_skeleton.AddDep);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public string GenerateFullTestSuite(List<BehaviourTestSuite> profileTests, List<AspectTestSuite> aspectTests, bool invertPriority = false)
|
||||
{
|
||||
|
||||
|
||||
_skeleton.AddDep("inv");
|
||||
_skeleton.AddDep("double_compare");
|
||||
|
||||
|
||||
var aspectTestSuite =
|
||||
string.Join("\n\n",
|
||||
string.Join("\n\n",
|
||||
aspectTests
|
||||
.Where(x => x != null)
|
||||
.Select(
|
||||
|
@ -81,7 +81,8 @@ namespace AspectedRouting.IO.itinero1
|
|||
foreach (var key in keysToCheck)
|
||||
{
|
||||
var newKey = key.Replace(".", "_");
|
||||
if (newKey == key) {
|
||||
if (newKey == key)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
tags[newKey] = tags[key];
|
||||
|
@ -94,7 +95,8 @@ namespace AspectedRouting.IO.itinero1
|
|||
}
|
||||
|
||||
var expectedPriority = "" + expected.Priority;
|
||||
if (invertPriority) {
|
||||
if (invertPriority)
|
||||
{
|
||||
expectedPriority = $"inv({expectedPriority})";
|
||||
}
|
||||
|
||||
|
@ -118,7 +120,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
var tests =
|
||||
testSuite.Tests
|
||||
.Select((test, i) => GenerateAspectUnitTestCall(fName, i, test.expected, test.tags))
|
||||
|
|
|
@ -13,13 +13,13 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
var defaultValue = args[0];
|
||||
var func = args[1];
|
||||
var funcArg = args[2];
|
||||
|
||||
|
||||
return Snippets.Convert(lua, assignTo, func.Apply(funcArg))
|
||||
+ "\n"
|
||||
+"if ("+assignTo+" == nil) then\n"
|
||||
+ " " + assignTo + " = " + lua.ToLua(defaultValue)+"\n"
|
||||
+"end";
|
||||
|
||||
+ "if (" + assignTo + " == nil) then\n"
|
||||
+ " " + assignTo + " = " + lua.ToLua(defaultValue) + "\n"
|
||||
+ "end";
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,7 +15,8 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
{
|
||||
var c = lua.Context;
|
||||
|
||||
if (!(args[0].Evaluate(c) is List<IExpression> order)) {
|
||||
if (!(args[0].Evaluate(c) is List<IExpression> order))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -23,16 +24,19 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
if (!UnApply(
|
||||
IsFunc(Funcs.StringStringToTags),
|
||||
IsMapping(mappings)
|
||||
).Invoke(args[1])) {
|
||||
).Invoke(args[1]))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mappings.Count != 1) {
|
||||
if (mappings.Count != 1)
|
||||
{
|
||||
throw new Exception("Multiple possible implementations at this point - should not happen");
|
||||
}
|
||||
|
||||
if (mappings.Count == 0) {
|
||||
|
||||
if (mappings.Count == 0)
|
||||
{
|
||||
|
||||
}
|
||||
var mapping = mappings.First();
|
||||
var tags = args[2];
|
||||
|
@ -40,30 +44,35 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
var varName = "tags";
|
||||
|
||||
var result = "";
|
||||
if (tags is LuaLiteral literal) {
|
||||
if (tags is LuaLiteral literal)
|
||||
{
|
||||
varName = literal.Lua;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
result += Snippets.Convert(lua, "tags", tags);
|
||||
}
|
||||
|
||||
// We _reverse_ the order, so that the _most_ important one is at the _bottom_
|
||||
// The most important one will then _overwrite_ the result value
|
||||
order.Reverse();
|
||||
foreach (var t in order) {
|
||||
if (!(t.Evaluate(c) is string key)) {
|
||||
foreach (var t in order)
|
||||
{
|
||||
if (!(t.Evaluate(c) is string key))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var func = mapping.StringToResultFunctions[key];
|
||||
|
||||
|
||||
result += "if (" + varName + "[\"" + key + "\"] ~= nil) then\n";
|
||||
result += " "+Snippets.Convert(lua, assignTo, func.Apply(new LuaLiteral(Typs.String, "tags[\""+key+"\"]"))).Indent();
|
||||
result += " " + Snippets.Convert(lua, assignTo, func.Apply(new LuaLiteral(Typs.String, "tags[\"" + key + "\"]"))).Indent();
|
||||
result += "\n";
|
||||
result += "end\n";
|
||||
// note: we do not do an 'elseif' as we have to fallthrough
|
||||
if (result.Contains("tags[\"nil\"]")) {
|
||||
Console.WriteLine("Warning: FirstMatchOf has a 'nil' in the indexes due to expression "+t.ToString());
|
||||
if (result.Contains("tags[\"nil\"]"))
|
||||
{
|
||||
Console.WriteLine("Warning: FirstMatchOf has a 'nil' in the indexes due to expression " + t.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,28 +20,32 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
UnApply(IsFunc(Funcs.StringStringToTags),
|
||||
IsMapping(mappings)),
|
||||
Assign(actualArgs)
|
||||
).Invoke(args[0])) {
|
||||
).Invoke(args[0]))
|
||||
{
|
||||
var actualArg = actualArgs.First();
|
||||
var mapping = mappings.First();
|
||||
|
||||
if (mapping.StringToResultFunctions.Count != 1) {
|
||||
if (mapping.StringToResultFunctions.Count != 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var (key, func) = mapping.StringToResultFunctions.ToList().First();
|
||||
var result = "";
|
||||
var tags = "";
|
||||
if (actualArg is LuaLiteral l) {
|
||||
if (actualArg is LuaLiteral l)
|
||||
{
|
||||
tags = l.Lua;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
tags = lua.FreeVar("tags");
|
||||
result += "local " + tags+"\n";
|
||||
result += "local " + tags + "\n";
|
||||
result += Snippets.Convert(lua, tags, actualArg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var v = lua.FreeVar("value");
|
||||
result += "local " + v + " = " + tags + "[\"" + key + "\"]\n";
|
||||
result += Snippets.Convert(lua, assignTo, func.Apply(new LuaLiteral(Typs.String, v)));
|
||||
|
|
|
@ -15,21 +15,23 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
var fValue = args[1];
|
||||
IExpression fElse = null;
|
||||
var arg = args[2];
|
||||
if (args.Count == 4) {
|
||||
if (args.Count == 4)
|
||||
{
|
||||
arg = args[3];
|
||||
fElse = args[2];
|
||||
}
|
||||
|
||||
var c = lua.FreeVar("cond");
|
||||
var result = "";
|
||||
result += "local " + c+"\n";
|
||||
result += "local " + c + "\n";
|
||||
var condApplied = fCond.Apply(arg);
|
||||
var isString = condApplied.Types.First().Equals(Typs.String);
|
||||
result += Snippets.Convert(lua, c, condApplied)+"\n";
|
||||
result += Snippets.Convert(lua, c, condApplied) + "\n";
|
||||
result += $"if ( {c} or {c} == \"yes\" ) then \n";
|
||||
result += " " + Snippets.Convert(lua, assignTo, fValue.Apply(arg)).Indent() ;
|
||||
result += " " + Snippets.Convert(lua, assignTo, fValue.Apply(arg)).Indent();
|
||||
|
||||
if (fElse != null) {
|
||||
if (fElse != null)
|
||||
{
|
||||
result += "else\n";
|
||||
result += " " + Snippets.Convert(lua, assignTo, fElse.Apply(arg)).Indent();
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
var cond = args[0].Optimize(out _);
|
||||
var ifTrue = args[1];
|
||||
IExpression ifElse = null;
|
||||
if (args.Count == 3) {
|
||||
if (args.Count == 3)
|
||||
{
|
||||
ifElse = args[2];
|
||||
}
|
||||
|
||||
|
@ -32,9 +33,9 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
if (fa.First().ToString() == ifElse.ToString())
|
||||
{
|
||||
var result = "";
|
||||
|
||||
|
||||
// We calculate the value that we need
|
||||
result += Snippets.Convert(lua,assignTo , ifElse)+"\n";
|
||||
result += Snippets.Convert(lua, assignTo, ifElse) + "\n";
|
||||
result += "if (" + assignTo + " == nil) then\n";
|
||||
result += " " + Snippets.Convert(lua, assignTo, ifTrue).Indent();
|
||||
result += "end\n";
|
||||
|
@ -48,14 +49,15 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
{
|
||||
var c = lua.FreeVar("cond");
|
||||
var result = "";
|
||||
result += "local " + c+"\n";
|
||||
|
||||
var isString = cond.Types.First().Equals(Typs.String);
|
||||
result += Snippets.Convert(lua, c, cond)+"\n";
|
||||
result += $"if ( {c} or {c} == \"yes\" ) then \n";
|
||||
result += " " + Snippets.Convert(lua, assignTo, ifTrue).Indent() ;
|
||||
result += "local " + c + "\n";
|
||||
|
||||
if (ifElse != null) {
|
||||
var isString = cond.Types.First().Equals(Typs.String);
|
||||
result += Snippets.Convert(lua, c, cond) + "\n";
|
||||
result += $"if ( {c} or {c} == \"yes\" ) then \n";
|
||||
result += " " + Snippets.Convert(lua, assignTo, ifTrue).Indent();
|
||||
|
||||
if (ifElse != null)
|
||||
{
|
||||
result += "else\n";
|
||||
result += " " + Snippets.Convert(lua, assignTo, ifElse).Indent();
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
{
|
||||
|
||||
var result = Snippets.Convert(lua, assignTo, args[0]);
|
||||
result += " "+ assignTo +" = 1 / " + assignTo;
|
||||
result += " " + assignTo + " = 1 / " + assignTo;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
|
||||
public override string Convert(LuaSkeleton.LuaSkeleton lua, string assignTo, List<IExpression> args)
|
||||
{
|
||||
|
||||
|
||||
// Multiply multiplies a list of values - we thus have to handle _each_ arg
|
||||
// Note: we get a single argument which is an expression resulting in a list of values
|
||||
|
||||
|
@ -78,7 +78,7 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
{
|
||||
// Print a 'listDot', assume 'tags' is the applied argument
|
||||
var arg = new List<IExpression>();
|
||||
|
||||
|
||||
var listDotArgs = new List<IExpression>();
|
||||
if (args.Count == 1 && UnApply(
|
||||
UnApply(IsFunc(Funcs.ListDot),
|
||||
|
@ -130,13 +130,13 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
|
||||
|
||||
var constantArgs = new List<Constant>();
|
||||
if (args.Count == 1 && IsConstant(constantArgs).Invoke(args[0]))
|
||||
{
|
||||
|
@ -161,10 +161,10 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Console.Error.WriteLine("ListFoldingSnippet encountered an unsupported expression");
|
||||
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
@ -188,9 +188,11 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
IsFunc(Funcs.StringStringToTags),
|
||||
IsMapping(mappings)
|
||||
)
|
||||
).Invoke(app)) {
|
||||
).Invoke(app))
|
||||
{
|
||||
var mapping = mappings.First();
|
||||
if (mapping.StringToResultFunctions.Count == 1) {
|
||||
if (mapping.StringToResultFunctions.Count == 1)
|
||||
{
|
||||
var kv = mapping.StringToResultFunctions.ToList().First();
|
||||
return (kv.Key, kv.Value);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
/// </summary>
|
||||
public readonly Function ImplementsFunction;
|
||||
|
||||
protected LuaSnippet(Function implements)
|
||||
protected LuaSnippet(Function implements)
|
||||
{
|
||||
ImplementsFunction = implements;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
public MaxSnippet() : base(Funcs.Max, "nil") { }
|
||||
public override string Combine(string assignTo, string value)
|
||||
{
|
||||
return Utils.Lines("if ( "+ assignTo + " == nil or" + assignTo + " < " + value + " ) then",
|
||||
return Utils.Lines("if ( " + assignTo + " == nil or" + assignTo + " < " + value + " ) then",
|
||||
" " + assignTo + " = " + value,
|
||||
"end");
|
||||
}
|
||||
|
|
|
@ -19,10 +19,12 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
|
||||
var tags = "";
|
||||
var result = "";
|
||||
if (tagsToken is LuaLiteral lit) {
|
||||
if (tagsToken is LuaLiteral lit)
|
||||
{
|
||||
tags = lit.Lua;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
tags = lua.FreeVar("tags");
|
||||
result += "local " + tags + "\n";
|
||||
result += Snippets.Convert(lua, tags, tagsToken);
|
||||
|
|
|
@ -17,17 +17,19 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
var result = "";
|
||||
var neededKeys = lua.FreeVar("neededKeys");
|
||||
var tags = "";
|
||||
if (tagsExpr is LuaLiteral literal) {
|
||||
if (tagsExpr is LuaLiteral literal)
|
||||
{
|
||||
tags = literal.Lua;
|
||||
}
|
||||
else {
|
||||
tags = lua.FreeVar("tags");
|
||||
result += $"local {tags}";
|
||||
result += Snippets.Convert(lua, tags, tagsExpr);
|
||||
else
|
||||
{
|
||||
tags = lua.FreeVar("tags");
|
||||
result += $"local {tags}";
|
||||
result += Snippets.Convert(lua, tags, tagsExpr);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
result += $"local {neededKeys}\n";
|
||||
result += Snippets.Convert(lua, neededKeys, neededKeysExpr);
|
||||
var key = lua.FreeVar("key");
|
||||
|
@ -36,7 +38,7 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
result += $" local {value} = {tags}[{key}]\n";
|
||||
result += $" if ({value} == nil) then\n";
|
||||
result += $" -- The value is nil, so mustmatch probably fails...\n";
|
||||
|
||||
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,9 +26,11 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
var vLua = new LuaLiteral(Typs.String, v);
|
||||
|
||||
var mappings = new List<string>();
|
||||
foreach (var kv in _mapping.StringToResultFunctions) {
|
||||
foreach (var kv in _mapping.StringToResultFunctions)
|
||||
{
|
||||
var f = kv.Value;
|
||||
if (f.Types.First() is Curry) {
|
||||
if (f.Types.First() is Curry)
|
||||
{
|
||||
f = f.Apply(vLua);
|
||||
}
|
||||
mappings.Add("if (" + v + " == \"" + kv.Key + "\") then\n " + assignTo + " = " + lua.ToLua(f));
|
||||
|
|
|
@ -33,37 +33,44 @@ namespace AspectedRouting.IO.LuaSnippets
|
|||
public static string Convert(LuaSkeleton.LuaSkeleton lua, string assignTo, IExpression e)
|
||||
{
|
||||
|
||||
var opt = e.Optimize(out _);
|
||||
var opt = e.Optimize(out _);
|
||||
// Note that optimization might optimize to a _subtype_ of the original expresion - which is fine!
|
||||
var origType = e.Types.First();
|
||||
var optType = opt.Types.First();
|
||||
if (!origType.Equals(optType) && !origType.IsSuperSet(optType)) {
|
||||
if (!origType.Equals(optType) && !origType.IsSuperSet(optType))
|
||||
{
|
||||
throw new Exception("Optimization went wrong!");
|
||||
}
|
||||
e = opt;
|
||||
var deconstructed = e.DeconstructApply();
|
||||
|
||||
if (deconstructed != null){
|
||||
|
||||
if (deconstructed.Value.f is Mapping m) {
|
||||
if (deconstructed != null)
|
||||
{
|
||||
|
||||
if (deconstructed.Value.f is Mapping m)
|
||||
{
|
||||
return new SimpleMappingSnippet(m).Convert(lua, assignTo, deconstructed.Value.args);
|
||||
}
|
||||
|
||||
|
||||
if (deconstructed.Value.f is Function f
|
||||
&& SnippetsIndex.TryGetValue(f.Name, out var snippet)) {
|
||||
&& SnippetsIndex.TryGetValue(f.Name, out var snippet))
|
||||
{
|
||||
var optimized = snippet.Convert(lua, assignTo, deconstructed.Value.args);
|
||||
if (optimized != null) {
|
||||
if (optimized != null)
|
||||
{
|
||||
return optimized + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
return assignTo + " = " + lua.ToLua(e)+"\n";
|
||||
return assignTo + " = " + lua.ToLua(e) + "\n";
|
||||
}
|
||||
catch (Exception err) {
|
||||
catch (Exception)
|
||||
{
|
||||
return "print(\"ERROR COMPILER BUG\");\n";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace AspectedRouting.IO
|
|||
public static void GenerateHelpText(string saveTo = null)
|
||||
{
|
||||
var format = File.ReadAllText("Format.md");
|
||||
var helpText =format +"\n\n" + TypeOverview +
|
||||
var helpText = format + "\n\n" + TypeOverview +
|
||||
FunctionOverview;
|
||||
|
||||
if (saveTo == null)
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
{
|
||||
public partial class LuaPrinter1
|
||||
{
|
||||
|
||||
|
||||
private (string implementation, HashSet<string> extraKeys) GenerateMembershipPreprocessor()
|
||||
{
|
||||
// Extra keys are the names of introduced tag-keys, e.g. '_relation:bicycle_fastest:cycle_highway'
|
||||
|
@ -44,7 +44,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
" legacy_relation_preprocessor(relation_tags, result)"
|
||||
};
|
||||
_skeleton.AddDep("legacy");
|
||||
|
||||
|
||||
foreach (var (calledInFunction, expr) in memberships)
|
||||
{
|
||||
func.Add($"\n\n -- {calledInFunction} ---");
|
||||
|
|
|
@ -64,7 +64,7 @@ namespace AspectedRouting.IO.itinero2
|
|||
};
|
||||
|
||||
var testPrinter = new LuaTestPrinter(_skeleton,
|
||||
new List<string> {"unitTestProfile2"});
|
||||
new List<string> { "unitTestProfile2" });
|
||||
var tests = testPrinter.GenerateFullTestSuite(
|
||||
_behaviourTestSuite.ToList(),
|
||||
new List<AspectTestSuite>(),
|
||||
|
|
|
@ -14,20 +14,23 @@ namespace AspectedRouting.IO.jsonParser
|
|||
{
|
||||
internal static IExpression ParseProfileProperty(JsonElement e, Context c, string property, IExpression defaultExpression = null)
|
||||
{
|
||||
if (!e.TryGetProperty(property, out var prop)) {
|
||||
if (!e.TryGetProperty(property, out var prop))
|
||||
{
|
||||
if (defaultExpression == null)
|
||||
{
|
||||
throw new ArgumentException("Not a valid profile: the declaration expression for '" + property +
|
||||
"' is missing");
|
||||
}
|
||||
|
||||
Console.Error.WriteLine("WARNING: no expression defined for "+property+", using the default instead");
|
||||
Console.Error.WriteLine("WARNING: no expression defined for " + property + ", using the default instead");
|
||||
return defaultExpression;
|
||||
}
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
var expr = ParseExpression(prop, c);
|
||||
if (!expr.Types.Any()) {
|
||||
if (!expr.Types.Any())
|
||||
{
|
||||
throw new Exception($"Could not parse field {property}, no valid typing for expression found");
|
||||
}
|
||||
|
||||
|
@ -35,21 +38,26 @@ namespace AspectedRouting.IO.jsonParser
|
|||
expr = Funcs.Either(Funcs.Id, Funcs.Const, expr);
|
||||
var specialized = expr.Specialize(new Curry(Typs.Tags, new Var("a")));
|
||||
|
||||
if (specialized == null) {
|
||||
if (specialized == null)
|
||||
{
|
||||
throw new ArgumentException("The expression for " + property +
|
||||
" hasn't the right type of 'Tags -> a'; it has types " +
|
||||
string.Join(",", expr.Types) + "\n" + expr.TypeBreakdown());
|
||||
}
|
||||
|
||||
var pruned = specialized.PruneTypes(type => {
|
||||
if (!(type is Curry c)) {
|
||||
var pruned = specialized.PruneTypes(type =>
|
||||
{
|
||||
if (!(type is Curry c))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Equals(c.ArgType, Typs.Tags)) {
|
||||
if (!Equals(c.ArgType, Typs.Tags))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (c.ResultType is Curry) {
|
||||
if (c.ResultType is Curry)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -59,7 +67,8 @@ namespace AspectedRouting.IO.jsonParser
|
|||
}
|
||||
return true;
|
||||
});
|
||||
if (pruned.SpecializeToSmallestType().Types.Count() != 1) {
|
||||
if (pruned.SpecializeToSmallestType().Types.Count() != 1)
|
||||
{
|
||||
throw new ArgumentException("The expression for " + property +
|
||||
" hasn't the right type of 'Tags -> a'; it has multiple types " +
|
||||
string.Join(",", pruned.Types) + "\n" + pruned.TypeBreakdown());
|
||||
|
@ -67,7 +76,8 @@ namespace AspectedRouting.IO.jsonParser
|
|||
|
||||
return pruned.Optimize(out _);
|
||||
}
|
||||
catch (Exception exc) {
|
||||
catch (Exception exc)
|
||||
{
|
||||
throw new Exception("While parsing the property " + property, exc);
|
||||
}
|
||||
}
|
||||
|
@ -75,20 +85,25 @@ namespace AspectedRouting.IO.jsonParser
|
|||
private static Dictionary<string, IExpression> ParseParameters(this JsonElement e)
|
||||
{
|
||||
var ps = new Dictionary<string, IExpression>();
|
||||
foreach (var obj in e.EnumerateObject()) {
|
||||
foreach (var obj in e.EnumerateObject())
|
||||
{
|
||||
var nm = obj.Name.TrimStart('#');
|
||||
if (nm == "") {
|
||||
if (nm == "")
|
||||
{
|
||||
// This is a comment - not a parameter!
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (obj.Value.ValueKind) {
|
||||
switch (obj.Value.ValueKind)
|
||||
{
|
||||
case JsonValueKind.String:
|
||||
var v = obj.Value.ToString();
|
||||
if (v.Equals("yes") || v.Equals("no")) {
|
||||
if (v.Equals("yes") || v.Equals("no"))
|
||||
{
|
||||
ps[nm] = new Constant(Typs.Bool, v);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
ps[nm] = new Constant(v);
|
||||
}
|
||||
|
||||
|
@ -119,7 +134,8 @@ namespace AspectedRouting.IO.jsonParser
|
|||
|
||||
private static string Get(this JsonElement json, string key)
|
||||
{
|
||||
if (json.TryGetProperty(key, out var p)) {
|
||||
if (json.TryGetProperty(key, out var p))
|
||||
{
|
||||
return p.GetString();
|
||||
}
|
||||
|
||||
|
@ -128,7 +144,8 @@ namespace AspectedRouting.IO.jsonParser
|
|||
|
||||
private static string TryGet(this JsonElement json, string key)
|
||||
{
|
||||
if (json.TryGetProperty(key, out var p)) {
|
||||
if (json.TryGetProperty(key, out var p))
|
||||
{
|
||||
return p.GetString();
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@ namespace AspectedRouting.IO.md
|
|||
public void AddTitle(string title, int level)
|
||||
{
|
||||
var str = "";
|
||||
for (var i = 0; i < level; i++) {
|
||||
for (var i = 0; i < level; i++)
|
||||
{
|
||||
str += "#";
|
||||
}
|
||||
|
||||
|
@ -53,7 +54,8 @@ namespace AspectedRouting.IO.md
|
|||
_c = c.WithAspectName(behaviour);
|
||||
_c.DefinedFunctions["speed"] = new AspectMetadata(profile.Speed, "speed", "The speed this vehicle is going",
|
||||
"", "km/h", "", true);
|
||||
if (!profile.Behaviours.ContainsKey(behaviour)) {
|
||||
if (!profile.Behaviours.ContainsKey(behaviour))
|
||||
{
|
||||
throw new ArgumentException("Profile does not contain behaviour " + behaviour);
|
||||
}
|
||||
}
|
||||
|
@ -70,11 +72,13 @@ namespace AspectedRouting.IO.md
|
|||
bool nullOnSame = false)
|
||||
{
|
||||
var profile = _profile.Run(_c, _behaviour, tags);
|
||||
if (!reference.HasValue) {
|
||||
if (!reference.HasValue)
|
||||
{
|
||||
return "| " + msg + " | " + profile.Speed + " | " + profile.Priority + " | ";
|
||||
}
|
||||
|
||||
if (reference.Equals(profile) && nullOnSame) {
|
||||
if (reference.Equals(profile) && nullOnSame)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -90,26 +94,33 @@ namespace AspectedRouting.IO.md
|
|||
var b = _profile.Behaviours[_behaviour];
|
||||
|
||||
var tableEntries = new List<string>();
|
||||
foreach (var (key, vals) in usedTags) {
|
||||
foreach (var (key, vals) in usedTags)
|
||||
{
|
||||
var values = vals;
|
||||
if (values.Count == 0 && key == "maxspeed") {
|
||||
if (values.Count == 0 && key == "maxspeed")
|
||||
{
|
||||
tableEntries.Add($" | {key}=* (example values below)");
|
||||
values = new HashSet<string> {
|
||||
"20", "30", "50", "70", "90", "120", "150"
|
||||
};
|
||||
}
|
||||
|
||||
if (values.Count == 0) {
|
||||
if (values.Count == 0)
|
||||
{
|
||||
tableEntries.Add($" | {key}=*");
|
||||
}
|
||||
|
||||
if (values.Count > 0) {
|
||||
foreach (var value in values) {
|
||||
var tags = new Dictionary<string, string> {
|
||||
if (values.Count > 0)
|
||||
{
|
||||
foreach (var value in values)
|
||||
{
|
||||
var tags = new Dictionary<string, string>
|
||||
{
|
||||
[key] = value
|
||||
};
|
||||
var entry = TableEntry($"{key}={value} ", tags, reference);
|
||||
if (entry == null) {
|
||||
if (entry == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -125,13 +136,15 @@ namespace AspectedRouting.IO.md
|
|||
|
||||
public Dictionary<string, IExpression> TagsWithPriorityInfluence()
|
||||
{
|
||||
|
||||
|
||||
var p = _profile;
|
||||
var parameters = _profile.ParametersFor(_behaviour);
|
||||
var withInfluence = new Dictionary<string, IExpression>();
|
||||
|
||||
foreach (var kv in p.Priority) {
|
||||
if (parameters[kv.Key].Equals(0.0) || parameters[kv.Key].Equals(0)) {
|
||||
foreach (var kv in p.Priority)
|
||||
{
|
||||
if (parameters[kv.Key].Equals(0.0) || parameters[kv.Key].Equals(0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -149,23 +162,28 @@ namespace AspectedRouting.IO.md
|
|||
|
||||
var overridenParams = new HashSet<string>();
|
||||
var paramValues = new Dictionary<string, object>();
|
||||
foreach (var kv in p.DefaultParameters) {
|
||||
foreach (var kv in p.DefaultParameters)
|
||||
{
|
||||
paramValues[kv.Key] = kv.Value.Evaluate(_c);
|
||||
}
|
||||
|
||||
foreach (var kv in b) {
|
||||
foreach (var kv in b)
|
||||
{
|
||||
paramValues[kv.Key] = kv.Value.Evaluate(_c);
|
||||
overridenParams.Add(kv.Key);
|
||||
}
|
||||
|
||||
var mainFormulaParts = p.Priority.Select(delegate(KeyValuePair<string, IExpression> kv) {
|
||||
var mainFormulaParts = p.Priority.Select(delegate (KeyValuePair<string, IExpression> kv)
|
||||
{
|
||||
var key = kv.Key;
|
||||
var param = paramValues[key];
|
||||
if (param.Equals(0) || param.Equals(0.0)) {
|
||||
if (param.Equals(0) || param.Equals(0.0))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if (overridenParams.Contains(key)) {
|
||||
if (overridenParams.Contains(key))
|
||||
{
|
||||
param = "**" + param + "**";
|
||||
}
|
||||
|
||||
|
@ -186,14 +204,16 @@ namespace AspectedRouting.IO.md
|
|||
|
||||
md.Add(p.Description);
|
||||
|
||||
if (b.ContainsKey("description")) {
|
||||
if (b.ContainsKey("description"))
|
||||
{
|
||||
md.Add(b["description"].Evaluate(_c).ToString());
|
||||
}
|
||||
|
||||
|
||||
md.Add("This profile is calculated as following (non-default keys are bold):", MainFormula());
|
||||
|
||||
var residentialTags = new Dictionary<string, string> {
|
||||
var residentialTags = new Dictionary<string, string>
|
||||
{
|
||||
["highway"] = "residential"
|
||||
};
|
||||
|
||||
|
@ -204,7 +224,7 @@ namespace AspectedRouting.IO.md
|
|||
md.AddTitle("Tags influencing priority", 2);
|
||||
md.Add(
|
||||
"Priority is what influences which road to take. The routeplanner will search a way where `1/priority` is minimal.");
|
||||
addTagsTable(reference, TagsWithPriorityInfluence().Values.PossibleTagsRecursive(_c));
|
||||
addTagsTable(reference, TagsWithPriorityInfluence().Values.PossibleTagsRecursive(_c));
|
||||
|
||||
md.AddTitle("Tags influencing speed", 2);
|
||||
md.Add(
|
||||
|
|
|
@ -19,11 +19,14 @@ namespace AspectedRouting.Language
|
|||
void AddParams(IExpression e, string inFunction)
|
||||
{
|
||||
var parms = e.UsedParameters();
|
||||
foreach (var param in parms) {
|
||||
if (parameters.TryGetValue(param.ParamName, out var typesOldUsage)) {
|
||||
foreach (var param in parms)
|
||||
{
|
||||
if (parameters.TryGetValue(param.ParamName, out var typesOldUsage))
|
||||
{
|
||||
var (types, oldUsage) = typesOldUsage;
|
||||
var unified = types.SpecializeTo(param.Types);
|
||||
if (unified == null) {
|
||||
if (unified == null)
|
||||
{
|
||||
throw new ArgumentException("Inconsistent parameter usage: the paremeter " +
|
||||
param.ParamName + " is used\n" +
|
||||
$" in {oldUsage} as {string.Join(",", types)}\n" +
|
||||
|
@ -31,7 +34,8 @@ namespace AspectedRouting.Language
|
|||
"which can not be unified");
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
parameters[param.ParamName] = (param.Types.ToList(), inFunction);
|
||||
}
|
||||
}
|
||||
|
@ -42,16 +46,19 @@ namespace AspectedRouting.Language
|
|||
AddParams(profile.Oneway, "profile definition for " + profile.Name + ".oneway");
|
||||
AddParams(profile.Speed, "profile definition for " + profile.Name + ".speed");
|
||||
|
||||
foreach (var (key, expr) in profile.Priority) {
|
||||
foreach (var (key, expr) in profile.Priority)
|
||||
{
|
||||
AddParams(new Parameter(key), profile.Name + ".priority.lefthand");
|
||||
AddParams(expr, profile.Name + ".priority");
|
||||
}
|
||||
|
||||
var calledFunctions = profile.CalledFunctionsRecursive(context).Values
|
||||
.SelectMany(ls => ls).ToHashSet();
|
||||
foreach (var calledFunction in calledFunctions) {
|
||||
foreach (var calledFunction in calledFunctions)
|
||||
{
|
||||
var func = context.GetFunction(calledFunction);
|
||||
if (func is AspectMetadata meta && meta.ProfileInternal) {
|
||||
if (func is AspectMetadata meta && meta.ProfileInternal)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -66,8 +73,10 @@ namespace AspectedRouting.Language
|
|||
public static HashSet<Parameter> UsedParameters(this IExpression e)
|
||||
{
|
||||
var result = new HashSet<Parameter>();
|
||||
e.Visit(expr => {
|
||||
if (expr is Parameter p) {
|
||||
e.Visit(expr =>
|
||||
{
|
||||
if (expr is Parameter p)
|
||||
{
|
||||
result.Add(p);
|
||||
}
|
||||
|
||||
|
@ -88,14 +97,18 @@ namespace AspectedRouting.Language
|
|||
|
||||
void ScanExpression(IExpression e, string inFunction)
|
||||
{
|
||||
if (!result.ContainsKey(inFunction)) {
|
||||
if (!result.ContainsKey(inFunction))
|
||||
{
|
||||
result.Add(inFunction, new List<string>());
|
||||
}
|
||||
|
||||
e.Visit(x => {
|
||||
if (x is FunctionCall fc) {
|
||||
e.Visit(x =>
|
||||
{
|
||||
if (x is FunctionCall fc)
|
||||
{
|
||||
result[inFunction].Add(fc.CalledFunctionName);
|
||||
if (!result.ContainsKey(fc.CalledFunctionName)) {
|
||||
if (!result.ContainsKey(fc.CalledFunctionName))
|
||||
{
|
||||
calledFunctions.Enqueue(fc.CalledFunctionName);
|
||||
}
|
||||
}
|
||||
|
@ -109,12 +122,14 @@ namespace AspectedRouting.Language
|
|||
ScanExpression(profile.Oneway, profile.Name + ".oneway");
|
||||
ScanExpression(profile.Speed, profile.Name + ".speed");
|
||||
|
||||
foreach (var (key, expr) in profile.Priority) {
|
||||
foreach (var (key, expr) in profile.Priority)
|
||||
{
|
||||
ScanExpression(new Parameter(key), $"{profile.Name}.priority.{key}.lefthand");
|
||||
ScanExpression(expr, $"{profile.Name}.priority.{key}");
|
||||
}
|
||||
|
||||
while (calledFunctions.TryDequeue(out var calledFunction)) {
|
||||
while (calledFunctions.TryDequeue(out var calledFunction))
|
||||
{
|
||||
var func = c.GetFunction(calledFunction);
|
||||
ScanExpression(func, calledFunction);
|
||||
}
|
||||
|
@ -132,13 +147,15 @@ namespace AspectedRouting.Language
|
|||
var queue = new Queue<IExpression>();
|
||||
exprs.ForEach(queue.Enqueue);
|
||||
|
||||
while (queue.TryDequeue(out var next)) {
|
||||
while (queue.TryDequeue(out var next))
|
||||
{
|
||||
var (p, deps) = next.DirectlyCalled();
|
||||
parameters.UnionWith(p);
|
||||
var toCheck = deps.Except(dependencies);
|
||||
dependencies.UnionWith(deps);
|
||||
|
||||
foreach (var fName in toCheck) {
|
||||
foreach (var fName in toCheck)
|
||||
{
|
||||
queue.Enqueue(ctx.GetFunction(fName));
|
||||
}
|
||||
}
|
||||
|
@ -159,12 +176,15 @@ namespace AspectedRouting.Language
|
|||
var parameters = new HashSet<string>();
|
||||
var dependencies = new HashSet<string>();
|
||||
|
||||
expr.Visit(e => {
|
||||
if (e is FunctionCall fc) {
|
||||
expr.Visit(e =>
|
||||
{
|
||||
if (e is FunctionCall fc)
|
||||
{
|
||||
dependencies.Add(fc.CalledFunctionName);
|
||||
}
|
||||
|
||||
if (e is Parameter p) {
|
||||
if (e is Parameter p)
|
||||
{
|
||||
parameters.Add(p.ParamName);
|
||||
}
|
||||
|
||||
|
@ -191,7 +211,8 @@ namespace AspectedRouting.Language
|
|||
string MetaList(IEnumerable<string> paramNames)
|
||||
{
|
||||
var metaInfo = "";
|
||||
foreach (var paramName in paramNames) {
|
||||
foreach (var paramName in paramNames)
|
||||
{
|
||||
var _ = usedMetadata.TryGetValue(paramName, out var inFunction) ||
|
||||
usedMetadata.TryGetValue('#' + paramName, out inFunction);
|
||||
metaInfo += $"\n - {paramName} (used in {inFunction.inFunction})";
|
||||
|
@ -203,36 +224,43 @@ namespace AspectedRouting.Language
|
|||
var usedParameters = usedMetadata.Keys.Select(key => key.TrimStart('#')).ToList();
|
||||
|
||||
var diff = usedParameters.ToHashSet().Except(defaultParameters).ToList();
|
||||
if (diff.Any()) {
|
||||
if (diff.Any())
|
||||
{
|
||||
throw new ArgumentException("No default value set for parameter: " + MetaList(diff));
|
||||
}
|
||||
|
||||
|
||||
var unused = defaultParameters.Except(usedParameters);
|
||||
if (unused.Any()) {
|
||||
if (unused.Any())
|
||||
{
|
||||
Console.WriteLine("[WARNING] A default value is set for parameter, but it is unused: " +
|
||||
string.Join(", ", unused));
|
||||
}
|
||||
|
||||
var paramsUsedInBehaviour = new HashSet<string>();
|
||||
|
||||
foreach (var (behaviourName, behaviourParams) in pmd.Behaviours) {
|
||||
foreach (var (behaviourName, behaviourParams) in pmd.Behaviours)
|
||||
{
|
||||
var sum = 0.0;
|
||||
var explanation = "";
|
||||
paramsUsedInBehaviour.UnionWith(behaviourParams.Keys.Select(k => k.Trim('#')));
|
||||
foreach (var (paramName, _) in pmd.Priority) {
|
||||
if (!pmd.DefaultParameters.ContainsKey(paramName)) {
|
||||
foreach (var (paramName, _) in pmd.Priority)
|
||||
{
|
||||
if (!pmd.DefaultParameters.ContainsKey(paramName))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"The behaviour {behaviourName} uses a parameter for which no default is set: {paramName}");
|
||||
}
|
||||
|
||||
if (!behaviourParams.TryGetValue(paramName, out var weight)) {
|
||||
if (!behaviourParams.TryGetValue(paramName, out var weight))
|
||||
{
|
||||
explanation += $"\n - {paramName} = default (not set)";
|
||||
continue;
|
||||
}
|
||||
|
||||
var weightObj = weight.Evaluate(context);
|
||||
|
||||
if (!(weightObj is double d)) {
|
||||
if (!(weightObj is double d))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"The parameter {paramName} is not a numeric value in profile {behaviourName}");
|
||||
}
|
||||
|
@ -241,7 +269,8 @@ namespace AspectedRouting.Language
|
|||
explanation += $"\n - {paramName} = {d}";
|
||||
}
|
||||
|
||||
if (Math.Abs(sum) < 0.0001) {
|
||||
if (Math.Abs(sum) < 0.0001)
|
||||
{
|
||||
throw new ArgumentException("Profile " + behaviourName +
|
||||
": the summed parameters to calculate the weight are zero or very low:" +
|
||||
explanation);
|
||||
|
@ -250,41 +279,49 @@ namespace AspectedRouting.Language
|
|||
|
||||
|
||||
var defaultOnly = defaultParameters.Except(paramsUsedInBehaviour).ToList();
|
||||
if (defaultOnly.Any()) {
|
||||
if (defaultOnly.Any())
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[{pmd.Name}] WARNING: Some parameters only have a default value: {string.Join(", ", defaultOnly)}");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static void SanityCheck(this IExpression e)
|
||||
{
|
||||
e.Visit(expr => {
|
||||
e.Visit(expr =>
|
||||
{
|
||||
var order = new List<IExpression>();
|
||||
var mapping = new List<IExpression>();
|
||||
if (Deconstruct.UnApply(
|
||||
Deconstruct.UnApply(Deconstruct.IsFunc(Funcs.FirstOf), Deconstruct.Assign(order)),
|
||||
Deconstruct.Assign(mapping)
|
||||
).Invoke(expr)) {
|
||||
var expectedKeys = ((IEnumerable<object>)order.First().Evaluate(null)).Select(o => {
|
||||
if (o is IExpression x) {
|
||||
return (string)x.Evaluate(null);
|
||||
}
|
||||
).Invoke(expr))
|
||||
{
|
||||
var expectedKeys = ((IEnumerable<object>)order.First().Evaluate(null)).Select(o =>
|
||||
{
|
||||
if (o is IExpression x)
|
||||
{
|
||||
return (string)x.Evaluate(null);
|
||||
}
|
||||
|
||||
return (string)o;
|
||||
})
|
||||
return (string)o;
|
||||
})
|
||||
.ToHashSet();
|
||||
var actualKeys = mapping.First().PossibleTags().Keys;
|
||||
var missingInOrder = actualKeys.Where(key => !expectedKeys.Contains(key)).ToList();
|
||||
var missingInMapping = expectedKeys.Where(key => !actualKeys.Contains(key)).ToList();
|
||||
if (missingInOrder.Any() || missingInMapping.Any()) {
|
||||
if (missingInOrder.Any() || missingInMapping.Any())
|
||||
{
|
||||
var missingInOrderMsg = "";
|
||||
if (missingInOrder.Any()) {
|
||||
if (missingInOrder.Any())
|
||||
{
|
||||
missingInOrderMsg = $"The order misses keys {string.Join(",", missingInOrder)}\n";
|
||||
}
|
||||
|
||||
var missingInMappingMsg = "";
|
||||
if (missingInMapping.Any()) {
|
||||
if (missingInMapping.Any())
|
||||
{
|
||||
missingInMappingMsg =
|
||||
$"The mapping misses mappings for keys {string.Join(", ", missingInMapping)}\n";
|
||||
}
|
||||
|
@ -310,24 +347,30 @@ namespace AspectedRouting.Language
|
|||
public static Dictionary<string, HashSet<string>> PossibleTags(this IEnumerable<IExpression> exprs)
|
||||
{
|
||||
var usedTags = new Dictionary<string, HashSet<string>>();
|
||||
foreach (var expr in exprs) {
|
||||
foreach (var expr in exprs)
|
||||
{
|
||||
var possible = expr.PossibleTags();
|
||||
if (possible == null) {
|
||||
if (possible == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var (key, values) in possible) {
|
||||
if (!usedTags.TryGetValue(key, out var collection)) {
|
||||
foreach (var (key, values) in possible)
|
||||
{
|
||||
if (!usedTags.TryGetValue(key, out var collection))
|
||||
{
|
||||
// This is the first time we see this collection
|
||||
collection = new HashSet<string>();
|
||||
usedTags[key] = collection;
|
||||
}
|
||||
|
||||
foreach (var v in values) {
|
||||
foreach (var v in values)
|
||||
{
|
||||
collection.Add(v);
|
||||
}
|
||||
|
||||
if (values.Count == 0) {
|
||||
if (values.Count == 0)
|
||||
{
|
||||
collection.Add("*");
|
||||
}
|
||||
}
|
||||
|
@ -336,30 +379,37 @@ namespace AspectedRouting.Language
|
|||
return usedTags;
|
||||
}
|
||||
|
||||
public static Dictionary<string, HashSet<string>> PossibleTagsRecursive(this IEnumerable<IExpression> exprs, Context c)
|
||||
{ var usedTags = new Dictionary<string, HashSet<string>>();
|
||||
foreach (var e in exprs) {
|
||||
public static Dictionary<string, HashSet<string>> PossibleTagsRecursive(this IEnumerable<IExpression> exprs, Context c)
|
||||
{
|
||||
var usedTags = new Dictionary<string, HashSet<string>>();
|
||||
foreach (var e in exprs)
|
||||
{
|
||||
var possibleTags = e.PossibleTagsRecursive(c);
|
||||
|
||||
if (possibleTags != null) {
|
||||
foreach (var tag in possibleTags) {
|
||||
if (possibleTags != null)
|
||||
{
|
||||
foreach (var tag in possibleTags)
|
||||
{
|
||||
usedTags[tag.Key] = tag.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return usedTags;
|
||||
}
|
||||
|
||||
|
||||
public static Dictionary<string, HashSet<string>> PossibleTagsRecursive(this IExpression e, Context c)
|
||||
{
|
||||
var allExpr = new List<IExpression>();
|
||||
var queue = new Queue<IExpression>();
|
||||
queue.Enqueue(e);
|
||||
do {
|
||||
do
|
||||
{
|
||||
var next = queue.Dequeue();
|
||||
allExpr.Add(next);
|
||||
next.Visit(expression => {
|
||||
if (expression is FunctionCall fc) {
|
||||
next.Visit(expression =>
|
||||
{
|
||||
if (expression is FunctionCall fc)
|
||||
{
|
||||
var called = c.GetFunction(fc.CalledFunctionName);
|
||||
queue.Enqueue(called);
|
||||
}
|
||||
|
@ -370,18 +420,23 @@ namespace AspectedRouting.Language
|
|||
|
||||
var result = new Dictionary<string, HashSet<string>>();
|
||||
|
||||
foreach (var expression in allExpr) {
|
||||
foreach (var expression in allExpr)
|
||||
{
|
||||
var subTags = expression.PossibleTags();
|
||||
if (subTags == null) {
|
||||
if (subTags == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var kv in subTags) {
|
||||
if (!result.ContainsKey(kv.Key)) {
|
||||
foreach (var kv in subTags)
|
||||
{
|
||||
if (!result.ContainsKey(kv.Key))
|
||||
{
|
||||
result[kv.Key] = new HashSet<string>();
|
||||
}
|
||||
|
||||
foreach (var val in kv.Value) {
|
||||
foreach (var val in kv.Value)
|
||||
{
|
||||
result[kv.Key].Add(val);
|
||||
}
|
||||
}
|
||||
|
@ -398,7 +453,8 @@ namespace AspectedRouting.Language
|
|||
public static Dictionary<string, HashSet<string>> PossibleTags(this IExpression e)
|
||||
{
|
||||
var mappings = new List<Mapping>();
|
||||
e.Visit(x => {
|
||||
e.Visit(x =>
|
||||
{
|
||||
/*
|
||||
var networkMapping = new List<IExpression>();
|
||||
if (Deconstruct.UnApply(
|
||||
|
@ -411,14 +467,16 @@ namespace AspectedRouting.Language
|
|||
return false;
|
||||
}*/
|
||||
|
||||
if (x is Mapping m) {
|
||||
if (x is Mapping m)
|
||||
{
|
||||
mappings.Add(m);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (mappings.Count == 0) {
|
||||
if (mappings.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -426,10 +484,13 @@ namespace AspectedRouting.Language
|
|||
var rootMapping = mappings[0];
|
||||
var result = new Dictionary<string, HashSet<string>>();
|
||||
|
||||
foreach (var (key, expr) in rootMapping.StringToResultFunctions) {
|
||||
foreach (var (key, expr) in rootMapping.StringToResultFunctions)
|
||||
{
|
||||
var values = new List<string>();
|
||||
expr.Visit(x => {
|
||||
if (x is Mapping m) {
|
||||
expr.Visit(x =>
|
||||
{
|
||||
if (x is Mapping m)
|
||||
{
|
||||
values.AddRange(m.StringToResultFunctions.Keys);
|
||||
}
|
||||
|
||||
|
@ -455,16 +516,19 @@ namespace AspectedRouting.Language
|
|||
|
||||
void HandleExpression(IExpression e, string calledIn)
|
||||
{
|
||||
e.Visit(f => {
|
||||
e.Visit(f =>
|
||||
{
|
||||
var mapping = new List<IExpression>();
|
||||
if (Deconstruct.UnApply(Deconstruct.IsFunc(Funcs.MemberOf),
|
||||
Deconstruct.Assign(mapping)
|
||||
).Invoke(f)) {
|
||||
).Invoke(f))
|
||||
{
|
||||
memberships.Add(calledIn, mapping.First());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (f is FunctionCall fc) {
|
||||
if (f is FunctionCall fc)
|
||||
{
|
||||
calledFunctionQueue.Enqueue(fc.CalledFunctionName);
|
||||
}
|
||||
|
||||
|
@ -472,12 +536,15 @@ namespace AspectedRouting.Language
|
|||
});
|
||||
}
|
||||
|
||||
foreach (var e in calledFunctions) {
|
||||
foreach (var e in calledFunctions)
|
||||
{
|
||||
HandleExpression(e, "profile_root");
|
||||
}
|
||||
|
||||
while (calledFunctionQueue.TryDequeue(out var functionName)) {
|
||||
if (alreadyAnalysedFunctions.Contains(functionName)) {
|
||||
while (calledFunctionQueue.TryDequeue(out var functionName))
|
||||
{
|
||||
if (alreadyAnalysedFunctions.Contains(functionName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace AspectedRouting.Language
|
|||
{
|
||||
Parameters.Add(name, new Constant(value));
|
||||
}
|
||||
|
||||
|
||||
public void AddParameter(string name, IExpression value)
|
||||
{
|
||||
Parameters.Add(name, value);
|
||||
|
|
|
@ -17,7 +17,8 @@ namespace AspectedRouting.Language
|
|||
/// <returns></returns>
|
||||
public static (IExpression f, List<IExpression> args)? DeconstructApply(this IExpression e)
|
||||
{
|
||||
if (!(e is Apply _)) {
|
||||
if (!(e is Apply _))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -25,7 +26,8 @@ namespace AspectedRouting.Language
|
|||
|
||||
var fs = new List<IExpression>();
|
||||
|
||||
while (UnApply(Assign(fs), Assign(argss)).Invoke(e)) {
|
||||
while (UnApply(Assign(fs), Assign(argss)).Invoke(e))
|
||||
{
|
||||
e = fs.First();
|
||||
fs.Clear();
|
||||
}
|
||||
|
@ -37,8 +39,10 @@ namespace AspectedRouting.Language
|
|||
|
||||
public static Func<IExpression, bool> IsMapping(List<Mapping> collect)
|
||||
{
|
||||
return e => {
|
||||
if (e is Mapping m) {
|
||||
return e =>
|
||||
{
|
||||
if (e is Mapping m)
|
||||
{
|
||||
collect.Add(m);
|
||||
return true;
|
||||
}
|
||||
|
@ -63,7 +67,8 @@ namespace AspectedRouting.Language
|
|||
|
||||
public static Func<IExpression, bool> Assign(List<IExpression> collect)
|
||||
{
|
||||
return e => {
|
||||
return e =>
|
||||
{
|
||||
collect.Add(e);
|
||||
return true;
|
||||
};
|
||||
|
@ -71,20 +76,24 @@ namespace AspectedRouting.Language
|
|||
|
||||
public static Func<IExpression, bool> IsFunc(Function f)
|
||||
{
|
||||
return e => {
|
||||
if (!(e is Function fe)) {
|
||||
return e =>
|
||||
{
|
||||
if (!(e is Function fe))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return f.Name.Equals(fe.Name);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static Func<IExpression, bool> IsFunctionCall(List<string> names)
|
||||
{
|
||||
return e => {
|
||||
if (!(e is FunctionCall fc)) {
|
||||
return e =>
|
||||
{
|
||||
if (!(e is FunctionCall fc))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -105,20 +114,27 @@ namespace AspectedRouting.Language
|
|||
Func<IExpression, bool> matchArg,
|
||||
bool matchAny = false)
|
||||
{
|
||||
return e => {
|
||||
if (!(e is Apply apply)) {
|
||||
return e =>
|
||||
{
|
||||
if (!(e is Apply apply))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var (_, (f, a)) in apply.FunctionApplications) {
|
||||
foreach (var (_, (f, a)) in apply.FunctionApplications)
|
||||
{
|
||||
var doesMatch = matchFunc.Invoke(f) && matchArg.Invoke(a);
|
||||
if (matchAny) {
|
||||
if (doesMatch) {
|
||||
if (matchAny)
|
||||
{
|
||||
if (doesMatch)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!doesMatch) {
|
||||
else
|
||||
{
|
||||
if (!doesMatch)
|
||||
{
|
||||
// All must match - otherwise we might have registered a wrong collection
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace AspectedRouting.Language.Expression
|
|||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (FunctionApplications.ContainsKey(actualResultType))
|
||||
{
|
||||
continue;
|
||||
|
@ -207,10 +207,10 @@ namespace AspectedRouting.Language.Expression
|
|||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// At this point, we know there only is a single type;
|
||||
// We can safely assume all the 'assign' will only match a single entry
|
||||
|
||||
|
||||
{
|
||||
// id a => a
|
||||
var arg = new List<IExpression>();
|
||||
|
@ -238,18 +238,18 @@ namespace AspectedRouting.Language.Expression
|
|||
Assign(arg)
|
||||
).Invoke(this))
|
||||
{
|
||||
var a = arg.First();
|
||||
var c = exprs.First();
|
||||
if (c.Types.All(t => t is ListType))
|
||||
{
|
||||
// The constant is a list
|
||||
var o = (List<IExpression>)c.Get();
|
||||
somethingChanged = true;
|
||||
return new Constant(
|
||||
o.Select(e => e.Apply(a).Optimize(out var _)).ToList()
|
||||
);
|
||||
}
|
||||
// fallthrough!
|
||||
var a = arg.First();
|
||||
var c = exprs.First();
|
||||
if (c.Types.All(t => t is ListType))
|
||||
{
|
||||
// The constant is a list
|
||||
var o = (List<IExpression>)c.Get();
|
||||
somethingChanged = true;
|
||||
return new Constant(
|
||||
o.Select(e => e.Apply(a).Optimize(out var _)).ToList()
|
||||
);
|
||||
}
|
||||
// fallthrough!
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -282,7 +282,7 @@ namespace AspectedRouting.Language.Expression
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
// ifdotted fcondition fthen <null> arg => if (fcondition arg) (fthen arg) (felse arg)
|
||||
var fcondition = new List<IExpression>();
|
||||
|
@ -421,10 +421,10 @@ namespace AspectedRouting.Language.Expression
|
|||
somethingChanged = false;
|
||||
var f = fRaw.Optimize(out var scf);
|
||||
somethingChanged |= scf;
|
||||
|
||||
|
||||
if (f.Types.Count() == 0)
|
||||
{
|
||||
throw new ArgumentException("Optimizing " + f + " failed, no types returned. The original expression\n "+fRaw.ToString()+"has types"+string.Join("\n ", fRaw.Types));
|
||||
throw new ArgumentException("Optimizing " + f + " failed, no types returned. The original expression\n " + fRaw.ToString() + "has types" + string.Join("\n ", fRaw.Types));
|
||||
}
|
||||
|
||||
a = a.Optimize(out var sca);
|
||||
|
@ -537,24 +537,24 @@ namespace AspectedRouting.Language.Expression
|
|||
{
|
||||
if (this.Types.Count() == 1)
|
||||
{
|
||||
var f = this.F.Repr().Replace("\n", "\n ");
|
||||
var a = this.A.Repr().Replace("\n", "\n ");
|
||||
var f = this.F.Repr().Replace("\n", "\n ");
|
||||
var a = this.A.Repr().Replace("\n", "\n ");
|
||||
|
||||
return $"new Apply( // {string.Join(" ; ", this.Types)}\n {f},\n {a})";
|
||||
return $"new Apply( // {string.Join(" ; ", this.Types)}\n {f},\n {a})";
|
||||
}
|
||||
|
||||
var r = "new Apply(";
|
||||
|
||||
foreach (var (type, (f, a)) in this.FunctionApplications)
|
||||
{
|
||||
r += "\n // " + type+"\n";
|
||||
r += " | " + f.Repr().Replace("\n", "\n | ")+",\n";
|
||||
r += " | " + a.Repr().Replace("\n", "\n | ")+"\n";
|
||||
r += "\n // " + type + "\n";
|
||||
r += " | " + f.Repr().Replace("\n", "\n | ") + ",\n";
|
||||
r += " | " + a.Repr().Replace("\n", "\n | ") + "\n";
|
||||
}
|
||||
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -37,7 +37,8 @@ namespace AspectedRouting.Language.Expression
|
|||
public IExpression PruneTypes(Func<Type, bool> allowedTypes)
|
||||
{
|
||||
var passedTypes = this.Types.Where(allowedTypes);
|
||||
if (!passedTypes.Any()) {
|
||||
if (!passedTypes.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -72,9 +73,9 @@ namespace AspectedRouting.Language.Expression
|
|||
|
||||
if (ArgNames == null)
|
||||
{
|
||||
throw new Exception("ArgNames not set for "+Name);
|
||||
throw new Exception("ArgNames not set for " + Name);
|
||||
}
|
||||
|
||||
|
||||
foreach (var n in ArgNames)
|
||||
{
|
||||
dict[n] = new List<Type>();
|
||||
|
@ -101,7 +102,7 @@ namespace AspectedRouting.Language.Expression
|
|||
|
||||
return dict;
|
||||
}
|
||||
|
||||
|
||||
public bool Equals(IExpression other)
|
||||
{
|
||||
if (other is Function f)
|
||||
|
@ -114,7 +115,7 @@ namespace AspectedRouting.Language.Expression
|
|||
|
||||
public string Repr()
|
||||
{
|
||||
return "Funcs."+this.Name;
|
||||
return "Funcs." + this.Name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,13 +33,14 @@ namespace AspectedRouting.Language.Expression
|
|||
Types = types;
|
||||
}
|
||||
|
||||
public FunctionCall(string name, Type type): this(name, new []{type}){
|
||||
|
||||
}
|
||||
|
||||
public object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
|
||||
public FunctionCall(string name, Type type) : this(name, new[] { type })
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
|
||||
var func = c.GetFunction(_name);
|
||||
c = c.WithAspectName(_name);
|
||||
return func.Evaluate(c, arguments);
|
||||
|
@ -59,7 +60,8 @@ namespace AspectedRouting.Language.Expression
|
|||
public IExpression PruneTypes(Func<Type, bool> allowedTypes)
|
||||
{
|
||||
var passedTypes = this.Types.Where(allowedTypes);
|
||||
if (!passedTypes.Any()) {
|
||||
if (!passedTypes.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ namespace AspectedRouting.Language
|
|||
}
|
||||
}
|
||||
|
||||
return e.Specialize(new[] {smallest});
|
||||
return e.Specialize(new[] { smallest });
|
||||
}
|
||||
|
||||
public static IExpression Apply(this IExpression function, IEnumerable<IExpression> args)
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var arg = ((IEnumerable<object>) arguments[0].Evaluate(c)).Select(o => (string) o);
|
||||
var arg = ((IEnumerable<object>)arguments[0].Evaluate(c)).Select(o => (string)o);
|
||||
|
||||
|
||||
if (arg.Any(str => str == null || str.Equals("no") || str.Equals("false")))
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace AspectedRouting.Language.Functions
|
|||
public override string Description { get; } =
|
||||
"Returns 'yes' if the second argument is bigger then the first argument. (Works great in combination with $dot)";
|
||||
|
||||
public override List<string> ArgNames { get; } = new List<string> {"minimum", "f", "then","else"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "minimum", "f", "then", "else" };
|
||||
|
||||
private static Type a = new Var("a");
|
||||
private static Type b = new Var("b");
|
||||
|
@ -20,7 +20,7 @@ namespace AspectedRouting.Language.Functions
|
|||
new[]
|
||||
{
|
||||
Curry.ConstructFrom(a,
|
||||
Typs.Double,
|
||||
Typs.Double,
|
||||
new Curry(b,Typs.Double),
|
||||
a, a, b),
|
||||
})
|
||||
|
@ -50,7 +50,7 @@ namespace AspectedRouting.Language.Functions
|
|||
var x = arguments[4];
|
||||
|
||||
var arg1 = arguments[1].Evaluate(c, x);
|
||||
|
||||
|
||||
if (minimum == null || arg1 == null)
|
||||
{
|
||||
return null;
|
||||
|
@ -60,7 +60,7 @@ namespace AspectedRouting.Language.Functions
|
|||
{
|
||||
minimum = e.Evaluate(c);
|
||||
}
|
||||
|
||||
|
||||
if (arg1 is IExpression e1)
|
||||
{
|
||||
arg1 = e1.Evaluate(c);
|
||||
|
@ -68,12 +68,12 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
if (minimum is int i0)
|
||||
{
|
||||
minimum = (double) i0;
|
||||
minimum = (double)i0;
|
||||
}
|
||||
|
||||
|
||||
if (arg1 is int i1)
|
||||
{
|
||||
arg1 = (double) i1;
|
||||
arg1 = (double)i1;
|
||||
}
|
||||
|
||||
if (minimum is double d0 && arg1 is double d1)
|
||||
|
@ -86,7 +86,7 @@ namespace AspectedRouting.Language.Functions
|
|||
return @else;
|
||||
}
|
||||
|
||||
throw new InvalidCastException("One of the arguments is not a number: "+minimum+", "+arg1);
|
||||
throw new InvalidCastException("One of the arguments is not a number: " + minimum + ", " + arg1);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace AspectedRouting.Language.Functions
|
|||
public class Concat : Function
|
||||
{
|
||||
public override string Description { get; } = "Concatenates two strings";
|
||||
public override List<string> ArgNames { get; } = new List<string>{"a","b"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "a", "b" };
|
||||
public Concat() : base("concat", true,
|
||||
new[]
|
||||
{
|
||||
|
@ -33,8 +33,8 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var arg0 = (string) arguments[0].Evaluate(c);
|
||||
var arg1 = (string) arguments[1].Evaluate(c);
|
||||
var arg0 = (string)arguments[0].Evaluate(c);
|
||||
var arg1 = (string)arguments[1].Evaluate(c);
|
||||
return arg0 + arg1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace AspectedRouting.Language.Functions
|
|||
public override string Description { get; } =
|
||||
"Small utility function, which takes two arguments `a` and `b` and returns `a`. Used extensively to insert freedom";
|
||||
|
||||
public override List<string> ArgNames { get; } = new List<string>{"a","b"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "a", "b" };
|
||||
|
||||
public Const() : base("firstArg", true,
|
||||
new[]
|
||||
|
@ -19,7 +19,7 @@ namespace AspectedRouting.Language.Functions
|
|||
}
|
||||
)
|
||||
{
|
||||
Funcs.AddBuiltin(this,"const");
|
||||
Funcs.AddBuiltin(this, "const");
|
||||
}
|
||||
|
||||
private Const(IEnumerable<Type> types) : base("firstArg", types
|
||||
|
@ -27,7 +27,7 @@ namespace AspectedRouting.Language.Functions
|
|||
{
|
||||
}
|
||||
|
||||
public override object Evaluate(Context c,params IExpression[] arguments)
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
if (arguments.Length == 1)
|
||||
{
|
||||
|
@ -35,13 +35,13 @@ namespace AspectedRouting.Language.Functions
|
|||
}
|
||||
|
||||
var f = arguments[0];
|
||||
var args = new IExpression [arguments.Length - 2];
|
||||
var args = new IExpression[arguments.Length - 2];
|
||||
for (var i = 2; i < arguments.Length; i++)
|
||||
{
|
||||
args[i - 2] = arguments[i];
|
||||
}
|
||||
|
||||
return f.Evaluate(c,args);
|
||||
return f.Evaluate(c, args);
|
||||
}
|
||||
|
||||
public override IExpression Specialize(IEnumerable<Type> allowedTypes)
|
||||
|
|
|
@ -6,10 +6,11 @@ using Type = AspectedRouting.Language.Typ.Type;
|
|||
namespace AspectedRouting.Language.Functions
|
||||
{
|
||||
public class ConstRight : Function
|
||||
{ public override string Description { get; } =
|
||||
"Small utility function, which takes two arguments `a` and `b` and returns `b`. Used extensively to insert freedom";
|
||||
{
|
||||
public override string Description { get; } =
|
||||
"Small utility function, which takes two arguments `a` and `b` and returns `b`. Used extensively to insert freedom";
|
||||
|
||||
public override List<string> ArgNames { get; } = new List<string>{"a","b"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "a", "b" };
|
||||
|
||||
public ConstRight() : base("constRight", true,
|
||||
new[]
|
||||
|
@ -25,7 +26,7 @@ namespace AspectedRouting.Language.Functions
|
|||
{
|
||||
}
|
||||
|
||||
public override object Evaluate(Context c,params IExpression[] arguments)
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var argsFor1 = new IExpression[arguments.Length - 2];
|
||||
for (var i = 2; i < arguments.Length; i++)
|
||||
|
|
|
@ -293,11 +293,11 @@ namespace AspectedRouting.Language.Functions
|
|||
if (_o is IEnumerable<IExpression> exprs)
|
||||
{
|
||||
return "new Constant(new []{" +
|
||||
string.Join(",\n ", exprs.Select(e => e.Repr().Replace("\n","\n ")))
|
||||
string.Join(",\n ", exprs.Select(e => e.Repr().Replace("\n", "\n ")))
|
||||
+ "})";
|
||||
}
|
||||
|
||||
return "new Constant(" + (_o?.ToString() ?? null)+ ")";
|
||||
return "new Constant(" + (_o?.ToString() ?? null) + ")";
|
||||
}
|
||||
|
||||
public void EvaluateAll(Context c, HashSet<IExpression> addTo)
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace AspectedRouting.Language.Functions
|
|||
public override string Description { get; } =
|
||||
"Given a list of values, checks if the argument is contained in the list.";
|
||||
|
||||
public override List<string> ArgNames { get; } = new List<string>{"list","a"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "list", "a" };
|
||||
|
||||
public ContainedIn() : base("containedIn", true,
|
||||
new[]
|
||||
|
@ -30,7 +30,7 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var list = (IEnumerable<IExpression>) arguments[0].Evaluate(c);
|
||||
var list = (IEnumerable<IExpression>)arguments[0].Evaluate(c);
|
||||
var arg = arguments[1];
|
||||
|
||||
var result = new List<object>();
|
||||
|
|
|
@ -7,11 +7,11 @@ namespace AspectedRouting.Language.Functions
|
|||
{
|
||||
public class Default : Function
|
||||
{
|
||||
|
||||
public override string Description { get; } = "Calculates function `f` for the given argument. If the result is `null`, the default value is returned instead";
|
||||
public override List<string> ArgNames { get; } = new List<string> {"defaultValue", "f"};
|
||||
|
||||
|
||||
public override string Description { get; } = "Calculates function `f` for the given argument. If the result is `null`, the default value is returned instead";
|
||||
public override List<string> ArgNames { get; } = new List<string> { "defaultValue", "f" };
|
||||
|
||||
|
||||
private static Var a = new Var("a");
|
||||
private static Var b = new Var("b");
|
||||
public Default() : base("default", true,
|
||||
|
@ -37,13 +37,13 @@ namespace AspectedRouting.Language.Functions
|
|||
return new Default(unified);
|
||||
}
|
||||
|
||||
public override object Evaluate(Context c,params IExpression[] arguments)
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var defaultValue = arguments[0];
|
||||
var func = arguments[1];
|
||||
var args= arguments.ToList().GetRange(2, arguments.Length - 2).ToArray();
|
||||
var args = arguments.ToList().GetRange(2, arguments.Length - 2).ToArray();
|
||||
|
||||
var calculated = func.Evaluate(c,args);
|
||||
var calculated = func.Evaluate(c, args);
|
||||
if (calculated == null)
|
||||
{
|
||||
return defaultValue.Evaluate(c);
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace AspectedRouting.Language.Functions
|
|||
public override string Description { get; } =
|
||||
"Higher order function: converts `f (g a)` into `(dot f g) a`. In other words, this fuses `f` and `g` in a new function, which allows the argument to be lifted out of the expression ";
|
||||
|
||||
public override List<string> ArgNames { get; } = new List<string> {"f", "g", "a"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "f", "g", "a" };
|
||||
public static readonly Var A = new Var("a");
|
||||
public static readonly Var B = new Var("b");
|
||||
public static readonly Var C = new Var("c");
|
||||
|
@ -36,12 +36,12 @@ namespace AspectedRouting.Language.Functions
|
|||
{
|
||||
if (arguments.Count() <= 2)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
var f0 = arguments[0];
|
||||
var f1 = arguments[1];
|
||||
var resultType = ((Curry) f1.Types.First()).ResultType;
|
||||
var resultType = ((Curry)f1.Types.First()).ResultType;
|
||||
var a = arguments[2];
|
||||
return f0.Evaluate(c, new Constant(resultType, f1.Evaluate(c, a)));
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace AspectedRouting.Language.Functions
|
|||
"The type system is then able to figure out which implementation is needed an discards the unneeded implementations.\n\n" +
|
||||
"Disclaimer: _you should never ever need this in your profiles_";
|
||||
|
||||
public override List<string> ArgNames { get; } = new List<string>{"f","g","a"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "f", "g", "a" };
|
||||
private static Var a = new Var("a");
|
||||
private static Var b = new Var("b");
|
||||
private static Var c = new Var("c");
|
||||
|
@ -53,7 +53,7 @@ namespace AspectedRouting.Language.Functions
|
|||
{
|
||||
}
|
||||
|
||||
public override object Evaluate(Context _,params IExpression[] arguments)
|
||||
public override object Evaluate(Context _, params IExpression[] arguments)
|
||||
{
|
||||
throw new ArgumentException("EitherFunc not sufficiently specialized");
|
||||
}
|
||||
|
|
|
@ -5,8 +5,9 @@ using AspectedRouting.Language.Typ;
|
|||
namespace AspectedRouting.Language.Functions
|
||||
{
|
||||
public class Eq : Function
|
||||
{ public override string Description { get; } = "Returns 'yes' if both values _are_ the same";
|
||||
public override List<string> ArgNames { get; } = new List<string>{"a","b"};
|
||||
{
|
||||
public override string Description { get; } = "Returns 'yes' if both values _are_ the same";
|
||||
public override List<string> ArgNames { get; } = new List<string> { "a", "b" };
|
||||
public Eq() : base("eq", true,
|
||||
new[]
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace AspectedRouting.Language.Functions
|
|||
"It will try the function for the first key (and it's respective value). If the function fails (it gives null), it'll try the next key.\n\n" +
|
||||
"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("first_match_of", true,
|
||||
new[]
|
||||
|
@ -26,7 +26,7 @@ namespace AspectedRouting.Language.Functions
|
|||
)
|
||||
})
|
||||
{
|
||||
Funcs.AddBuiltin( this,"firstMatchOf");
|
||||
Funcs.AddBuiltin(this, "firstMatchOf");
|
||||
}
|
||||
|
||||
private FirstMatchOf(IEnumerable<Type> types) : base("first_match_of", types)
|
||||
|
@ -46,7 +46,7 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var order = ((IEnumerable<object>) arguments[0].Evaluate(c))
|
||||
var order = ((IEnumerable<object>)arguments[0].Evaluate(c))
|
||||
.Select(o =>
|
||||
{
|
||||
if (o is string s)
|
||||
|
@ -55,11 +55,11 @@ namespace AspectedRouting.Language.Functions
|
|||
}
|
||||
else
|
||||
{
|
||||
return (string) ((IExpression) o).Evaluate(c);
|
||||
return (string)((IExpression)o).Evaluate(c);
|
||||
}
|
||||
});
|
||||
var function = arguments[1];
|
||||
var tags = (Dictionary<string, string>) arguments[2].Evaluate(c);
|
||||
var tags = (Dictionary<string, string>)arguments[2].Evaluate(c);
|
||||
|
||||
var singletonDict = new Dictionary<string, string>();
|
||||
|
||||
|
@ -75,7 +75,7 @@ namespace AspectedRouting.Language.Functions
|
|||
continue;
|
||||
}
|
||||
|
||||
return ((List<object>) result).First();
|
||||
return ((List<object>)result).First();
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -15,32 +15,38 @@ namespace AspectedRouting.Language.Functions
|
|||
new Curry(new Var("b"), new ListType(new Var("a"))),
|
||||
new Curry(new Var("b"), new Var("a")))
|
||||
}
|
||||
) { }
|
||||
)
|
||||
{ }
|
||||
|
||||
private HeadFunction(IEnumerable<Type> unified) : base("head", unified) { }
|
||||
|
||||
public override string Description { get; } =
|
||||
"Select the first non-null value of a list; returns 'null' on empty list or on null";
|
||||
|
||||
public override List<string> ArgNames { get; } = new List<string> {"ls"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "ls" };
|
||||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
object o = arguments[0];
|
||||
if (o is Apply app) {
|
||||
if (o is Apply app)
|
||||
{
|
||||
o = app.Evaluate(c, arguments.SubArray(1));
|
||||
}
|
||||
|
||||
while (o is IExpression e) {
|
||||
while (o is IExpression e)
|
||||
{
|
||||
o = e.Evaluate(c);
|
||||
}
|
||||
|
||||
if (!(o is IEnumerable<object> ls)) {
|
||||
if (!(o is IEnumerable<object> ls))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var v in ls) {
|
||||
if (v != null) {
|
||||
foreach (var v in ls)
|
||||
{
|
||||
if (v != null)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +57,8 @@ namespace AspectedRouting.Language.Functions
|
|||
public override IExpression Specialize(IEnumerable<Type> allowedTypes)
|
||||
{
|
||||
var unified = Types.SpecializeTo(allowedTypes);
|
||||
if (unified == null) {
|
||||
if (unified == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,9 @@ using AspectedRouting.Language.Typ;
|
|||
namespace AspectedRouting.Language.Functions
|
||||
{
|
||||
public class Id : Function
|
||||
{ public override string Description { get; } = "Returns the argument unchanged - the identity function. Seems useless at first sight, but useful in parsing";
|
||||
public override List<string> ArgNames { get; } = new List<string>{"a"};
|
||||
{
|
||||
public override string Description { get; } = "Returns the argument unchanged - the identity function. Seems useless at first sight, but useful in parsing";
|
||||
public override List<string> ArgNames { get; } = new List<string> { "a" };
|
||||
public Id() : base("id", true,
|
||||
new[]
|
||||
{
|
||||
|
@ -31,9 +32,9 @@ namespace AspectedRouting.Language.Functions
|
|||
return new Id(unified);
|
||||
}
|
||||
|
||||
public override object Evaluate(Context c,params IExpression[] arguments)
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
return arguments[0].Evaluate(c,arguments.ToList().GetRange(1, arguments.Length-1).ToArray());
|
||||
return arguments[0].Evaluate(c, arguments.ToList().GetRange(1, arguments.Length - 1).ToArray());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ namespace AspectedRouting.Language.Functions
|
|||
" The 'then' branch is returned if the condition returns the string `yes` or `true`." +
|
||||
" Otherwise, the `else` branch is taken (including if the condition returns `null`)" +
|
||||
"If the `else` branch is not set, `null` is returned if the condition evaluates to false.";
|
||||
public override List<string> ArgNames { get; } = new List<string> {"condition", "then", "else"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "condition", "then", "else" };
|
||||
|
||||
public If() : base("if_then_else", true,
|
||||
new[]
|
||||
|
@ -34,7 +34,7 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var condition = arguments[0].Evaluate(c);
|
||||
var condition = arguments[0].Evaluate(c);
|
||||
var then = arguments[1];
|
||||
IExpression @else = null;
|
||||
if (arguments.Length > 2)
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace AspectedRouting.Language.Functions
|
|||
{
|
||||
private static Var a = new Var("a");
|
||||
private static Var b = new Var("b");
|
||||
|
||||
|
||||
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)`" +
|
||||
|
@ -18,26 +18,26 @@ namespace AspectedRouting.Language.Functions
|
|||
" 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." +
|
||||
"In case the condition returns 'null', then the 'else'-branch is taken.";
|
||||
public override List<string> ArgNames { get; } = new List<string> {"condition", "then", "else"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "condition", "then", "else" };
|
||||
|
||||
public IfDotted() : base("if_then_else_dotted", true,
|
||||
new[]
|
||||
{
|
||||
|
||||
Curry.ConstructFrom(a,
|
||||
|
||||
Curry.ConstructFrom(a,
|
||||
new Curry(b, Typs.Bool),
|
||||
new Curry(b, a),
|
||||
b),
|
||||
Curry.ConstructFrom(a,
|
||||
Curry.ConstructFrom(a,
|
||||
new Curry(b, Typs.String),
|
||||
new Curry(b, a),
|
||||
b),
|
||||
Curry.ConstructFrom(a,
|
||||
Curry.ConstructFrom(a,
|
||||
new Curry(b, Typs.Bool),
|
||||
new Curry(b, a),
|
||||
new Curry(b, a),
|
||||
b),
|
||||
Curry.ConstructFrom(a,
|
||||
Curry.ConstructFrom(a,
|
||||
new Curry(b, Typs.String),
|
||||
new Curry(b, a),
|
||||
new Curry(b, a),
|
||||
|
@ -55,18 +55,18 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var conditionfunc = arguments[0];
|
||||
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);
|
||||
var condition = ((IExpression)conditionfunc).Apply(argument).Evaluate(c);
|
||||
|
||||
if (condition != null && (condition.Equals("yes") || condition.Equals("true") || condition.Equals(true)))
|
||||
{
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace AspectedRouting.Language.Functions
|
|||
public class Inv : Function
|
||||
{
|
||||
public override string Description { get; } = "Calculates `1/d`";
|
||||
public override List<string> ArgNames { get; } = new List<string> {"d"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "d" };
|
||||
|
||||
public Inv() : base("inv", true, new[]
|
||||
{
|
||||
|
@ -23,7 +23,7 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var arg = (double) arguments[0].Evaluate(c);
|
||||
var arg = (double)arguments[0].Evaluate(c);
|
||||
return 1 / arg;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace AspectedRouting.Language.Functions
|
|||
public class IsNull : Function
|
||||
{
|
||||
public override string Description { get; } = "Returns true if the given argument is null";
|
||||
public override List<string> ArgNames { get; } = new List<string>{"a"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "a" };
|
||||
|
||||
public IsNull() : base("is_null", true,
|
||||
new[]
|
||||
|
@ -38,7 +38,7 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var arg = (string) arguments[0].Evaluate(c);
|
||||
var arg = (string)arguments[0].Evaluate(c);
|
||||
if (arg == null)
|
||||
{
|
||||
return "yes";
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace AspectedRouting.Language.Functions
|
|||
"Listdot takes a list of functions `[f, g, h]` and and an argument `a`. It applies the argument on every single function." +
|
||||
"It conveniently lifts the argument out of the list.";
|
||||
|
||||
public override List<string> ArgNames { get; } = new List<string>{"list","a"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "list", "a" };
|
||||
|
||||
public ListDot() : base("listDot", true,
|
||||
new[]
|
||||
|
@ -32,7 +32,7 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var listOfFuncs = (IEnumerable<IExpression>) arguments[0].Evaluate(c);
|
||||
var listOfFuncs = (IEnumerable<IExpression>)arguments[0].Evaluate(c);
|
||||
var arg = arguments[1];
|
||||
|
||||
var result = new List<object>();
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
|
||||
var newFunctions = new Dictionary<string, IExpression>();
|
||||
var functionType = unified.Select(c => ((Curry) c).ResultType);
|
||||
var functionType = unified.Select(c => ((Curry)c).ResultType);
|
||||
var enumerable = functionType.ToList();
|
||||
|
||||
foreach (var (k, expr) in StringToResultFunctions)
|
||||
|
@ -79,22 +79,24 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
|
||||
|
||||
var s = arguments[0].Evaluate(c);
|
||||
while (s is Constant constant)
|
||||
{
|
||||
s = constant.Evaluate(c);
|
||||
}
|
||||
|
||||
|
||||
var key = (string) s;
|
||||
|
||||
var key = (string)s;
|
||||
var otherARgs = arguments.ToList().GetRange(1, arguments.Length - 1);
|
||||
if (!StringToResultFunctions.ContainsKey(key)) { // This is really roundabout, but it has to be
|
||||
if (!StringToResultFunctions.ContainsKey(key))
|
||||
{ // This is really roundabout, but it has to be
|
||||
return null;
|
||||
}
|
||||
|
||||
var resultFunction = StringToResultFunctions[key];
|
||||
if (resultFunction == null) {
|
||||
if (resultFunction == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace AspectedRouting.Language.Functions
|
|||
public override string Description { get; } =
|
||||
"Returns the biggest value in the list. For a list of booleans, this acts as 'or'";
|
||||
|
||||
public override List<string> ArgNames { get; } = new List<string> {"list"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "list" };
|
||||
|
||||
public Max() : base("max", true,
|
||||
new[]
|
||||
|
@ -45,7 +45,7 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var ls = ((IEnumerable<object>) arguments[0].Evaluate(c)).Where(o => o != null);
|
||||
var ls = ((IEnumerable<object>)arguments[0].Evaluate(c)).Where(o => o != null);
|
||||
|
||||
var expectedType = (Types.First() as Curry).ResultType;
|
||||
switch (expectedType)
|
||||
|
|
|
@ -13,7 +13,8 @@ namespace AspectedRouting.Language.Functions
|
|||
new Curry(Typs.Tags, Typs.Bool),
|
||||
new Curry(Typs.Tags, Typs.Bool))
|
||||
}
|
||||
) { }
|
||||
)
|
||||
{ }
|
||||
|
||||
public MemberOf(IEnumerable<Type> types) : base("memberOf", types) { }
|
||||
|
||||
|
@ -39,10 +40,11 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
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('$');
|
||||
|
||||
if (tags.TryGetValue("_relation:" + name, out var v)) {
|
||||
if (tags.TryGetValue("_relation:" + name, out var v))
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
|
@ -57,20 +59,24 @@ namespace AspectedRouting.Language.Functions
|
|||
}
|
||||
|
||||
var keyParts = tag.Key.Split(":");
|
||||
if (keyParts.Length != 3) {
|
||||
if (keyParts.Length != 3)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var relationName = keyParts[1];
|
||||
if (!relationTags.ContainsKey(relationName)) {
|
||||
if (!relationTags.ContainsKey(relationName))
|
||||
{
|
||||
relationTags.Add(relationName, new Dictionary<string, string>());
|
||||
}
|
||||
|
||||
relationTags[relationName].Add(keyParts[2], tag.Value);
|
||||
}
|
||||
|
||||
foreach (var relationTagging in relationTags) {
|
||||
foreach (var relationTagging in relationTags)
|
||||
{
|
||||
var result = arguments[0].Evaluate(c, new Constant(relationTagging.Value));
|
||||
if (result.Equals("yes")) {
|
||||
if (result.Equals("yes"))
|
||||
{
|
||||
return "yes";
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +87,8 @@ namespace AspectedRouting.Language.Functions
|
|||
public override IExpression Specialize(IEnumerable<Type> allowedTypes)
|
||||
{
|
||||
var unified = Types.SpecializeTo(allowedTypes);
|
||||
if (unified == null) {
|
||||
if (unified == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace AspectedRouting.Language.Functions
|
|||
public override string Description { get; } =
|
||||
"Out of a list of values, gets the smallest value. In case of a list of bools, this acts as `and`. Note that 'null'-values are ignored.";
|
||||
|
||||
public override List<string> ArgNames { get; } = new List<string> {"list"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "list" };
|
||||
|
||||
public Min() : base("min", true,
|
||||
new[]
|
||||
|
@ -44,8 +44,8 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var ls = ((IEnumerable<object>) arguments[0].Evaluate(c)).Where(o => o != null);
|
||||
var expectedType = ((Curry) Types.First()).ResultType;
|
||||
var ls = ((IEnumerable<object>)arguments[0].Evaluate(c)).Where(o => o != null);
|
||||
var expectedType = ((Curry)Types.First()).ResultType;
|
||||
|
||||
switch (expectedType)
|
||||
{
|
||||
|
@ -67,7 +67,7 @@ namespace AspectedRouting.Language.Functions
|
|||
o = e.Evaluate(c);
|
||||
}
|
||||
|
||||
return (double) o;
|
||||
return (double)o;
|
||||
}).Min();
|
||||
default:
|
||||
return ls.Select(o =>
|
||||
|
@ -77,7 +77,7 @@ namespace AspectedRouting.Language.Functions
|
|||
o = e.Evaluate(c);
|
||||
}
|
||||
|
||||
return (int) o;
|
||||
return (int)o;
|
||||
}).Min();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace AspectedRouting.Language.Functions
|
|||
public override string Description { get; } =
|
||||
"Multiplies all the values in a given list. On a list of booleans, this acts as 'and' or 'all', as `false` and `no` are interpreted as zero. Null values are ignored and thus considered to be `one`";
|
||||
|
||||
public override List<string> ArgNames { get; } = new List<string> {"list"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "list" };
|
||||
|
||||
|
||||
public Multiply() : base("multiply", true,
|
||||
|
@ -43,8 +43,8 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var ls = ((IEnumerable<object>) arguments[0].Evaluate(c)).Where(o => o != null);
|
||||
var expectedType = ((Curry) Types.First()).ResultType;
|
||||
var ls = ((IEnumerable<object>)arguments[0].Evaluate(c)).Where(o => o != null);
|
||||
var expectedType = ((Curry)Types.First()).ResultType;
|
||||
|
||||
|
||||
switch (expectedType)
|
||||
|
@ -81,7 +81,7 @@ namespace AspectedRouting.Language.Functions
|
|||
o = e.Evaluate(c);
|
||||
}
|
||||
|
||||
mult *= (double) o;
|
||||
mult *= (double)o;
|
||||
}
|
||||
|
||||
return mult;
|
||||
|
@ -95,7 +95,7 @@ namespace AspectedRouting.Language.Functions
|
|||
o = e.Evaluate(c);
|
||||
}
|
||||
|
||||
multI *= (int) o;
|
||||
multI *= (int)o;
|
||||
}
|
||||
|
||||
return multI;
|
||||
|
|
|
@ -42,7 +42,8 @@ namespace AspectedRouting.Language.Functions
|
|||
public override IExpression Specialize(IEnumerable<Type> allowedTypes)
|
||||
{
|
||||
var unified = Types.SpecializeTo(allowedTypes);
|
||||
if (unified == null) {
|
||||
if (unified == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -51,43 +52,50 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var neededKeys = (IEnumerable<object>) arguments[0].Evaluate(c);
|
||||
var neededKeys = (IEnumerable<object>)arguments[0].Evaluate(c);
|
||||
var function = arguments[1];
|
||||
|
||||
var tags = (Dictionary<string, string>) arguments[2].Evaluate(c);
|
||||
var tags = (Dictionary<string, string>)arguments[2].Evaluate(c);
|
||||
|
||||
foreach (var oo in neededKeys) {
|
||||
foreach (var oo in neededKeys)
|
||||
{
|
||||
var o = oo;
|
||||
while (o is IExpression e) {
|
||||
while (o is IExpression e)
|
||||
{
|
||||
o = e.Evaluate(c);
|
||||
}
|
||||
|
||||
if (!(o is string tagKey)) {
|
||||
if (!(o is string tagKey))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!tags.ContainsKey(tagKey)) {
|
||||
if (!tags.ContainsKey(tagKey))
|
||||
{
|
||||
// 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) {
|
||||
if (applied == null)
|
||||
{
|
||||
return "no";
|
||||
}
|
||||
if (applied.Equals("yes") || (applied is IEnumerable<object> l && l.Count() > 0 && l.ToList()[0].Equals("yes")) ) {
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
var result = (IEnumerable<object>) function.Evaluate(c, new Constant(tags));
|
||||
var result = (IEnumerable<object>)function.Evaluate(c, new Constant(tags));
|
||||
|
||||
if (result.Any(o =>
|
||||
o == null ||
|
||||
o is string s && (s.Equals("no") || s.Equals("false")))) {
|
||||
o is string s && (s.Equals("no") || s.Equals("false"))))
|
||||
{
|
||||
// The mapped function is executed. If the mapped function gives 'no', null or 'false' for any value, "no" is returned
|
||||
return "no";
|
||||
}
|
||||
|
|
|
@ -7,14 +7,14 @@ namespace AspectedRouting.Language.Functions
|
|||
public class NotEq : Function
|
||||
{
|
||||
public override string Description { get; } = "OVerloaded function, either boolean not or returns 'yes' if the two passed in values are _not_ the same;";
|
||||
public override List<string> ArgNames { get; } = new List<string> {"a", "b"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "a", "b" };
|
||||
|
||||
public NotEq() : base("notEq", true,
|
||||
new[]
|
||||
{
|
||||
Curry.ConstructFrom(Typs.Bool, new Var("a"), new Var("a")),
|
||||
Curry.ConstructFrom(Typs.String, new Var("a"), new Var("a")),
|
||||
new Curry(Typs.Bool, Typs.Bool),
|
||||
new Curry(Typs.Bool, Typs.Bool),
|
||||
})
|
||||
{
|
||||
Funcs.AddBuiltin(this, "not");
|
||||
|
@ -43,7 +43,7 @@ namespace AspectedRouting.Language.Functions
|
|||
var booleanArg = arguments[0].Evaluate(c);
|
||||
return booleanArg.Equals("no");
|
||||
}
|
||||
|
||||
|
||||
var arg0 = arguments[0].Evaluate(c);
|
||||
var arg1 = arguments[1].Evaluate(c);
|
||||
if ((!(arg0?.Equals(arg1) ?? false)))
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public Parameter(string s)
|
||||
{
|
||||
Types = new[] {new Var("parameter") };
|
||||
Types = new[] { new Var("parameter") };
|
||||
ParamName = s;
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ namespace AspectedRouting.Language.Functions
|
|||
{
|
||||
var paramName = ParamName.TrimStart('#'); // context saves paramnames without '#'
|
||||
var value = c?.Parameters?.GetValueOrDefault(paramName, null);
|
||||
if(value is Constant constant)
|
||||
if (value is Constant constant)
|
||||
{
|
||||
return constant.Evaluate(c);
|
||||
}
|
||||
|
@ -37,11 +37,11 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public IExpression Specialize(IEnumerable<Type> allowedTypes)
|
||||
{
|
||||
/* var filtered = allowedTypes.Where(at => !(at is Curry));
|
||||
if (filtered.Count() == 0)
|
||||
{
|
||||
return null;
|
||||
}*/
|
||||
/* var filtered = allowedTypes.Where(at => !(at is Curry));
|
||||
if (filtered.Count() == 0)
|
||||
{
|
||||
return null;
|
||||
}*/
|
||||
var unified = Types.SpecializeTo(allowedTypes);
|
||||
if (unified == null)
|
||||
{
|
||||
|
@ -50,11 +50,12 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
return new Parameter(unified, ParamName);
|
||||
}
|
||||
|
||||
|
||||
public IExpression PruneTypes(System.Func<Type, bool> allowedTypes)
|
||||
{
|
||||
var passedTypes = this.Types.Where(allowedTypes);
|
||||
if (!passedTypes.Any()) {
|
||||
if (!passedTypes.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -66,7 +67,7 @@ namespace AspectedRouting.Language.Functions
|
|||
somethingChanged = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public IExpression OptimizeWithArgument(IExpression arg)
|
||||
{
|
||||
throw new NotSupportedException("Trying to invoke a parameter");
|
||||
|
@ -81,7 +82,7 @@ namespace AspectedRouting.Language.Functions
|
|||
{
|
||||
if (other is Parameter p)
|
||||
{
|
||||
return p.ParamName.Equals( this.ParamName);
|
||||
return p.ParamName.Equals(this.ParamName);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -96,7 +97,7 @@ namespace AspectedRouting.Language.Functions
|
|||
{
|
||||
return ParamName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ namespace AspectedRouting.Language.Functions
|
|||
public class Parse : Function
|
||||
{
|
||||
public override string Description { get; } = "Parses a string into a numerical value. Returns 'null' if parsing fails or no input is given. If a duration is given (e.g. `01:15`), then the number of minutes (75) is returned";
|
||||
public override List<string> ArgNames { get; } = new List<string>{"s"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "s" };
|
||||
|
||||
public Parse() : base("parse", true,
|
||||
new[]
|
||||
|
@ -39,18 +39,18 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var arg = (string) arguments[0].Evaluate(c);
|
||||
var expectedType = ((Curry) Types.First()).ResultType;
|
||||
var arg = (string)arguments[0].Evaluate(c);
|
||||
var expectedType = ((Curry)Types.First()).ResultType;
|
||||
|
||||
var duration = Regex.Match(arg, @"^(\d+):(\d+)$");
|
||||
if (duration.Success)
|
||||
{
|
||||
// This is a duration of the form 'hh:mm' -> we return the total minute count
|
||||
var hours = int.Parse(duration.Groups[1].Value);
|
||||
var minutes = int.Parse(duration.Groups[2].Value);
|
||||
arg = (hours * 60 + minutes).ToString();
|
||||
var hours = int.Parse(duration.Groups[1].Value);
|
||||
var minutes = int.Parse(duration.Groups[2].Value);
|
||||
arg = (hours * 60 + minutes).ToString();
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
|
@ -62,12 +62,12 @@ namespace AspectedRouting.Language.Functions
|
|||
default: return int.Parse(arg);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception)
|
||||
{
|
||||
Console.Error.WriteLine("Could not parse " + arg + " as " + expectedType);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ namespace AspectedRouting.Language.Functions
|
|||
"*stringToTags* converts a function `string -> string -> a` into a function `tags -> [a]`. " +
|
||||
"It is used internally to convert a hash of functions. `stringToTags` shouldn't be needed when implementing profiles.";
|
||||
|
||||
public override List<string> ArgNames { get; } = new List<string> {"f", "tags"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "f", "tags" };
|
||||
|
||||
private static readonly Type _baseFunction =
|
||||
Curry.ConstructFrom(new Var("a"), Typs.String, Typs.String);
|
||||
|
@ -33,8 +33,9 @@ namespace AspectedRouting.Language.Functions
|
|||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var f = arguments[0];
|
||||
var tags = (Dictionary<string, string>) arguments[1].Evaluate(c);
|
||||
if (tags == null) {
|
||||
var tags = (Dictionary<string, string>)arguments[1].Evaluate(c);
|
||||
if (tags == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var result = new List<object>();
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace AspectedRouting.Language.Functions
|
|||
public class Sum : Function
|
||||
{
|
||||
public override string Description { get; } = "Sums all the numbers in the given list. If the list is a list of booleans, `yes` or `true` will be considered to equal `1`. Null values are ignored (and thus handled as being `0`)";
|
||||
public override List<string> ArgNames { get; } = new List<string>{"list"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "list" };
|
||||
|
||||
public Sum() : base("sum", true,
|
||||
new[]
|
||||
|
@ -17,7 +17,7 @@ namespace AspectedRouting.Language.Functions
|
|||
new Curry(new ListType(Typs.Int), Typs.Int),
|
||||
new Curry(new ListType(Typs.PDouble), Typs.PDouble),
|
||||
new Curry(new ListType(Typs.Double), Typs.Double),
|
||||
new Curry(new ListType(Typs.Bool), Typs.Int),
|
||||
new Curry(new ListType(Typs.Bool), Typs.Int),
|
||||
})
|
||||
{
|
||||
Funcs.AddBuiltin(this, "plus");
|
||||
|
@ -44,15 +44,15 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var ls = ((IEnumerable<object>) arguments[0]
|
||||
var ls = ((IEnumerable<object>)arguments[0]
|
||||
.Evaluate(c))
|
||||
.Where(o => o!=null);
|
||||
.Where(o => o != null);
|
||||
var expectedType = (Types.First() as Curry).ResultType;
|
||||
|
||||
switch (expectedType)
|
||||
{
|
||||
case BoolType _:
|
||||
var sumB= 0;
|
||||
var sumB = 0;
|
||||
foreach (var o in ls)
|
||||
{
|
||||
if (o.Equals("yes") || o.Equals("true"))
|
||||
|
@ -67,7 +67,7 @@ namespace AspectedRouting.Language.Functions
|
|||
var sum = 0.0;
|
||||
foreach (var o in ls)
|
||||
{
|
||||
sum += (double) o;
|
||||
sum += (double)o;
|
||||
}
|
||||
|
||||
return sum;
|
||||
|
@ -75,7 +75,7 @@ namespace AspectedRouting.Language.Functions
|
|||
var sumI = 1;
|
||||
foreach (var o in ls)
|
||||
{
|
||||
sumI += (int) o;
|
||||
sumI += (int)o;
|
||||
}
|
||||
|
||||
return sumI;
|
||||
|
|
|
@ -7,10 +7,10 @@ namespace AspectedRouting.Language.Functions
|
|||
public class ToString : Function
|
||||
{
|
||||
public override string Description { get; } = "Converts a value into a human readable string";
|
||||
public override List<string> ArgNames { get; } = new List<string>{"obj"};
|
||||
public override List<string> ArgNames { get; } = new List<string> { "obj" };
|
||||
|
||||
public ToString() : base("to_string", true,
|
||||
new[] {new Curry(new Var("a"), Typs.String)})
|
||||
new[] { new Curry(new Var("a"), Typs.String) })
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@ namespace AspectedRouting.Language.Typ
|
|||
ArgType = argType;
|
||||
ResultType = resultType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private static string ToString(Type argType, Type resultType)
|
||||
{
|
||||
var arg = argType.ToString();
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace AspectedRouting.Language.Typ
|
|||
Typs.AddBuiltin(this);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
|
|
@ -30,16 +30,20 @@ namespace AspectedRouting.Language.Typ
|
|||
var results = new HashSet<Type>();
|
||||
|
||||
allowedTypes = allowedTypes.ToList();
|
||||
foreach (var t0 in types0) {
|
||||
foreach (var allowed in allowedTypes) {
|
||||
foreach (var t0 in types0)
|
||||
{
|
||||
foreach (var allowed in allowedTypes)
|
||||
{
|
||||
var unified = t0.Unify(allowed, reverseSuperSet);
|
||||
if (unified != null) {
|
||||
if (unified != null)
|
||||
{
|
||||
results.Add(unified);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (results.Any()) {
|
||||
if (results.Any())
|
||||
{
|
||||
return results;
|
||||
}
|
||||
|
||||
|
@ -59,12 +63,14 @@ namespace AspectedRouting.Language.Typ
|
|||
public static Type Unify(this Type t0, Type t1, bool reverseSuperset = false)
|
||||
{
|
||||
var table = t0.UnificationTable(t1, reverseSuperset);
|
||||
if (table == null) {
|
||||
if (table == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var subbed = t0.Substitute(table);
|
||||
if (reverseSuperset) {
|
||||
if (reverseSuperset)
|
||||
{
|
||||
return SelectSmallestUnion(t1, subbed);
|
||||
}
|
||||
|
||||
|
@ -79,7 +85,8 @@ namespace AspectedRouting.Language.Typ
|
|||
/// <returns></returns>
|
||||
private static Type SelectSmallestUnion(this Type wider, Type smaller)
|
||||
{
|
||||
switch (wider) {
|
||||
switch (wider)
|
||||
{
|
||||
case Var a:
|
||||
return a;
|
||||
case ListType l when smaller is ListType lsmaller:
|
||||
|
@ -93,7 +100,8 @@ namespace AspectedRouting.Language.Typ
|
|||
cWider.ResultType.SelectSmallestUnion(cSmaller.ResultType);
|
||||
return new Curry(arg, result);
|
||||
default:
|
||||
if (wider.IsSuperSet(smaller) && !smaller.IsSuperSet(wider)) {
|
||||
if (wider.IsSuperSet(smaller) && !smaller.IsSuperSet(wider))
|
||||
{
|
||||
return smaller;
|
||||
}
|
||||
|
||||
|
@ -111,7 +119,8 @@ namespace AspectedRouting.Language.Typ
|
|||
public static IEnumerable<Type> UnifyAll(this Type t0, IEnumerable<Type> t1)
|
||||
{
|
||||
var result = t1.Select(t => t0.Unify(t)).ToHashSet();
|
||||
if (result.Any(x => x == null)) {
|
||||
if (result.Any(x => x == null))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -121,7 +130,8 @@ namespace AspectedRouting.Language.Typ
|
|||
|
||||
public static Type Substitute(this Type t0, Dictionary<string, Type> substitutions)
|
||||
{
|
||||
switch (t0) {
|
||||
switch (t0)
|
||||
{
|
||||
case Var a when substitutions.TryGetValue(a.Name, out var t):
|
||||
return t;
|
||||
case ListType l:
|
||||
|
@ -151,7 +161,8 @@ namespace AspectedRouting.Language.Typ
|
|||
|
||||
bool AddSubs(string key, Type valueToAdd)
|
||||
{
|
||||
if (substitutionsOn0.TryGetValue(key, out var oldSubs)) {
|
||||
if (substitutionsOn0.TryGetValue(key, out var oldSubs))
|
||||
{
|
||||
return oldSubs.Equals(valueToAdd);
|
||||
}
|
||||
|
||||
|
@ -161,12 +172,15 @@ namespace AspectedRouting.Language.Typ
|
|||
|
||||
bool AddAllSubs(Dictionary<string, Type> table)
|
||||
{
|
||||
if (table == null) {
|
||||
if (table == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var (key, tp) in table) {
|
||||
if (!AddSubs(key, tp)) {
|
||||
foreach (var (key, tp) in table)
|
||||
{
|
||||
if (!AddSubs(key, tp))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -174,45 +188,54 @@ namespace AspectedRouting.Language.Typ
|
|||
return true;
|
||||
}
|
||||
|
||||
switch (t0) {
|
||||
switch (t0)
|
||||
{
|
||||
case Var a:
|
||||
if (!AddSubs(a.Name, t1)) {
|
||||
if (!AddSubs(a.Name, t1))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
break;
|
||||
case ListType l0 when t1 is ListType l1: {
|
||||
var table = l0.InnerType.UnificationTable(l1.InnerType, reverseSupersetRelation);
|
||||
if (!AddAllSubs(table)) {
|
||||
return null;
|
||||
case ListType l0 when t1 is ListType l1:
|
||||
{
|
||||
var table = l0.InnerType.UnificationTable(l1.InnerType, reverseSupersetRelation);
|
||||
if (!AddAllSubs(table))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case Curry curry0 when t1 is Curry curry1:
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
case Curry curry0 when t1 is Curry curry1: {
|
||||
// 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;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
if (t1 is Var v) {
|
||||
if (t1 is Var v)
|
||||
{
|
||||
AddSubs(v.Name, t0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!reverseSupersetRelation && !t0.IsSuperSet(t1)) {
|
||||
if (!reverseSupersetRelation && !t0.IsSuperSet(t1))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (reverseSupersetRelation && !t1.IsSuperSet(t0)) {
|
||||
if (reverseSupersetRelation && !t1.IsSuperSet(t0))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -227,23 +250,28 @@ namespace AspectedRouting.Language.Typ
|
|||
// We do not have to worry about overlapping names, as they should be cleaned before calling this method
|
||||
|
||||
bool appliedTransitivity;
|
||||
do {
|
||||
do
|
||||
{
|
||||
appliedTransitivity = false;
|
||||
var keys = substitutionsOn0.Keys.ToList();
|
||||
foreach (var key in keys) {
|
||||
foreach (var key in keys)
|
||||
{
|
||||
var val = substitutionsOn0[key];
|
||||
var usedVars = val.UsedVariables();
|
||||
var isContained = keys.Any(usedVars.Contains);
|
||||
if (!isContained) {
|
||||
if (!isContained)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var newVal = val.Substitute(substitutionsOn0);
|
||||
if (newVal.Equals(val)) {
|
||||
if (newVal.Equals(val))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (newVal.UsedVariables().Contains(key) && !newVal.Equals(new Var(key))) {
|
||||
if (newVal.UsedVariables().Contains(key) && !newVal.Equals(new Var(key)))
|
||||
{
|
||||
// The substitution contains itself; and it is bigger then itself
|
||||
// This means that $a is substituted by e.g. ($a -> $x), implying an infinite and contradictory type
|
||||
return null;
|
||||
|
@ -260,20 +288,23 @@ namespace AspectedRouting.Language.Typ
|
|||
public static HashSet<string> UsedVariables(this Type t0, HashSet<string> addTo = null)
|
||||
{
|
||||
addTo ??= new HashSet<string>();
|
||||
switch (t0) {
|
||||
switch (t0)
|
||||
{
|
||||
case Var a:
|
||||
addTo.Add(a.Name);
|
||||
break;
|
||||
case ListType l0: {
|
||||
l0.InnerType.UsedVariables(addTo);
|
||||
break;
|
||||
}
|
||||
case ListType l0:
|
||||
{
|
||||
l0.InnerType.UsedVariables(addTo);
|
||||
break;
|
||||
}
|
||||
|
||||
case Curry curry0: {
|
||||
curry0.ArgType.UsedVariables(addTo);
|
||||
curry0.ResultType.UsedVariables(addTo);
|
||||
break;
|
||||
}
|
||||
case Curry curry0:
|
||||
{
|
||||
curry0.ArgType.UsedVariables(addTo);
|
||||
curry0.ResultType.UsedVariables(addTo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -282,11 +313,13 @@ namespace AspectedRouting.Language.Typ
|
|||
|
||||
public static bool IsSuperSet(this Type t0, Type t1)
|
||||
{
|
||||
if (t0 is Var || t1 is Var) {
|
||||
if (t0 is Var || t1 is Var)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (t0) {
|
||||
switch (t0)
|
||||
{
|
||||
case StringType _ when t1 is BoolType _:
|
||||
return true;
|
||||
case DoubleType _ when t1 is PDoubleType _:
|
||||
|
@ -305,10 +338,11 @@ namespace AspectedRouting.Language.Typ
|
|||
case ListType l0 when t1 is ListType l1:
|
||||
return l0.InnerType.IsSuperSet(l1.InnerType);
|
||||
|
||||
case Curry c0 when t1 is Curry c1: {
|
||||
return c0.ResultType.IsSuperSet(c1.ResultType) &&
|
||||
c1.ArgType.IsSuperSet(c0.ArgType); // contravariance for arguments: reversed order!
|
||||
}
|
||||
case Curry c0 when t1 is Curry c1:
|
||||
{
|
||||
return c0.ResultType.IsSuperSet(c1.ResultType) &&
|
||||
c1.ArgType.IsSuperSet(c0.ArgType); // contravariance for arguments: reversed order!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -329,7 +363,8 @@ namespace AspectedRouting.Language.Typ
|
|||
|
||||
// The substitution table
|
||||
var subsTable = new Dictionary<string, Type>();
|
||||
foreach (var v in variablesToRename) {
|
||||
foreach (var v in variablesToRename)
|
||||
{
|
||||
var newValue = Var.Fresh(alreadyUsed);
|
||||
subsTable.Add(v, newValue);
|
||||
alreadyUsed.Add(newValue.Name);
|
||||
|
@ -342,7 +377,8 @@ namespace AspectedRouting.Language.Typ
|
|||
public static List<Type> Uncurry(this Type t)
|
||||
{
|
||||
var args = new List<Type>();
|
||||
while (t is Curry c) {
|
||||
while (t is Curry c)
|
||||
{
|
||||
args.Add(c.ArgType);
|
||||
t = c.ResultType;
|
||||
}
|
||||
|
@ -365,12 +401,16 @@ namespace AspectedRouting.Language.Typ
|
|||
{
|
||||
var widest = new HashSet<Type>();
|
||||
|
||||
foreach (var type0 in t0) {
|
||||
foreach (var type1 in t1) {
|
||||
foreach (var type0 in t0)
|
||||
{
|
||||
foreach (var type1 in t1)
|
||||
{
|
||||
var t = WidestCommonType(type0, type1);
|
||||
if (t != null) {
|
||||
if (t != null)
|
||||
{
|
||||
var (type, subsTable) = t.Value;
|
||||
if (subsTable != null) {
|
||||
if (subsTable != null)
|
||||
{
|
||||
type = type.Substitute(subsTable);
|
||||
}
|
||||
widest.Add(type);
|
||||
|
@ -397,44 +437,54 @@ namespace AspectedRouting.Language.Typ
|
|||
{
|
||||
// First things first: we try to unify
|
||||
|
||||
if (t0 is Curry c0 && t1 is Curry c1) {
|
||||
if (t0 is Curry c0 && t1 is Curry c1)
|
||||
{
|
||||
var arg = SmallestCommonType(c0.ArgType, c1.ArgType);
|
||||
var result = WidestCommonType(c0.ResultType, c1.ResultType);
|
||||
if (arg == null) {
|
||||
if (arg == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var (argT, subs0) = arg.Value;
|
||||
|
||||
if (result == null) {
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var (resultT, subs1) = result.Value;
|
||||
return (new Curry(argT, resultT), MergeDicts(subs0, subs1));
|
||||
}
|
||||
|
||||
|
||||
if (t0 is Var v) {
|
||||
if (t1 is Var vx) {
|
||||
if (v.Name == vx.Name) {
|
||||
|
||||
if (t0 is Var v)
|
||||
{
|
||||
if (t1 is Var vx)
|
||||
{
|
||||
if (v.Name == vx.Name)
|
||||
{
|
||||
return (t0, null);
|
||||
}
|
||||
}
|
||||
return (t1, new Dictionary<string, Type> {{v.Name, t1}});
|
||||
return (t1, new Dictionary<string, Type> { { v.Name, t1 } });
|
||||
}
|
||||
|
||||
if (t1 is Var v1) {
|
||||
return (t0, new Dictionary<string, Type> {{v1.Name, t1}});
|
||||
if (t1 is Var v1)
|
||||
{
|
||||
return (t0, new Dictionary<string, Type> { { v1.Name, t1 } });
|
||||
}
|
||||
if (t0 == t1 || t0.Equals(t1)) {
|
||||
if (t0 == t1 || t0.Equals(t1))
|
||||
{
|
||||
return (t0, null);
|
||||
}
|
||||
var t0IsSuperT1 = t0.IsSuperSet(t1);
|
||||
var t1IsSuperT0 = t1.IsSuperSet(t0);
|
||||
if (t0IsSuperT1 && !t1IsSuperT0) {
|
||||
if (t0IsSuperT1 && !t1IsSuperT0)
|
||||
{
|
||||
return (t0, null);
|
||||
}
|
||||
|
||||
if (t1IsSuperT0 && !t0IsSuperT1) {
|
||||
if (t1IsSuperT0 && !t0IsSuperT1)
|
||||
{
|
||||
return (t1, null);
|
||||
}
|
||||
|
||||
|
@ -444,20 +494,25 @@ namespace AspectedRouting.Language.Typ
|
|||
private static Dictionary<string, Type> MergeDicts(Dictionary<string, Type> subs0,
|
||||
Dictionary<string, Type> subs1)
|
||||
{
|
||||
if (subs0 == null && subs1 == null) {
|
||||
if (subs0 == null && subs1 == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var subsTable = new Dictionary<string, Type>();
|
||||
|
||||
void AddSubs(Dictionary<string, Type> dict)
|
||||
{
|
||||
if (dict == null) {
|
||||
if (dict == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var kv in dict) {
|
||||
if (subsTable.TryGetValue(kv.Key, out var t)) {
|
||||
foreach (var kv in dict)
|
||||
{
|
||||
if (subsTable.TryGetValue(kv.Key, out var t))
|
||||
{
|
||||
// We have seen this variable-type-name before... We should check if it matches
|
||||
if (t.Equals(kv.Value)) {
|
||||
if (t.Equals(kv.Value))
|
||||
{
|
||||
// Ok! No problem!
|
||||
// It is already added anyway, so we continue
|
||||
continue;
|
||||
|
@ -467,13 +522,15 @@ namespace AspectedRouting.Language.Typ
|
|||
throw new Exception(t + " != " + kv.Value);
|
||||
}
|
||||
|
||||
if (kv.Value is Var v) {
|
||||
if (v.Name == kv.Key) {
|
||||
if (kv.Value is Var v)
|
||||
{
|
||||
if (v.Name == kv.Key)
|
||||
{
|
||||
// Well, this is a useless substitution...
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
subsTable[kv.Key] = kv.Value;
|
||||
}
|
||||
}
|
||||
|
@ -481,10 +538,11 @@ namespace AspectedRouting.Language.Typ
|
|||
AddSubs(subs0);
|
||||
AddSubs(subs1);
|
||||
|
||||
if (!subsTable.Any()) {
|
||||
if (!subsTable.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return subsTable;
|
||||
}
|
||||
|
||||
|
@ -502,41 +560,49 @@ namespace AspectedRouting.Language.Typ
|
|||
/// <returns></returns>
|
||||
public static (Type, Dictionary<string, Type> substitutionTable)? SmallestCommonType(Type t0, Type t1)
|
||||
{
|
||||
if (t0 is Curry c0 && t1 is Curry c1) {
|
||||
if (t0 is Curry c0 && t1 is Curry c1)
|
||||
{
|
||||
var arg = WidestCommonType(c0.ArgType, c1.ArgType);
|
||||
var result = SmallestCommonType(c0.ResultType, c1.ResultType);
|
||||
if (arg == null) {
|
||||
if (arg == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var (argT, subs0) = arg.Value;
|
||||
|
||||
if (result == null) {
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var (resultT, subs1) = result.Value;
|
||||
return (new Curry(argT, resultT), MergeDicts(subs0, subs1));
|
||||
}
|
||||
|
||||
|
||||
if (t0 is Var v) {
|
||||
return (t1, new Dictionary<string, Type> {{v.Name, t1}});
|
||||
|
||||
|
||||
if (t0 is Var v)
|
||||
{
|
||||
return (t1, new Dictionary<string, Type> { { v.Name, t1 } });
|
||||
}
|
||||
|
||||
if (t1 is Var v1) {
|
||||
return (t0, new Dictionary<string, Type> {{v1.Name, t1}});
|
||||
if (t1 is Var v1)
|
||||
{
|
||||
return (t0, new Dictionary<string, Type> { { v1.Name, t1 } });
|
||||
}
|
||||
|
||||
if (t0 == t1 || t0.Equals(t1)) {
|
||||
if (t0 == t1 || t0.Equals(t1))
|
||||
{
|
||||
return (t0, null);
|
||||
}
|
||||
|
||||
|
||||
var t0IsSuperT1 = t0.IsSuperSet(t1);
|
||||
var t1IsSuperT0 = t1.IsSuperSet(t0);
|
||||
if (t0IsSuperT1 && !t1IsSuperT0) {
|
||||
if (t0IsSuperT1 && !t1IsSuperT0)
|
||||
{
|
||||
return (t0, null);
|
||||
}
|
||||
|
||||
if (t1IsSuperT0 && !t0IsSuperT1) {
|
||||
if (t1IsSuperT0 && !t0IsSuperT1)
|
||||
{
|
||||
return (t1, null);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,11 +19,13 @@ 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;
|
||||
}
|
||||
|
||||
|
@ -31,7 +33,8 @@ 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));
|
||||
}
|
||||
|
||||
|
@ -44,11 +47,13 @@ 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();
|
||||
}
|
||||
|
||||
|
@ -60,33 +65,40 @@ namespace AspectedRouting
|
|||
IEnumerable<string> jsonFiles, IReadOnlyCollection<string> testFiles, Context context, DateTime lastChange)
|
||||
{
|
||||
var result = new List<(ProfileMetaData profile, List<BehaviourTestSuite> profileTests)>();
|
||||
foreach (var jsonFile in jsonFiles) {
|
||||
try {
|
||||
foreach (var jsonFile in jsonFiles)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
@ -99,23 +111,28 @@ 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",
|
||||
|
@ -123,20 +140,25 @@ namespace AspectedRouting
|
|||
continue;
|
||||
}
|
||||
|
||||
if (read.Equals("clear")) {
|
||||
for (var i = 0; i < 80; i++) {
|
||||
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;
|
||||
}
|
||||
|
@ -145,11 +167,13 @@ namespace AspectedRouting
|
|||
beh = beh.Substring(beh.IndexOf(".") + 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));
|
||||
}
|
||||
|
@ -160,8 +184,10 @@ 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;
|
||||
}
|
||||
|
||||
|
@ -173,17 +199,19 @@ namespace AspectedRouting
|
|||
var v = strSplit[1].Trim();
|
||||
tags[k] = v;
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception)
|
||||
{
|
||||
Console.Error.WriteLine("Could not parse tag: "+str);
|
||||
Console.Error.WriteLine("Could not parse tag: " + str);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
var result = profile.Run(c, behaviour, tags);
|
||||
Console.WriteLine(result);
|
||||
}
|
||||
catch (Exception e) {
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
Console.WriteLine(e.Message);
|
||||
}
|
||||
|
@ -193,7 +221,8 @@ 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;
|
||||
}
|
||||
|
@ -203,10 +232,12 @@ namespace AspectedRouting
|
|||
|
||||
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()) {
|
||||
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()) {
|
||||
if (values.Any())
|
||||
{
|
||||
vs = string.Join(", ", values);
|
||||
}
|
||||
|
||||
|
@ -220,7 +251,7 @@ namespace AspectedRouting
|
|||
{
|
||||
var errMessage = MainWithError(args);
|
||||
if (errMessage == null) return 0;
|
||||
|
||||
|
||||
Console.WriteLine(errMessage);
|
||||
return 1;
|
||||
}
|
||||
|
@ -229,19 +260,23 @@ namespace AspectedRouting
|
|||
List<(AspectMetadata aspect, AspectTestSuite tests)> aspects, string outputDir,
|
||||
List<(ProfileMetaData profile, List<BehaviourTestSuite> profileTests)> profiles, DateTime lastChange)
|
||||
{
|
||||
if (!Directory.Exists($"{outputDir}/profile-documentation/")) {
|
||||
if (!Directory.Exists($"{outputDir}/profile-documentation/"))
|
||||
{
|
||||
Directory.CreateDirectory($"{outputDir}/profile-documentation/");
|
||||
}
|
||||
|
||||
if (!Directory.Exists($"{outputDir}/itinero1/")) {
|
||||
if (!Directory.Exists($"{outputDir}/itinero1/"))
|
||||
{
|
||||
Directory.CreateDirectory($"{outputDir}/itinero1/");
|
||||
}
|
||||
|
||||
if (!Directory.Exists($"{outputDir}/itinero2/")) {
|
||||
if (!Directory.Exists($"{outputDir}/itinero2/"))
|
||||
{
|
||||
Directory.CreateDirectory($"{outputDir}/itinero2/");
|
||||
}
|
||||
|
||||
foreach (var (profile, profileTests) in profiles) {
|
||||
foreach (var (profile, profileTests) in profiles)
|
||||
{
|
||||
PrintUsedTags(profile, context);
|
||||
|
||||
var aspectTests = aspects.Select(a => a.tests).ToList();
|
||||
|
@ -249,7 +284,7 @@ namespace AspectedRouting
|
|||
aspectTests,
|
||||
profileTests
|
||||
).ToLua();
|
||||
|
||||
|
||||
var itinero1ProfileFile = Path.Combine($"{outputDir}/itinero1/" + profile.Name + ".lua");
|
||||
File.WriteAllText(itinero1ProfileFile, luaProfile);
|
||||
Console.WriteLine($"Written {(new FileInfo(itinero1ProfileFile)).FullName}");
|
||||
|
@ -261,16 +296,19 @@ namespace AspectedRouting
|
|||
profileMd.AddTitle("Default parameters", 4);
|
||||
profileMd.Add("| name | value | ", "| ---- | ---- | ",
|
||||
string.Join("\n",
|
||||
profile.DefaultParameters.Select(delegate(KeyValuePair<string, IExpression> kv) {
|
||||
profile.DefaultParameters.Select(delegate (KeyValuePair<string, IExpression> kv)
|
||||
{
|
||||
var v = kv.Value.Evaluate(context);
|
||||
if (!(v is string || v is int || v is double)) {
|
||||
if (!(v is string || v is int || v is double))
|
||||
{
|
||||
v = "_special value_";
|
||||
}
|
||||
return $" | {kv.Key} | {v} |";
|
||||
}))
|
||||
}))
|
||||
);
|
||||
|
||||
foreach (var (behaviourName, vars) in profile.Behaviours) {
|
||||
foreach (var (behaviourName, vars) in profile.Behaviours)
|
||||
{
|
||||
var lua2behaviour = new LuaPrinter2(
|
||||
profile,
|
||||
behaviourName,
|
||||
|
@ -311,19 +349,21 @@ namespace AspectedRouting
|
|||
|
||||
public static string MainWithError(string[] args)
|
||||
{
|
||||
if (args.Length < 2) {
|
||||
if (args.Length < 2)
|
||||
{
|
||||
return "Usage: <directory where all aspects and profiles can be found> <outputdirectory>";
|
||||
}
|
||||
|
||||
var inputDir = args[0];
|
||||
var outputDir = args[1];
|
||||
|
||||
if (!Directory.Exists(outputDir)) {
|
||||
if (!Directory.Exists(outputDir))
|
||||
{
|
||||
Directory.CreateDirectory(outputDir);
|
||||
}
|
||||
|
||||
MdPrinter.GenerateHelpText(outputDir + "helpText.md");
|
||||
Console.WriteLine("Written helptext to "+outputDir);
|
||||
Console.WriteLine("Written helptext to " + outputDir);
|
||||
|
||||
|
||||
var files = Directory.EnumerateFiles(inputDir, "*.json", SearchOption.AllDirectories)
|
||||
|
@ -333,8 +373,10 @@ 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;
|
||||
}
|
||||
|
||||
|
@ -346,14 +388,17 @@ namespace AspectedRouting
|
|||
|
||||
var aspects = ParseAspects(files, tests, context);
|
||||
|
||||
foreach (var (aspect, _) in aspects) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -363,20 +408,24 @@ namespace AspectedRouting
|
|||
|
||||
// With everything parsed and typechecked, time for tests
|
||||
var testsOk = true;
|
||||
foreach (var (aspect, t) in aspects) {
|
||||
if (t == null) {
|
||||
foreach (var (aspect, t) in aspects)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
@ -384,12 +433,14 @@ namespace AspectedRouting
|
|||
}
|
||||
|
||||
|
||||
if (!args.Contains("--no-repl")) {
|
||||
if (!args.Contains("--no-repl"))
|
||||
{
|
||||
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;
|
||||
|
|
|
@ -17,7 +17,8 @@ namespace AspectedRouting.Tests
|
|||
AspectMetadata functionToApply,
|
||||
IEnumerable<(string expected, Dictionary<string, string> tags)> tests)
|
||||
{
|
||||
if (functionToApply == null) {
|
||||
if (functionToApply == null)
|
||||
{
|
||||
throw new NullReferenceException("functionToApply is null");
|
||||
}
|
||||
|
||||
|
@ -33,8 +34,10 @@ namespace AspectedRouting.Tests
|
|||
|
||||
var tests = new List<(string, Dictionary<string, string>)>();
|
||||
|
||||
foreach (var test in all.GetRange(1, all.Count - 1)) {
|
||||
if (string.IsNullOrEmpty(test.Trim())) {
|
||||
foreach (var test in all.GetRange(1, all.Count - 1))
|
||||
{
|
||||
if (string.IsNullOrEmpty(test.Trim()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -42,9 +45,11 @@ namespace AspectedRouting.Tests
|
|||
var expected = testData[0];
|
||||
var vals = testData.GetRange(1, testData.Count - 1);
|
||||
var tags = new Dictionary<string, string>();
|
||||
for (var i = 0; i < keys.Count; i++) {
|
||||
if (i < vals.Count && !string.IsNullOrEmpty(vals[i])) {
|
||||
tags[keys[i]] = vals[i].Trim(new []{'"'}).Replace("\"","\\\"");
|
||||
for (var i = 0; i < keys.Count; i++)
|
||||
{
|
||||
if (i < vals.Count && !string.IsNullOrEmpty(vals[i]))
|
||||
{
|
||||
tags[keys[i]] = vals[i].Trim(new[] { '"' }).Replace("\"", "\\\"");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,8 +66,10 @@ namespace AspectedRouting.Tests
|
|||
public AspectTestSuite WithoutRelationTests()
|
||||
{
|
||||
var newTests = new List<(string expected, Dictionary<string, string> tags)>();
|
||||
foreach (var (expected, tags) in Tests) {
|
||||
if (tags.Keys.Any(key => key.StartsWith("_relation") && key.Split(":").Length ==3)) {
|
||||
foreach (var (expected, tags) in Tests)
|
||||
{
|
||||
if (tags.Keys.Any(key => key.StartsWith("_relation") && key.Split(":").Length == 3))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
newTests.Add((expected, tags));
|
||||
|
@ -76,31 +83,38 @@ namespace AspectedRouting.Tests
|
|||
{
|
||||
var failed = false;
|
||||
var testCase = 0;
|
||||
foreach (var test in Tests) {
|
||||
foreach (var test in Tests)
|
||||
{
|
||||
testCase++;
|
||||
var context = new Context().WithAspectName("unittest");
|
||||
foreach (var (key, value) in test.tags) {
|
||||
if (key.StartsWith("#")) {
|
||||
foreach (var (key, value) in test.tags)
|
||||
{
|
||||
if (key.StartsWith("#"))
|
||||
{
|
||||
context.AddParameter(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
var actual = FunctionToApply.Evaluate(context, new Constant(test.tags));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(test.expected)) {
|
||||
if (string.IsNullOrWhiteSpace(test.expected))
|
||||
{
|
||||
failed = true;
|
||||
Console.WriteLine(
|
||||
$"[{FunctionToApply.Name}] Line {testCase + 1} failed:\n The expected value is not defined or only whitespace. Do you want null? Write null in your test as expected value\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (test.expected == "null" && actual == null) {
|
||||
if (test.expected == "null" && actual == null)
|
||||
{
|
||||
// Test ok
|
||||
continue;
|
||||
}
|
||||
|
||||
if (actual == null) {
|
||||
if (actual == null)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[{FunctionToApply.Name}] Line {testCase + 1} failed:\n Expected: {test.expected}\n actual value is not defined (null)\n tags: {test.tags.Pretty()}\n");
|
||||
failed = true;
|
||||
|
@ -111,13 +125,15 @@ namespace AspectedRouting.Tests
|
|||
var doesMatch = (actual is double d && Math.Abs(double.Parse(test.expected, NumberStyles.Any, CultureInfo.InvariantCulture) - d) < 0.0001)
|
||||
|| actual.ToString().Equals(test.expected);
|
||||
|
||||
if (!doesMatch) {
|
||||
if (!doesMatch)
|
||||
{
|
||||
failed = true;
|
||||
Console.WriteLine(
|
||||
$"[{FunctionToApply.Name}] Line {testCase + 1} failed:\n Expected: {test.expected}\n actual: {actual}\n tags: {test.tags.Pretty()}\n");
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[{FunctionToApply.Name}] Line {testCase + 1} ERROR:\n Expected: {test.expected}\n error message: {e.Message}\n tags: {test.tags.Pretty()}\n");
|
||||
Console.WriteLine(e);
|
||||
|
|
|
@ -20,32 +20,35 @@ namespace AspectedRouting.Tests
|
|||
|
||||
private static string str(string s)
|
||||
{
|
||||
if (s == null) {
|
||||
if (s == null)
|
||||
{
|
||||
return "<null>";
|
||||
}
|
||||
|
||||
if (s == "") {
|
||||
if (s == "")
|
||||
{
|
||||
return "<empty string>";
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
return string.Join("\n ",
|
||||
" access "+str(Access),
|
||||
"oneway "+str(Oneway),
|
||||
"speed "+Speed,
|
||||
"priority "+Priority,
|
||||
"because \n "+str(PriorityExplanation)
|
||||
" access " + str(Access),
|
||||
"oneway " + str(Oneway),
|
||||
"speed " + Speed,
|
||||
"priority " + Priority,
|
||||
"because \n " + str(PriorityExplanation)
|
||||
);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (!(obj is ProfileResult other)) {
|
||||
if (!(obj is ProfileResult other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return other.Access == this.Access && other.Oneway == this.Oneway && other.Priority == this.Priority && other.Speed == this.Speed;
|
||||
|
|
|
@ -98,7 +98,7 @@ namespace AspectedRouting.Tests
|
|||
{
|
||||
if (i < vals.Count && !string.IsNullOrEmpty(vals[i]))
|
||||
{
|
||||
tags[keys[i]] = vals[i].Trim(new []{'\"'}).Replace("\"","\\\"");
|
||||
tags[keys[i]] = vals[i].Trim(new[] { '\"' }).Replace("\"", "\\\"");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,10 +140,10 @@ namespace AspectedRouting.Tests
|
|||
|
||||
public bool RunTest(Context c, int i, ProfileResult expected, Dictionary<string, string> tags)
|
||||
{
|
||||
void Err(string message, object exp, object act)
|
||||
void Err(string message, object exp, object act, string extra = "")
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[{Profile.Name}.{BehaviourName}]: Test on line {i + 1} failed: {message}; expected {exp} but got {act}\n {{{tags.Pretty()}}}");
|
||||
$"[{Profile.Name}.{BehaviourName}]: Test on line {i + 1} failed: {message}: expected {exp} but got {act};\n{extra}\n {{{tags.Pretty()}}}");
|
||||
}
|
||||
|
||||
var actual = Profile.Run(c, BehaviourName, tags);
|
||||
|
@ -178,9 +178,10 @@ namespace AspectedRouting.Tests
|
|||
|
||||
if (Math.Abs(actual.Priority - expected.Priority) > 0.0001)
|
||||
{
|
||||
Err($"weight incorrect. Calculation is {string.Join(" + ", actual.PriorityExplanation)}",
|
||||
Err($"weight incorrect",
|
||||
expected.Priority,
|
||||
actual.Priority);
|
||||
actual.Priority,
|
||||
$"Calculation is \n{actual.PriorityExplanation}");
|
||||
success = false;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue