Upload from upload_mods.ps1
This commit is contained in:
124
Scripts/Utilities/ExpressionParser/Sprache/CommentParser.cs
Normal file
124
Scripts/Utilities/ExpressionParser/Sprache/CommentParser.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
namespace Sprache
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs customizable comment parsers.
|
||||
/// </summary>
|
||||
public class CommentParser : IComment
|
||||
{
|
||||
///<summary>
|
||||
///Single-line comment header.
|
||||
///</summary>
|
||||
public string Single { get; set; }
|
||||
|
||||
///<summary>
|
||||
///Newline character preference.
|
||||
///</summary>
|
||||
public string NewLine { get; set; }
|
||||
|
||||
///<summary>
|
||||
///Multi-line comment opener.
|
||||
///</summary>
|
||||
public string MultiOpen { get; set; }
|
||||
|
||||
///<summary>
|
||||
///Multi-line comment closer.
|
||||
///</summary>
|
||||
public string MultiClose { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a Comment with C-style headers and Windows newlines.
|
||||
/// </summary>
|
||||
public CommentParser()
|
||||
{
|
||||
Single = "//";
|
||||
MultiOpen = "/*";
|
||||
MultiClose = "*/";
|
||||
NewLine = "\n";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a Comment with custom multi-line headers and newline characters.
|
||||
/// Single-line headers are made null, it is assumed they would not be used.
|
||||
/// </summary>
|
||||
/// <param name="multiOpen"></param>
|
||||
/// <param name="multiClose"></param>
|
||||
/// <param name="newLine"></param>
|
||||
public CommentParser(string multiOpen, string multiClose, string newLine)
|
||||
{
|
||||
Single = null;
|
||||
MultiOpen = multiOpen;
|
||||
MultiClose = multiClose;
|
||||
NewLine = newLine;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a Comment with custom headers and newline characters.
|
||||
/// </summary>
|
||||
/// <param name="single"></param>
|
||||
/// <param name="multiOpen"></param>
|
||||
/// <param name="multiClose"></param>
|
||||
/// <param name="newLine"></param>
|
||||
public CommentParser(string single, string multiOpen, string multiClose, string newLine)
|
||||
{
|
||||
Single = single;
|
||||
MultiOpen = multiOpen;
|
||||
MultiClose = multiClose;
|
||||
NewLine = newLine;
|
||||
}
|
||||
|
||||
///<summary>
|
||||
///Parse a single-line comment.
|
||||
///</summary>
|
||||
public Parser<string> SingleLineComment
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Single == null)
|
||||
throw new ParseException("Field 'Single' is null; single-line comments not allowed.");
|
||||
|
||||
return from first in Parse.String(Single)
|
||||
from rest in Parse.CharExcept(NewLine).Many().Text()
|
||||
select rest;
|
||||
}
|
||||
private set { }
|
||||
}
|
||||
|
||||
///<summary>
|
||||
///Parse a multi-line comment.
|
||||
///</summary>
|
||||
public Parser<string> MultiLineComment
|
||||
{
|
||||
get
|
||||
{
|
||||
if (MultiOpen == null)
|
||||
throw new ParseException("Field 'MultiOpen' is null; multi-line comments not allowed.");
|
||||
else if (MultiClose == null)
|
||||
throw new ParseException("Field 'MultiClose' is null; multi-line comments not allowed.");
|
||||
|
||||
return from first in Parse.String(MultiOpen)
|
||||
from rest in Parse.AnyChar
|
||||
.Until(Parse.String(MultiClose)).Text()
|
||||
select rest;
|
||||
}
|
||||
private set { }
|
||||
}
|
||||
|
||||
///<summary>
|
||||
///Parse a comment.
|
||||
///</summary>
|
||||
public Parser<string> AnyComment
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Single != null && MultiOpen != null && MultiClose != null)
|
||||
return SingleLineComment.Or(MultiLineComment);
|
||||
else if (Single != null && (MultiOpen == null || MultiClose == null))
|
||||
return SingleLineComment;
|
||||
else if (Single == null && (MultiOpen != null && MultiClose != null))
|
||||
return MultiLineComment;
|
||||
else throw new ParseException("Unable to parse comment; check values of fields 'MultiOpen' and 'MultiClose'.");
|
||||
}
|
||||
private set { }
|
||||
}
|
||||
}
|
||||
}
|
||||
43
Scripts/Utilities/ExpressionParser/Sprache/IComment.cs
Normal file
43
Scripts/Utilities/ExpressionParser/Sprache/IComment.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
namespace Sprache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a customizable comment parser.
|
||||
/// </summary>
|
||||
public interface IComment
|
||||
{
|
||||
///<summary>
|
||||
/// Single-line comment header.
|
||||
///</summary>
|
||||
string Single { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// Newline character preference.
|
||||
///</summary>
|
||||
string NewLine { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// Multi-line comment opener.
|
||||
///</summary>
|
||||
string MultiOpen { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// Multi-line comment closer.
|
||||
///</summary>
|
||||
string MultiClose { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// Parse a single-line comment.
|
||||
///</summary>
|
||||
Parser<string> SingleLineComment { get; }
|
||||
|
||||
///<summary>
|
||||
/// Parse a multi-line comment.
|
||||
///</summary>
|
||||
Parser<string> MultiLineComment { get; }
|
||||
|
||||
///<summary>
|
||||
/// Parse a comment.
|
||||
///</summary>
|
||||
Parser<string> AnyComment { get; }
|
||||
}
|
||||
}
|
||||
26
Scripts/Utilities/ExpressionParser/Sprache/ICommentedOfT.cs
Normal file
26
Scripts/Utilities/ExpressionParser/Sprache/ICommentedOfT.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Sprache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a commented result with its leading and trailing comments.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the matched result.</typeparam>
|
||||
public interface ICommented<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the leading comments.
|
||||
/// </summary>
|
||||
IEnumerable<string> LeadingComments { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the resulting value.
|
||||
/// </summary>
|
||||
T Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the trailing comments.
|
||||
/// </summary>
|
||||
IEnumerable<string> TrailingComments { get; }
|
||||
}
|
||||
}
|
||||
53
Scripts/Utilities/ExpressionParser/Sprache/IInput.cs
Normal file
53
Scripts/Utilities/ExpressionParser/Sprache/IInput.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Sprache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an input for parsing.
|
||||
/// </summary>
|
||||
public interface IInput : IEquatable<IInput>
|
||||
{
|
||||
/// <summary>
|
||||
/// Advances the input.
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="IInput" /> that is advanced.</returns>
|
||||
/// <exception cref="System.InvalidOperationException">The input is already at the end of the source.</exception>
|
||||
IInput Advance();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the whole source.
|
||||
/// </summary>
|
||||
string Source { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current <see cref="System.Char" />.
|
||||
/// </summary>
|
||||
char Current { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the end of the source is reached.
|
||||
/// </summary>
|
||||
bool AtEnd { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current positon.
|
||||
/// </summary>
|
||||
int Position { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current line number.
|
||||
/// </summary>
|
||||
int Line { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current column.
|
||||
/// </summary>
|
||||
int Column { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Memos used by this input
|
||||
/// </summary>
|
||||
IDictionary<object, object> Memos { get; }
|
||||
}
|
||||
}
|
||||
18
Scripts/Utilities/ExpressionParser/Sprache/IPositionAware.cs
Normal file
18
Scripts/Utilities/ExpressionParser/Sprache/IPositionAware.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
namespace Sprache
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface for objects that have a source <see cref="Position"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the matched result.</typeparam>
|
||||
public interface IPositionAware<out T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Set the start <see cref="Position"/> and the matched length.
|
||||
/// </summary>
|
||||
/// <param name="startPos">The start position</param>
|
||||
/// <param name="length">The matched length.</param>
|
||||
/// <returns>The matched result.</returns>
|
||||
T SetPos(Position startPos, int length);
|
||||
}
|
||||
}
|
||||
36
Scripts/Utilities/ExpressionParser/Sprache/IResultOfT.cs
Normal file
36
Scripts/Utilities/ExpressionParser/Sprache/IResultOfT.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Sprache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a parsing result.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The result type.</typeparam>
|
||||
public interface IResult<out T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the resulting value.
|
||||
/// </summary>
|
||||
T Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether wether parsing was successful.
|
||||
/// </summary>
|
||||
bool WasSuccessful { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error message.
|
||||
/// </summary>
|
||||
string Message { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parser expectations in case of error.
|
||||
/// </summary>
|
||||
IEnumerable<string> Expectations { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the remainder of the input.
|
||||
/// </summary>
|
||||
IInput Remainder { get; }
|
||||
}
|
||||
}
|
||||
29
Scripts/Utilities/ExpressionParser/Sprache/ITextSpanOfT.cs
Normal file
29
Scripts/Utilities/ExpressionParser/Sprache/ITextSpanOfT.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
namespace Sprache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a text span of the matched result.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the matched result.</typeparam>
|
||||
public interface ITextSpan<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the resulting value.
|
||||
/// </summary>
|
||||
T Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the starting <see cref="Position"/>.
|
||||
/// </summary>
|
||||
Position Start { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ending <see cref="Position"/>.
|
||||
/// </summary>
|
||||
Position End { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the text span.
|
||||
/// </summary>
|
||||
int Length { get; }
|
||||
}
|
||||
}
|
||||
156
Scripts/Utilities/ExpressionParser/Sprache/Input.cs
Normal file
156
Scripts/Utilities/ExpressionParser/Sprache/Input.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Sprache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an input for parsing.
|
||||
/// </summary>
|
||||
public class Input : IInput
|
||||
{
|
||||
private readonly string _source;
|
||||
private readonly int _position;
|
||||
private readonly int _line;
|
||||
private readonly int _column;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of memos assigned to the <see cref="Input" /> instance.
|
||||
/// </summary>
|
||||
public IDictionary<object, object> Memos { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Input" /> class.
|
||||
/// </summary>
|
||||
/// <param name="source">The source.</param>
|
||||
public Input(string source)
|
||||
: this(source, 0)
|
||||
{
|
||||
}
|
||||
|
||||
internal Input(string source, int position, int line = 1, int column = 1)
|
||||
{
|
||||
_source = source;
|
||||
_position = position;
|
||||
_line = line;
|
||||
_column = column;
|
||||
|
||||
Memos = new Dictionary<object, object>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Advances the input.
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="IInput" /> that is advanced.</returns>
|
||||
/// <exception cref="System.InvalidOperationException">The input is already at the end of the source.</exception>
|
||||
public IInput Advance()
|
||||
{
|
||||
if (AtEnd)
|
||||
throw new InvalidOperationException("The input is already at the end of the source.");
|
||||
|
||||
return new Input(_source, _position + 1, Current == '\n' ? _line + 1 : _line, Current == '\n' ? 1 : _column + 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the whole source.
|
||||
/// </summary>
|
||||
public string Source { get { return _source; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current <see cref="System.Char" />.
|
||||
/// </summary>
|
||||
public char Current { get { return _source[_position]; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the end of the source is reached.
|
||||
/// </summary>
|
||||
public bool AtEnd { get { return _position == _source.Length; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current positon.
|
||||
/// </summary>
|
||||
public int Position { get { return _position; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current line number.
|
||||
/// </summary>
|
||||
public int Line { get { return _line; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current column.
|
||||
/// </summary>
|
||||
public int Column { get { return _column; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents the current object.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string that represents the current object.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("Line {0}, Column {1}", _line, _column);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as a hash function for a particular type.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A hash code for the current <see cref="Input" />.
|
||||
/// </returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return ((_source != null ? _source.GetHashCode() : 0) * 397) ^ _position;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="Input" />.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="Input" />; otherwise, false.
|
||||
/// </returns>
|
||||
/// <param name="obj">The object to compare with the current object. </param>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as IInput);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the current <see cref="Input" /> is equal to another object of the same type.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
|
||||
/// </returns>
|
||||
/// <param name="other">An object to compare with this object.</param>
|
||||
public bool Equals(IInput other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return string.Equals(_source, other.Source) && _position == other.Position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the left <see cref="Input" /> is equal to the right <see cref="Input" />.
|
||||
/// </summary>
|
||||
/// <param name="left">The left <see cref="Input" />.</param>
|
||||
/// <param name="right">The right <see cref="Input" />.</param>
|
||||
/// <returns>true if both objects are equal.</returns>
|
||||
public static bool operator ==(Input left, Input right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the left <see cref="Input" /> is not equal to the right <see cref="Input" />.
|
||||
/// </summary>
|
||||
/// <param name="left">The left <see cref="Input" />.</param>
|
||||
/// <param name="right">The right <see cref="Input" />.</param>
|
||||
/// <returns>true if the objects are not equal.</returns>
|
||||
public static bool operator !=(Input left, Input right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
145
Scripts/Utilities/ExpressionParser/Sprache/Option.cs
Normal file
145
Scripts/Utilities/ExpressionParser/Sprache/Option.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using System;
|
||||
|
||||
namespace Sprache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an optional result.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The result type.</typeparam>
|
||||
public interface IOption<out T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is empty.
|
||||
/// </summary>
|
||||
bool IsEmpty { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is defined.
|
||||
/// </summary>
|
||||
bool IsDefined { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the matched result or a default value.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
T GetOrDefault();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the matched result.
|
||||
/// </summary>
|
||||
T Get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for <see cref="IOption<T>"/>.
|
||||
/// </summary>
|
||||
public static class OptionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the value or else returns a default value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The result type.</typeparam>
|
||||
/// <param name="option"></param>
|
||||
/// <param name="defaultValue">The default value.</param>
|
||||
/// <returns></returns>
|
||||
public static T GetOrElse<T>(this IOption<T> option, T defaultValue)
|
||||
{
|
||||
if (option == null) throw new ArgumentNullException(nameof(option));
|
||||
return option.IsEmpty ? defaultValue : option.Get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a function over the value or else returns an empty option.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The input type.</typeparam>
|
||||
/// <typeparam name="U">The output type.</typeparam>
|
||||
/// <param name="option">The option containing the value to apply <paramref name="map" /> to.</param>
|
||||
/// <param name="map">The function to apply to the value of <paramref name="option" />.</param>
|
||||
/// <returns>An options result containing the result if there was an input value.</returns>
|
||||
public static IOption<U> Select<T, U>(this IOption<T> option, Func<T,U> map)
|
||||
{
|
||||
if (option == null) throw new ArgumentNullException(nameof(option));
|
||||
return option.IsDefined ? (IOption<U>) new Some<U>(map(option.Get())) : new None<U>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds the value to a function with optional result and flattens the result to a single optional.
|
||||
/// A result projection is applied aftherwards.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The input type.</typeparam>
|
||||
/// <typeparam name="U">The output type of <paramref name="bind" />.</typeparam>
|
||||
/// <typeparam name="V">The final output type.</typeparam>
|
||||
/// <param name="option">The option containing the value to bind to.</param>
|
||||
/// <param name="bind">The function that receives the input values and returns an optional value.</param>
|
||||
/// <param name="project">The function that is projects the result of <paramref name="bind" />.</param>
|
||||
/// <returns>An option result containing the result if there were was an input value and bind result.</returns>
|
||||
public static IOption<V> SelectMany<T,U,V>(this IOption<T> option, Func<T,IOption<U>> bind, Func<T,U,V> project)
|
||||
{
|
||||
if (option == null) throw new ArgumentNullException(nameof(option));
|
||||
if (option.IsEmpty) return new None<V>();
|
||||
|
||||
var t = option.Get();
|
||||
return bind(t).Select(u => project(t,u));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds the value to a function with optional result and flattens the result to a single optional.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The input type.</typeparam>
|
||||
/// <typeparam name="U">The output type.</typeparam>
|
||||
/// <param name="option">The option containing the value to bind to.</param>
|
||||
/// <param name="bind">The function that receives the input values and returns an optional value.</param>
|
||||
/// <returns>An option result containing the result if there were was an input value and bind result.</returns>
|
||||
public static IOption<U> SelectMany<T,U>(this IOption<T> option, Func<T,IOption<U>> bind) => option.SelectMany(bind, (_,x) => x);
|
||||
}
|
||||
|
||||
internal abstract class AbstractOption<T> : IOption<T>
|
||||
{
|
||||
public abstract bool IsEmpty { get; }
|
||||
|
||||
public bool IsDefined
|
||||
{
|
||||
get { return !IsEmpty; }
|
||||
}
|
||||
|
||||
public T GetOrDefault()
|
||||
{
|
||||
return IsEmpty ? default(T) : Get();
|
||||
}
|
||||
|
||||
public abstract T Get();
|
||||
}
|
||||
|
||||
internal sealed class Some<T> : AbstractOption<T>
|
||||
{
|
||||
private readonly T _value;
|
||||
|
||||
public Some(T value)
|
||||
{
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public override bool IsEmpty
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override T Get()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class None<T> : AbstractOption<T>
|
||||
{
|
||||
public override bool IsEmpty
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override T Get()
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get value from None.");
|
||||
}
|
||||
}
|
||||
}
|
||||
126
Scripts/Utilities/ExpressionParser/Sprache/Parse.Commented.cs
Normal file
126
Scripts/Utilities/ExpressionParser/Sprache/Parse.Commented.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniLinq;
|
||||
|
||||
namespace Sprache
|
||||
{
|
||||
partial class Parse
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a text span of the matched result.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the matched result.</typeparam>
|
||||
private class TextSpan<T> : ITextSpan<T>
|
||||
{
|
||||
public T Value { get; set; }
|
||||
|
||||
public Position Start { get; set; }
|
||||
|
||||
public Position End { get; set; }
|
||||
|
||||
public int Length { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a parser that returns the <see cref="ITextSpan{T}"/> of the parsed value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The result type of the given parser.</typeparam>
|
||||
/// <param name="parser">The parser to wrap.</param>
|
||||
/// <returns>A parser for the text span of the given parser.</returns>
|
||||
public static Parser<ITextSpan<T>> Span<T>(this Parser<T> parser)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
|
||||
return i =>
|
||||
{
|
||||
var r = parser(i);
|
||||
if (r.WasSuccessful)
|
||||
{
|
||||
var span = new TextSpan<T>
|
||||
{
|
||||
Value = r.Value,
|
||||
Start = Position.FromInput(i),
|
||||
End = Position.FromInput(r.Remainder),
|
||||
Length = r.Remainder.Position - i.Position,
|
||||
};
|
||||
|
||||
return Result.Success(span, r.Remainder);
|
||||
}
|
||||
|
||||
return Result.Failure<ITextSpan<T>>(r.Remainder, r.Message, r.Expectations);
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a commented result with its leading and trailing comments.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the matched result.</typeparam>
|
||||
private class CommentedValue<T> : ICommented<T>
|
||||
{
|
||||
public CommentedValue(T value)
|
||||
{
|
||||
LeadingComments = TrailingComments = EmptyStringList;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public CommentedValue(IEnumerable<string> leading, T value, IEnumerable<string> trailing)
|
||||
{
|
||||
LeadingComments = leading ?? EmptyStringList;
|
||||
Value = value;
|
||||
TrailingComments = trailing ?? EmptyStringList;
|
||||
}
|
||||
|
||||
public T Value { get; }
|
||||
|
||||
public IEnumerable<string> LeadingComments { get; }
|
||||
|
||||
public IEnumerable<string> TrailingComments { get; }
|
||||
}
|
||||
|
||||
private static readonly string[] EmptyStringList = new string[0];
|
||||
|
||||
private static readonly IComment DefaultCommentParser = new CommentParser();
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a parser that consumes a whitespace and all comments
|
||||
/// parsed by the commentParser.AnyComment parser, but parses only one trailing
|
||||
/// comment that starts exactly on the last line of the parsed value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The result type of the given parser.</typeparam>
|
||||
/// <param name="parser">The parser to wrap.</param>
|
||||
/// <param name="commentParser">The comment parser.</param>
|
||||
/// <returns>An extended Token() version of the given parser.</returns>
|
||||
public static Parser<ICommented<T>> Commented<T>(this Parser<T> parser, IComment commentParser = null)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
|
||||
// consume any comment supported by the comment parser
|
||||
var comment = (commentParser ?? DefaultCommentParser).AnyComment;
|
||||
|
||||
// parses any whitespace except for the new lines
|
||||
var whiteSpaceExceptForNewLine = WhiteSpace.Except(Chars("\r\n")).Many().Text();
|
||||
|
||||
// returns true if the second span starts on the first span's last line
|
||||
bool IsSameLine(ITextSpan<T> first, ITextSpan<string> second) =>
|
||||
first.End.Line == second.Start.Line;
|
||||
|
||||
// single comment span followed by a whitespace
|
||||
var commentSpan =
|
||||
from cs in comment.Span()
|
||||
from ws in whiteSpaceExceptForNewLine
|
||||
select cs;
|
||||
|
||||
// add leading and trailing comments to the parser
|
||||
return
|
||||
from leadingWhiteSpace in WhiteSpace.Many()
|
||||
from leadingComments in comment.Token().Many()
|
||||
from valueSpan in parser.Span()
|
||||
from trailingWhiteSpace in whiteSpaceExceptForNewLine
|
||||
from trailingPreview in commentSpan.Many().Preview()
|
||||
let trailingCount = trailingPreview.GetOrElse(Enumerable.Empty<ITextSpan<string>>())
|
||||
.Where(c => IsSameLine(valueSpan, c)).Count()
|
||||
from trailingComments in commentSpan.Repeat(trailingCount)
|
||||
select new CommentedValue<T>(leadingComments, valueSpan.Value, trailingComments.Select(c => c.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
80
Scripts/Utilities/ExpressionParser/Sprache/Parse.Optional.cs
Normal file
80
Scripts/Utilities/ExpressionParser/Sprache/Parse.Optional.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
|
||||
namespace Sprache
|
||||
{
|
||||
partial class Parse
|
||||
{
|
||||
/// <summary>
|
||||
/// Construct a parser that indicates that the given parser
|
||||
/// is optional. The returned parser will succeed on
|
||||
/// any input no matter whether the given parser
|
||||
/// succeeds or not.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The result type of the given parser.</typeparam>
|
||||
/// <param name="parser">The parser to wrap.</param>
|
||||
/// <returns>An optional version of the given parser.</returns>
|
||||
public static Parser<IOption<T>> Optional<T>(this Parser<T> parser)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
|
||||
return i =>
|
||||
{
|
||||
var pr = parser(i);
|
||||
|
||||
if (pr.WasSuccessful)
|
||||
return Result.Success(new Some<T>(pr.Value), pr.Remainder);
|
||||
|
||||
return Result.Success(new None<T>(), i);
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs the eXclusive version of the Optional{T} parser.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The result type of the given parser</typeparam>
|
||||
/// <param name="parser">The parser to wrap</param>
|
||||
/// <returns>An eXclusive optional version of the given parser.</returns>
|
||||
/// <seealso cref="XOr"/>
|
||||
public static Parser<IOption<T>> XOptional<T>(this Parser<T> parser)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
|
||||
return i =>
|
||||
{
|
||||
var result = parser(i);
|
||||
|
||||
if (result.WasSuccessful)
|
||||
return Result.Success(new Some<T>(result.Value), result.Remainder);
|
||||
|
||||
if (result.Remainder.Equals(i))
|
||||
return Result.Success(new None<T>(), i);
|
||||
|
||||
return Result.Failure<IOption<T>>(result.Remainder, result.Message, result.Expectations);
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a parser that indicates that the given parser is optional
|
||||
/// and non-consuming. The returned parser will succeed on
|
||||
/// any input no matter whether the given parser succeeds or not.
|
||||
/// In any case, it won't consume any input, like a positive look-ahead in regex.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The result type of the given parser.</typeparam>
|
||||
/// <param name="parser">The parser to wrap.</param>
|
||||
/// <returns>A non-consuming version of the given parser.</returns>
|
||||
public static Parser<IOption<T>> Preview<T>(this Parser<T> parser)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
|
||||
return i =>
|
||||
{
|
||||
var result = parser(i);
|
||||
|
||||
if (result.WasSuccessful)
|
||||
return Result.Success(new Some<T>(result.Value), i);
|
||||
|
||||
return Result.Success(new None<T>(), i);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
namespace Sprache
|
||||
{
|
||||
partial class Parse
|
||||
{
|
||||
/// <summary>
|
||||
/// Construct a parser that will set the position to the position-aware
|
||||
/// T on succsessful match.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="parser"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<T> Positioned<T>(this Parser<T> parser) where T : IPositionAware<T>
|
||||
{
|
||||
return i =>
|
||||
{
|
||||
var r = parser(i);
|
||||
|
||||
if (r.WasSuccessful)
|
||||
{
|
||||
return Result.Success(r.Value.SetPos(Position.FromInput(i), r.Remainder.Position - i.Position), r.Remainder);
|
||||
}
|
||||
return r;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
namespace Sprache
|
||||
{
|
||||
partial class Parse
|
||||
{
|
||||
/// <summary>
|
||||
/// \n or \r\n
|
||||
/// </summary>
|
||||
public static Parser<string> LineEnd =
|
||||
(from r in Char('\r').Optional()
|
||||
from n in Char('\n')
|
||||
select r.IsDefined ? r.Get().ToString() + n : n.ToString())
|
||||
.Named("LineEnd");
|
||||
|
||||
/// <summary>
|
||||
/// line ending or end of input
|
||||
/// </summary>
|
||||
public static Parser<string> LineTerminator =
|
||||
Return("").End()
|
||||
.Or(LineEnd.End())
|
||||
.Or(LineEnd)
|
||||
.Named("LineTerminator");
|
||||
|
||||
/// <summary>
|
||||
/// Parser for identifier starting with <paramref name="firstLetterParser"/> and continuing with <paramref name="tailLetterParser"/>
|
||||
/// </summary>
|
||||
public static Parser<string> Identifier(Parser<char> firstLetterParser, Parser<char> tailLetterParser)
|
||||
{
|
||||
return
|
||||
from firstLetter in firstLetterParser
|
||||
from tail in tailLetterParser.Many().Text()
|
||||
select firstLetter + tail;
|
||||
}
|
||||
}
|
||||
}
|
||||
158
Scripts/Utilities/ExpressionParser/Sprache/Parse.Sequence.cs
Normal file
158
Scripts/Utilities/ExpressionParser/Sprache/Parse.Sequence.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
namespace Sprache
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniLinq;
|
||||
|
||||
partial class Parse
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="parser"></param>
|
||||
/// <param name="delimiter"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="U"></typeparam>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public static Parser<IEnumerable<T>> DelimitedBy<T, U>(this Parser<T> parser, Parser<U> delimiter)
|
||||
{
|
||||
return DelimitedBy(parser, delimiter, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="parser"></param>
|
||||
/// <param name="delimiter"></param>
|
||||
/// <param name="minimumCount"></param>
|
||||
/// <param name="maximumCount"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="U"></typeparam>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public static Parser<IEnumerable<T>> DelimitedBy<T, U>(this Parser<T> parser, Parser<U> delimiter, int? minimumCount, int? maximumCount)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
if (delimiter == null) throw new ArgumentNullException(nameof(delimiter));
|
||||
|
||||
return from head in parser.Once()
|
||||
from tail in
|
||||
(from separator in delimiter
|
||||
from item in parser
|
||||
select item).Repeat(minimumCount - 1, maximumCount - 1)
|
||||
select head.Concat(tail);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fails on the first itemParser failure, if it reads at least one character.
|
||||
/// </summary>
|
||||
/// <param name="itemParser"></param>
|
||||
/// <param name="delimiter"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="U"></typeparam>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public static Parser<IEnumerable<T>> XDelimitedBy<T, U>(this Parser<T> itemParser, Parser<U> delimiter)
|
||||
{
|
||||
if (itemParser == null) throw new ArgumentNullException(nameof(itemParser));
|
||||
if (delimiter == null) throw new ArgumentNullException(nameof(delimiter));
|
||||
|
||||
return from head in itemParser.Once()
|
||||
from tail in
|
||||
(from separator in delimiter
|
||||
from item in itemParser
|
||||
select item).XMany()
|
||||
select head.Concat(tail);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="parser"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public static Parser<IEnumerable<T>> Repeat<T>(this Parser<T> parser, int count)
|
||||
{
|
||||
return Repeat(parser, count, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="parser"></param>
|
||||
/// <param name="minimumCount"></param>
|
||||
/// <param name="maximumCount"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public static Parser<IEnumerable<T>> Repeat<T>(this Parser<T> parser, int? minimumCount, int? maximumCount)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
|
||||
return i =>
|
||||
{
|
||||
var remainder = i;
|
||||
var result = new List<T>();
|
||||
|
||||
var count = 0;
|
||||
|
||||
var r = parser(remainder);
|
||||
while (r.WasSuccessful && (maximumCount == null || count < maximumCount.Value))
|
||||
{
|
||||
count++;
|
||||
|
||||
result.Add(r.Value);
|
||||
|
||||
remainder = r.Remainder;
|
||||
r = parser(remainder);
|
||||
}
|
||||
|
||||
if (minimumCount.HasValue && count < minimumCount.Value)
|
||||
{
|
||||
var what = r.Remainder.AtEnd
|
||||
? "end of input"
|
||||
: r.Remainder.Current.ToString();
|
||||
|
||||
var msg = $"Unexpected '{what}'";
|
||||
string exp;
|
||||
if (minimumCount == maximumCount)
|
||||
exp = $"'{StringExtensions.Join(", ", r.Expectations)}' {minimumCount.Value} times, but found {count}";
|
||||
else if (maximumCount == null)
|
||||
exp = $"'{StringExtensions.Join(", ", r.Expectations)}' minimum {minimumCount.Value} times, but found {count}";
|
||||
else
|
||||
exp = $"'{StringExtensions.Join(", ", r.Expectations)}' between {minimumCount.Value} and {maximumCount.Value} times, but found {count}";
|
||||
|
||||
return Result.Failure<IEnumerable<T>>(i, msg, new[] { exp });
|
||||
}
|
||||
|
||||
return Result.Success<IEnumerable<T>>(result, remainder);
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="parser"></param>
|
||||
/// <param name="open"></param>
|
||||
/// <param name="close"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="U"></typeparam>
|
||||
/// <typeparam name="V"></typeparam>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public static Parser<T> Contained<T, U, V>(this Parser<T> parser, Parser<U> open, Parser<V> close)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
if (open == null) throw new ArgumentNullException(nameof(open));
|
||||
if (close == null) throw new ArgumentNullException(nameof(close));
|
||||
|
||||
return from o in open
|
||||
from item in parser
|
||||
from c in close
|
||||
select item;
|
||||
}
|
||||
}
|
||||
}
|
||||
782
Scripts/Utilities/ExpressionParser/Sprache/Parse.cs
Normal file
782
Scripts/Utilities/ExpressionParser/Sprache/Parse.cs
Normal file
@@ -0,0 +1,782 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using UniLinq;
|
||||
|
||||
namespace Sprache
|
||||
{
|
||||
/// <summary>
|
||||
/// Parsers and combinators.
|
||||
/// </summary>
|
||||
public static partial class Parse
|
||||
{
|
||||
/// <summary>
|
||||
/// TryParse a single character matching 'predicate'
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="description"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<char> Char(Predicate<char> predicate, string description)
|
||||
{
|
||||
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
|
||||
if (description == null) throw new ArgumentNullException(nameof(description));
|
||||
|
||||
return i =>
|
||||
{
|
||||
if (!i.AtEnd)
|
||||
{
|
||||
if (predicate(i.Current))
|
||||
return Result.Success(i.Current, i.Advance());
|
||||
|
||||
return Result.Failure<char>(i,
|
||||
$"unexpected '{i.Current}'",
|
||||
new[] { description });
|
||||
}
|
||||
|
||||
return Result.Failure<char>(i,
|
||||
"Unexpected end of input reached",
|
||||
new[] { description });
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a single character except those matching <paramref name="predicate"/>.
|
||||
/// </summary>
|
||||
/// <param name="predicate">Characters not to match.</param>
|
||||
/// <param name="description">Description of characters that don't match.</param>
|
||||
/// <returns>A parser for characters except those matching <paramref name="predicate"/>.</returns>
|
||||
public static Parser<char> CharExcept(Predicate<char> predicate, string description)
|
||||
{
|
||||
return Char(c => !predicate(c), "any character except " + description);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a single character c.
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<char> Char(char c)
|
||||
{
|
||||
return Char(ch => c == ch, char.ToString(c));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Parse a single character of any in c
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<char> Chars(params char[] c)
|
||||
{
|
||||
return Char(c.Contains, StringExtensions.Join("|", c));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a single character of any in c
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<char> Chars(string c)
|
||||
{
|
||||
return Char(c.ToEnumerable().Contains, StringExtensions.Join("|", c.ToEnumerable()));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Parse a single character except c.
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<char> CharExcept(char c)
|
||||
{
|
||||
return CharExcept(ch => c == ch, char.ToString(c));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a single character except for those in the given parameters
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<char> CharExcept(IEnumerable<char> c)
|
||||
{
|
||||
var chars = c as char[] ?? c.ToArray();
|
||||
return CharExcept(chars.Contains, StringExtensions.Join("|", chars));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a single character except for those in c
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<char> CharExcept(string c)
|
||||
{
|
||||
return CharExcept(c.ToEnumerable().Contains, StringExtensions.Join("|", c.ToEnumerable()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a single character in a case-insensitive fashion.
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<char> IgnoreCase(char c)
|
||||
{
|
||||
return Char(ch => char.ToLower(c) == char.ToLower(ch), char.ToString(c));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a string in a case-insensitive fashion.
|
||||
/// </summary>
|
||||
/// <param name="s"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<IEnumerable<char>> IgnoreCase(string s)
|
||||
{
|
||||
if (s == null) throw new ArgumentNullException(nameof(s));
|
||||
|
||||
return s
|
||||
.ToEnumerable()
|
||||
.Select(IgnoreCase)
|
||||
.Aggregate(Return(Enumerable.Empty<char>()),
|
||||
(a, p) => a.Concat(p.Once()))
|
||||
.Named(s);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse any character.
|
||||
/// </summary>
|
||||
public static readonly Parser<char> AnyChar = Char(c => true, "any character");
|
||||
|
||||
/// <summary>
|
||||
/// Parse a whitespace.
|
||||
/// </summary>
|
||||
public static readonly Parser<char> WhiteSpace = Char(char.IsWhiteSpace, "whitespace");
|
||||
|
||||
/// <summary>
|
||||
/// Parse a digit.
|
||||
/// </summary>
|
||||
public static readonly Parser<char> Digit = Char(char.IsDigit, "digit");
|
||||
|
||||
/// <summary>
|
||||
/// Parse a letter.
|
||||
/// </summary>
|
||||
public static readonly Parser<char> Letter = Char(char.IsLetter, "letter");
|
||||
|
||||
/// <summary>
|
||||
/// Parse a letter or digit.
|
||||
/// </summary>
|
||||
public static readonly Parser<char> LetterOrDigit = Char(char.IsLetterOrDigit, "letter or digit");
|
||||
|
||||
/// <summary>
|
||||
/// Parse a lowercase letter.
|
||||
/// </summary>
|
||||
public static readonly Parser<char> Lower = Char(char.IsLower, "lowercase letter");
|
||||
|
||||
/// <summary>
|
||||
/// Parse an uppercase letter.
|
||||
/// </summary>
|
||||
public static readonly Parser<char> Upper = Char(char.IsUpper, "uppercase letter");
|
||||
|
||||
/// <summary>
|
||||
/// Parse a numeric character.
|
||||
/// </summary>
|
||||
public static readonly Parser<char> Numeric = Char(char.IsNumber, "numeric character");
|
||||
|
||||
/// <summary>
|
||||
/// Parse a string of characters.
|
||||
/// </summary>
|
||||
/// <param name="s"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<IEnumerable<char>> String(string s)
|
||||
{
|
||||
if (s == null) throw new ArgumentNullException(nameof(s));
|
||||
|
||||
return s
|
||||
.ToEnumerable()
|
||||
.Select(Char)
|
||||
.Aggregate(Return(Enumerable.Empty<char>()),
|
||||
(a, p) => a.Concat(p.Once()))
|
||||
.Named(s);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a parser that will fail if the given parser succeeds,
|
||||
/// and will succeed if the given parser fails. In any case, it won't
|
||||
/// consume any input. It's like a negative look-ahead in regex.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The result type of the given parser</typeparam>
|
||||
/// <param name="parser">The parser to wrap</param>
|
||||
/// <returns>A parser that is the opposite of the given parser.</returns>
|
||||
public static Parser<object> Not<T>(this Parser<T> parser)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
|
||||
return i =>
|
||||
{
|
||||
var result = parser(i);
|
||||
|
||||
if (result.WasSuccessful)
|
||||
{
|
||||
var msg = $"`{StringExtensions.Join(", ", result.Expectations)}' was not expected";
|
||||
return Result.Failure<object>(i, msg, new string[0]);
|
||||
}
|
||||
return Result.Success<object>(null, i);
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse first, and if successful, then parse second.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="U"></typeparam>
|
||||
/// <param name="first"></param>
|
||||
/// <param name="second"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<U> Then<T, U>(this Parser<T> first, Func<T, Parser<U>> second)
|
||||
{
|
||||
if (first == null) throw new ArgumentNullException(nameof(first));
|
||||
if (second == null) throw new ArgumentNullException(nameof(second));
|
||||
|
||||
return i => first(i).IfSuccess(s => second(s.Value)(s.Remainder));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a stream of elements.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="parser"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>Implemented imperatively to decrease stack usage.</remarks>
|
||||
public static Parser<IEnumerable<T>> Many<T>(this Parser<T> parser)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
|
||||
return i =>
|
||||
{
|
||||
var remainder = i;
|
||||
var result = new List<T>();
|
||||
var r = parser(i);
|
||||
|
||||
while (r.WasSuccessful)
|
||||
{
|
||||
if (remainder.Equals(r.Remainder))
|
||||
break;
|
||||
|
||||
result.Add(r.Value);
|
||||
remainder = r.Remainder;
|
||||
r = parser(remainder);
|
||||
}
|
||||
|
||||
return Result.Success<IEnumerable<T>>(result, remainder);
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a stream of elements, failing if any element is only partially parsed.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of element to parse.</typeparam>
|
||||
/// <param name="parser">A parser that matches a single element.</param>
|
||||
/// <returns>A <see cref="Parser{T}"/> that matches the sequence.</returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Using <seealso cref="XMany{T}(Parser{T})"/> may be preferable to <seealso cref="Many{T}(Parser{T})"/>
|
||||
/// where the first character of each match identified by <paramref name="parser"/>
|
||||
/// is sufficient to determine whether the entire match should succeed. The X*
|
||||
/// methods typically give more helpful errors and are easier to debug than their
|
||||
/// unqualified counterparts.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <seealso cref="XOr"/>
|
||||
public static Parser<IEnumerable<T>> XMany<T>(this Parser<T> parser)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
|
||||
return parser.Many().Then(m => parser.Once().XOr(Return(m)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TryParse a stream of elements with at least one item.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="parser"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<IEnumerable<T>> AtLeastOnce<T>(this Parser<T> parser)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
|
||||
return parser.Once().Then(t1 => parser.Many().Select(ts => t1.Concat(ts)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TryParse a stream of elements with at least one item. Except the first
|
||||
/// item, all other items will be matched with the <code>XMany</code> operator.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="parser"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<IEnumerable<T>> XAtLeastOnce<T>(this Parser<T> parser)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
|
||||
return parser.Once().Then(t1 => parser.XMany().Select(ts => t1.Concat(ts)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse end-of-input.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="parser"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<T> End<T>(this Parser<T> parser)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
|
||||
return i => parser(i).IfSuccess(s =>
|
||||
s.Remainder.AtEnd
|
||||
? s
|
||||
: Result.Failure<T>(
|
||||
s.Remainder,
|
||||
string.Format("unexpected '{0}'", s.Remainder.Current),
|
||||
new[] { "end of input" }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Take the result of parsing, and project it onto a different domain.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="U"></typeparam>
|
||||
/// <param name="parser"></param>
|
||||
/// <param name="convert"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<U> Select<T, U>(this Parser<T> parser, Func<T, U> convert)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
if (convert == null) throw new ArgumentNullException(nameof(convert));
|
||||
|
||||
return parser.Then(t => Return(convert(t)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse the token, embedded in any amount of whitespace characters.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="parser"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<T> Token<T>(this Parser<T> parser)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
|
||||
return from leading in WhiteSpace.Many()
|
||||
from item in parser
|
||||
from trailing in WhiteSpace.Many()
|
||||
select item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refer to another parser indirectly. This allows circular compile-time dependency between parsers.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="reference"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<T> Ref<T>(Func<Parser<T>> reference)
|
||||
{
|
||||
if (reference == null) throw new ArgumentNullException(nameof(reference));
|
||||
|
||||
Parser<T> p = null;
|
||||
|
||||
return i =>
|
||||
{
|
||||
if (p == null)
|
||||
p = reference();
|
||||
|
||||
if (i.Memos.ContainsKey(p))
|
||||
{
|
||||
var pResult = i.Memos[p] as IResult<T>;
|
||||
if (pResult.WasSuccessful)
|
||||
return pResult;
|
||||
throw new ParseException(pResult.ToString());
|
||||
}
|
||||
|
||||
i.Memos[p] = Result.Failure<T>(i,
|
||||
"Left recursion in the grammar.",
|
||||
new string[0]);
|
||||
var result = p(i);
|
||||
i.Memos[p] = result;
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a stream of characters to a string.
|
||||
/// </summary>
|
||||
/// <param name="characters"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<string> Text(this Parser<IEnumerable<char>> characters)
|
||||
{
|
||||
return characters.Select(chs => new string(chs.ToArray()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse first, if it succeeds, return first, otherwise try second.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="first"></param>
|
||||
/// <param name="second"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<T> Or<T>(this Parser<T> first, Parser<T> second)
|
||||
{
|
||||
if (first == null) throw new ArgumentNullException(nameof(first));
|
||||
if (second == null) throw new ArgumentNullException(nameof(second));
|
||||
|
||||
return i =>
|
||||
{
|
||||
var fr = first(i);
|
||||
if (!fr.WasSuccessful)
|
||||
{
|
||||
return second(i).IfFailure(sf => DetermineBestError(fr, sf));
|
||||
}
|
||||
|
||||
if (fr.Remainder.Equals(i))
|
||||
return second(i).IfFailure(sf => fr);
|
||||
|
||||
return fr;
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Names part of the grammar for help with error messages.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="parser"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<T> Named<T>(this Parser<T> parser, string name)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
|
||||
return i => parser(i).IfFailure(f => f.Remainder.Equals(i) ?
|
||||
Result.Failure<T>(f.Remainder, f.Message, new[] { name }) :
|
||||
f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse first, if it succeeds, return first, otherwise try second.
|
||||
/// Assumes that the first parsed character will determine the parser chosen (see Try).
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="first"></param>
|
||||
/// <param name="second"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<T> XOr<T>(this Parser<T> first, Parser<T> second)
|
||||
{
|
||||
if (first == null) throw new ArgumentNullException(nameof(first));
|
||||
if (second == null) throw new ArgumentNullException(nameof(second));
|
||||
|
||||
return i => {
|
||||
var fr = first(i);
|
||||
if (!fr.WasSuccessful)
|
||||
{
|
||||
// The 'X' part
|
||||
if (!fr.Remainder.Equals(i))
|
||||
return fr;
|
||||
|
||||
return second(i).IfFailure(sf => DetermineBestError(fr, sf));
|
||||
}
|
||||
|
||||
// This handles a zero-length successful application of first.
|
||||
if (fr.Remainder.Equals(i))
|
||||
return second(i).IfFailure(sf => fr);
|
||||
|
||||
return fr;
|
||||
};
|
||||
}
|
||||
|
||||
// Examines two results presumably obtained at an "Or" junction; returns the result with
|
||||
// the most information, or if they apply at the same input position, a union of the results.
|
||||
static IResult<T> DetermineBestError<T>(IResult<T> firstFailure, IResult<T> secondFailure)
|
||||
{
|
||||
if (secondFailure.Remainder.Position > firstFailure.Remainder.Position)
|
||||
return secondFailure;
|
||||
|
||||
if (secondFailure.Remainder.Position == firstFailure.Remainder.Position)
|
||||
return Result.Failure<T>(
|
||||
firstFailure.Remainder,
|
||||
firstFailure.Message,
|
||||
firstFailure.Expectations.Union(secondFailure.Expectations));
|
||||
|
||||
return firstFailure;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a stream of elements containing only one item.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="parser"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<IEnumerable<T>> Once<T>(this Parser<T> parser)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
|
||||
return parser.Select(r => (IEnumerable<T>)new[] { r });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Concatenate two streams of elements.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="first"></param>
|
||||
/// <param name="second"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<IEnumerable<T>> Concat<T>(this Parser<IEnumerable<T>> first, Parser<IEnumerable<T>> second)
|
||||
{
|
||||
if (first == null) throw new ArgumentNullException(nameof(first));
|
||||
if (second == null) throw new ArgumentNullException(nameof(second));
|
||||
|
||||
return first.Then(f => second.Select(f.Concat));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Succeed immediately and return value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<T> Return<T>(T value)
|
||||
{
|
||||
return i => Result.Success(value, i);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Version of Return with simpler inline syntax.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="U"></typeparam>
|
||||
/// <param name="parser"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<U> Return<T, U>(this Parser<T> parser, U value)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
return parser.Select(t => value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt parsing only if the <paramref name="except"/> parser fails.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="U"></typeparam>
|
||||
/// <param name="parser"></param>
|
||||
/// <param name="except"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<T> Except<T, U>(this Parser<T> parser, Parser<U> except)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
if (except == null) throw new ArgumentNullException(nameof(except));
|
||||
|
||||
// Could be more like: except.Then(s => s.Fail("..")).XOr(parser)
|
||||
return i =>
|
||||
{
|
||||
var r = except(i);
|
||||
if (r.WasSuccessful)
|
||||
return Result.Failure<T>(i, "Excepted parser succeeded.", new[] { "other than the excepted input" });
|
||||
return parser(i);
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a sequence of items until a terminator is reached.
|
||||
/// Returns the sequence, discarding the terminator.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="U"></typeparam>
|
||||
/// <param name="parser"></param>
|
||||
/// <param name="until"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<IEnumerable<T>> Until<T, U>(this Parser<T> parser, Parser<U> until)
|
||||
{
|
||||
return parser.Except(until).Many().Then(r => until.Return(r));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Succeed if the parsed value matches predicate.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="parser"></param>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<T> Where<T>(this Parser<T> parser, Func<T, bool> predicate)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
|
||||
|
||||
return i => parser(i).IfSuccess(s =>
|
||||
predicate(s.Value) ? s : Result.Failure<T>(i,
|
||||
string.Format("Unexpected {0}.", s.Value),
|
||||
new string[0]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Monadic combinator Then, adapted for Linq comprehension syntax.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="U"></typeparam>
|
||||
/// <typeparam name="V"></typeparam>
|
||||
/// <param name="parser"></param>
|
||||
/// <param name="selector"></param>
|
||||
/// <param name="projector"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<V> SelectMany<T, U, V>(
|
||||
this Parser<T> parser,
|
||||
Func<T, Parser<U>> selector,
|
||||
Func<T, U, V> projector)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
if (selector == null) throw new ArgumentNullException(nameof(selector));
|
||||
if (projector == null) throw new ArgumentNullException(nameof(projector));
|
||||
|
||||
return parser.Then(t => selector(t).Select(u => projector(t, u)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Chain a left-associative operator.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="TOp"></typeparam>
|
||||
/// <param name="op"></param>
|
||||
/// <param name="operand"></param>
|
||||
/// <param name="apply"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<T> ChainOperator<T, TOp>(
|
||||
Parser<TOp> op,
|
||||
Parser<T> operand,
|
||||
Func<TOp, T, T, T> apply)
|
||||
{
|
||||
if (op == null) throw new ArgumentNullException(nameof(op));
|
||||
if (operand == null) throw new ArgumentNullException(nameof(operand));
|
||||
if (apply == null) throw new ArgumentNullException(nameof(apply));
|
||||
return operand.Then(first => ChainOperatorRest(first, op, operand, apply, Or));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Chain a left-associative operator.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="TOp"></typeparam>
|
||||
/// <param name="op"></param>
|
||||
/// <param name="operand"></param>
|
||||
/// <param name="apply"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<T> XChainOperator<T, TOp>(
|
||||
Parser<TOp> op,
|
||||
Parser<T> operand,
|
||||
Func<TOp, T, T, T> apply)
|
||||
{
|
||||
if (op == null) throw new ArgumentNullException(nameof(op));
|
||||
if (operand == null) throw new ArgumentNullException(nameof(operand));
|
||||
if (apply == null) throw new ArgumentNullException(nameof(apply));
|
||||
return operand.Then(first => ChainOperatorRest(first, op, operand, apply, XOr));
|
||||
}
|
||||
|
||||
static Parser<T> ChainOperatorRest<T, TOp>(
|
||||
T firstOperand,
|
||||
Parser<TOp> op,
|
||||
Parser<T> operand,
|
||||
Func<TOp, T, T, T> apply,
|
||||
Func<Parser<T>, Parser<T>, Parser<T>> or)
|
||||
{
|
||||
if (op == null) throw new ArgumentNullException(nameof(op));
|
||||
if (operand == null) throw new ArgumentNullException(nameof(operand));
|
||||
if (apply == null) throw new ArgumentNullException(nameof(apply));
|
||||
return or(op.Then(opvalue =>
|
||||
operand.Then(operandValue =>
|
||||
ChainOperatorRest(apply(opvalue, firstOperand, operandValue), op, operand, apply, or))),
|
||||
Return(firstOperand));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Chain a right-associative operator.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="TOp"></typeparam>
|
||||
/// <param name="op"></param>
|
||||
/// <param name="operand"></param>
|
||||
/// <param name="apply"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<T> ChainRightOperator<T, TOp>(
|
||||
Parser<TOp> op,
|
||||
Parser<T> operand,
|
||||
Func<TOp, T, T, T> apply)
|
||||
{
|
||||
if (op == null) throw new ArgumentNullException(nameof(op));
|
||||
if (operand == null) throw new ArgumentNullException(nameof(operand));
|
||||
if (apply == null) throw new ArgumentNullException(nameof(apply));
|
||||
return operand.Then(first => ChainRightOperatorRest(first, op, operand, apply, Or));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Chain a right-associative operator.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="TOp"></typeparam>
|
||||
/// <param name="op"></param>
|
||||
/// <param name="operand"></param>
|
||||
/// <param name="apply"></param>
|
||||
/// <returns></returns>
|
||||
public static Parser<T> XChainRightOperator<T, TOp>(
|
||||
Parser<TOp> op,
|
||||
Parser<T> operand,
|
||||
Func<TOp, T, T, T> apply)
|
||||
{
|
||||
if (op == null) throw new ArgumentNullException(nameof(op));
|
||||
if (operand == null) throw new ArgumentNullException(nameof(operand));
|
||||
if (apply == null) throw new ArgumentNullException(nameof(apply));
|
||||
return operand.Then(first => ChainRightOperatorRest(first, op, operand, apply, XOr));
|
||||
}
|
||||
|
||||
static Parser<T> ChainRightOperatorRest<T, TOp>(
|
||||
T lastOperand,
|
||||
Parser<TOp> op,
|
||||
Parser<T> operand,
|
||||
Func<TOp, T, T, T> apply,
|
||||
Func<Parser<T>, Parser<T>, Parser<T>> or)
|
||||
{
|
||||
if (op == null) throw new ArgumentNullException(nameof(op));
|
||||
if (operand == null) throw new ArgumentNullException(nameof(operand));
|
||||
if (apply == null) throw new ArgumentNullException(nameof(apply));
|
||||
return or(op.Then(opvalue =>
|
||||
operand.Then(operandValue =>
|
||||
ChainRightOperatorRest(operandValue, op, operand, apply, or)).Then(r =>
|
||||
Return(apply(opvalue, lastOperand, r)))),
|
||||
Return(lastOperand));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a number.
|
||||
/// </summary>
|
||||
public static readonly Parser<string> Number = Numeric.AtLeastOnce().Text();
|
||||
|
||||
static Parser<string> DecimalWithoutLeadingDigits(CultureInfo ci = null)
|
||||
{
|
||||
return from nothing in Return("")
|
||||
// dummy so that CultureInfo.CurrentCulture is evaluated later
|
||||
from dot in String((ci ?? CultureInfo.CurrentCulture).NumberFormat.NumberDecimalSeparator).Text()
|
||||
from fraction in Number
|
||||
select dot + fraction;
|
||||
}
|
||||
|
||||
static Parser<string> DecimalWithLeadingDigits(CultureInfo ci = null)
|
||||
{
|
||||
return Number.Then(n => DecimalWithoutLeadingDigits(ci).XOr(Return("")).Select(f => n + f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a decimal number using the current culture's separator character.
|
||||
/// </summary>
|
||||
public static readonly Parser<string> Decimal = DecimalWithLeadingDigits().XOr(DecimalWithoutLeadingDigits());
|
||||
|
||||
/// <summary>
|
||||
/// Parse a decimal number with separator '.'.
|
||||
/// </summary>
|
||||
public static readonly Parser<string> DecimalInvariant = DecimalWithLeadingDigits(CultureInfo.InvariantCulture)
|
||||
.XOr(DecimalWithoutLeadingDigits(CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
50
Scripts/Utilities/ExpressionParser/Sprache/ParseException.cs
Normal file
50
Scripts/Utilities/ExpressionParser/Sprache/ParseException.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
|
||||
namespace Sprache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an error that occurs during parsing.
|
||||
/// </summary>
|
||||
public class ParseException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ParseException" /> class.
|
||||
/// </summary>
|
||||
public ParseException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ParseException" /> class with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public ParseException(string message) : base(message) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ParseException" /> class with a specified error message
|
||||
/// and the position where the error occured.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
/// <param name="position">The position where the error occured.</param>
|
||||
public ParseException(string message, Position position) : base(message)
|
||||
{
|
||||
if (position == null) throw new ArgumentNullException(nameof(position));
|
||||
|
||||
Position = position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ParseException" /> class with a specified error message
|
||||
/// and a reference to the inner exception that is the cause of this exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception,
|
||||
/// or a null reference (Nothing in Visual Basic) if no inner exception is specified.</param>
|
||||
public ParseException(string message, Exception innerException) : base(message, innerException) { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position of the parsing failure if one is available; otherwise, null.
|
||||
/// </summary>
|
||||
public Position Position {
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
54
Scripts/Utilities/ExpressionParser/Sprache/ParserOfT.cs
Normal file
54
Scripts/Utilities/ExpressionParser/Sprache/ParserOfT.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
|
||||
namespace Sprache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a parser.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the result.</typeparam>
|
||||
/// <param name="input">The input to parse.</param>
|
||||
/// <returns>The result of the parser.</returns>
|
||||
public delegate IResult<T> Parser<out T>(IInput input);
|
||||
|
||||
/// <summary>
|
||||
/// Contains some extension methods for <see cref="Parser<T>" />.
|
||||
/// </summary>
|
||||
public static class ParserExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Tries to parse the input without throwing an exception.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the result.</typeparam>
|
||||
/// <param name="parser">The parser.</param>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <returns>The result of the parser</returns>
|
||||
public static IResult<T> TryParse<T>(this Parser<T> parser, string input)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
if (input == null) throw new ArgumentNullException(nameof(input));
|
||||
|
||||
return parser(new Input(input));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the specified input string.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the result.</typeparam>
|
||||
/// <param name="parser">The parser.</param>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <returns>The result of the parser.</returns>
|
||||
/// <exception cref="Sprache.ParseException">It contains the details of the parsing error.</exception>
|
||||
public static T Parse<T>(this Parser<T> parser, string input)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
if (input == null) throw new ArgumentNullException(nameof(input));
|
||||
|
||||
var result = parser.TryParse(input);
|
||||
|
||||
if(result.WasSuccessful)
|
||||
return result.Value;
|
||||
|
||||
throw new ParseException(result.ToString(), Position.FromInput(result.Remainder));
|
||||
}
|
||||
}
|
||||
}
|
||||
136
Scripts/Utilities/ExpressionParser/Sprache/Position.cs
Normal file
136
Scripts/Utilities/ExpressionParser/Sprache/Position.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using System;
|
||||
|
||||
namespace Sprache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a position in the input.
|
||||
/// </summary>
|
||||
public class Position : IEquatable<Position>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Position" /> class.
|
||||
/// </summary>
|
||||
/// <param name="pos">The position.</param>
|
||||
/// <param name="line">The line number.</param>
|
||||
/// <param name="column">The column.</param>
|
||||
public Position(int pos, int line, int column)
|
||||
{
|
||||
Pos = pos;
|
||||
Line = line;
|
||||
Column = column;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an new <see cref="Position"/> instance from a given <see cref="IInput"/> object.
|
||||
/// </summary>
|
||||
/// <param name="input">The current input.</param>
|
||||
/// <returns>A new <see cref="Position"/> instance.</returns>
|
||||
public static Position FromInput(IInput input)
|
||||
{
|
||||
return new Position(input.Position, input.Line, input.Column);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current positon.
|
||||
/// </summary>
|
||||
public int Pos
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current line number.
|
||||
/// </summary>
|
||||
public int Line
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current column.
|
||||
/// </summary>
|
||||
public int Column
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="Position" />.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="Position" />; otherwise, false.
|
||||
/// </returns>
|
||||
/// <param name="obj">The object to compare with the current object. </param>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as Position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the current <see cref="Position" /> is equal to another object of the same type.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
|
||||
/// </returns>
|
||||
/// <param name="other">An object to compare with this object.</param>
|
||||
public bool Equals(Position other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return Pos == other.Pos
|
||||
&& Line == other.Line
|
||||
&& Column == other.Column;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the left <see cref="Position" /> is equal to the right <see cref="Position" />.
|
||||
/// </summary>
|
||||
/// <param name="left">The left <see cref="Position" />.</param>
|
||||
/// <param name="right">The right <see cref="Position" />.</param>
|
||||
/// <returns>true if both objects are equal.</returns>
|
||||
public static bool operator ==(Position left, Position right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the left <see cref="Position" /> is not equal to the right <see cref="Position" />.
|
||||
/// </summary>
|
||||
/// <param name="left">The left <see cref="Position" />.</param>
|
||||
/// <param name="right">The right <see cref="Position" />.</param>
|
||||
/// <returns>true if the objects are not equal.</returns>
|
||||
public static bool operator !=(Position left, Position right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as a hash function for a particular type.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A hash code for the current <see cref="Position" />.
|
||||
/// </returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var h = 31;
|
||||
h = h * 13 + Pos;
|
||||
h = h * 13 + Line;
|
||||
h = h * 13 + Column;
|
||||
return h;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents the current object.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string that represents the current object.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("Line {0}, Column {1}", Line, Column);
|
||||
}
|
||||
}
|
||||
}
|
||||
111
Scripts/Utilities/ExpressionParser/Sprache/Result.cs
Normal file
111
Scripts/Utilities/ExpressionParser/Sprache/Result.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniLinq;
|
||||
|
||||
namespace Sprache
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains helper functions to create <see cref="IResult<T>"/> instances.
|
||||
/// </summary>
|
||||
public static class Result
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a success result.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the result (value).</typeparam>
|
||||
/// <param name="value">The sucessfully parsed value.</param>
|
||||
/// <param name="remainder">The remainder of the input.</param>
|
||||
/// <returns>The new <see cref="IResult<T>"/>.</returns>
|
||||
public static IResult<T> Success<T>(T value, IInput remainder)
|
||||
{
|
||||
return new Result<T>(value, remainder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a failure result.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the result.</typeparam>
|
||||
/// <param name="remainder">The remainder of the input.</param>
|
||||
/// <param name="message">The error message.</param>
|
||||
/// <param name="expectations">The parser expectations.</param>
|
||||
/// <returns>The new <see cref="IResult<T>"/>.</returns>
|
||||
public static IResult<T> Failure<T>(IInput remainder, string message, IEnumerable<string> expectations)
|
||||
{
|
||||
return new Result<T>(remainder, message, expectations);
|
||||
}
|
||||
}
|
||||
|
||||
internal class Result<T> : IResult<T>
|
||||
{
|
||||
private readonly T _value;
|
||||
private readonly IInput _remainder;
|
||||
private readonly bool _wasSuccessful;
|
||||
private readonly string _message;
|
||||
private readonly IEnumerable<string> _expectations;
|
||||
|
||||
public Result(T value, IInput remainder)
|
||||
{
|
||||
_value = value;
|
||||
_remainder = remainder;
|
||||
_wasSuccessful = true;
|
||||
_message = null;
|
||||
_expectations = Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
public Result(IInput remainder, string message, IEnumerable<string> expectations)
|
||||
{
|
||||
_value = default(T);
|
||||
_remainder = remainder;
|
||||
_wasSuccessful = false;
|
||||
_message = message;
|
||||
_expectations = expectations;
|
||||
}
|
||||
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!WasSuccessful)
|
||||
throw new InvalidOperationException("No value can be computed.");
|
||||
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool WasSuccessful { get { return _wasSuccessful; } }
|
||||
|
||||
public string Message { get { return _message; } }
|
||||
|
||||
public IEnumerable<string> Expectations { get { return _expectations; } }
|
||||
|
||||
public IInput Remainder { get { return _remainder; } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (WasSuccessful)
|
||||
return string.Format("Successful parsing of {0}.", Value);
|
||||
|
||||
var expMsg = "";
|
||||
|
||||
if (Expectations.Any())
|
||||
expMsg = " expected " + Expectations.Aggregate((e1, e2) => e1 + " or " + e2);
|
||||
|
||||
var recentlyConsumed = CalculateRecentlyConsumed();
|
||||
|
||||
return string.Format("Parsing failure: {0};{1} ({2}); recently consumed: {3}", Message, expMsg, Remainder, recentlyConsumed);
|
||||
}
|
||||
|
||||
private string CalculateRecentlyConsumed()
|
||||
{
|
||||
const int windowSize = 10;
|
||||
|
||||
var totalConsumedChars = Remainder.Position;
|
||||
var windowStart = totalConsumedChars - windowSize;
|
||||
windowStart = windowStart < 0 ? 0 : windowStart;
|
||||
|
||||
var numberOfRecentlyConsumedChars = totalConsumedChars - windowStart;
|
||||
|
||||
return Remainder.Source.Substring(windowStart, numberOfRecentlyConsumedChars);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Scripts/Utilities/ExpressionParser/Sprache/ResultHelper.cs
Normal file
26
Scripts/Utilities/ExpressionParser/Sprache/ResultHelper.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace Sprache
|
||||
{
|
||||
internal static class ResultHelper
|
||||
{
|
||||
public static IResult<U> IfSuccess<T, U>(this IResult<T> result, Func<IResult<T>, IResult<U>> next)
|
||||
{
|
||||
if(result == null) throw new ArgumentNullException(nameof(result));
|
||||
|
||||
if (result.WasSuccessful)
|
||||
return next(result);
|
||||
|
||||
return Result.Failure<U>(result.Remainder, result.Message, result.Expectations);
|
||||
}
|
||||
|
||||
public static IResult<T> IfFailure<T>(this IResult<T> result, Func<IResult<T>, IResult<T>> next)
|
||||
{
|
||||
if (result == null) throw new ArgumentNullException(nameof(result));
|
||||
|
||||
return result.WasSuccessful
|
||||
? result
|
||||
: next(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniLinq;
|
||||
|
||||
namespace Sprache
|
||||
{
|
||||
internal static class StringExtensions
|
||||
{
|
||||
public static IEnumerable<char> ToEnumerable(this string @this)
|
||||
{
|
||||
#if STRING_IS_ENUMERABLE
|
||||
return @this;
|
||||
#else
|
||||
if (@this == null) throw new ArgumentNullException(nameof(@this));
|
||||
|
||||
for (var i = 0; i < @this.Length; ++i)
|
||||
{
|
||||
yield return @this[i];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string Join<T>(string separator, IEnumerable<T> values)
|
||||
{
|
||||
#if STRING_JOIN_ENUMERABLE
|
||||
return string.Join(separator, values);
|
||||
#else
|
||||
return string.Join(separator, values.Select(v => v.ToString()).ToArray());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
21
Scripts/Utilities/ExpressionParser/Sprache/licence.txt
Normal file
21
Scripts/Utilities/ExpressionParser/Sprache/licence.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2011 Nicholas Blumhardt
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
Reference in New Issue
Block a user