From 6d62f6eeb035b704488775de3610dc71a9b08046 Mon Sep 17 00:00:00 2001 From: redxef Date: Tue, 17 Dec 2024 17:56:14 +0100 Subject: [PATCH] feat: allow calling generic methods. --- .../Lisp/Interpreter.cs | 63 ++++++++++++++++++- Tests/Tests.cs | 5 ++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/Jellyfin.Plugin.SmartPlaylist/Lisp/Interpreter.cs b/Jellyfin.Plugin.SmartPlaylist/Lisp/Interpreter.cs index 1aa7fca..2b8b75f 100644 --- a/Jellyfin.Plugin.SmartPlaylist/Lisp/Interpreter.cs +++ b/Jellyfin.Plugin.SmartPlaylist/Lisp/Interpreter.cs @@ -112,6 +112,9 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp { } public class Builtins : Dictionary { + + private static Dictionary AssemblyMapping = new Dictionary(); + 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(Func op, IEnumerable args) where T : Expression { T agg = (T) args.First(); @@ -250,11 +254,68 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp { IList r = new List(); 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 args) { + Object o = new Object(((IInner) args.First()).Inner()); + String s = (String) args.Skip(1).First(); + IEnumerable l; + if (args.Skip(2).First() is Boolean lb && lb == Boolean.FALSE) { + l = new List(); + } 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 types; + if (args.Skip(3).First() is Boolean lb_ && lb_ == Boolean.FALSE) { + types = new List(); + } 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(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 r = new List(); + 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 { diff --git a/Tests/Tests.cs b/Tests/Tests.cs index 264deb7..826456d 100644 --- a/Tests/Tests.cs +++ b/Tests/Tests.cs @@ -17,6 +17,9 @@ namespace Tests public int I() { return _i; } + public string G() { + 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]