Last active
September 11, 2021 12:42
-
-
Save vendethiel/e05b6574e4b547b3af8af046d57b1bac to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
namespace FauxCombinator | |
{ | |
public abstract class Parser<T> | |
{ | |
protected (T, string) parseSeparatedBy(string text, string sep, Func<string, (T, string)> subparse, Func<List<T>, T> ctor) | |
{ | |
var nodes = new List<T>(); | |
while (true) | |
{ | |
var (node, rest) = subparse(text); | |
if (node == null) | |
return (default, rest); | |
nodes.Add(node); | |
// New text | |
text = rest; | |
// Can we find a separator? If yes, return a list of all these elements | |
if (!text.StartsWith(sep)) | |
return (ctor(nodes), text); | |
// Otherwise, remove the separator and parse the next element | |
text = text.Substring(sep.Length); | |
} | |
} | |
public (T, string) parseBetween(string text, string start, string end, Func<string, (T, string)> subparseBetween, Func<string, (T, string)> subparseOther) | |
{ | |
// See if we are surrounded | |
if (text.StartsWith(start)) | |
{ | |
// If we are, parse that way | |
var (sub, rest) = subparseBetween(text.Substring(1)); | |
if (sub == null) | |
return (default, rest); | |
if (!text.StartsWith(end)) | |
return (default, "Expected closing " + end); | |
return (sub, rest.Substring(end.Length)); | |
} | |
// Otherwise, continue parsing a level below | |
return subparseOther(text); | |
} | |
public (int, string) parseInt(string text) | |
{ | |
var (iChar, rest) = parseOneOf(text, "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"); | |
if (iChar == null) | |
return (null, "Could not parse int"); | |
var i = Int32.Parse(iChar); | |
var (i2Char, rest2) = parseInt(rest); | |
if (i2Char == null) | |
return (i, rest); | |
var i2 = Int32.Parse(i2Char); | |
return (i * 10 + i2, rest2); | |
} | |
public (string, string) parseOneOf(string text, params string[] options) | |
{ | |
foreach (string option in options) | |
{ | |
if (text.StartsWith(option)) | |
{ | |
var l = option.Length; | |
return (text.Substring(0, l), text.Substring(l)); | |
} | |
} | |
return (null, "No option match"); | |
} | |
} | |
public class ConditionParser : Parser<ConditionParser.IConditionNode> | |
{ | |
public interface IConditionNode | |
{ | |
} | |
public class OrNode : IConditionNode | |
{ | |
public OrNode(List<IConditionNode> nodes) | |
{ | |
Nodes = nodes; | |
} | |
public List<IConditionNode> Nodes { get; } | |
} | |
public class AndNode : IConditionNode | |
{ | |
public AndNode(List<IConditionNode> nodes) | |
{ | |
Nodes = nodes; | |
} | |
public List<IConditionNode> Nodes { get; } | |
} | |
public class OpNode | |
{ | |
public OpNode(string op, string lhs, string rhs) | |
{ | |
Op = op; | |
Lhs = lhs; | |
Rhs = rhs; | |
} | |
public string Op { get; } | |
public string Lhs { get; } | |
public string Rhs { get; } | |
} | |
public (IConditionNode, string) parse(string text) | |
{ | |
return parseOrLevel(text); | |
} | |
public (IConditionNode, string) parseOrLevel(string text) | |
{ | |
return parseSeparatedBy(text, "|", parseAndLevel, nodes => new OrNode(nodes)); | |
} | |
public (IConditionNode, string) parseAndLevel(string text) | |
{ | |
return parseSeparatedBy(text, "&", parseParenLevel, nodes => new AndNode(nodes)); | |
} | |
public (IConditionNode, string) parseParenLevel(string text) | |
{ | |
return parseBetween(text, "(", ")", parse, parseOpLevel); | |
} | |
public (string, string) parseValue(string text) | |
{ | |
var (i, iRest) = parseInt(text); | |
if (i != null) | |
return (i.ToString, iRest); | |
return parseStat(text); | |
} | |
public (string, string) parseStat(string text) | |
{ | |
return parseOneOf(text, "Sc", "PB"); | |
} | |
public (string, string) parseOp(string text) | |
{ | |
return parseOneOf(text, "=", "!", ">", "<"); | |
} | |
public (IConditionNode, string) parseOpLevel(string text) | |
{ | |
var (lhs, lhsRest) = parseValue(text); | |
if (lhs == null) | |
return (null, lhsRest); | |
var (op, opRest) = parseOp(lhsRest); | |
if (op == null) | |
return (null, opRest); | |
var (rhs, rest) = parseValue(opRest); | |
if (rhs == null) | |
return (null, rest); | |
return (new OpNode(op, lhs, rhs), rest); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment