Compare commits

...

2 commits

3 changed files with 71 additions and 1 deletions

View file

@ -112,6 +112,9 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
}
public class Builtins : Dictionary<string, Function> {
private static Dictionary<string, Assembly> AssemblyMapping = new Dictionary<string, Assembly>();
public Builtins() : base() {
this["atom"] = _atom;
this["eq"] = _eq;
@ -147,6 +150,7 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
this["haskeys"] = _haskeys;
this["getitems"] = _getitems;
this["invoke"] = _invoke;
this["invoke-generic"] = _invoke_generic;
}
private static T _agg<T>(Func<T, T, T> op, IEnumerable<Expression> args) where T : Expression {
T agg = (T) args.First();
@ -250,11 +254,68 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
IList<Expression> r = new List<Expression>();
MethodInfo? mi = o.Value().GetType().GetMethod(s.Value(), l_types);
if (mi == null) {
throw new ApplicationException($"{o.Value()} has not method {s.Value()}");
throw new ApplicationException($"{o.Value()} has no method {s.Value()}");
}
return Object.FromBase(mi.Invoke(o.Value(), l_));
}
private static Type? GetType(string name) {
return Type.GetType(
name,
(name) => { return AppDomain.CurrentDomain.GetAssemblies().Where(z => z.FullName == name.FullName).FirstOrDefault(); },
null,
true
);
}
private static Expression _invoke_generic(IEnumerable<Expression> args) {
Object o = new Object(((IInner) args.First()).Inner());
String s = (String) args.Skip(1).First();
IEnumerable<Expression> l;
if (args.Skip(2).First() is Boolean lb && lb == Boolean.FALSE) {
l = new List<Expression>();
} else if (args.Skip(2).First() is Cons lc) {
l = lc.ToList();
} else {
throw new ApplicationException($"Expected a list of arguments, got {args.Skip(2).First()}");
}
IEnumerable<Type> types;
if (args.Skip(3).First() is Boolean lb_ && lb_ == Boolean.FALSE) {
types = new List<Type>();
} else if (args.Skip(3).First() is Cons lc) {
types = lc.ToList().Select(x => GetType(((String) x).Value())).ToList();
} else {
throw new ApplicationException($"Expected a list of arguments, got {args.Skip(3).First()}");
}
object[]? l_ = l.Select<Expression, object>(x => {
switch (x) {
case Integer s:
return s.Value();
case Boolean b:
return b.Value();
case String s:
return s.Value();
case Object o:
return o.Value();
case Cons c:
return c.ToList().ToList();
}
throw new ApplicationException($"Unhandled value {x} (type {x.GetType()})");
}).ToArray();
Type[] l_types = l_.Select( x => {
return x.GetType();
}).ToArray();
IList<Expression> r = new List<Expression>();
MethodInfo? mi = o.Value().GetType().GetMethod(s.Value(), l_types);
if (mi == null) {
throw new ApplicationException($"{o.Value()} has no method {s.Value()}");
}
mi = mi.MakeGenericMethod(types.ToArray());
return Object.FromBase(mi.Invoke(o.Value(), l_));
}
}
public class BuiltinsLater : Dictionary<string, FunctionLater> {

View file

@ -143,6 +143,10 @@ namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks {
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) {
_logger.LogInformation("Started regenerate Smart Playlists");
_logger.LogDebug("Loaded Assemblies:");
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) {
_logger.LogDebug("- {0}", asm);
}
foreach (SmartPlaylistDto dto in await _store.GetAllSmartPlaylistsAsync()) {
var changedDto = false;
if (dto.Playlists.Length == 0) {

View file

@ -17,6 +17,9 @@ namespace Tests
public int I() {
return _i;
}
public string G<E>() {
return typeof(E).FullName;
}
}
public class Test {
@ -202,6 +205,8 @@ namespace Tests
Assert.Equal("(5 nil)", string.Format("{0}", r));
r = e.eval("""(invoke o "I" nil)""");
Assert.Equal("5", string.Format("{0}", r));
r = e.eval("""(invoke-generic o "G" nil ((lambda (. args) args) "System.String"))""");
Assert.Equal("\"System.String\"", string.Format("{0}", r));
}
[Fact]