145 lines
5 KiB
C#
145 lines
5 KiB
C#
|
using System.Reflection;
|
||
|
using Jellyfin.Plugin.SmartPlaylist.Util;
|
||
|
|
||
|
namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler {
|
||
|
public interface IToken<T> {
|
||
|
T value { get; }
|
||
|
abstract static IToken<T>? take<E>(E program);
|
||
|
}
|
||
|
|
||
|
public abstract class Token<T>: IToken<T> , IEquatable<Token<T>> {
|
||
|
protected readonly T _value;
|
||
|
protected Token(T value) {
|
||
|
_value = value;
|
||
|
}
|
||
|
public T value { get => _value; }
|
||
|
public static IToken<T>? take<E>(E program) {
|
||
|
throw new NotImplementedException("Subclass this class");
|
||
|
}
|
||
|
public bool Equals(Token<T>? b) {
|
||
|
return b != null && _value != null && _value.Equals(b._value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class SpaceToken : Token<string> {
|
||
|
private SpaceToken(string value) : base(value) {}
|
||
|
private static IToken<string>? take(CharStream program) {
|
||
|
if (program.available() == 0) {
|
||
|
return null;
|
||
|
}
|
||
|
if (program.get() == ' ') {
|
||
|
return new SpaceToken(" ");
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class GroupingToken: Token<string> {
|
||
|
private GroupingToken(string value) : base(value) {}
|
||
|
private static IToken<string>? take(CharStream program) {
|
||
|
if (program.available() == 0) {
|
||
|
return null;
|
||
|
}
|
||
|
char t = program.get();
|
||
|
if ("()\"'".Contains(t)) {
|
||
|
return new GroupingToken(t.ToString());
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
private GroupingToken? _closing_value() {
|
||
|
if (_value == "(") {
|
||
|
return new GroupingToken(")");
|
||
|
} else if (_value == ")") {
|
||
|
return null;
|
||
|
}
|
||
|
return new GroupingToken(_value);
|
||
|
}
|
||
|
public GroupingToken? closing_value { get => _closing_value(); }
|
||
|
}
|
||
|
|
||
|
class AtomToken : Token<string> {
|
||
|
private AtomToken(string value) : base(value) {}
|
||
|
private static IToken<string>? take(CharStream program) {
|
||
|
string value = "";
|
||
|
while (program.available() > 0) {
|
||
|
char t = program.get();
|
||
|
if (!"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".Contains(t)) {
|
||
|
if (value.Equals("")) {
|
||
|
return null;
|
||
|
}
|
||
|
program.rewind(1);
|
||
|
return new AtomToken(value);
|
||
|
}
|
||
|
value += t;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class OperatorToken : Token<string> {
|
||
|
private OperatorToken(string value) : base(value) {}
|
||
|
private static IToken<string>? take(CharStream program) {
|
||
|
if (program.available() == 0) {
|
||
|
return null;
|
||
|
}
|
||
|
return new OperatorToken(program.get().ToString());
|
||
|
//char t = program.get();
|
||
|
//if ("+-*/%".Contains(t)) {
|
||
|
// return new OperatorToken(t.ToString());
|
||
|
//}
|
||
|
//return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class CharStream: Stream<char> {
|
||
|
public CharStream(IList<char> items) : base(items) {}
|
||
|
public CharStream(string items) : base(items.ToCharArray().Cast<char>().ToList()) {}
|
||
|
}
|
||
|
|
||
|
public class StringTokenStream : Stream<Token<string>> {
|
||
|
private static readonly IList<Type> _classes = new List<Type> {
|
||
|
typeof(SpaceToken),
|
||
|
typeof(GroupingToken),
|
||
|
typeof(AtomToken),
|
||
|
typeof(OperatorToken),
|
||
|
};
|
||
|
protected StringTokenStream(IList<Token<string>> tokens) : base(tokens) {}
|
||
|
private static StringTokenStream generate(CharStream program) {
|
||
|
IList<Token<string>> result = new List<Token<string>>();
|
||
|
int prev_avail = 0;
|
||
|
while (true) {
|
||
|
if (prev_avail == program.available() && prev_avail == 0) {
|
||
|
break;
|
||
|
} else if (prev_avail == program.available()) {
|
||
|
throw new ApplicationException("Program is invalid");
|
||
|
}
|
||
|
prev_avail = program.available();
|
||
|
foreach (Type c in _classes) {
|
||
|
Token<string>? t = (Token<string>?) c.GetMethod(
|
||
|
"take",
|
||
|
BindingFlags.NonPublic | BindingFlags.Static,
|
||
|
null,
|
||
|
CallingConventions.Any,
|
||
|
new Type[] { typeof(CharStream) },
|
||
|
null
|
||
|
)?.Invoke(
|
||
|
null,
|
||
|
new object[]{program}
|
||
|
);
|
||
|
if (t == null) {
|
||
|
program.rewind();
|
||
|
continue;
|
||
|
}
|
||
|
program.commit();
|
||
|
result.Add(t);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return new StringTokenStream(result);
|
||
|
}
|
||
|
public static StringTokenStream generate(string program) {
|
||
|
return StringTokenStream.generate(new CharStream(program));
|
||
|
}
|
||
|
}
|
||
|
}
|