jellyfin-smart-playlist/Jellyfin.Plugin.SmartPlaylist/Lisp/Compiler/Parser.cs

109 lines
3.6 KiB
C#

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<string> 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<string> 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<string> 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<Expression> expressions = new List<Expression>();
while (_sts.Available() > 0) {
Token<string> 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);
}
}
}