|
| 1 | +package scala.util.parsing.combinator |
| 2 | + |
| 3 | +import org.junit.Test |
| 4 | +import org.junit.Assert.assertEquals |
| 5 | +import org.junit.Assert.assertTrue |
| 6 | + |
| 7 | +import scala.util.parsing.combinator.syntactical.StandardTokenParsers |
| 8 | + |
| 9 | +class PackratParsersTest { |
| 10 | + |
| 11 | + @Test |
| 12 | + def test1: Unit = { |
| 13 | + import grammars1._ |
| 14 | + val head = phrase(term) |
| 15 | + |
| 16 | + def extractResult(r : ParseResult[Int]): Int = r match { |
| 17 | + case Success(a,_) => a |
| 18 | + case NoSuccess(a,_) => sys.error(a) |
| 19 | + } |
| 20 | + def check(expected: Int, expr: String): Unit = { |
| 21 | + val parseResult = head(new lexical.Scanner(expr)) |
| 22 | + val result = extractResult(parseResult) |
| 23 | + assertEquals(expected, result) |
| 24 | + } |
| 25 | + |
| 26 | + check(1, "1") |
| 27 | + check(3, "1+2") |
| 28 | + check(5, "9-4") |
| 29 | + check(81, "9*9") |
| 30 | + check(4, "8/2") |
| 31 | + check(37, "4*9-0/7+9-8*1") |
| 32 | + check(9, "(1+2)*3") |
| 33 | + } |
| 34 | + |
| 35 | + @Test |
| 36 | + def test2: Unit = { |
| 37 | + import grammars2._ |
| 38 | + val head = phrase(exp) |
| 39 | + |
| 40 | + def extractResult(r : ParseResult[Int]): Int = r match { |
| 41 | + case Success(a,_) => a |
| 42 | + case NoSuccess(a,_) => sys.error(a) |
| 43 | + } |
| 44 | + def check(expected: Int, expr: String): Unit = { |
| 45 | + val parseResult = head(new lexical.Scanner(expr)) |
| 46 | + val result = extractResult(parseResult) |
| 47 | + assertEquals(expected, result) |
| 48 | + } |
| 49 | + |
| 50 | + check(1, "1") |
| 51 | + check(3, "1+2") |
| 52 | + check(81, "9*9") |
| 53 | + check(43, "4*9+7") |
| 54 | + check(59, "4*9+7*2+3*3") |
| 55 | + check(188, "4*9+7*2+3*3+9*5+7*6*2") |
| 56 | + check(960, "4*(9+7)*(2+3)*3") |
| 57 | + } |
| 58 | + |
| 59 | + @Test |
| 60 | + def test3: Unit = { |
| 61 | + import grammars3._ |
| 62 | + val head = phrase(AnBnCn) |
| 63 | + def extractResult(r: ParseResult[AnBnCnResult]): AnBnCnResult = r match { |
| 64 | + case Success(a,_) => a |
| 65 | + case NoSuccess(a,_) => sys.error(a) |
| 66 | + } |
| 67 | + def threeLists(as: List[Symbol], bs: List[Symbol], cs: List[Symbol]): AnBnCnResult = { |
| 68 | + val as1 = as.map(_.name) |
| 69 | + val bs1 = bs.map(_.name) |
| 70 | + val cs1 = cs.map(_.name) |
| 71 | + new ~(new ~(as1, bs1), cs1) |
| 72 | + } |
| 73 | + def assertSuccess(expected1: List[Symbol], expected2: List[Symbol], expected3: List[Symbol], |
| 74 | + input: String): Unit = { |
| 75 | + val expected = threeLists(expected1, expected2, expected3) |
| 76 | + val parseResult = head(new lexical.Scanner(input)) |
| 77 | + val result = extractResult(parseResult) |
| 78 | + assertEquals(expected, result) |
| 79 | + } |
| 80 | + |
| 81 | + assertSuccess(List('a, 'b), List('a), List('b, 'c), "a b c") |
| 82 | + assertSuccess(List('a, 'a, 'b, 'b), List('a, 'a), List('b, 'b, 'c, 'c), "a a b b c c") |
| 83 | + assertSuccess(List('a, 'a, 'a, 'b, 'b, 'b), List('a, 'a, 'a), List('b, 'b, 'b, 'c, 'c, 'c), |
| 84 | + "a a a b b b c c c") |
| 85 | + assertSuccess(List('a, 'a, 'a, 'a, 'b, 'b, 'b, 'b), List('a, 'a, 'a, 'a), List('b, 'b, 'b, 'b, 'c, 'c, 'c, 'c), |
| 86 | + "a a a a b b b b c c c c") |
| 87 | + |
| 88 | + def assertFailure(expectedFailureMsg: String, input: String): Unit = { |
| 89 | + val packratReader = new PackratReader(new lexical.Scanner(input)) |
| 90 | + val parseResult = AnBnCn(packratReader) |
| 91 | + assertTrue(s"Not an instance of Failure: ${parseResult.toString()}", parseResult.isInstanceOf[Failure]) |
| 92 | + val failure = parseResult.asInstanceOf[Failure] |
| 93 | + assertEquals(expectedFailureMsg, failure.msg) |
| 94 | + } |
| 95 | + assertFailure("``b'' expected but `c' found", "a a a a b b b c c c c") |
| 96 | + assertFailure("end of input", "a a a a b b b b c c c") |
| 97 | + } |
| 98 | + |
| 99 | +} |
| 100 | + |
| 101 | +private object grammars1 extends StandardTokenParsers with PackratParsers { |
| 102 | + |
| 103 | + lexical.delimiters ++= List("+","-","*","/","(",")") |
| 104 | + lexical.reserved ++= List("Hello","World") |
| 105 | + |
| 106 | + /**** |
| 107 | + * term = term + fact | term - fact | fact |
| 108 | + * fact = fact * num | fact / num | num |
| 109 | + */ |
| 110 | + |
| 111 | + |
| 112 | + val term: PackratParser[Int] = (term~("+"~>fact) ^^ {case x~y => x+y} |
| 113 | + |term~("-"~>fact) ^^ {case x~y => x-y} |
| 114 | + |fact) |
| 115 | + |
| 116 | + val fact: PackratParser[Int] = (fact~("*"~>numericLit) ^^ {case x~y => x*y.toInt} |
| 117 | + |fact~("/"~>numericLit) ^^ {case x~y => x/y.toInt} |
| 118 | + |"("~>term<~")" |
| 119 | + |numericLit ^^ {_.toInt}) |
| 120 | +} |
| 121 | + |
| 122 | +private object grammars2 extends StandardTokenParsers with PackratParsers { |
| 123 | + |
| 124 | + lexical.delimiters ++= List("+","-","*","/","(",")") |
| 125 | + lexical.reserved ++= List("Hello","World") |
| 126 | + |
| 127 | + /* |
| 128 | + * exp = sum | prod | num |
| 129 | + * sum = exp ~ "+" ~ num |
| 130 | + * prod = exp ~ "*" ~ num |
| 131 | + */ |
| 132 | + |
| 133 | + val exp : PackratParser[Int] = sum | prod | numericLit ^^{_.toInt} | "("~>exp<~")" |
| 134 | + val sum : PackratParser[Int] = exp~("+"~>exp) ^^ {case x~y => x+y} |
| 135 | + val prod: PackratParser[Int] = exp~("*"~>(numericLit ^^{_.toInt} | exp)) ^^ {case x~y => x*y} |
| 136 | + |
| 137 | +} |
| 138 | + |
| 139 | +private object grammars3 extends StandardTokenParsers with PackratParsers { |
| 140 | + lexical.reserved ++= List("a","b", "c") |
| 141 | + val a: PackratParser[String] = memo("a") |
| 142 | + val b: PackratParser[String] = memo("b") |
| 143 | + val c: PackratParser[String] = memo("c") |
| 144 | + |
| 145 | + type AnBnCnResult = List[String] ~ List[String] ~ List[String] |
| 146 | + |
| 147 | + val AnBnCn: PackratParser[AnBnCnResult] = |
| 148 | + guard(repMany1(a,b) <~ not(b)) ~ rep1(a) ~ repMany1(b,c)// ^^{case x~y => x:::y} |
| 149 | + |
| 150 | + |
| 151 | + private def repMany[T](p: => Parser[T], q: => Parser[T]): Parser[List[T]] = |
| 152 | + ( p~repMany(p,q)~q ^^ {case x~xs~y => x::xs:::(y::Nil)} |
| 153 | + | success(Nil) |
| 154 | + ) |
| 155 | + |
| 156 | + def repMany1[T](p: => Parser[T], q: => Parser[T]): Parser[List[T]] = |
| 157 | + p~opt(repMany(p,q))~q ^^ {case x~Some(xs)~y => x::xs:::(y::Nil)} |
| 158 | + |
| 159 | +} |
0 commit comments