diff -r 15973df32613 -r 2bf1516d730f progs/parser-combinators/comb1.sc --- a/progs/parser-combinators/comb1.sc Wed Dec 21 14:33:05 2022 +0000 +++ b/progs/parser-combinators/comb1.sc Tue Apr 04 22:31:09 2023 +0100 @@ -6,39 +6,37 @@ // amm comb1.sc -// Note, in the lectures I did not show the implicit type constraint -// I : IsSeq, which means that the input type 'I' needs to be -// a sequence. +// Note, in the lectures I did not show the type bound +// using is: I => Seq[_], which means that the input +// type 'I' needs to be a sequence. -type IsSeq[A] = A => Seq[_] - -abstract class Parser[I : IsSeq, T]{ +abstract class Parser[I, T](using is: I => Seq[_]) { def parse(in: I): Set[(T, I)] def parse_all(in: I) : Set[T] = for ((hd, tl) <- parse(in); - if tl.isEmpty) yield hd + if is(tl).isEmpty) yield hd } // parser combinators // alternative parser -class AltParser[I : IsSeq, T](p: => Parser[I, T], - q: => Parser[I, T]) extends Parser[I, T] { +class AltParser[I, T](p: => Parser[I, T], + q: => Parser[I, T])(using I => Seq[_]) extends Parser[I, T] { def parse(in: I) = p.parse(in) ++ q.parse(in) } // sequence parser -class SeqParser[I : IsSeq, T, S](p: => Parser[I, T], - q: => Parser[I, S]) extends Parser[I, (T, S)] { +class SeqParser[I, T, S](p: => Parser[I, T], + q: => Parser[I, S])(using I => Seq[_]) extends Parser[I, (T, S)] { def parse(in: I) = for ((hd1, tl1) <- p.parse(in); (hd2, tl2) <- q.parse(tl1)) yield ((hd1, hd2), tl2) } // map parser -class MapParser[I : IsSeq, T, S](p: => Parser[I, T], - f: T => S) extends Parser[I, S] { +class MapParser[I, T, S](p: => Parser[I, T], + f: T => S)(using I => Seq[_]) extends Parser[I, S] { def parse(in: I) = for ((hd, tl) <- p.parse(in)) yield (f(hd), tl) } @@ -50,7 +48,7 @@ if (in != "" && in.head == c) Set((c, in.tail)) else Set() } -CharParser('c').parse("abc") +CharParser('a').parse("abc") // an atomic parser for parsing strings according to a regex import scala.util.matching.Regex @@ -67,7 +65,7 @@ def StrParser(s: String) = RegexParser(Regex.quote(s).r) NumParser.parse("a123a123bc") -StrParser("else").parse("eelsethen") +StrParser("else").parse("elsethen") // NumParserInt transforms a "string integer" into a propper Int @@ -81,23 +79,22 @@ // // p"<_some_string_>" +extension (sc: StringContext) + def p(args: Any*) = StrParser(sc.s(args:_*)) -implicit def parser_interpolation(sc: StringContext) = new { - def p(args: Any*) = StrParser(sc.s(args:_*)) -} (p"else").parse("elsethen") // more convenient syntax for parser combinators -implicit def ParserOps[I : IsSeq, T](p: Parser[I, T]) = new { +extension [I, T](p: Parser[I, T])(using I => Seq[_]) { def ||(q : => Parser[I, T]) = new AltParser[I, T](p, q) def ~[S] (q : => Parser[I, S]) = new SeqParser[I, T, S](p, q) - def map[S](f: => T => S) = new MapParser[I, T, S](p, f) + def mapp[S](f: => T => S) = new MapParser[I, T, S](p, f) } def toU(s: String) = s.map(_.toUpper) -(p"ELSE").map(toU(_)).parse("ELSEifthen") +(p"ELSE").mapp(toU(_)).parse("ELSEifthen") // these implicits allow us to use an infix notation for // sequences and alternatives; we also can write the usual @@ -110,12 +107,10 @@ val NumParserInt2 = NumParser.map(_.toInt) - - // A parser for palindromes (just returns them as string) lazy val Pal : Parser[String, String] = { - (p"a" ~ Pal ~ p"a").map{ case ((x, y), z) => s"$x$y$z" } || - (p"b" ~ Pal ~ p"b").map{ case ((x, y), z) => s"$x$y$z" } || + (p"a" ~ Pal ~ p"a").mapp{ case ((x, y), z) => s"$x$y$z" } || + (p"b" ~ Pal ~ p"b").mapp{ case ((x, y), z) => s"$x$y$z" } || p"a" || p"b" || p"" } @@ -131,7 +126,7 @@ // // (transforms '(' -> '{' , ')' -> '}' ) lazy val P : Parser[String, String] = { - (p"(" ~ P ~ p")" ~ P).map{ case (((_, x), _), y) => "{" + x + "}" + y } || + (p"(" ~ P ~ p")" ~ P).mapp{ case (((_, x), _), y) => "{" + x + "}" + y } || p"" } @@ -233,13 +228,28 @@ println(E.parse("1")) println(E.parse("(1)")) println(E.parse("((1))")) -//println(E.parse("(((1)))")) -//println(E.parse("((((1))))")) +println(E.parse("(((1)))")) +println(E.parse("((((1))))")) //println(E.parse("((((((1))))))")) //println(E.parse("(((((((1)))))))")) -//println(E.parse("((((((((1)))))))")) +//println(E.parse("((((((((1))))))))")) +// faster because of merge +lazy val E2: Parser[String, Int] = { + (T2 ~ (p"+" || p"-") ~ E2).mapp[Int]{ case ((x, y), z) => if (y == "+") x + z else x - z} || T2 } +lazy val T2: Parser[String, Int] = { + (F2 ~ p"*" ~ T2).mapp[Int]{ case ((x, _), z) => x * z } || F2 } +lazy val F2: Parser[String, Int] = { + (p"(" ~ E2 ~ p")").mapp[Int]{ case ((_, y), _) => y } || NumParserInt } -// runs with amm2 and amm3 +println("Runtime problem") +println(E2.parse("1")) +println(E2.parse("(1)")) +println(E2.parse("((1))")) +println(E2.parse("(((1)))")) +println(E2.parse("((((1))))")) +//println(E2.parse("((((((1))))))")) +//println(E2.parse("(((((((1)))))))")) +//println(E2.parse("((((((((1))))))))")) \ No newline at end of file