Created
July 29, 2013 12:52
-
-
Save iSynaptic/6104113 to your computer and use it in GitHub Desktop.
Sprache Interleaving Sample
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 SpracheTestbed | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
var parser = from greeting in Parse.String("Hello") | |
from sep in Parse.String(",") | |
from subject in Parse.String("World") | |
from punc in Parse.String("!") | |
select new { greeting, sep, subject, punc }; | |
parser = parser.InterleaveWith(Parse.String(" ")) | |
.InterleaveWith(Parse.String("\r")) | |
.InterleaveWith(Parse.String("\n")); | |
var result = parser(@" | |
Hello | |
, | |
World | |
! | |
"); | |
var val = result.Value; | |
Console.WriteLine("{0}{1} {2}{3}", val.greeting, val.sep, val.subject, val.punc); | |
} | |
} | |
public static class Parse | |
{ | |
public static Parser<char> AnyChar = context => | |
{ | |
if (context.AtEnd) | |
return Result.WithoutValue<char>("any character", context, new ParseError(context.Index, "Expected: any character")); | |
return Result.WithValue(context.Current, "any character", context.Advance()); | |
}; | |
public static Parser<string> String(string expected) | |
{ | |
if(expected == null) throw new ArgumentNullException("expected"); | |
return context => | |
{ | |
if (expected.Length == 0) | |
return Result.WithValue("", expected, context); | |
string sample = context.Input.Substring(context.Index, expected.Length); | |
if (sample == expected) | |
return Result.WithValue(expected, expected, context.Advance(sample.Length)); | |
return Result.WithoutValue<String>(expected, context, new ParseError(context.Index, string.Format("Expected string: \"{0}\"", expected))); | |
}; | |
} | |
public static Parser<T> InterleaveWith<T>(this Parser<T> @this, Parser<Object> interleaving) | |
{ | |
return context => | |
{ | |
interleaving = context.Interleaving != null | |
? context.Interleaving.Or(interleaving) | |
: interleaving; | |
var newContext = new Context(context.Input, context.Name, interleaving, context.Index); | |
return @this(newContext); | |
}; | |
} | |
public static Parser<T> Interleave<T>(this Parser<T> @this) | |
{ | |
return context => | |
{ | |
if (context.Interleaving != null) | |
{ | |
var result = context.Interleaving(context); | |
while (result.HasValue) | |
{ | |
context = result.Context; | |
result = context.Interleaving(context); | |
} | |
} | |
return @this(context); | |
}; | |
} | |
public static Parser<T> Or<T>(this Parser<T> first, Parser<T> second) | |
{ | |
return i => | |
{ | |
var fr = first(i); | |
return fr.HasValue ? fr : second(i); | |
}; | |
} | |
public static Parser<U> Select<T, U>(this Parser<T> @this, Func<T, U> selector) | |
{ | |
return context => | |
{ | |
var result = @this(context); | |
if (result.HasValue) | |
return Result.WithValue(selector(result.Value), result.Description, result.Context, result.Errors); | |
return Result.WithoutValue<U>(result.Description, result.Context, result.Errors); | |
}; | |
} | |
public static Parser<U> SelectMany<T, U>(this Parser<T> @this, Func<T, Parser<U>> selector) | |
{ | |
return context => | |
{ | |
var result = @this.Interleave()(context); | |
if (result.HasValue) | |
{ | |
var nextResult = selector(result.Value).Interleave()(result.Context); | |
return nextResult; | |
} | |
return Result.WithoutValue<U>(result.Description, result.Context, result.Errors); | |
}; | |
} | |
public static Parser<TResult> SelectMany<T, TIntermediate, TResult>(this Parser<T> @this, | |
Func<T, Parser<TIntermediate>> selector, | |
Func<T, TIntermediate, TResult> combiner) | |
{ | |
return SelectMany(@this, x => selector(x).Select(y => combiner(x, y))); | |
} | |
} | |
public static class Result | |
{ | |
public static IResult<T> WithValue<T>(T value, string description, Context context, params ParseError[] errors) | |
{ | |
return WithValue(value, description, context, (IEnumerable<ParseError>) errors); | |
} | |
public static IResult<T> WithValue<T>(T value, string description, Context context, IEnumerable<ParseError> errors) | |
{ | |
return new Result<T>(value, true, description, context, errors); | |
} | |
public static IResult<T> WithoutValue<T>(string description, Context context, params ParseError[] errors) | |
{ | |
return WithoutValue<T>(description, context, (IEnumerable<ParseError>)errors); | |
} | |
public static IResult<T> WithoutValue<T>(string description, Context context, IEnumerable<ParseError> errors) | |
{ | |
return new Result<T>(default(T), false, description, context, errors); | |
} | |
} | |
internal class Result<T> : IResult<T> | |
{ | |
public Result(T value, bool hasValue, string description, Context context, IEnumerable<ParseError> errors) | |
{ | |
Value = value; | |
HasValue = hasValue; | |
Description = description; | |
Context = context; | |
Errors = errors ?? new ParseError[0]; | |
} | |
public T Value { get; private set; } | |
public bool HasValue { get; private set; } | |
public string Description { get; private set; } | |
public Context Context { get; private set; } | |
public IEnumerable<ParseError> Errors { get; private set; } | |
} | |
public interface IResult<out T> | |
{ | |
T Value { get; } | |
Boolean HasValue { get; } | |
String Description { get; } | |
Context Context { get; } | |
IEnumerable<ParseError> Errors { get; } | |
} | |
public class ParseError | |
{ | |
public ParseError(int index, string message) | |
{ | |
Index = index; | |
Message = message; | |
} | |
public int Index { get; private set; } | |
public string Message { get; private set; } | |
} | |
public delegate IResult<T> Parser<out T>(Context context); | |
public class Context | |
{ | |
public readonly int Index; | |
public readonly string Input; | |
public readonly char Current; | |
public readonly string Name; | |
public readonly bool AtEnd; | |
public readonly Parser<Object> Interleaving; | |
public Context(string input, string name) | |
: this(input, name, null, 0) | |
{ | |
} | |
public Context(string input, string name, Parser<Object> interleaving) | |
: this(input, name, interleaving, 0) | |
{ | |
} | |
internal Context(string input, string name, Parser<Object> interleaving, int index) | |
{ | |
if(input == null) throw new ArgumentNullException("input"); | |
Input = input; | |
AtEnd = input.Length == index; | |
if(!AtEnd) | |
Current = input[index]; | |
Name = name ?? ""; | |
Interleaving = interleaving; | |
Index = index; | |
} | |
public Context Advance() { return Advance(1); } | |
public Context Advance(int count) | |
{ | |
if(count <= 0) | |
throw new ArgumentOutOfRangeException("count"); | |
int newIndex = Index + count; | |
if(AtEnd || newIndex > Input.Length) | |
throw new ArgumentException(String.Format("Advancing {0} charaters exceeds the length of the input.", count), "count"); | |
return new Context(Input, Name, Interleaving, newIndex); | |
} | |
public static implicit operator Context(string content) | |
{ | |
return new Context(content, null); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment