Skip to content

Instantly share code, notes, and snippets.

@amuradyan
Created May 2, 2022 21:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amuradyan/0ad22d170e5fc2e0983c7aaed96c6a1a to your computer and use it in GitHub Desktop.
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"
// 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