jellyfin-smart-playlist/Jellyfin.Plugin.SmartPlaylist/Lisp/Interpreter.cs

242 lines
10 KiB
C#
Raw Normal View History

2024-06-27 01:47:44 +02:00
using Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler;
namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
public class EnvironmentEntry {};
public class Entry: EnvironmentEntry {
private readonly Expression _expression;
public Entry(Expression expression) {
_expression = expression;
}
public Expression expression { get => _expression; }
};
public class NOOPEntry: Entry {
public NOOPEntry() : base(new Compiler.Boolean(false)) {}
}
public class Function : EnvironmentEntry {
private readonly Func<IList<Expression>, Expression> _func;
public Function(Func<IList<Expression>, Expression> func) {
_func = func;
}
public Func<IList<Expression>, Expression> func { get => _func; }
}
public class Environment : Dictionary<string, EnvironmentEntry> {
public static Environment create() {
Environment e = new Environment();
e.Add("+", new Function(op_add));
e.Add("-", new Function(op_sub));
e.Add("*", new Function(op_mul));
e.Add("/", new Function(op_div));
e.Add("%", new Function(op_rem));
e.Add(">", new Function(op_gt));
e.Add("<", new Function(op_lt));
e.Add(">=", new Function(op_ge));
e.Add("<=", new Function(op_le));
e.Add("==", new Function(op_eq));
e.Add("!=", new Function(op_ne));
e.Add("abs", new Function(op_abs));
e.Add("append", new Function(op_append));
e.Add("apply", new Function(op_apply));
e.Add("begin", new Function(op_begin));
return e;
}
private static T op_agg<T>(Func<T, T, T> op, IList<T> args) {
T agg = args[0];
foreach (var arg in args.Skip(1)) {
agg = op(agg, arg);
}
return agg;
}
private static Expression op_add(IList<Expression> args) {
Expression first = args[0];
switch (first) {
case Integer i:
return op_agg((a, b) => a + b, args.Select(x => (Integer) x).ToList());
case Compiler.String s:
return op_agg((a, b) => a + b, args.Select(x => (Compiler.String) x).ToList());
//case Compiler.List:
// return op_agg((a, b) => a + b, args.Select(x => (Compiler.List) x).ToList());
}
throw new ApplicationException("Don't know how to add these types");
}
private static Expression op_sub(IList<Expression> args) {
Expression first = args[0];
switch (first) {
case Integer i:
return op_agg((a, b) => a - b, args.Select(x => (Integer) x).ToList());
}
throw new ApplicationException("Don't know how to subtract these types");
}
private static Expression op_mul(IList<Expression> args) {
Expression first = args[0];
switch (first) {
case Integer i:
return op_agg((a, b) => a * b, args.Select(x => (Integer) x).ToList());
}
throw new ApplicationException("Don't know how to subtract these types");
}
private static Expression op_div(IList<Expression> args) {
Expression first = args[0];
switch (first) {
case Integer i:
return op_agg((a, b) => a / b, args.Select(x => (Integer) x).ToList());
}
throw new ApplicationException("Don't know how to subtract these types");
}
private static Expression op_rem(IList<Expression> args) {
Expression first = args[0];
switch (first) {
case Integer i:
return op_agg((a, b) => a % b, args.Select(x => (Integer) x).ToList());
}
throw new ApplicationException("Don't know how to subtract these types");
}
private static E op_cmp<T, E>(Func<T, T, E> op, IList<T> args) {
T first = args[0];
T second = args[1];
return op(first, second);
}
private static Expression op_gt(IList<Expression> args) {
Expression first = args[0];
switch (first) {
case Integer i:
return op_cmp((a, b) => a > b, args.Select(x => (Integer) x).ToList());
}
throw new ApplicationException("Don't know how to subtract these types");
}
private static Expression op_lt(IList<Expression> args) {
Expression first = args[0];
switch (first) {
case Integer i:
return op_cmp((a, b) => a < b, args.Select(x => (Integer) x).ToList());
}
throw new ApplicationException("Don't know how to subtract these types");
}
private static Expression op_ge(IList<Expression> args) {
Expression first = args[0];
switch (first) {
case Integer i:
return op_cmp((a, b) => a >= b, args.Select(x => (Integer) x).ToList());
}
throw new ApplicationException("Don't know how to subtract these types");
}
private static Expression op_le(IList<Expression> args) {
Expression first = args[0];
switch (first) {
case Integer i:
return op_cmp((a, b) => a <= b, args.Select(x => (Integer) x).ToList());
}
throw new ApplicationException("Don't know how to subtract these types");
}
private static Expression op_eq(IList<Expression> args) {
Expression first = args[0];
switch (first) {
case Integer i:
return op_cmp((a, b) => a == b, args.Select(x => (Integer) x).ToList());
}
throw new ApplicationException("Don't know how to subtract these types");
}
private static Expression op_ne(IList<Expression> args) {
Expression first = args[0];
switch (first) {
case Integer i:
return op_cmp((a, b) => a != b, args.Select(x => (Integer) x).ToList());
}
throw new ApplicationException("Don't know how to subtract these types");
}
private static Expression op_abs(IList<Expression> args) {
Expression first = args[0];
switch (first) {
case Integer i:
return i.value >= 0 ? i : new Integer(-i.value);
}
throw new ApplicationException("Don't know how to subtract these types");
}
private static Expression op_append(IList<Expression> args) {
Expression first = args[0];
switch (first) {
case List l:
return l + new List(args);
}
throw new ApplicationException("Don't know how to subtract these types");
}
private static Expression op_apply(IList<Expression> args) {
IList<EnvironmentEntry> e_list = (IList<EnvironmentEntry>) args.Select(x => new Entry(x)).ToList();
if (e_list.Count != 2) {
throw new ApplicationException("Expected exactly two arguments");
}
if (e_list[0].GetType() != typeof(Function)) {
throw new ApplicationException("Expected first argument to be a function to apply");
}
Function f = (Function) e_list[0];
IList<Expression> new_args = e_list.Skip(1).Select(x => ((Entry) x).expression).ToList();
return f.func(args);
}
private static Expression op_begin(IList<Expression> args) {
return args.Last();
}
public EnvironmentEntry eval(Expression expression) {
switch (expression) {
case Symbol s:
return this[s.name];
case Integer i:
return new Entry(i);
case Compiler.String s_:
return new Entry(s_);
case List list:
if (list.expressions[0].GetType() == typeof(Symbol)) {
if (((Symbol) list.expressions[0]).name.Equals("if")) {
Compiler.Boolean test = (Compiler.Boolean) ((Entry) eval(list.expressions[1])).expression;
return eval(list.expressions[2 + (test.value? 0 : 1)]);
}
if (((Symbol) list.expressions[0]).name.Equals("define")) {
Symbol test;
if (list.expressions[1].GetType() == typeof(Symbol)) {
test = (Symbol) list.expressions[1];
} else {
test = (Symbol) ((Entry) eval(list.expressions[1])).expression;
}
this[test.name] = eval(list.expressions[2]);
return new NOOPEntry();
}
}
IList<EnvironmentEntry> e_list = list.expressions.Select(x => eval(x)).ToList();
if (e_list.Count > 0 && e_list[0].GetType() == typeof(Function)) {
Function f = (Function) e_list[0];
IList<Expression> args = e_list.Skip(1).Select(x => ((Entry) x).expression).ToList();
return new Entry(f.func(args));
}
return new Entry(new List(list.expressions.Select(x => ((Entry) eval(x)).expression).ToList()));
}
throw new ApplicationException("Not handled case");
}
public EnvironmentEntry eval(Parser p) {
return eval(p.parse());
}
public EnvironmentEntry eval(StringTokenStream sts) {
return eval(new Parser(sts));
}
public EnvironmentEntry eval(string p) {
return eval(StringTokenStream.generate(p));
}
}
}