CultPong/Assets/Scripts/Parsley/Grammar.cs
2020-09-25 07:12:38 +01:00

178 lines
No EOL
6.5 KiB
C#

namespace Parsley
{
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Primitives;
public abstract class Grammar
{
protected void InferGrammarRuleNames()
{
var grammarRules =
GetType()
.GetRuntimeFields()
.Where(rule =>
rule.FieldType.GetTypeInfo().IsGenericType &&
rule.FieldType.GetGenericTypeDefinition() == typeof (GrammarRule<>));
foreach (var rule in grammarRules)
{
var value = rule.GetValue(this);
if (value != null)
{
var nameProperty = value.GetType().GetRuntimeProperty("Name");
var name = nameProperty.GetValue(value, null);
if (name as string == null)
nameProperty.SetValue(value, rule.Name, null);
}
}
}
public static Parser<T> Fail<T>()
{
return new FailingParser<T>();
}
public static Parser<Token> EndOfInput
{
get { return Token(TokenKind.EndOfInput); }
}
public static Parser<Token> Token(TokenKind kind)
{
return new TokenByKindParser(kind);
}
public static Parser<Token> Token(string expectation)
{
return new TokenByLiteralParser(expectation);
}
/// <summary>
/// ZeroOrMore(p) repeatedly applies an parser p until it fails, returing
/// the list of values returned by successful applications of p. At the
/// end of the sequence, p must fail without consuming input, otherwise the
/// sequence will fail with the error reported by p.
/// </summary>
public static Parser<IEnumerable<T>> ZeroOrMore<T>(Parser<T> item)
{
return new ZeroOrMoreParser<T>(item);
}
/// <summary>
/// OneOrMore(p) behaves like ZeroOrMore(p), except that p must succeed at least one time.
/// </summary>
public static Parser<IEnumerable<T>> OneOrMore<T>(Parser<T> item)
{
return from first in item
from rest in ZeroOrMore(item)
select List(first, rest);
}
/// <summary>
/// ZeroOrMore(p, s) parses zero or more occurrences of p separated by occurrences of s,
/// returning the list of values returned by successful applications of p.
/// </summary>
public static Parser<IEnumerable<T>> ZeroOrMore<T, S>(Parser<T> item, Parser<S> separator)
{
return Choice(OneOrMore(item, separator), Zero<T>());
}
/// <summary>
/// OneOrMore(p, s) behaves like ZeroOrMore(p, s), except that p must succeed at least one time.
/// </summary>
public static Parser<IEnumerable<T>> OneOrMore<T, S>(Parser<T> item, Parser<S> separator)
{
return from first in item
from rest in ZeroOrMore(from sep in separator
from next in item
select next)
select List(first, rest);
}
/// <summary>
/// Between(left, goal, right) parses its arguments in order. If all three
/// parsers succeed, the result of the goal parser is returned.
/// </summary>
public static Parser<TGoal> Between<TLeft, TGoal, TRight>(Parser<TLeft> left,
Parser<TGoal> goal,
Parser<TRight> right)
{
return from L in left
from G in goal
from R in right
select G;
}
/// <summary>
/// Optional(p) is equivalent to p whenever p succeeds or when p fails after consuming input.
/// If p fails without consuming input, Optional(p) succeeds.
/// </summary>
public static Parser<T> Optional<T>(Parser<T> parser)
{
var nothing = default(T).SucceedWithThisValue();
return Choice(parser, nothing);
}
/// <summary>
/// The parser Attempt(p) behaves like parser p, except that it pretends
/// that it hasn't consumed any input when an error occurs. This combinator
/// is used whenever arbitrary look ahead is needed.
/// </summary>
public static Parser<T> Attempt<T>(Parser<T> parse)
{
return new AttemptParser<T>(parse);
}
/// <summary>
/// Choice() always fails without consuming input.
///
/// Choice(p) is equivalent to p.
///
/// For 2 or more inputs, parsers are applied from left
/// to right. If a parser succeeds, its reply is returned.
/// If a parser fails without consuming input, the next parser
/// is attempted. If a parser fails after consuming input,
/// subsequent parsers will not be attempted. As long as
/// parsers conume no input, their error messages are merged.
///
/// Choice is 'predictive' since p[n+1] is only tried when
/// p[n] didn't consume any input (i.e. the look-ahead is 1).
/// This non-backtracking behaviour allows for both an efficient
/// implementation of the parser combinators and the generation
/// of good error messages.
/// </summary>
public static Parser<T> Choice<T>(params Parser<T>[] parsers)
{
if (parsers.Length == 0)
return Fail<T>();
return new ChoiceParser<T>(parsers);
}
/// <summary>
/// When parser p consumes any input, Label(p, e) is the same as p.
/// When parser p does not consume any input, Label(p, e) is the same
/// as p, except any messages are replaced with expectation e.
/// </summary>
public static Parser<T> Label<T>(Parser<T> parser, string expectation)
{
return new LabeledParser<T>(parser, expectation);
}
private static IEnumerable<T> List<T>(T first, IEnumerable<T> rest)
{
yield return first;
foreach (T item in rest)
yield return item;
}
private static Parser<IEnumerable<T>> Zero<T>()
{
return Enumerable.Empty<T>().SucceedWithThisValue();
}
}
}