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)))
|
||||
"""
|
||||
);
|
||||
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> {
|
||||
|
||||
private static Dictionary<string, Type?> ResolvedTypes = new Dictionary<string, Type?>();
|
||||
private Random Random;
|
||||
|
||||
public Builtins() : base() {
|
||||
Random = new Random();
|
||||
this["atom"] = _atom;
|
||||
this["eq"] = _eq;
|
||||
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["haskeys"] = _haskeys;
|
||||
this["getitems"] = _getitems;
|
||||
this["invoke"] = _invoke;
|
||||
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 {
|
||||
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: `(is-favourite)`.
|
||||
|
||||
*Go [here](lisp.md) to get a overview of the built-in functions.*
|
||||
|
||||
### SortProgram
|
||||
|
||||
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("(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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
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