# Parser Combinator ### Agenda - Parser - Parser representation - Cat-parse library - Parser combinator in Lox --- ## What is a parser? ```graphviz digraph { // compound=true rankdir=LR graph [ fontname="Source Sans Pro", fontsize=12 ]; node [ fontname="Source Sans Pro", fontsize=12]; edge [ fontname="Source Sans Pro", fontsize=12 ]; a [label="Text"] [shape=box] b [label="AST/Tokens"] a -> b [label = "compiler/interpreter"] } ``` ```graphviz digraph { // compound=true rankdir=LR graph [ fontname="Source Sans Pro", fontsize=12 ]; node [ fontname="Source Sans Pro", fontsize=12]; edge [ fontname="Source Sans Pro", fontsize=12 ]; a [label="String"] [shape=box] b [label="JSON value"] a -> b [label = "JSON parser"] } ``` --- ### Parser representation What is the type representation for parser? --- ### The first try ```Scala type Parser[A] = String => A ``` --- ### Better version ```Scala type Parser[A] = String => Either[ParserError, A] ``` --- ### Probably the best? ```scala type Parser[A] = String => Either[ParserError, (String, A)]) ``` --- ### Parser Combinator library [cat-parse](https://github.com/typelevel/cats-parse) ```scala sealed abstract class Parser[+A] { final def parse(str: String): Either[Parser.Error, (String, A)] // Attempt to parse all of the input `str` into an `A` value. final def parseAll(str: String): Either[Parser.Error, A] } ``` --- ```scala import cats.parse.Parser val p: Parser[Char] = Parser.anyChar p.parse("t") // res0: Either[Error, Tuple2[String, Char]] = Right((,t)) p.parse("") // res1: Either[Error, Tuple2[String, Char]] = Left(Error(0,NonEmptyList(InRange(0,,)))) p.parse("two") // res2: Either[Error, Tuple2[String, Char]] = Right((wo,t)) ``` --- ### Predefined Parser ```scala // Parser.char('a').parse("abc") => Right(("bc", 'a')) def char(c: Char): Parser[Char] // Parser.string("hello").parse("hello world") => Right((" world", "hello")) def string(str: String): Parser[String] // Parser.charIn('a', 'b', 'c').parse("bca") => Right(("ca", 'b')) def charIn(c0: Char, cs: Char*): Parser[Char] = ``` --- ### Predefined Parser ```scala // A-Z and a-z, without diacritics def Parser.alpha: Parser[Char] // 0-9 def Parser.digit: Parser[Char] ``` --- ### Combinators ```scala sealed abstract class Parser[+A] { // val p = Parser.string("hello").map(s => s ++ " parser") // p.parser("hello world") => Right((" world", "hello parser")) def map[B](fn: A => B): Parser[B] // val p = Parser.string("hello").as("Xin chao") // p.parser("hello world") => Right((" world", "Xin chao")) def as[B](b: B): Parser[B] } ``` --- ### Combinators ```scala sealed abstract class Parser[+A] { // val p = Parser.char('a') | Parser.char('b') // p.parser("bca") => Right(("ca", 'b')) def |(that: Parser[A]): Parser[A] //Sequence another parser after this one, combining // both results into a tuple. // val p = Parser.char('a') ~ Parser.char('b') // p.parser("abc") => Right(("c", ('a', 'b'))) def ~[B](that: Parser[B]): Parser[(A, B)] = } ``` --- ### Combinators ```scala sealed abstract class Parser[+A] { // val p = Parser.not(Parser.char('a')) // p.parser("bca") => Right(("ca", Unit)) // p.parser("abc") => Left(ParserError(...)) def not(pa: Parser[Any]): Parser[Unit] = // parse zero or more characters as long as they don't match p def until0(p: Parser[Any]): Parser[String] = // parse 1 or more characters as long as they don't match p def until(p: Parser[Any]): Parser[String] = } ``` --- ### Combinators ```scala sealed abstract class Parser[+A] { def oneOf[A](parsers: List[Parser[A]]): Parser[A] = { def rep0: Parser[List[A]] def rep: Parser[NonEmptyList[A]] } ``` --- ### Combinators ```scala sealed abstract class Parser[+A] { // discard the value in the parser // Parser.char('a').void.parse("abc") => Right(("bc", Unit)) def void: Parser0[Unit] = // x *> y` is equivalent to `(x.void ~ y).map(_._2)`. def *>[B](that: Parser[B]): Parser[B] = // `x <* y` is equivalent to `(x ~ y.void).map(_._1)`. def <*[B](that: Parser[B]): Parser[A] = } ```
{"metaMigratedAt":"2023-06-16T15:20:02.611Z","metaMigratedFrom":"Content","title":"Parser Combinator","breaks":true,"contributors":"[{\"id\":\"28f9d763-8570-42fd-946f-90c08e4ae219\",\"add\":4283,\"del\":68}]"}
    191 views