using System.Diagnostics; using Jellyfin.Plugin.SmartPlaylist.Lisp; namespace Jellyfin.Plugin.SmartPlaylist.Lisp.Compiler { public class Parser { private 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 = ""; while (_sts.Available() > 0) { Token t = _sts.Get(); if (t.value == end.value) { break; } r += t.value; } _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); } Debug.Assert(end != null); IList expressions = new List(); while (_sts.Available() > 0) { Token t = _sts.Get(); if (t.value == end.value) { _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()); } return Cons.FromList(expressions); } 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); } } }