233 lines
8.2 KiB
C#
233 lines
8.2 KiB
C#
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
public abstract class Expression : IFormattable {
|
|
public abstract string ToString(string? format, IFormatProvider? provider);
|
|
}
|
|
public abstract class Atom : Expression {}
|
|
public class Symbol : Atom {
|
|
private readonly string _name;
|
|
public Symbol(string name) {
|
|
_name = name;
|
|
}
|
|
public string name { get => _name; }
|
|
public override string ToString(string? format, IFormatProvider? provider) {
|
|
return _name;
|
|
}
|
|
}
|
|
public class Boolean : Atom, IComparable<Boolean, Boolean> {
|
|
private readonly bool _value;
|
|
public Boolean(bool value) {
|
|
_value = value;
|
|
}
|
|
public bool value { get => _value; }
|
|
public override string ToString(string? format, IFormatProvider? provider) {
|
|
return _value? "t" : "nil";
|
|
}
|
|
public static Boolean operator ==(Boolean a, Boolean b) {
|
|
return new Boolean(a.value == b.value);
|
|
}
|
|
public static Boolean operator !=(Boolean a, Boolean b) {
|
|
return new Boolean(a.value != b.value);
|
|
}
|
|
}
|
|
public class Integer : Atom, IAddable<Integer>, ISubtractable<Integer>, IMultiplicatable<Integer>, IDivisible<Integer>, ISortable<Integer, Boolean>, IComparable<Integer, Boolean> {
|
|
private readonly int _value;
|
|
public Integer(int value) {
|
|
_value = value;
|
|
}
|
|
public int value { get => _value; }
|
|
public override string ToString(string? format, IFormatProvider? provider) {
|
|
return _value.ToString("0", provider);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
public class String : Atom, IAddable<String> {
|
|
private readonly string _value;
|
|
public String(string value) {
|
|
_value = value;
|
|
}
|
|
public string value { get => _value; }
|
|
public override string ToString(string? format, IFormatProvider? provider) {
|
|
return "\"" + _value + "\"";
|
|
}
|
|
public static String operator +(String a, String b) {
|
|
return new String (a.value + b.value);
|
|
}
|
|
}
|
|
public class Object : Atom {
|
|
private readonly object _value;
|
|
public Object(object value) {
|
|
_value = value;
|
|
}
|
|
public object value { get => _value; }
|
|
public override string ToString(string? format, IFormatProvider? provider) {
|
|
return _value.ToString();
|
|
}
|
|
public static Atom FromBase(object o) {
|
|
switch (o) {
|
|
case bool b:
|
|
return new Boolean(b);
|
|
case int i:
|
|
return new Integer(i);
|
|
case string s:
|
|
return new String(s);
|
|
default:
|
|
return new Object(o);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class List : Expression {
|
|
private IList<Expression> _expressions;
|
|
public List(IList<Expression> expressions) {
|
|
_expressions = expressions;
|
|
}
|
|
public IList<Expression> expressions { get => _expressions; }
|
|
public override string ToString(string? format, IFormatProvider? provider) {
|
|
string r = "(";
|
|
foreach (var e in _expressions) {
|
|
r += " ";
|
|
r += e.ToString("0", provider);
|
|
}
|
|
return r + ")";
|
|
}
|
|
public static List operator +(List a, List b) {
|
|
List<Expression> r = new List<Expression>();
|
|
r.AddRange(a.expressions);
|
|
r.AddRange(b.expressions);
|
|
return new List(r);
|
|
}
|
|
}
|
|
|
|
public class Parser {
|
|
private StringTokenStream _sts;
|
|
public Parser(StringTokenStream tokens) {
|
|
_sts = tokens;
|
|
}
|
|
|
|
public Expression parse() {
|
|
Token<string> token = _sts.get();
|
|
switch (token) {
|
|
case GroupingToken gt:
|
|
return parse_grouping(gt, gt.closing_value);
|
|
case AtomToken at:
|
|
return parse_atom(at);
|
|
case OperatorToken ot:
|
|
return new Symbol(ot.value);
|
|
case SpaceToken sp:
|
|
return parse();
|
|
}
|
|
return parse();
|
|
}
|
|
|
|
Expression parse_string(GroupingToken start, GroupingToken end) {
|
|
Debug.Assert(start.value == end.value);
|
|
Debug.Assert("'\"".Contains(start.value));
|
|
string r = "";
|
|
while (_sts.available() > 0) {
|
|
Token<string> t = _sts.get();
|
|
if (t.value == end.value) {
|
|
break;
|
|
}
|
|
r += t.value;
|
|
}
|
|
_sts.commit();
|
|
return new String(r);
|
|
}
|
|
|
|
Expression parse_grouping(GroupingToken start, GroupingToken end) {
|
|
if ("'\"".Contains(start.value)) {
|
|
return parse_string(start, end);
|
|
}
|
|
IList<Expression> expressions = new List<Expression>();
|
|
while (_sts.available() > 0) {
|
|
Token<string> t = _sts.get();
|
|
if (t.value == end.value) {
|
|
_sts.commit();
|
|
break;
|
|
}
|
|
_sts.rewind(1);
|
|
expressions.Add(parse());
|
|
}
|
|
return new List(expressions);
|
|
}
|
|
|
|
Expression parse_atom(AtomToken at) {
|
|
int parsed_value;
|
|
if (int.TryParse(at.value, out parsed_value)) {
|
|
_sts.commit();
|
|
return new Integer(parsed_value);
|
|
}
|
|
if (at.value.Equals("t")) {
|
|
return new Boolean(true);
|
|
}
|
|
if (at.value.Equals("nil")) {
|
|
return new Boolean(false);
|
|
}
|
|
_sts.commit();
|
|
return new Symbol(at.value);
|
|
}
|
|
}
|
|
}
|