dotnet format

This commit is contained in:
Pieter Vander Vennet 2022-10-17 16:08:20 +02:00
parent d304a80e7b
commit 43590ad69f
70 changed files with 1040 additions and 675 deletions

View file

@ -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}\")";
}
}

View file

@ -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";
}
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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 =>

View file

@ -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))

View file

@ -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";
}
}
}

View file

@ -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());
}
}

View file

@ -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)));

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -17,7 +17,7 @@ namespace AspectedRouting.IO.LuaSnippets
/// </summary>
public readonly Function ImplementsFunction;
protected LuaSnippet(Function implements)
protected LuaSnippet(Function implements)
{
ImplementsFunction = implements;
}

View file

@ -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");
}

View file

@ -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);

View file

@ -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();
}
}

View file

@ -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));

View file

@ -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";
}
}

View file

@ -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)

View file

@ -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} ---");

View file

@ -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>(),

View file

@ -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();
}

View file

@ -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(

View file

@ -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;
}

View file

@ -33,7 +33,7 @@ namespace AspectedRouting.Language
{
Parameters.Add(name, new Constant(value));
}
public void AddParameter(string name, IExpression value)
{
Parameters.Add(name, value);

View file

@ -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;
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}

View file

@ -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)

View file

@ -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")))

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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)

View file

@ -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++)

View file

@ -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)

View file

@ -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>();

View file

@ -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);

View file

@ -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)));
}

View file

@ -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");
}

View file

@ -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[]
{

View file

@ -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;

View file

@ -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;
}

View file

@ -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());
}
}
}

View file

@ -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)

View file

@ -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)))
{

View file

@ -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;
}

View file

@ -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";

View file

@ -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>();

View file

@ -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;
}

View file

@ -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)

View file

@ -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;
}

View file

@ -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();
}
}

View file

@ -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;

View file

@ -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";
}

View file

@ -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)))

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}
}

View file

@ -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>();

View file

@ -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;

View file

@ -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) })
{
}

View file

@ -14,8 +14,8 @@ namespace AspectedRouting.Language.Typ
ArgType = argType;
ResultType = resultType;
}
private static string ToString(Type argType, Type resultType)
{
var arg = argType.ToString();

View file

@ -10,7 +10,7 @@ namespace AspectedRouting.Language.Typ
Typs.AddBuiltin(this);
}
}
public string Name { get; }

View file

@ -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);
}

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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;
}