Last active
August 29, 2015 14:01
-
-
Save AlexeyRaga/55ea31d659e89ae005c2 to your computer and use it in GitHub Desktop.
Semi-Ideomatic Parsers in C#
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; | |
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); | |
} | |
} | |
} |
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
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