2024-06-29 18:29:40 +02:00
|
|
|
using System.Reflection;
|
|
|
|
|
2024-06-27 01:47:44 +02:00
|
|
|
using Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler;
|
|
|
|
|
|
|
|
namespace Jellyfin.Plugin.SmartPlaylist.Lisp {
|
2024-06-29 18:29:40 +02:00
|
|
|
using Function = Func<IList<Expression>, Expression>;
|
|
|
|
using FunctionLater = Func<Executor, IList<Expression>, Expression>;
|
2024-06-27 01:47:44 +02:00
|
|
|
|
2024-10-26 03:49:52 +02:00
|
|
|
public class Procedure : Expression {
|
|
|
|
private Compiler.List _parameters;
|
|
|
|
private Expression _body;
|
|
|
|
public Procedure(Compiler.List parameters, Expression body) {
|
|
|
|
_parameters = parameters;
|
|
|
|
_body = body;
|
|
|
|
}
|
|
|
|
private static IEnumerable<(T1, T2)> Zip<T1, T2>(IEnumerable<T1> a, IEnumerable<T2> b) {
|
|
|
|
using (var e1 = a.GetEnumerator()) using (var e2 = b.GetEnumerator()) {
|
|
|
|
while (e1.MoveNext() && e2.MoveNext()) {
|
|
|
|
yield return (e1.Current, e2.Current);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
|
|
int hash = 17;
|
|
|
|
hash *= 23;
|
|
|
|
hash += _parameters.GetHashCode();
|
|
|
|
hash *= 23;
|
|
|
|
hash += _body.GetHashCode();
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
public override bool Equals(Expression? other) {
|
|
|
|
if (other is Procedure other_p) {
|
|
|
|
return _parameters == other_p._parameters && _body == other_p._body;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
public override object Inner() {
|
|
|
|
throw new ApplicationException("This is not sensible");
|
|
|
|
}
|
|
|
|
public override string ToString() {
|
|
|
|
return $"(lambda {_parameters} {_body})";
|
|
|
|
}
|
|
|
|
|
|
|
|
public Expression Call(Executor e, IList<Expression> args) {
|
|
|
|
var p = _parameters.expressions.Select(x => x.Inner().ToString()).ToList();
|
|
|
|
Executor new_e = new Executor(new SubEnvironment(e.environment), e.builtins, e.builtinsLater);
|
|
|
|
foreach (var tuple in Zip<string, Expression>(p, args)) {
|
|
|
|
new_e.environment.Set(tuple.Item1, tuple.Item2);
|
|
|
|
}
|
|
|
|
return new_e.eval(_body);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public interface IEnvironment<K, V> {
|
|
|
|
public V Get(K k);
|
|
|
|
public void Set(K k, V v);
|
|
|
|
public IEnvironment<K, V>? Find(K k);
|
|
|
|
}
|
|
|
|
|
|
|
|
public class Environment : Dictionary<string, Expression>, IEnvironment<string, Expression> {
|
|
|
|
public Expression? Get(string k) {
|
|
|
|
if (TryGetValue(k, out Expression v)) {
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
public void Set(string k, Expression v) {
|
|
|
|
Add(k, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
public IEnvironment<string, Expression>? Find(string k) {
|
|
|
|
if (ContainsKey(k)) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public class SubEnvironment : Dictionary<string, Expression>, IEnvironment<string, Expression> {
|
|
|
|
private IEnvironment<string, Expression> _super;
|
|
|
|
public SubEnvironment(IEnvironment<string, Expression> super) {
|
|
|
|
_super = super;
|
|
|
|
}
|
|
|
|
public Expression? Get(string k) {
|
|
|
|
if (TryGetValue(k, out Expression v)) {
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
public void Set(string k, Expression v) {
|
|
|
|
Add(k, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
public IEnvironment<string, Expression>? Find(string k) {
|
|
|
|
if (ContainsKey(k)) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
return _super.Find(k);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-29 18:29:40 +02:00
|
|
|
public class Builtins : Dictionary<string, Function> {
|
|
|
|
public Builtins() : base() {
|
|
|
|
this["+"] = _add;
|
|
|
|
this["-"] = _sub;
|
|
|
|
this["*"] = _mul;
|
|
|
|
this["/"] = _div;
|
|
|
|
this["%"] = _mod;
|
|
|
|
this[">"] = _gt;
|
|
|
|
this["<"] = _lt;
|
|
|
|
this[">="] = _ge;
|
|
|
|
this["<="] = _le;
|
|
|
|
this["eq?"] = _eq;
|
|
|
|
this["="] = _eq;
|
2024-10-27 00:29:56 +02:00
|
|
|
this["!="] = _ne;
|
2024-06-29 18:29:40 +02:00
|
|
|
this["abs"] = _abs;
|
|
|
|
this["append"] = _append;
|
|
|
|
this["begin"] = _begin;
|
|
|
|
this["car"] = _car;
|
|
|
|
this["cdr"] = _cdr;
|
|
|
|
this["cons"] = _cons;
|
2024-10-25 20:20:18 +02:00
|
|
|
this["not"] = _not;
|
2024-06-29 18:29:40 +02:00
|
|
|
this["length"] = _length;
|
|
|
|
this["haskeys"] = _haskeys;
|
|
|
|
this["getitems"] = _getitems;
|
2024-10-25 02:18:13 +02:00
|
|
|
this["invoke"] = _invoke;
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
|
|
|
|
2024-06-29 18:29:40 +02:00
|
|
|
private static T _agg<T>(Func<T, T, T> op, IList<T> args) {
|
2024-06-27 01:47:44 +02:00
|
|
|
T agg = args[0];
|
|
|
|
foreach (var arg in args.Skip(1)) {
|
|
|
|
agg = op(agg, arg);
|
|
|
|
}
|
|
|
|
return agg;
|
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
private static Expression _add(IList<Expression> args) {
|
2024-06-27 01:47:44 +02:00
|
|
|
Expression first = args[0];
|
|
|
|
switch (first) {
|
|
|
|
case Integer i:
|
2024-06-29 18:29:40 +02:00
|
|
|
return _agg((a, b) => a + b, args.Select(x => (Integer) x).ToList());
|
2024-06-27 01:47:44 +02:00
|
|
|
case Compiler.String s:
|
2024-06-29 18:29:40 +02:00
|
|
|
return _agg((a, b) => a + b, args.Select(x => (Compiler.String) x).ToList());
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
throw new ApplicationException();
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
private static Expression _sub(IList<Expression> args) {
|
2024-06-27 01:47:44 +02:00
|
|
|
Expression first = args[0];
|
|
|
|
switch (first) {
|
|
|
|
case Integer i:
|
2024-06-29 18:29:40 +02:00
|
|
|
return _agg((a, b) => a - b, args.Select(x => (Integer) x).ToList());
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
throw new ApplicationException();
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
private static Expression _mul(IList<Expression> args) {
|
2024-06-27 01:47:44 +02:00
|
|
|
Expression first = args[0];
|
|
|
|
switch (first) {
|
|
|
|
case Integer i:
|
2024-06-29 18:29:40 +02:00
|
|
|
return _agg((a, b) => a * b, args.Select(x => (Integer) x).ToList());
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
throw new ApplicationException();
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
private static Expression _div(IList<Expression> args) {
|
2024-06-27 01:47:44 +02:00
|
|
|
Expression first = args[0];
|
|
|
|
switch (first) {
|
|
|
|
case Integer i:
|
2024-06-29 18:29:40 +02:00
|
|
|
return _agg((a, b) => a / b, args.Select(x => (Integer) x).ToList());
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
throw new ApplicationException();
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
private static Expression _mod(IList<Expression> args) {
|
2024-06-27 01:47:44 +02:00
|
|
|
Expression first = args[0];
|
|
|
|
switch (first) {
|
|
|
|
case Integer i:
|
2024-06-29 18:29:40 +02:00
|
|
|
return _agg((a, b) => a % b, args.Select(x => (Integer) x).ToList());
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
throw new ApplicationException();
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
private static E _cmp<T, E>(Func<T, T, E> op, IList<T> args) {
|
2024-06-27 01:47:44 +02:00
|
|
|
T first = args[0];
|
|
|
|
T second = args[1];
|
|
|
|
return op(first, second);
|
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
private static Expression _gt(IList<Expression> args) {
|
2024-06-27 01:47:44 +02:00
|
|
|
Expression first = args[0];
|
|
|
|
switch (first) {
|
|
|
|
case Integer i:
|
2024-06-29 18:29:40 +02:00
|
|
|
return _cmp((a, b) => a > b, args.Select(x => (Integer) x).ToList());
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
throw new ApplicationException();
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
private static Expression _lt(IList<Expression> args) {
|
2024-06-27 01:47:44 +02:00
|
|
|
Expression first = args[0];
|
|
|
|
switch (first) {
|
|
|
|
case Integer i:
|
2024-06-29 18:29:40 +02:00
|
|
|
return _cmp((a, b) => a < b, args.Select(x => (Integer) x).ToList());
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
throw new ApplicationException();
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
private static Expression _ge(IList<Expression> args) {
|
2024-06-27 01:47:44 +02:00
|
|
|
Expression first = args[0];
|
|
|
|
switch (first) {
|
|
|
|
case Integer i:
|
2024-06-29 18:29:40 +02:00
|
|
|
return _cmp((a, b) => a >= b, args.Select(x => (Integer) x).ToList());
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
throw new ApplicationException();
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
private static Expression _le(IList<Expression> args) {
|
2024-06-27 01:47:44 +02:00
|
|
|
Expression first = args[0];
|
|
|
|
switch (first) {
|
|
|
|
case Integer i:
|
2024-06-29 18:29:40 +02:00
|
|
|
return _cmp((a, b) => a <= b, args.Select(x => (Integer) x).ToList());
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
throw new ApplicationException();
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
private static Expression _eq(IList<Expression> args) {
|
2024-10-27 00:28:08 +02:00
|
|
|
bool r = _cmp((a, b) => a == b, args);
|
|
|
|
return new Compiler.Boolean(r);
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
private static Expression _ne(IList<Expression> args) {
|
2024-10-27 00:28:08 +02:00
|
|
|
bool r = _cmp((a, b) => a != b, args);
|
|
|
|
return new Compiler.Boolean(r);
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
private static Expression _abs(IList<Expression> args) {
|
2024-06-27 01:47:44 +02:00
|
|
|
Expression first = args[0];
|
|
|
|
switch (first) {
|
|
|
|
case Integer i:
|
|
|
|
return i.value >= 0 ? i : new Integer(-i.value);
|
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
throw new ApplicationException();
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
private static Expression _append(IList<Expression> args) {
|
2024-06-27 01:47:44 +02:00
|
|
|
Expression first = args[0];
|
|
|
|
switch (first) {
|
|
|
|
case List l:
|
|
|
|
return l + new List(args);
|
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
throw new ApplicationException();
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
private static Expression _begin(IList<Expression> args) {
|
|
|
|
return args.Last();
|
|
|
|
}
|
|
|
|
private static Expression _car(IList<Expression> args) {
|
|
|
|
return ((List) args.First()).expressions.First();
|
|
|
|
}
|
|
|
|
private static Expression _cdr(IList<Expression> args) {
|
|
|
|
return new List(((List) args.First()).expressions.Skip(1).ToList());
|
|
|
|
}
|
|
|
|
private static Expression _cons(IList<Expression> args) {
|
|
|
|
switch (args[1]) {
|
|
|
|
case Compiler.List other_list:
|
|
|
|
return (new Compiler.List(new []{args[0]}.ToList()) + new Compiler.List(other_list.expressions));
|
|
|
|
case Atom other_atom:
|
|
|
|
return new Compiler.List(new[]{args[0], args[1]}.ToList());
|
|
|
|
}
|
|
|
|
throw new ApplicationException();
|
|
|
|
}
|
2024-10-25 20:20:18 +02:00
|
|
|
private static Expression _not(IList<Expression> args) {
|
|
|
|
if (args[0] == new Compiler.Boolean(false)) {
|
|
|
|
return new Compiler.Boolean(true);
|
|
|
|
}
|
|
|
|
return new Compiler.Boolean(false);
|
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
private static Expression _length(IList<Expression> args) {
|
|
|
|
return new Integer(((Compiler.List)args[0]).expressions.Count());
|
|
|
|
}
|
|
|
|
private static Expression _haskeys(IList<Expression> args) {
|
|
|
|
Compiler.Object o = (Compiler.Object) args[0];
|
|
|
|
foreach (var e in args.Skip(1)) {
|
|
|
|
Compiler.String s = (Compiler.String) e;
|
|
|
|
PropertyInfo? pi = o.value.GetType().GetProperty(s.value);
|
|
|
|
if (pi != null) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
MethodInfo? mi = o.value.GetType().GetMethod(s.value);
|
|
|
|
if (mi != null) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
FieldInfo? fi = o.value.GetType().GetField(s.value);
|
|
|
|
if (fi != null) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
return new Compiler.Boolean(false);
|
|
|
|
}
|
|
|
|
return new Compiler.Boolean(true);
|
|
|
|
}
|
|
|
|
private static Expression _getitems(IList<Expression> args) {
|
|
|
|
Compiler.Object o = (Compiler.Object) args[0];
|
|
|
|
IList<Expression> r = new List<Expression>();
|
|
|
|
foreach (var e in args.Skip(1)) {
|
|
|
|
Compiler.String s = (Compiler.String) e;
|
|
|
|
PropertyInfo? pi = o.value.GetType().GetProperty(s.value);
|
|
|
|
if (pi != null) {
|
|
|
|
r.Add(Compiler.Object.FromBase(pi.GetValue(o.value)));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
FieldInfo? fi = o.value.GetType().GetField(s.value);
|
|
|
|
if (fi != null) {
|
|
|
|
r.Add(Compiler.Object.FromBase(fi.GetValue(o.value)));
|
|
|
|
continue;
|
|
|
|
}
|
2024-10-25 02:18:13 +02:00
|
|
|
throw new ApplicationException($"{o.value} has no property or field {s.value}");
|
2024-06-29 18:29:40 +02:00
|
|
|
}
|
|
|
|
return new Compiler.List(r);
|
|
|
|
}
|
2024-10-25 02:18:13 +02:00
|
|
|
|
|
|
|
private static Expression _invoke(IList<Expression> args) {
|
|
|
|
Compiler.Object o = (Compiler.Object) args[0];
|
|
|
|
Compiler.String s = (Compiler.String) args[1];
|
|
|
|
Compiler.List l = (Compiler.List) args[2];
|
|
|
|
IList<Expression> r = new List<Expression>();
|
|
|
|
MethodInfo? mi = o.value.GetType().GetMethod(s.value);
|
|
|
|
if (mi == null) {
|
|
|
|
throw new ApplicationException($"{o.value} has not method {s.value}");
|
|
|
|
}
|
|
|
|
return Compiler.Object.FromBase(mi.Invoke(o.value, (object?[]?) l.Inner()));
|
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
}
|
2024-06-27 01:47:44 +02:00
|
|
|
|
2024-06-29 18:29:40 +02:00
|
|
|
public class BuiltinsLater : Dictionary<string, FunctionLater> {
|
|
|
|
public BuiltinsLater() : base() {
|
|
|
|
this["if"] = _if;
|
|
|
|
this["define"] = _define;
|
2024-10-26 03:49:52 +02:00
|
|
|
this["lambda"] = _lambda;
|
2024-06-29 18:29:40 +02:00
|
|
|
this["apply"] = _apply;
|
2024-10-25 20:20:18 +02:00
|
|
|
this["and"] = _and;
|
|
|
|
this["or"] = _or;
|
2024-06-29 18:29:40 +02:00
|
|
|
}
|
|
|
|
private static Expression _if(Executor e, IList<Expression> args) {
|
2024-10-25 20:20:18 +02:00
|
|
|
bool test = e.eval(args[0]) != (new Compiler.Boolean(false));
|
2024-06-29 18:29:40 +02:00
|
|
|
return e.eval(args[1 + (test ? 0 : 1)]);
|
|
|
|
}
|
|
|
|
private static Expression _define(Executor e, IList<Expression> args) {
|
2024-10-26 03:49:52 +02:00
|
|
|
var refname = ((Symbol) args[0]).name;
|
|
|
|
e.environment.Set(refname, e.eval(args[1]));
|
2024-06-29 18:29:40 +02:00
|
|
|
return new Compiler.Boolean(false); // NOOP
|
|
|
|
}
|
2024-10-26 03:49:52 +02:00
|
|
|
private static Expression _lambda(Executor e, IList<Expression> args) {
|
|
|
|
return new Procedure((Compiler.List) args[0], args[1]);
|
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
private static Expression _apply(Executor e, IList<Expression> args) {
|
|
|
|
if (args[0].GetType() != typeof(Symbol)) {
|
|
|
|
throw new ApplicationException();
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
if (args[1].GetType() != typeof(List)) {
|
|
|
|
throw new ApplicationException();
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
Symbol arg0 = (Compiler.Symbol) args[0];
|
|
|
|
Compiler.List other_args = (Compiler.List) args[1];
|
|
|
|
return e.EvalFunction(arg0, other_args.expressions);
|
|
|
|
}
|
2024-10-25 20:20:18 +02:00
|
|
|
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;
|
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public class Executor {
|
2024-10-26 03:49:52 +02:00
|
|
|
IEnvironment<string, Expression> _environment;
|
2024-06-29 18:29:40 +02:00
|
|
|
Builtins _builtins;
|
|
|
|
BuiltinsLater _builtinsLater;
|
2024-10-26 03:49:52 +02:00
|
|
|
public Executor(IEnvironment<string, Expression> environment, Builtins builtins, BuiltinsLater builtinsLater) {
|
2024-06-29 18:29:40 +02:00
|
|
|
_environment = environment;
|
|
|
|
_builtins = builtins;
|
|
|
|
_builtinsLater = builtinsLater;
|
|
|
|
}
|
|
|
|
public Executor() {
|
|
|
|
_environment = new Environment();
|
|
|
|
_builtins = new Builtins();
|
|
|
|
_builtinsLater = new BuiltinsLater();
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
|
|
|
|
2024-10-26 03:49:52 +02:00
|
|
|
public IEnvironment<string, Expression> environment { get => _environment; }
|
2024-06-29 18:29:40 +02:00
|
|
|
public Builtins builtins { get => _builtins; }
|
|
|
|
public BuiltinsLater builtinsLater { get => _builtinsLater; }
|
|
|
|
|
|
|
|
public Expression EvalFunction(Symbol fcname, IList<Expression> args) {
|
2024-10-26 03:49:52 +02:00
|
|
|
if (_environment.Find(fcname.name) is IEnvironment<string, Expression> _e) {
|
|
|
|
Expression? first = _e.Get(fcname.name);
|
2024-06-29 18:29:40 +02:00
|
|
|
return new List(new []{first}.ToList()) + new List(args.Select(x => eval(x)).ToList());
|
|
|
|
}
|
|
|
|
if (_builtins.ContainsKey(fcname.name)) {
|
|
|
|
Function fc = _builtins[fcname.name];
|
|
|
|
return fc(args.Select(x => eval(x)).ToList());
|
|
|
|
}
|
|
|
|
if (_builtinsLater.ContainsKey(fcname.name)) {
|
|
|
|
FunctionLater fc = _builtinsLater[fcname.name];
|
|
|
|
return fc(this, args);
|
|
|
|
}
|
|
|
|
throw new ApplicationException($"Key '{fcname.name}' not found in environment or builtins");
|
|
|
|
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
|
|
|
|
2024-06-29 18:29:40 +02:00
|
|
|
public Expression eval(Expression expression) {
|
2024-06-27 01:47:44 +02:00
|
|
|
switch (expression) {
|
|
|
|
case Symbol s:
|
2024-10-26 03:49:52 +02:00
|
|
|
return _environment.Find(s.name).Get(s.name);
|
2024-06-29 18:29:40 +02:00
|
|
|
case Compiler.Boolean b:
|
|
|
|
return b;
|
2024-06-27 01:47:44 +02:00
|
|
|
case Integer i:
|
2024-06-29 18:29:40 +02:00
|
|
|
return i;
|
|
|
|
case Compiler.String s:
|
|
|
|
return s;
|
2024-10-26 03:49:52 +02:00
|
|
|
case Procedure p:
|
|
|
|
return p;
|
2024-06-27 01:47:44 +02:00
|
|
|
case List list:
|
2024-10-27 00:28:08 +02:00
|
|
|
if (list.expressions.Count == 0) {
|
|
|
|
return list;
|
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
// do we really want to allow shadowing of builtins?
|
2024-06-27 01:47:44 +02:00
|
|
|
if (list.expressions[0].GetType() == typeof(Symbol)) {
|
2024-10-26 03:49:52 +02:00
|
|
|
return eval(EvalFunction((Symbol) list.expressions[0], list.expressions.Skip(1).ToList()));
|
|
|
|
}
|
|
|
|
if (list.expressions[0].GetType() == typeof(Procedure)) {
|
|
|
|
Procedure procedure = (Procedure) list.expressions[0];
|
|
|
|
return eval(procedure.Call(this, list.expressions.Skip(1).ToList()));
|
|
|
|
}
|
|
|
|
var l = new List(list.expressions.Select(x => eval(x)).ToList());
|
|
|
|
if (l.expressions[0].GetType() == typeof(Procedure)) {
|
|
|
|
return eval(l);
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-10-26 03:49:52 +02:00
|
|
|
return l;
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-10-26 03:49:52 +02:00
|
|
|
throw new ApplicationException($"Not handled case '{expression}'");
|
2024-06-27 01:47:44 +02:00
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
public Expression eval(Parser p) {
|
2024-06-27 01:47:44 +02:00
|
|
|
return eval(p.parse());
|
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
public Expression eval(StringTokenStream sts) {
|
2024-06-27 01:47:44 +02:00
|
|
|
return eval(new Parser(sts));
|
|
|
|
}
|
2024-06-29 18:29:40 +02:00
|
|
|
public Expression eval(string p) {
|
2024-06-27 01:47:44 +02:00
|
|
|
return eval(StringTokenStream.generate(p));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|