344 lines
12 KiB
C#
344 lines
12 KiB
C#
namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
|
|
interface IAddable<T> where T : IAddable<T> {
|
|
static abstract T operator +(T left, T right);
|
|
}
|
|
|
|
interface ISubtractable<T> where T : ISubtractable<T> {
|
|
static abstract T operator -(T left, T right);
|
|
}
|
|
|
|
interface IMultiplicatable<T> where T : IMultiplicatable<T> {
|
|
static abstract T operator *(T left, T right);
|
|
}
|
|
|
|
interface IDivisible<T> where T : IDivisible<T> {
|
|
static abstract T operator /(T left, T right);
|
|
static abstract T operator %(T left, T right);
|
|
}
|
|
|
|
interface ISortable<T, E> where T : ISortable<T, E> {
|
|
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<T, E> where T : IComparable<T, E> {
|
|
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<Expression, bool> {
|
|
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<V> : 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<V> 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<int>, IAddable<Integer>, ISubtractable<Integer>, IMultiplicatable<Integer>, IDivisible<Integer>, ISortable<Integer, Boolean> {
|
|
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<bool> {
|
|
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<Expression> ToList() {
|
|
if (_value) {
|
|
throw new ApplicationException("Cannot use t as list");
|
|
}
|
|
return new List<Expression>();
|
|
}
|
|
}
|
|
public class String: Scalar<string>, ISortable<String, Boolean> {
|
|
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<Expression> 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<Expression> ToList() {
|
|
var l = new List<Expression>();
|
|
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() {
|
|
if (Item1 is Symbol SymbolItem1
|
|
&& SymbolItem1.Name() == "quote"
|
|
&& Item2 is Cons ConsItem2
|
|
&& ConsItem2.Item2.Equals(Boolean.FALSE)
|
|
) {
|
|
return $"'{ConsItem2.Item1}";
|
|
}
|
|
return $"({ToStringSimple()})";
|
|
}
|
|
}
|
|
|
|
public class Object : Scalar<object> {
|
|
internal 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<object> e:
|
|
return Cons.FromList(e.Select(x => FromBase(x)));
|
|
default:
|
|
return new Object(o);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class Procedure : Expression {
|
|
private IEnumerable<Symbol> _parameters;
|
|
private Expression _body;
|
|
private bool _eval_args;
|
|
public Procedure(IEnumerable<Symbol> 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<Expression> 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;
|
|
}
|
|
}
|
|
|
|
}
|