Created
May 2, 2022 21:53
-
-
Save amuradyan/0ad22d170e5fc2e0983c7aaed96c6a1a to your computer and use it in GitHub Desktop.
Some code from "Parser Combinators: A Type-Driven Approach To Input ProcessiEOF by Bastien Louërat"
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
// https://www.signifytechnology.com/blog/2019/01/parser-combinators-a-type-driven-approach-to-input-processieof-by-bastien-louerat?source=google.com | |
sealed trait Parser[A] | |
case class Exactly(char: Char) extends Parser[Char] | |
val parserOfA: Parser[Char] = Exactly('a') | |
val parserOfB: Parser[Char] = Exactly('b') | |
sealed trait Error | |
case object EOF extends Error | |
case object UnknownParser extends Error | |
case class Unexpected(char: Char) extends Error | |
case class Or[A](parser1: Parser[A], parser2: Parser[A]) extends Parser[A] | |
val aOrB: Parser[Char] = Or(parserOfA, parserOfB) | |
case class NEL[A](head: A, tail: List[A]) { | |
def map[B](f: A => B): NEL[B] = NEL(f(head), tail.map(f)) | |
} | |
def oneOf[A](parsers: NEL[Parser[A]]): Parser[A] = parsers.tail.foldLeft(parsers.head) (Or(_, _)) | |
case class And[A, B](parser1: Parser[A], parser2: Parser[B]) extends Parser[(A, B)] | |
val AandB: Parser[(Char, Char)] = And(parserOfA, parserOfB) | |
def run[A](parser: Parser[A])(input: String): Either[Error, (A, String)] = parser match | |
case Exactly(char) => input.headOption.toRight(EOF).flatMap { head => | |
if (head == char) Right(char, input.tail) | |
else Left(Unexpected(head)) | |
} | |
case Or(parser1, parser2) => run(parser1)(input) match { | |
case Left(error) => run(parser2)(input) | |
case right => right | |
} | |
case And(parser1, parser2) => for { | |
result1 <- run(parser1)(input) | |
result2 <- run(parser2)(result1._2) | |
} yield ((result1._1, result2._1), result2._2) | |
case _ => Left(UnknownParser) | |
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
val allDigits = NEL('0', ('1' to '9').toList) | |
val digitParser = oneOf(allDigits.map(Exactly(_))) | |
run(parserOfA)("") | |
run(parserOfA)("a") | |
run(parserOfB)("b") | |
run(parserOfA)("a") | |
run(aOrB)("") | |
run(aOrB)("a") | |
run(aOrB)("b") | |
run(aOrB)("c") | |
run(digitParser)("") | |
run(digitParser)("a") | |
run(digitParser)("1") | |
run(digitParser)("9") | |
run(digitParser)("0") | |
val AtoZ = NEL('A', ('B' to 'Z').toList) | |
val atoz = oneOf(AtoZ.map(Exactly(_))) | |
run(atoz)("ABCD") | |
run(AandB)("a") | |
run(AandB)("b") | |
run(AandB)("ab") | |
run(AandB)("abcd") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment