using System.Diagnostics; using Jellyfin.Plugin.SmartPlaylist.Lisp; namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler { public class Parser { internal StringTokenStream _sts; public Parser(StringTokenStream tokens) { _sts = tokens; } public Parser(string s) { _sts = StringTokenStream.generate(s); } public Expression parse() { Token token = _sts.Get(); switch (token) { case GroupingToken gt: return parse_grouping(gt, gt.closing_value); case AtomToken at: return parse_atom(at); case SpaceToken sp: return parse(); } return parse(); } Expression parse_string(GroupingToken start, GroupingToken? end) { Debug.Assert(end != null); Debug.Assert(start.value == end.value); Debug.Assert("\"".Contains(start.value)); string r = ""; bool exit_ok = false; while (_sts.Available() > 0) { Token t = _sts.Get(); if (t.value == end.value) { exit_ok = true; break; } r += t.value; } if (!exit_ok) { throw new ApplicationException($"Failed to parse string, are you missing the closing quotes? String is: {r}"); } _sts.Commit(); return new String(r); } Expression parse_comment(GroupingToken start, GroupingToken? end) { Debug.Assert(end != null); Debug.Assert(";".Contains(start.value)); while (_sts.Available() > 0) { Token t = _sts.Get(); if (t.value == end.value) { break; } } _sts.Commit(); return parse(); } Expression parse_quote(GroupingToken start, GroupingToken? end) { Debug.Assert(end == null); Debug.Assert("'".Contains(start.value)); return Cons.FromList(new Expression[]{ new Symbol("quote"), parse()}); } Expression parse_grouping(GroupingToken start, GroupingToken? end) { if ("\"".Contains(start.value)) { return parse_string(start, end); } if ("'".Contains(start.value)) { return parse_quote(start, end); } if (";".Contains(start.value)) { return parse_comment(start, end); } if (end == null) { throw new ApplicationException($"Don't know how to parse grouping starting with token '{start.value}'"); } IList expressions = new List(); bool exit_ok = false; while (_sts.Available() > 0) { Token t = _sts.Get(); if (t.value == end.value) { exit_ok = true; _sts.Commit(); break; } if (t is SpaceToken) { // need this here because those tokens can never // return an expression and trying to parse the last // expression will not work if its only spaces and a // closing parentheses. continue; } _sts.Rewind(1); expressions.Add(parse()); } var r = Cons.FromList(expressions); if (!exit_ok) { throw new ApplicationException($"Failed to parse grouping, are you missing some closing braces? Parsed expressions: {r}"); } return r; } Expression parse_atom(AtomToken at) { int parsed_value; if (int.TryParse(at.value, out parsed_value)) { _sts.Commit(); return new Integer(parsed_value); } if (at.value.Equals("t")) { return Boolean.TRUE; } if (at.value.Equals("nil")) { return Boolean.FALSE; } _sts.Commit(); return new Symbol(at.value); } } }