Skip to content

Instantly share code, notes, and snippets.

@iSynaptic
Created July 29, 2013 12:52
Show Gist options
  • Save iSynaptic/6104113 to your computer and use it in GitHub Desktop.
Save iSynaptic/6104113 to your computer and use it in GitHub Desktop.
Sprache Interleaving Sample
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