using Lisp_Boolean = Jellyfin.Plugin.SmartPlaylist.Lisp.Boolean; using Lisp_Object = Jellyfin.Plugin.SmartPlaylist.Lisp.Object; using Jellyfin.Plugin.SmartPlaylist.Lisp; using Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler; namespace Tests { public class O { int _i; bool _b; public O(int i, bool b) { _i = i; _b = b; } public int i { get => _i; } public bool b { get => _b; } public int I() { return _i; } public string G() { return typeof(E).FullName; } } public class Test { [Fact] public static void TestTokenizer() { StringTokenStream sts = StringTokenStream.generate("(\"some literal string\" def ghj +100 -+300 1 >= ++ !=)"); Assert.Equal("(", sts.Get().value); Assert.Equal("\"", sts.Get().value); Assert.Equal("some", sts.Get().value); Assert.Equal(" ", sts.Get().value); Assert.Equal("literal", sts.Get().value); Assert.Equal(" ", sts.Get().value); Assert.Equal("string", sts.Get().value); Assert.Equal("\"", sts.Get().value); Assert.Equal(" ", sts.Get().value); Assert.Equal("def", sts.Get().value); Assert.Equal(" ", sts.Get().value); Assert.Equal("ghj", sts.Get().value); Assert.Equal(" ", sts.Get().value); Assert.Equal("+100", sts.Get().value); Assert.Equal(" ", sts.Get().value); Assert.Equal("-+300", sts.Get().value); Assert.Equal(" ", sts.Get().value); Assert.Equal("1", sts.Get().value); Assert.Equal(" ", sts.Get().value); Assert.Equal(">=", sts.Get().value); Assert.Equal(" ", sts.Get().value); Assert.Equal("++", sts.Get().value); Assert.Equal(" ", sts.Get().value); Assert.Equal("!=", sts.Get().value); Assert.Equal(")", sts.Get().value); sts.Commit(); Assert.Equal(0, sts.Available()); } [Fact] public static void TestParser() { string program = "(+ 1 (* 2 3))"; StringTokenStream sts = StringTokenStream.generate(program); Parser p = new Parser(sts); Assert.Equal(program, string.Format("{0}", p.parse())); program = "(haskeys o \"i\" \"b\")"; sts = StringTokenStream.generate(program); p = new Parser(sts); Assert.Equal(program, string.Format("{0}", p.parse())); program = "(abc '(1 2 3))"; sts = StringTokenStream.generate(program); p = new Parser(sts); Assert.Equal("(abc (quote (1 2 3)))", string.Format("{0}", p.parse())); program = "(abc \"'(1 2 3)\")"; sts = StringTokenStream.generate(program); p = new Parser(sts); Assert.Equal(program, string.Format("{0}", p.parse())); } [Fact] public static void TestFunctions() { Executor e = new Executor(); Assert.Equal("(1 2 3)", e.eval("(quote (1 2 3))").ToString()); Assert.Equal("abc", e.eval("(quote abc)").ToString()); Assert.Equal("t", e.eval("(atom 1)").ToString()); Assert.Equal("nil", e.eval("(atom (quote (1 2 3)))").ToString()); Assert.Equal("t", e.eval("(eq 2 2)").ToString()); Assert.Equal("nil", e.eval("(eq 2 3)").ToString()); Assert.Equal("1", e.eval("(car (quote (1 2 3)))").ToString()); Assert.Equal("(2 3)", e.eval("(cdr (quote (1 2 3)))").ToString()); Assert.Equal("(1 . 2)", e.eval("(cons 1 2)").ToString()); Assert.Equal("(1 2)", e.eval("(cons 1 (cons 2 nil))").ToString()); Assert.Equal("(1)", e.eval("(cons 1 nil)").ToString()); Assert.Equal("(1)", e.eval("(cons 1 ())").ToString()); Assert.Equal("\"Case 2\"", e.eval(""" (cond ((eq 1 2) "Case 1") ((eq 2 2) "Case 2")) """).ToString()); Assert.Equal("\"Case 1\"", e.eval(""" (cond ((eq 2 2) "Case 1") ((eq 2 2) "Case 2")) """).ToString()); Assert.Equal("nil", e.eval(""" (cond ((eq 1 2) "Case 1") ((eq 3 2) "Case 2")) """).ToString()); Assert.Equal("t", e.eval("((lambda (a) (eq a a)) 2)").ToString()); Assert.Equal("t", e.eval("(begin (car (quote (nil 1))) t)").ToString()); Assert.Equal("(1)", e.eval("(begin t (cdr (quote (nil 1))))").ToString()); Assert.Equal("t", e.eval(""" (begin (define abc 10) (eq abc abc)) """).ToString()); Assert.Equal("1", e.eval(""" (begin (define if (lambda (condition a b) ( cond (condition a) (t b)))) (if (> 2 1) (car (quote (1 2 3))) (cdr (quote (2 3 4))))) """).ToString()); Assert.Equal("(3 4)", e.eval(""" (begin (define if (lambda (condition a b) ( cond (condition a) (t b)))) (if (> 0 1) (car (quote (1 2 3))) (cdr (quote (2 3 4))))) """).ToString()); } [Fact] public static void TestFunctionsAdvanced() { Executor e = new Executor(); Assert.Equal("2", e.eval(""" ((lambda (b) b) (car (quote (2 3)))) """).ToString()); Assert.Equal("(3 4 5)", e.eval(""" ((lambda (x y . z) z) 1 2 3 4 5) """).ToString()); Assert.Equal("3", e.eval(""" (begin (define if (lambda (condition a b) (cond (condition a) (t b)))) (if (< 1 2) 3 2)) """).ToString()); Assert.Equal("2", e.eval(""" (begin (define if (lambda (condition a b) (cond (condition a) (t b)))) (if (> 1 2) 3 2)) """).ToString()); Assert.Equal("1", e.eval(""" (begin (define if (lambda* (condition a b) ( cond ((eval condition) (eval a)) (t (eval b))))) (if (> 2 1) (car (quote (1 2 3))) (cdr (quote (2 3 4))))) """).ToString()); Assert.Equal("(3 4)", e.eval(""" (begin (define if (lambda* (condition a b) ( cond ((eval condition) (eval a)) (t (eval b))))) (if (> 0 1) (car (quote (1 2 3))) (cdr (quote (2 3 4))))) """).ToString()); Assert.Equal("120", e.eval(""" (begin (define f (lambda (n) (cond ((<= n 1) 1) (t (* n (f (- n 1))))))) (f 5)) """).ToString()); Assert.Equal("120", e.eval(""" (begin (define if (lambda* (condition a b) ( cond ((eval condition) (eval a)) (t (eval b))))) (define f (lambda (n) (if (<= n 1) 1 (* n (f (- n 1)))))) (f 5)) """).ToString()); Assert.Equal("(1 2 3 4 5)", e.eval(""" (begin (define if (lambda* (condition a b) ( cond ((eval condition) (eval a)) (t (eval b))))) ((lambda (. args) args) 1 2 3 4 5)) """).ToString()); Assert.Equal("t", e.eval(""" (begin (define null (lambda* (x) ( cond ((eval x) nil) (t t)))) (null nil)) """).ToString()); Assert.Equal("nil", e.eval(""" (begin (define null (lambda* (x) (cond ((eval x) nil) (t t)))) (null (quote (1 2)))) """).ToString()); } [Fact] public static void ObjectTest() { Executor e = new Executor(); Expression r; e.environment.Set("o", Lisp_Object.FromBase(new O(5, false))); r = e.eval("""(haskeys o "i" "b")"""); Assert.True(((Lisp_Boolean)r).Value()); r = e.eval("""(getitems o "i" "b")"""); 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] public static void DefaultEnvironmentTest() { Executor e = new Executor(new DefaultEnvironment()); Assert.Equal("1", e.eval("(if nil 0 1)").ToString()); Assert.Equal("0", e.eval("(if t 0 1)").ToString()); Assert.Equal("5", e.eval("(if t (if t 5 nil) nil)").ToString()); Assert.Equal("nil", e.eval("(if t (if nil 5 nil) nil)").ToString()); Assert.Equal("(1 2 3)", e.eval("(list 1 2 3)").ToString()); Assert.Equal("3", e.eval("(find 3 (list 1 2 3 4))").ToString()); Assert.Equal("nil", e.eval("(find 0 (list 1 2 3 4))").ToString()); Assert.Equal("(2 4 6)", e.eval("(map (lambda (x) (* x 2)) (quote (1 2 3)))").ToString()); Assert.Equal("nil", e.eval("(and 1 2 3 nil)").ToString()); Assert.Equal("t", e.eval("(and t t t t)").ToString()); Assert.Equal("t", e.eval("(or nil nil t nil)").ToString()); Assert.Equal("nil", e.eval("(or nil nil nil nil)").ToString()); Assert.Equal("t", e.eval("(any (lambda (x) (= x 2)) (list 1 2 3 4 5 6))").ToString()); Assert.Equal("nil", e.eval("(any (lambda (x) (= x 2)) (list 1 3 4 5 6))").ToString()); Assert.Equal("nil", e.eval("(any (lambda (x) (= x 2)) nil)").ToString()); Assert.Equal("t", e.eval("(all (lambda (x) (= 1 (% x 2))) (list 1 3 5))").ToString()); Assert.Equal("nil", e.eval("(all (lambda (x) (= 1 (% x 2))) (list 1 3 4 5))").ToString()); Assert.Equal("nil", e.eval("(all (lambda (x) (= x 2)) nil)").ToString()); Assert.Equal("10", e.eval("(fold (lambda (a b) (+ a b)) 0 (list 1 2 3 4))").ToString()); Assert.Equal("(2 3 4 5 6 7)", e.eval("(append (list 2 3 4) (list 5 6 7))").ToString()); Assert.Equal("(1 2 3 4 5 6 7)", e.eval("(qsort (lambda (a b) (> a b)) (list 5 4 7 3 2 6 1))").ToString()); //Assert.Equal("", e.eval("(rand)").ToString()); //Assert.Equal("", e.eval("(shuf (list 0 1 2 3 4 5 6))").ToString()); Assert.Equal("(1 2 3 4 5 6 7)", e.eval("(qsort (lambda (a b) (> a b)) '(5 4 7 3 2 6 1))").ToString()); } } }