namespace Jellyfin.Plugin.SmartPlaylist.Lisp { interface IAddable where T : IAddable { static abstract T operator +(T left, T right); } interface ISubtractable where T : ISubtractable { static abstract T operator -(T left, T right); } interface IMultiplicatable where T : IMultiplicatable { static abstract T operator *(T left, T right); } interface IDivisible where T : IDivisible { static abstract T operator /(T left, T right); static abstract T operator %(T left, T right); } interface ISortable where T : ISortable { static abstract E operator >(T left, T right); static abstract E operator <(T left, T right); static abstract E operator >=(T left, T right); static abstract E operator <=(T left, T right); } interface IComparable where T : IComparable { static abstract E operator ==(T left, T right); static abstract E operator !=(T left, T right); E Equals(T other); } interface IInner { public object Inner(); } public abstract class Expression: IComparable { public override abstract string? ToString(); public abstract override int GetHashCode(); public abstract bool Equals(Expression other); public override bool Equals(object? other) { if (other is Expression other_e) { return Equals(other_e); } return false; } public static bool operator ==(Expression left, Expression right) { return left.Equals(right); } public static bool operator !=(Expression left, Expression right) { return !left.Equals(right); } } public abstract class Atom : Expression {} public class Scalar : Atom, IInner where V : notnull { protected V _value; public Scalar(V value) { _value = value; } public override int GetHashCode() { return 17 * 23 + _value.GetHashCode(); } public override bool Equals(Expression other) { if (other is Scalar other_scalar) { return _value.Equals(other_scalar._value); } return false; } public override string? ToString() { return _value.ToString(); } public V Value() { return _value; } public object Inner() { return _value; } } public class Symbol : Atom { private string _name; public Symbol(string name) { _name = name; } public override int GetHashCode() { return 17 * 23 + _name.GetHashCode(); } public override bool Equals(Expression other) { if (other is Symbol other_symbol) { return _name.Equals(other_symbol._name); } return false; } public override string? ToString() { return _name.ToString(); } public string Name() { return _name; } } public class Integer : Scalar, IAddable, ISubtractable, IMultiplicatable, IDivisible, ISortable { public Integer(int value) : base(value) {} public static Integer operator +(Integer a, Integer b) { return new Integer(a._value + b._value); } public static Integer operator -(Integer a, Integer b) { return new Integer(a._value - b._value); } public static Integer operator *(Integer a, Integer b) { return new Integer(a._value * b._value); } public static Integer operator /(Integer a, Integer b) { return new Integer(a._value / b._value); } public static Integer operator %(Integer a, Integer b) { return new Integer(a._value % b._value); } public static Boolean operator >(Integer a, Integer b) { return (a._value > b._value) ? Boolean.TRUE : Boolean.FALSE; } public static Boolean operator <(Integer a, Integer b) { return (a._value < b._value) ? Boolean.TRUE : Boolean.FALSE; } public static Boolean operator >=(Integer a, Integer b) { return (a._value >= b._value) ? Boolean.TRUE : Boolean.FALSE; } public static Boolean operator <=(Integer a, Integer b) { return (a._value <= b._value) ? Boolean.TRUE : Boolean.FALSE; } public override int GetHashCode() { return base.GetHashCode(); } public override bool Equals(object? other) { return base.Equals(other); } public static Boolean operator ==(Integer a, Integer b) { return (a._value == b._value) ? Boolean.TRUE : Boolean.FALSE; } public static Boolean operator !=(Integer a, Integer b) { return (a._value != b._value) ? Boolean.TRUE : Boolean.FALSE; } } public class Boolean: Scalar { public static Boolean TRUE = new Boolean(true); public static Boolean FALSE = new Boolean(false); private Boolean(bool value) : base(value) {} public override string? ToString() { if (_value) { return "t"; } return "nil"; } public IList ToList() { if (_value) { throw new ApplicationException("Cannot use t as list"); } return new List(); } } public class String: Scalar, ISortable { public String(string value) : base(value) {} public override string? ToString() { return $"\"{base.ToString()}\""; } public static Boolean operator <(String a, String b) { return (a.Value().CompareTo(b.Value()) < 0) ? Boolean.TRUE : Boolean.FALSE; } public static Boolean operator >(String a, String b) { return b < a; } public static Boolean operator <=(String a, String b) { return (a.Value().CompareTo(b.Value()) <= 0) ? Boolean.TRUE : Boolean.FALSE; } public static Boolean operator >=(String a, String b) { return b <= a; } public override int GetHashCode() { return base.GetHashCode(); } public override bool Equals(object? other) { return base.Equals(other); } public static Boolean operator ==(String a, String b) { return (a._value == b._value) ? Boolean.TRUE : Boolean.FALSE; } public static Boolean operator !=(String a, String b) { return (a._value != b._value) ? Boolean.TRUE : Boolean.FALSE; } } public class Cons: Expression { public Expression Item1; public Expression Item2; public Cons(Expression item1, Expression item2) { Item1 = item1; Item2 = item2; } public static Expression FromList(IEnumerable expressions) { var e = expressions.ToList(); if (e.Count == 0) { return Boolean.FALSE; } var item1 = expressions.First(); if (e.Count == 1) { return new Cons(item1, Boolean.FALSE); } var item2 = expressions.Skip(1).ToList(); return new Cons(item1, FromList(item2)); } public IEnumerable ToList() { var l = new List(); l.Add(Item1); if (Item2 == Boolean.FALSE) { return l; } if (Item2 is Cons item2_cons) { l.AddRange(item2_cons.ToList()); return l; } l.Add(Item2); return l; } public override int GetHashCode() { var hash = 17; hash *= 23; hash += Item1.GetHashCode(); hash *= 23; hash += Item2.GetHashCode(); return hash; } public override bool Equals(Expression other) { if (other is Cons other_list) { return Item1.Equals(other_list.Item1) && Item2.Equals(other_list.Item2); } return false; } private string? ToStringSimple() { if (Item2.Equals(Boolean.FALSE)) { return Item1.ToString(); } if (Item2 is Cons item2_cons) { return $"{Item1} {item2_cons.ToStringSimple()}"; } return $"{Item1} . {Item2}"; } public override string? ToString() { return $"({ToStringSimple()})"; } } public class Object : Scalar { public Object(object value) : base(value) { } public static Expression FromBase(object? o) { if (o == null) { return Boolean.FALSE; } switch (o) { case bool b: return b ? Boolean.TRUE : Boolean.FALSE; case int i: return new Integer(i); case string s: return new String(s); case Expression e: return e; case IEnumerable e: return Cons.FromList(e.Select(x => FromBase(x))); default: return new Object(o); } } } public class Procedure : Expression { private IEnumerable _parameters; private Expression _body; private bool _eval_args; public Procedure(IEnumerable parameters, Expression body, bool eval_args) { _parameters = parameters; _body = body; _eval_args = eval_args; } public override int GetHashCode() { int hash = 17; hash *= 23; hash += _parameters.GetHashCode(); hash *= 23; hash += _body.GetHashCode(); return hash; } public override bool Equals(Expression? other) { if (other is Procedure other_p) { return _parameters == other_p._parameters && _body == other_p._body; } return false; } public override string ToString() { var star = _eval_args ? "" : "*"; return $"(lambda{star} {Cons.FromList(_parameters)} {_body})"; } private Expression __eval(Executor e, Expression exp) { if (!_eval_args) return exp; return e.eval(exp); } private Expression _eval(Executor e, Expression exp) { var r = __eval(e, exp); //Console.WriteLine($"{exp} = {r}"); return r; } public Expression Call(Executor e, IList args) { Executor new_e = new Executor(new SubEnvironment(e.environment), e.builtins, e.builtinsLater); var _params = _parameters.Select(x => x.Name()).ToArray(); var idx_rest = -1; IList<(string, Expression)> name_args = new List<(string, Expression)>(); for (var i = 0; i < _parameters.Count(); i++) { var name = _params[i]; if (name.Equals(".")) { idx_rest = i + 1; break; } name_args.Add((name, _eval(e, args[i]))); } if (idx_rest > 0) { name_args.Add((_params[idx_rest], Cons.FromList(args.Skip(idx_rest - 1).Select(x => _eval(e, x))))); } foreach (var na in name_args) { new_e.environment.Set(na.Item1, na.Item2); } var r = new_e.eval(_body); return r; } } }