Skip to content

Instantly share code, notes, and snippets.

@AlexeyRaga
Last active August 29, 2015 14:01
Show Gist options
  • Save AlexeyRaga/55ea31d659e89ae005c2 to your computer and use it in GitHub Desktop.
Save AlexeyRaga/55ea31d659e89ae005c2 to your computer and use it in GitHub Desktop.
Semi-Ideomatic Parsers in C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
namespace SemiIdeomaticParsers
{
public delegate Tuple<T, string> Parser<T>(string value);
public static class ParserMonad
{
public static Parser<T> Return<T>(T value) { return s => Tuple.Create(value, s); }
public static Parser<String> Raw(String grammar)
{
return x =>
{
var m = Regex.Match(x, grammar);
if (!m.Success) return null;
return Tuple.Create(m.Value, x.Substring(m.Index + m.Length));
};
}
public static Parser<U> Bind<T, U>(this Parser<T> p, Func<T, Parser<U>> f)
{
return s =>
{
var t = p(s);
return f(t.Item1)(t.Item2);
};
}
public static Parser<U> Map<T, U>(this Parser<T> p, Func<T, U> f) {
return s =>
{
var t = p(s);
return Tuple.Create(f(t.Item1), t.Item2);
};
}
public static T Run<T>(this Parser<T> p, string rawValue) { return p(rawValue).Item1; }
}
public static class ParserLinq
{
public static Parser<K> SelectMany<T, U, K>(this Parser<T> p, Func<T, Parser<U>> f, Func<T, U, K> g)
{
return p.Bind(x => f(x).Bind(y => ParserMonad.Return(g(x, y))));
}
}
public static class Parsers
{
public static Parser<T> ParseMap<T>(string grammar, Func<String, T> f)
{
return ParserMonad.Raw(grammar).Map(f);
}
public static Parser<long> Long = ParseMap(@"\d+", Int64.Parse);
public static Parser<String> Word = ParserMonad.Raw(@"\w+");
public static Parser<String> Space = ParserMonad.Raw(@"\s+");
}
class Program
{
static void Main(string[] args)
{
var parser =
from name in Parsers.Word
from age in Parsers.Long
from likes in Parsers.ParseMap(@"likes\s+(\w+)", x => x.Substring(x.IndexOf(" ")+1))
select String.Format("{0} is {1}, interests: {2}", name, age, likes);
var result = parser.Run("Alexey is about 37 years old and likes FP");
Console.WriteLine(result);
}
}
}
class Parser[A](self: String => (A, String)) extends ((String) => (A, String)) {
def apply(value: String) = self(value)
def flatMap[B](f: A => Parser[B]) = Parser { s =>
val (res, tail) = self(s)
f(res)(tail)
}
def map[B](f: A => B) = Parser { s =>
val (res, tail) = self(s)
(f(res), tail)
}
def run(value: String) = self(value)._1
}
object Parser {
def apply[A](f: String => (A, String)) = new Parser(f)
def raw(grammar: String) = Parser { s =>
grammar.r.findFirstMatchIn(s).fold(("", s))(m => (m.group(0), s.substring(m.end)))
}
}
object ParserOps {
def parseMap[A](grammar: String)(f: String => A) = Parser raw grammar map f
val long = parseMap[Long]("\\d+")(_.toLong)
val word = Parser.raw("\\w+")
val space = Parser.raw("\\s+")
}
object TestApp extends App {
import ParserOps._
val parser = for {
name <- word
age <- long
likes <- parseMap("likes\\s+(\\w+)")(x => x.substring(x.indexOf(" ")+1))
} yield s"$name is $age, interests: $likes"
println(parser.run("alexey is about 37 years old and he likes FP"))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment