Compare commits
2 commits
81184c23a7
...
f0bfecad71
Author | SHA1 | Date | |
---|---|---|---|
f0bfecad71 | |||
f73f501642 |
4 changed files with 145 additions and 1 deletions
|
@ -78,6 +78,23 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
|
||||||
(sort fc list00)))
|
(sort fc list00)))
|
||||||
"""
|
"""
|
||||||
);
|
);
|
||||||
|
this["rand"] = e.eval(
|
||||||
|
"""
|
||||||
|
(lambda
|
||||||
|
(. a)
|
||||||
|
(cond
|
||||||
|
((null a) (random))
|
||||||
|
((null (cdr a)) (% (random) (car a)))
|
||||||
|
(t (+
|
||||||
|
(car a)
|
||||||
|
(%
|
||||||
|
(random)
|
||||||
|
(-
|
||||||
|
(car (cdr a))
|
||||||
|
(car a)))))))
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
this["shuf"] = new Symbol("shuffle");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,8 +131,10 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
|
||||||
public class Builtins : Dictionary<string, Function> {
|
public class Builtins : Dictionary<string, Function> {
|
||||||
|
|
||||||
private static Dictionary<string, Type?> ResolvedTypes = new Dictionary<string, Type?>();
|
private static Dictionary<string, Type?> ResolvedTypes = new Dictionary<string, Type?>();
|
||||||
|
private Random Random;
|
||||||
|
|
||||||
public Builtins() : base() {
|
public Builtins() : base() {
|
||||||
|
Random = new Random();
|
||||||
this["atom"] = _atom;
|
this["atom"] = _atom;
|
||||||
this["eq"] = _eq;
|
this["eq"] = _eq;
|
||||||
this["car"] = _car;
|
this["car"] = _car;
|
||||||
|
@ -146,11 +165,17 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
|
||||||
this["string<"] = (x) => _cmp((String a, String b) => a < b, x);
|
this["string<"] = (x) => _cmp((String a, String b) => a < b, x);
|
||||||
this["string<="] = (x) => _cmp((String a, String b) => a <= b, x);
|
this["string<="] = (x) => _cmp((String a, String b) => a <= b, x);
|
||||||
|
|
||||||
|
|
||||||
this["haskeys"] = _haskeys;
|
this["haskeys"] = _haskeys;
|
||||||
this["getitems"] = _getitems;
|
this["getitems"] = _getitems;
|
||||||
this["invoke"] = _invoke;
|
this["invoke"] = _invoke;
|
||||||
this["invoke-generic"] = _invoke_generic;
|
this["invoke-generic"] = _invoke_generic;
|
||||||
|
|
||||||
|
this["random"] = (x) => new Lisp.Integer(Random.Next());
|
||||||
|
this["shuffle"] = (x) => {
|
||||||
|
var newx = ((Lisp.Cons) x.First()).ToList().ToArray();
|
||||||
|
Random.Shuffle<Expression>(newx);
|
||||||
|
return Lisp.Cons.FromList(newx);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
private static T _agg<T>(Func<T, T, T> op, IEnumerable<Expression> args) where T : Expression {
|
private static T _agg<T>(Func<T, T, T> op, IEnumerable<Expression> args) where T : Expression {
|
||||||
T agg = (T) args.First();
|
T agg = (T) args.First();
|
||||||
|
|
|
@ -102,6 +102,8 @@ The configuration page defines some useful functions to make it easier
|
||||||
to create filters. The above filter for liked items could be simplified
|
to create filters. The above filter for liked items could be simplified
|
||||||
to: `(is-favourite)`.
|
to: `(is-favourite)`.
|
||||||
|
|
||||||
|
*Go [here](lisp.md) to get a overview of the built-in functions.*
|
||||||
|
|
||||||
### SortProgram
|
### SortProgram
|
||||||
|
|
||||||
This works exactly like [Program](#program), but the input is the
|
This works exactly like [Program](#program), but the input is the
|
||||||
|
|
|
@ -233,6 +233,9 @@ namespace Tests
|
||||||
Assert.Equal("10", e.eval("(fold (lambda (a b) (+ a b)) 0 (list 1 2 3 4))").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("(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("(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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
114
lisp.md
Normal file
114
lisp.md
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
# The lisp interpreter
|
||||||
|
|
||||||
|
The interpreter is a lisp-like language used to build the filter
|
||||||
|
expressions.
|
||||||
|
|
||||||
|
## Builtins
|
||||||
|
|
||||||
|
**atom**: check if a receive value is a atom. `(atom 1)`
|
||||||
|
|
||||||
|
**eq**: check if two values are equal. `(eq (quote a) (quote a))`
|
||||||
|
|
||||||
|
**car**: get the first item of the cons.
|
||||||
|
|
||||||
|
**cdr**: get the remainder of the list.
|
||||||
|
|
||||||
|
**cons**: create a new cons. `(cons 1 2)`
|
||||||
|
|
||||||
|
**begin**: evaluate a series of statements, returning the result of the
|
||||||
|
last one. `(begin t nil)`
|
||||||
|
|
||||||
|
**+-\*/%**: arithmetic operations for integers. `(+ 1 2)`
|
||||||
|
|
||||||
|
**=**, **!=**, **<**, **<=**, **>**, **>=**: compare two integers. `(> 1 2)`
|
||||||
|
|
||||||
|
**not**: negate the given value. `nil -> t`, everything else will be `nil`. `(not (quote a))`
|
||||||
|
|
||||||
|
**string=**, **string!=**, **string<**, **string<=**, **string>**, **string>=**: compare two strings. `(> "1" "2")`
|
||||||
|
|
||||||
|
**haskeys**: takes any object and a variadic number of arguments (strings) and
|
||||||
|
returns a list with either `t` or `nil` describing if a corresponding property/field/method
|
||||||
|
with that name exists on the object. `(haskeys mystring "Length")`
|
||||||
|
|
||||||
|
**getitems**: takes any object and a variadic number of arguments
|
||||||
|
(strings) and returns the values of the fields/properties. `(getitems mystring "Length" "Chars")`
|
||||||
|
|
||||||
|
**invoke**: takes 3 arguments and invokes the method defined on the
|
||||||
|
object.
|
||||||
|
|
||||||
|
The first argument is the object on which to invoke the method, the
|
||||||
|
second one is the name of the method and the third one is a list of
|
||||||
|
arguments to pass to the method. `(invoke mystring "Lower" nil)`
|
||||||
|
|
||||||
|
**invoke-generic**: the same as **invoke**, but takes a fourth
|
||||||
|
argument, a list of string describing the types for the generic method.
|
||||||
|
`(invoke-generic mybaseitem "FindParent" nil (list "MediaBrowser.Controller.Entities.Audio.MusicArtist, MediaBrowser.Controller"))`
|
||||||
|
|
||||||
|
**random**: gives a random integer. `(random)`
|
||||||
|
|
||||||
|
**shuffle**: shuffles a list. `(shuffle (list 1 2 3 4))`
|
||||||
|
|
||||||
|
**quote**: quotes a value. `(quote a)`
|
||||||
|
|
||||||
|
**eval**: evaluates a expression. `(eval (quote a))`
|
||||||
|
|
||||||
|
**cond**: checks conditions and evaluates the corresponding expression.
|
||||||
|
```
|
||||||
|
(cond
|
||||||
|
(> 1 2) t
|
||||||
|
(t) f)
|
||||||
|
```
|
||||||
|
|
||||||
|
**if**: a conditional. `(if t 1 2)`
|
||||||
|
|
||||||
|
**define**: defines a new symbol. `(define foo 1)`, `(define add (lambda (a b) (+ a b)))`
|
||||||
|
|
||||||
|
**let**: define variables in the let context and evaluate the last
|
||||||
|
expression. `(let (a 1) (b 2) (+ a b))`
|
||||||
|
|
||||||
|
**let\***: the same as **let**, but allows to reference variables
|
||||||
|
defined earlier in the let statement. `(let* (a 1) (b (+ 2 a)) (+ a b))`
|
||||||
|
|
||||||
|
**apply**: call a function with the specified arguments. `(apply + (list 1 2))`
|
||||||
|
|
||||||
|
**and**: evaluate the given expressions in order, if any one of them
|
||||||
|
evaluates to `nil` return early with that value, otherwise return the
|
||||||
|
last value. `(and 1 2 nil 3)`
|
||||||
|
|
||||||
|
**or**: return `nil` if all arguments evaluate to `nil` otherwise the
|
||||||
|
first non-nil value.
|
||||||
|
|
||||||
|
## Derived builtins
|
||||||
|
|
||||||
|
**null**: the same as **not**. Can be useful to indicate semantics of a
|
||||||
|
program.
|
||||||
|
|
||||||
|
**list**: create a list from the given arguments. `(list 1 2 3)`
|
||||||
|
|
||||||
|
**find**: find if an item is in the given list and return it, otherwise
|
||||||
|
return `nil`. `(find 4 (list 1 2 3 4 5 6 7))`
|
||||||
|
|
||||||
|
**map**: apply a function to every item in the list. `(map (lambda (x) (* 2 x)) (list 1 2 3))`
|
||||||
|
|
||||||
|
**fold**: also known as reduce. Apply the function to a sequence of
|
||||||
|
values, reducing the sequence to a single item. It takes a initial value
|
||||||
|
which is returned for empty lists. `(fold (lambda (a b) (+ a b)) 0 (list 1 2 3 4))`
|
||||||
|
|
||||||
|
**any**: equivalent to `(apply or (map function list))`.
|
||||||
|
`(any (lambda (a) (% a 2)) (list 2 4 6 7 8))`
|
||||||
|
|
||||||
|
**all**: equivalent to `(apply and (map function list))`.
|
||||||
|
`(all (lambda (a) (% a 2)) (list 2 4 6 7 8))`
|
||||||
|
|
||||||
|
**append**: append an item to the given list. `(append (list 1 2 3) 4)`
|
||||||
|
|
||||||
|
**qsort**: quicksort, takes a comparison function and the list.
|
||||||
|
`(qsort (lambda (a b) (> a b)) (list 1 2 6 4 9 1 19 0))`
|
||||||
|
|
||||||
|
**rand**: get a random integer. Takes either zero, one or two arguments.
|
||||||
|
If zero arguments are given it gives a random integer from all possibly
|
||||||
|
representable integers. If one argument is given it gives a integer
|
||||||
|
between `0` (inclusive) and `n` (exclusive). If two arguments are given
|
||||||
|
it gives a integer between `a` (inclusive) and `b` (exclusive).
|
||||||
|
|
||||||
|
**shuf**: same as **shuffle**.
|
Loading…
Reference in a new issue