fix: parsing of multi-character operators and tests.

This commit is contained in:
redxef 2024-10-25 20:20:18 +02:00
parent 1be3b17bae
commit eb45ed5772
Signed by: redxef
GPG key ID: 7DAC3AA211CBD921
4 changed files with 60 additions and 10 deletions

View file

@ -276,7 +276,7 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler {
case AtomToken at: case AtomToken at:
return parse_atom(at); return parse_atom(at);
case OperatorToken ot: case OperatorToken ot:
return new Symbol(ot.value); return parse_operator(ot);
case SpaceToken sp: case SpaceToken sp:
return parse(); return parse();
} }
@ -330,5 +330,19 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler {
_sts.commit(); _sts.commit();
return new Symbol(at.value); return new Symbol(at.value);
} }
Expression parse_operator(OperatorToken ot) {
string v = ot.value;
while (_sts.available() > 0) {
Token<string> t = _sts.get();
if (t is OperatorToken ot_) {
v += ot_.value;
continue;
}
_sts.rewind(1);
break;
}
return new Symbol(v);
}
} }
} }

View file

@ -27,6 +27,7 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
this["car"] = _car; this["car"] = _car;
this["cdr"] = _cdr; this["cdr"] = _cdr;
this["cons"] = _cons; this["cons"] = _cons;
this["not"] = _not;
this["length"] = _length; this["length"] = _length;
this["haskeys"] = _haskeys; this["haskeys"] = _haskeys;
this["getitems"] = _getitems; this["getitems"] = _getitems;
@ -170,6 +171,12 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
} }
throw new ApplicationException(); throw new ApplicationException();
} }
private static Expression _not(IList<Expression> args) {
if (args[0] == new Compiler.Boolean(false)) {
return new Compiler.Boolean(true);
}
return new Compiler.Boolean(false);
}
private static Expression _length(IList<Expression> args) { private static Expression _length(IList<Expression> args) {
return new Integer(((Compiler.List)args[0]).expressions.Count()); return new Integer(((Compiler.List)args[0]).expressions.Count());
} }
@ -231,9 +238,11 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
this["if"] = _if; this["if"] = _if;
this["define"] = _define; this["define"] = _define;
this["apply"] = _apply; this["apply"] = _apply;
this["and"] = _and;
this["or"] = _or;
} }
private static Expression _if(Executor e, IList<Expression> args) { private static Expression _if(Executor e, IList<Expression> args) {
bool test = ((Compiler.Boolean) e.eval(args[0])).value; bool test = e.eval(args[0]) != (new Compiler.Boolean(false));
return e.eval(args[1 + (test ? 0 : 1)]); return e.eval(args[1 + (test ? 0 : 1)]);
} }
private static Expression _define(Executor e, IList<Expression> args) { private static Expression _define(Executor e, IList<Expression> args) {
@ -251,6 +260,22 @@ namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
Compiler.List other_args = (Compiler.List) args[1]; Compiler.List other_args = (Compiler.List) args[1];
return e.EvalFunction(arg0, other_args.expressions); return e.EvalFunction(arg0, other_args.expressions);
} }
private static Expression _and(Executor e, IList<Expression> args) {
Expression result = new Compiler.Boolean(false);
foreach (var exp in args) {
result = e.eval(exp);
if (result == new Compiler.Boolean(false)) { return result; }
}
return result;
}
private static Expression _or(Executor e, IList<Expression> args) {
Expression result = new Compiler.Boolean(false);
foreach (var exp in args) {
result = e.eval(exp);
if (result != new Compiler.Boolean(false)) { return result; }
}
return result;
}
} }
public class Executor { public class Executor {

View file

@ -110,7 +110,7 @@ namespace Jellyfin.Plugin.SmartPlaylist.ScheduledTasks {
_logger.LogDebug("Item {0} evaluated to {1}", i, r.ToString()); _logger.LogDebug("Item {0} evaluated to {1}", i, r.ToString());
if (r is Lisp_Boolean r_bool) { if (r is Lisp_Boolean r_bool) {
if (r_bool.value) { if (r_bool.value) {
_logger.LogDebug("Added "{0}" to Smart Playlist {1}", i, smartPlaylist.Name); _logger.LogDebug("Added '{0}' to Smart Playlist {1}", i, smartPlaylist.Name);
results.Add(i.Id); results.Add(i.Id);
} }
} else { } else {

View file

@ -21,7 +21,7 @@ namespace Tests
public class Test { public class Test {
[Fact] [Fact]
public static void TestTokenizer() { public static void TestTokenizer() {
StringTokenStream sts = StringTokenStream.generate("(\"some literal string\" def ghj +100 -+300 1 ++ !=)"); StringTokenStream sts = StringTokenStream.generate("(\"some literal string\" def ghj +100 -+300 1 >= ++ !=)");
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, "some"); Assert.Equal(sts.get().value, "some");
@ -44,9 +44,14 @@ namespace Tests
Assert.Equal(sts.get().value, " "); Assert.Equal(sts.get().value, " ");
Assert.Equal(sts.get().value, "1"); Assert.Equal(sts.get().value, "1");
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, " ");
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, ")"); Assert.Equal(sts.get().value, ")");
sts.commit(); sts.commit();
Assert.Equal(sts.available(), 0); Assert.Equal(sts.available(), 0);
@ -97,6 +102,12 @@ namespace Tests
e = new Executor().eval("(length (cons 1 (2 3)))"); e = new Executor().eval("(length (cons 1 (2 3)))");
Assert.Equal(string.Format("{0}", e), "3"); Assert.Equal(string.Format("{0}", e), "3");
e = new Executor().eval("(>= 2 2)");
Assert.Equal(string.Format("{0}", e), "t");
e = new Executor().eval("(> 2 2))");
Assert.Equal(string.Format("{0}", e), "nil");
} }
[Fact] [Fact]
public static void ObjectTest() { public static void ObjectTest() {