using System.Reflection; using Jellyfin.Plugin.SmartPlaylist.Util; namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler { public interface IToken { T value { get; } abstract static IToken? take(E program); } public abstract class Token: IToken , IEquatable> { protected readonly T _value; protected Token(T value) { _value = value; } public T value { get => _value; } public static IToken? take(E program) { throw new NotImplementedException("Subclass this class"); } public bool Equals(Token? b) { return b != null && _value != null && _value.Equals(b._value); } } class SpaceToken : Token { private SpaceToken(string value) : base(value) {} private static IToken? take(CharStream program) { string spaces = " \n"; if (program.Available() == 0) { return null; } var t = program.Get(); if (spaces.Contains(t)) { return new SpaceToken(t.ToString()); } return null; } } class GroupingToken: Token { private GroupingToken(string value) : base(value) {} private static IToken? 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; } else if (_value == "'") { return null; } else if (_value == ";") { return new GroupingToken("\n"); } return new GroupingToken(_value); } public GroupingToken? closing_value { get => _closing_value(); } } class AtomToken : Token { private AtomToken(string value) : base(value) {} private static IToken? take(CharStream program) { string value = ""; while (program.Available() > 0) { char t = program.Get(); if (" \n()\"".Contains(t)) { if (value.Equals("")) { return null; } program.Rewind(1); return new AtomToken(value); } value += t; } if (value.Equals("")) { return null; } return new AtomToken(value); } } class CharStream: Stream { public CharStream(IList items) : base(items) {} public CharStream(string items) : base(items.ToCharArray().Cast().ToList()) {} } public class StringTokenStream : Stream> { private static readonly IList _classes = new List { typeof(SpaceToken), typeof(GroupingToken), typeof(AtomToken), }; protected StringTokenStream(IList> tokens) : base(tokens) {} private static StringTokenStream generate(CharStream program) { IList> result = new List>(); 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, still available: {program.Available()}"); } prev_avail = program.Available(); foreach (Type c in _classes) { Token? t = (Token?) 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)); } } }