Skip to content

Instantly share code, notes, and snippets.

@vendethiel
Last active September 11, 2021 12:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vendethiel/e05b6574e4b547b3af8af046d57b1bac to your computer and use it in GitHub Desktop.
Save vendethiel/e05b6574e4b547b3af8af046d57b1bac to your computer and use it in GitHub Desktop.
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