AspectedRouting/AspectedRouting/Language/Functions/Constant.cs
2020-06-17 17:23:48 +02:00

307 lines
No EOL
8.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using AspectedRouting.Language.Typ;
using Type = AspectedRouting.Language.Typ.Type;
namespace AspectedRouting.Language.Functions
{
public class Constant : IExpression
{
public IEnumerable<Type> Types { get; }
private readonly object _o;
public Constant(IEnumerable<Type> types, object o)
{
Types = types.ToList();
if (o is IEnumerable<IExpression> enumerable)
{
o = enumerable.ToList();
if (enumerable.Any(x => x == null))
{
throw new NullReferenceException("Some subexpression is null");
}
}
_o = o;
}
public Constant(Type t, object o)
{
Types = new List<Type> {t};
if (o is IEnumerable<IExpression> enumerable)
{
o = enumerable.ToList();
if (enumerable.Any(x => x == null))
{
throw new NullReferenceException("Some subexpression is null");
}
}
_o = o;
}
public Constant(IExpression[] exprs)
{
var tps = exprs
.SpecializeToCommonTypes(out var specializedVersions)
.Select(t => new ListType(t));
if(specializedVersions.Any(x => x == null))
{
throw new Exception("Specializing to common types failed for "+string.Join("," ,exprs.Select(e => e.ToString())));
}
Types = tps.ToList();
_o = specializedVersions;
}
public Constant(List<IExpression> exprs)
{
try
{
Types = exprs
.SpecializeToCommonTypes(out var specializedVersions).Select(t => new ListType(t)).ToList();
if (specializedVersions.Any(x => x == null))
{
throw new NullReferenceException("Some subexpression is null");
}
_o = specializedVersions.ToList();
}
catch (Exception e)
{
throw new Exception($"While creating a list with members " +
string.Join(", ", exprs.Select(x => x.Optimize())) +
$" {e.Message}", e);
}
}
public Constant(string s)
{
Types = new List<Type> {Typs.String};
_o = s;
}
public Constant(double d)
{
if (d >= 0)
{
Types = new[] {Typs.Double, Typs.PDouble};
}
else
{
Types = new[] {Typs.Double};
}
_o = d;
}
public Constant(int i)
{
if (i >= 0)
{
Types = new[] {Typs.Double, Typs.Nat, Typs.Nat, Typs.PDouble};
}
else
{
Types = new[] {Typs.Double, Typs.Int};
}
_o = i;
}
public Constant(Dictionary<string, string> tags)
{
Types = new[] {Typs.Tags};
_o = tags;
}
public object Evaluate(Context c, params IExpression[] args)
{
if (_o is IExpression e)
{
return e.Evaluate(c).Pretty();
}
if (Types.Count() > 1) return _o;
var t = Types.First();
switch (t)
{
case DoubleType _:
case PDoubleType _:
if (_o is int i)
{
return
(double) i; // I know, it seems absurd having to write this as it is nearly the same as the return beneath, but it _is_ needed
}
return (double) _o;
case IntType _:
case NatType _:
return (int) _o;
}
return _o;
}
public void EvaluateAll(Context c, HashSet<IExpression> addTo)
{
addTo.Add(this);
}
public IExpression Specialize(IEnumerable<Type> allowedTypesEnumerable)
{
var allowedTypes = allowedTypesEnumerable.ToList();
var unified = Types.SpecializeTo(allowedTypes);
if (unified == null)
{
return null;
}
var newO = _o;
if (_o is IExpression e)
{
newO = e.Specialize(allowedTypes);
}
if (_o is IEnumerable<IExpression> es)
{
var innerTypes = new List<Type>();
foreach (var allowedType in allowedTypes)
{
if (allowedType is ListType listType)
{
innerTypes.Add(listType.InnerType);
}
}
var specializedExpressions = new List<IExpression>();
foreach (var expr in es)
{
if (expr == null)
{
throw new NullReferenceException("Subexpression is null");
}
var specialized = expr.Specialize(innerTypes);
if (specialized == null)
{
// If a subexpression can not be specialized, this list cannot be specialized
return null;
}
specializedExpressions.Add(specialized);
}
newO = specializedExpressions;
}
return new Constant(unified, newO);
}
public IExpression Optimize()
{
if (_o is IEnumerable<IExpression> exprs)
{
// This is a list
var optExprs = new List<IExpression>();
foreach (var expression in exprs)
{
var exprOpt = expression.Optimize();
if (exprOpt == null || exprOpt.Types.Count() == 0)
{
throw new ArgumentException("Non-optimizable expression:" + expression);
}
optExprs.Add(exprOpt);
}
return new Constant(optExprs.ToList());
}
if (_o is IExpression expr)
{
// This is a list
return new Constant(expr.Types, expr.Optimize());
}
return this;
}
public IExpression OptimizeWithArgument(IExpression argument)
{
return this.Apply(argument);
}
public void Visit(Func<IExpression, bool> f)
{
if (_o is IExpression e)
{
e.Visit(f);
}
if (_o is IEnumerable<IExpression> es)
{
foreach (var x in es)
{
x.Visit(f);
}
}
f(this);
}
public override string ToString()
{
return _o.Pretty();
}
}
public static class ObjectExtensions
{
public static string Pretty(this object o, Context context = null)
{
switch (o)
{
case Dictionary<string, string> d:
var txt = "";
foreach (var (k, v) in d)
{
txt += $"{k}={v};";
}
return $"{{{txt}}}";
case Dictionary<string, List<string>> d:
var t = "";
foreach (var (k, v) in d)
{
var values = v.Pretty();
if (!v.Any())
{
values = "*";
}
t += k + "=" + values + "\n";
}
return t;
case Constant c:
return "<" + c.Evaluate(context).Pretty() + ">";
case List<object> ls:
return "[" + string.Join(", ", ls.Select(obj => obj.Pretty(context))) + "]";
case object[] arr:
return arr.ToList().Pretty();
case double[] arr:
return arr.Select(d => (object) d).ToList().Pretty();
case string s:
return "\"" + s.Replace("\"", "\\\"") + "\"";
case IEnumerable<object> ls:
return "[" + string.Join(", ", ls.Select(obj => obj.Pretty(context))) + "]";
}
return o.ToString();
}
}
}