2024-06-27 01:47:44 +02:00
|
|
|
using System.Diagnostics;
|
|
|
|
|
|
|
|
namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2024-06-29 18:29:40 +02:00
|
|
|
interface ISortable<T, E> where T : ISortable<T, E> {
|
2024-06-27 01:47:44 +02:00
|
|
|
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);
|
2024-06-29 18:29:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
interface IComparable<T, E> where T : IComparable<T, E> {
|
2024-06-27 01:47:44 +02:00
|
|
|
static abstract E operator ==(T left, T right);
|
|
|
|
static abstract E operator !=(T left, T right);
|
2024-10-24 23:53:21 +02:00
|
|
|
E Equals(T other);
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
|
|
|
|
2024-10-25 02:18:13 +02:00
|
|
|
public abstract class Expression: IComparable<Expression, bool> {
|
|
|
|
public override abstract string ToString();
|
2024-10-24 23:53:21 +02:00
|
|
|
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);
|
|
|
|
}
|
2024-10-25 02:18:13 +02:00
|
|
|
public abstract object Inner();
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
|
|
|
public abstract class Atom : Expression {}
|
|
|
|
public class Symbol : Atom {
|
|
|
|
private readonly string _name;
|
|
|
|
public Symbol(string name) {
|
|
|
|
_name = name;
|
|
|
|
}
|
|
|
|
public string name { get => _name; }
|
2024-10-24 23:53:21 +02:00
|
|
|
public override int GetHashCode() {
|
|
|
|
int hash = 17;
|
|
|
|
hash *= 23;
|
|
|
|
hash += _name.GetHashCode();
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
public override bool Equals(Expression? other) {
|
|
|
|
if (other is Symbol other_s) {
|
|
|
|
return _name == other_s._name;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2024-10-25 02:18:13 +02:00
|
|
|
public override string ToString() {
|
|
|
|
return _name;
|
|
|
|
}
|
|
|
|
public override object Inner() {
|
2024-06-27 01:47:44 +02:00
|
|
|
return _name;
|
|
|
|
}
|
|
|
|
}
|
2024-10-24 23:53:21 +02:00
|
|
|
|
|
|
|
public class Boolean : Atom {
|
2024-06-27 01:47:44 +02:00
|
|
|
private readonly bool _value;
|
|
|
|
public Boolean(bool value) {
|
|
|
|
_value = value;
|
|
|
|
}
|
|
|
|
public bool value { get => _value; }
|
2024-10-24 23:53:21 +02:00
|
|
|
public override int GetHashCode() {
|
|
|
|
int hash = 17;
|
|
|
|
hash *= 23;
|
|
|
|
hash += _value.GetHashCode();
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
public override bool Equals(Expression other) {
|
|
|
|
if (other is Boolean other_b) {
|
|
|
|
return _value == other_b.value;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2024-10-25 02:18:13 +02:00
|
|
|
public override string ToString() {
|
2024-06-27 01:47:44 +02:00
|
|
|
return _value? "t" : "nil";
|
|
|
|
}
|
2024-10-25 02:18:13 +02:00
|
|
|
public override object Inner() {
|
|
|
|
return _value;
|
|
|
|
}
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-10-24 23:53:21 +02:00
|
|
|
|
|
|
|
public class Integer : Atom, IAddable<Integer>, ISubtractable<Integer>, IMultiplicatable<Integer>, IDivisible<Integer>, ISortable<Integer, Boolean> {
|
2024-06-27 01:47:44 +02:00
|
|
|
private readonly int _value;
|
|
|
|
public Integer(int value) {
|
|
|
|
_value = value;
|
|
|
|
}
|
|
|
|
public int value { get => _value; }
|
2024-10-24 23:53:21 +02:00
|
|
|
public override int GetHashCode() {
|
|
|
|
int hash = 17;
|
|
|
|
hash *= 23;
|
|
|
|
hash += _value.GetHashCode();
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
public override bool Equals(Expression other) {
|
|
|
|
if (other is Integer other_i) {
|
|
|
|
return _value == other_i._value;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2024-10-25 02:18:13 +02:00
|
|
|
public override string ToString() {
|
2024-10-24 23:53:21 +02:00
|
|
|
return _value.ToString();
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
|
|
|
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 new Boolean(a.value > b.value);
|
|
|
|
}
|
|
|
|
public static Boolean operator <(Integer a, Integer b) {
|
|
|
|
return new Boolean(a.value < b.value);
|
|
|
|
}
|
|
|
|
public static Boolean operator >=(Integer a, Integer b) {
|
|
|
|
return new Boolean(a.value >= b.value);
|
|
|
|
}
|
|
|
|
public static Boolean operator <=(Integer a, Integer b) {
|
|
|
|
return new Boolean(a.value <= b.value);
|
|
|
|
}
|
|
|
|
public static Boolean operator ==(Integer a, Integer b) {
|
|
|
|
return new Boolean(a.value == b.value);
|
|
|
|
}
|
|
|
|
public static Boolean operator !=(Integer a, Integer b) {
|
|
|
|
return new Boolean(a.value != b.value);
|
|
|
|
}
|
2024-10-25 02:18:13 +02:00
|
|
|
public override object Inner() {
|
|
|
|
return _value;
|
|
|
|
}
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-10-24 23:53:21 +02:00
|
|
|
|
2024-06-27 01:47:44 +02:00
|
|
|
public class String : Atom, IAddable<String> {
|
|
|
|
private readonly string _value;
|
|
|
|
public String(string value) {
|
|
|
|
_value = value;
|
|
|
|
}
|
|
|
|
public string value { get => _value; }
|
2024-10-24 23:53:21 +02:00
|
|
|
public override int GetHashCode() {
|
|
|
|
int hash = 17;
|
|
|
|
hash *= 23;
|
|
|
|
hash += _value.GetHashCode();
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
public override bool Equals(Expression other) {
|
|
|
|
if (other is String other_s) {
|
|
|
|
return _value == other_s._value;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2024-10-25 02:18:13 +02:00
|
|
|
public override string ToString() {
|
2024-06-27 01:47:44 +02:00
|
|
|
return "\"" + _value + "\"";
|
|
|
|
}
|
|
|
|
public static String operator +(String a, String b) {
|
|
|
|
return new String (a.value + b.value);
|
|
|
|
}
|
2024-10-25 02:18:13 +02:00
|
|
|
public override object Inner() {
|
|
|
|
return _value;
|
|
|
|
}
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-10-24 23:53:21 +02:00
|
|
|
|
2024-06-29 18:29:40 +02:00
|
|
|
public class Object : Atom {
|
|
|
|
private readonly object _value;
|
|
|
|
public Object(object value) {
|
|
|
|
_value = value;
|
|
|
|
}
|
|
|
|
public object value { get => _value; }
|
2024-10-24 23:53:21 +02:00
|
|
|
public override int GetHashCode() {
|
|
|
|
int hash = 17;
|
|
|
|
hash *= 23;
|
|
|
|
hash += _value.GetHashCode();
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
public override bool Equals(Expression other) {
|
|
|
|
if (other is Object other_o) {
|
|
|
|
return _value == other_o._value;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2024-10-25 02:18:13 +02:00
|
|
|
public override string ToString() {
|
2024-06-29 18:29:40 +02:00
|
|
|
return _value.ToString();
|
|
|
|
}
|
2024-10-30 21:15:16 +01:00
|
|
|
public static Expression FromBase(object? o) {
|
|
|
|
if (o == null) {
|
|
|
|
return new Boolean(false);
|
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
switch (o) {
|
|
|
|
case bool b:
|
|
|
|
return new Boolean(b);
|
|
|
|
case int i:
|
|
|
|
return new Integer(i);
|
|
|
|
case string s:
|
|
|
|
return new String(s);
|
2024-10-27 00:51:54 +02:00
|
|
|
case IEnumerable<object> e:
|
|
|
|
return new List(e.Select(x => Object.FromBase(x)).ToList());
|
2024-06-29 18:29:40 +02:00
|
|
|
default:
|
|
|
|
return new Object(o);
|
|
|
|
}
|
|
|
|
}
|
2024-10-25 02:18:13 +02:00
|
|
|
public override object Inner() {
|
|
|
|
return _value;
|
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
}
|
|
|
|
|
2024-06-27 01:47:44 +02:00
|
|
|
public class List : Expression {
|
|
|
|
private IList<Expression> _expressions;
|
|
|
|
public List(IList<Expression> expressions) {
|
|
|
|
_expressions = expressions;
|
|
|
|
}
|
|
|
|
public IList<Expression> expressions { get => _expressions; }
|
2024-10-24 23:53:21 +02:00
|
|
|
public override int GetHashCode() {
|
|
|
|
int hash = 17;
|
|
|
|
foreach (Expression i in _expressions) {
|
|
|
|
hash *= 23;
|
|
|
|
hash += i.GetHashCode();
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-10-24 23:53:21 +02:00
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
public override bool Equals(Expression other) {
|
|
|
|
if (other is List other_l) {
|
2024-10-27 00:28:08 +02:00
|
|
|
return _expressions.SequenceEqual(other_l._expressions);
|
2024-10-24 23:53:21 +02:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2024-10-25 02:18:13 +02:00
|
|
|
public override string ToString() {
|
|
|
|
return "(" + string.Join(" ", _expressions.Select(x => x.ToString())) + ")";
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
|
|
|
public static List operator +(List a, List b) {
|
2024-06-29 18:29:40 +02:00
|
|
|
List<Expression> r = new List<Expression>();
|
|
|
|
r.AddRange(a.expressions);
|
|
|
|
r.AddRange(b.expressions);
|
|
|
|
return new List(r);
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-10-25 02:18:13 +02:00
|
|
|
public override object Inner() {
|
|
|
|
return _expressions.Select(x => x.Inner()).ToArray();
|
|
|
|
}
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public class Parser {
|
|
|
|
private StringTokenStream _sts;
|
|
|
|
public Parser(StringTokenStream tokens) {
|
|
|
|
_sts = tokens;
|
|
|
|
}
|
2024-10-27 00:51:54 +02:00
|
|
|
public Parser(string s) {
|
|
|
|
_sts = StringTokenStream.generate(s);
|
|
|
|
}
|
2024-06-27 01:47:44 +02:00
|
|
|
|
|
|
|
public Expression parse() {
|
2024-10-26 23:45:13 +02:00
|
|
|
Token<string> token = _sts.Get();
|
2024-06-27 01:47:44 +02:00
|
|
|
switch (token) {
|
|
|
|
case GroupingToken gt:
|
|
|
|
return parse_grouping(gt, gt.closing_value);
|
|
|
|
case AtomToken at:
|
|
|
|
return parse_atom(at);
|
|
|
|
case OperatorToken ot:
|
2024-10-25 20:20:18 +02:00
|
|
|
return parse_operator(ot);
|
2024-06-27 01:47:44 +02:00
|
|
|
case SpaceToken sp:
|
|
|
|
return parse();
|
|
|
|
}
|
|
|
|
return parse();
|
|
|
|
}
|
|
|
|
|
|
|
|
Expression parse_string(GroupingToken start, GroupingToken end) {
|
2024-06-29 18:29:40 +02:00
|
|
|
Debug.Assert(start.value == end.value);
|
2024-06-27 01:47:44 +02:00
|
|
|
Debug.Assert("'\"".Contains(start.value));
|
|
|
|
string r = "";
|
2024-10-26 23:45:13 +02:00
|
|
|
while (_sts.Available() > 0) {
|
|
|
|
Token<string> t = _sts.Get();
|
2024-06-29 18:29:40 +02:00
|
|
|
if (t.value == end.value) {
|
2024-06-27 01:47:44 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
r += t.value;
|
|
|
|
}
|
2024-10-26 23:45:13 +02:00
|
|
|
_sts.Commit();
|
2024-06-27 01:47:44 +02:00
|
|
|
return new String(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
Expression parse_grouping(GroupingToken start, GroupingToken end) {
|
2024-06-29 18:29:40 +02:00
|
|
|
if ("'\"".Contains(start.value)) {
|
|
|
|
return parse_string(start, end);
|
|
|
|
}
|
2024-06-27 01:47:44 +02:00
|
|
|
IList<Expression> expressions = new List<Expression>();
|
2024-10-26 23:45:13 +02:00
|
|
|
while (_sts.Available() > 0) {
|
|
|
|
Token<string> t = _sts.Get();
|
2024-06-29 18:29:40 +02:00
|
|
|
if (t.value == end.value) {
|
2024-10-26 23:45:13 +02:00
|
|
|
_sts.Commit();
|
2024-06-27 01:47:44 +02:00
|
|
|
break;
|
|
|
|
}
|
2024-10-26 23:45:13 +02:00
|
|
|
_sts.Rewind(1);
|
2024-06-27 01:47:44 +02:00
|
|
|
expressions.Add(parse());
|
|
|
|
}
|
|
|
|
return new List(expressions);
|
|
|
|
}
|
|
|
|
|
|
|
|
Expression parse_atom(AtomToken at) {
|
|
|
|
int parsed_value;
|
|
|
|
if (int.TryParse(at.value, out parsed_value)) {
|
2024-10-26 23:45:13 +02:00
|
|
|
_sts.Commit();
|
2024-06-27 01:47:44 +02:00
|
|
|
return new Integer(parsed_value);
|
|
|
|
}
|
|
|
|
if (at.value.Equals("t")) {
|
|
|
|
return new Boolean(true);
|
|
|
|
}
|
|
|
|
if (at.value.Equals("nil")) {
|
|
|
|
return new Boolean(false);
|
|
|
|
}
|
2024-10-26 23:45:13 +02:00
|
|
|
_sts.Commit();
|
2024-06-27 01:47:44 +02:00
|
|
|
return new Symbol(at.value);
|
|
|
|
}
|
2024-10-25 20:20:18 +02:00
|
|
|
|
|
|
|
Expression parse_operator(OperatorToken ot) {
|
|
|
|
string v = ot.value;
|
2024-10-26 23:45:13 +02:00
|
|
|
while (_sts.Available() > 0) {
|
|
|
|
Token<string> t = _sts.Get();
|
2024-10-25 20:20:18 +02:00
|
|
|
if (t is OperatorToken ot_) {
|
|
|
|
v += ot_.value;
|
|
|
|
continue;
|
|
|
|
}
|
2024-10-26 23:45:13 +02:00
|
|
|
_sts.Rewind(1);
|
2024-10-25 20:20:18 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return new Symbol(v);
|
|
|
|
}
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
|
|
|
}
|