MatchError in Scala parser combinator to parse query string - scala

I am creating a parser to parse a query string like this
e = "500.3" AND dt = "20190710" AND s in ("ERROR", "WARN") OR cat = "Conditional"
For the previous string I get the following:
Exception in thread "main" scala.MatchError: (Identifier(e),(=,StringLiteral(500.3))) (of class scala.Tuple2)
I am assuming that my grammar is fine (maybe not). Maybe someone can help find out why I am getting this error. Here is my parser.
class SearchQueryParser extends StandardTokenParsers {
lexical.reserved += ("OR", "AND")
lexical.delimiters += ( "<", "=", "<>", "!=", "<=", ">=", ">", "(", ")")
def expr: Parser[QueryExp] = orExp
def orExp: Parser[QueryExp] = andExp *("OR" ^^^ {(a: QueryExp, b: QueryExp) => BoolExp("OR", (a, b))})
def andExp: Parser[QueryExp] = compareExp *("AND" ^^^ {(a: QueryExp, b: QueryExp) => BoolExp("AND", (a, b))})
def compareExp: Parser[QueryExp] = {
identifier ~ rep(("=" | "<>" | "!=" | "<" | "<=" | ">" | ">=") ~ literal ^^ {
case op ~ rhs => (op, rhs)
}) ^^ {
case lhs ~ elems =>
elems.foldLeft(lhs) {
case (id, ("=", rhs: String)) => Binomial("=", id.str, rhs)
case (id, ("<>", rhs: String)) => Binomial("!=", id.str, rhs)
case (id, ("!=", rhs: String)) => Binomial("!=", id.str, rhs)
case (id, ("<", rhs: String)) => Binomial("<", id.str, rhs)
case (id, ("<=", rhs: String)) => Binomial("<=", id.str, rhs)
case (id, (">", rhs: String)) => Binomial(">", id.str, rhs)
case (id, (">=", rhs: String)) => Binomial(">=", id.str, rhs)
}
}
}
def literal: Parser[QueryExp] = stringLit ^^ (s => StringLiteral(s))
def identifier: Parser[QueryExp] = ident ^^ (s => Identifier(s))
def parse(queryStr: String): Option[QueryExp] = {
phrase(expr)(new lexical.Scanner(queryStr)) match {
case Success(r, _) => Option(r)
case x => println(x); None
}
}
}

I was able to find the issue. It seems like the error was produced because the partial function in the foldLeft(lhs) statement wasn't matching the tuple (=,StringLiteral(500.3))
As you can see, in every case statement of the partial function, I am trying to match a rhs of type String
...
case (id, ("=", rhs: String)) => Binomial("=", id.str, rhs)
case (id, ("<>", rhs: String)) => Binomial("!=", id.str, rhs)
case (id, ("!=", rhs: String)) => Binomial("!=", id.str, rhs)
...
However, as you can see in the error Exception in thread "main" scala.MatchError: (Identifier(e),(=,StringLiteral(500.3))) (of class scala.Tuple2) the input was parsed as a tuple of "=" and StringLiteral.
The solution was to change the type of the rhs parameter:
...
case (id, ("=", rhs: StringLiteral)) => Binomial("=", id.str, rhs.toString)
case (id, ("<>", rhs: StringLiteral)) => Binomial("!=", id.str, rhs.toString)
case (id, ("!=", rhs: StringLiteral)) => Binomial("!=", id.str, rhs.toString)
...

Related

Is it possible to use an if-else construct in a pattern match in Scala?

So this is a small Scala function which checks if a given List contains a given Char :
def contains: (List[Char], Char) => Boolean = {
case (Nil, c) => false
case (cs, c) if cs.head == c => true
case (cs, c) if cs.head != c => contains(cs.tail, c)
}
Now I was wondering if or rather how I could simplify the cases and have it be something like:
def contains: (List[Char], Char) => Boolean = {
case (Nil, c) => false
case (cs, c) if cs.head == c => true
else => contains(cs.tail,c)
}
The third case clause will be hit only if none of the first two matches, so you function effectively matches:
def contains: (List[Char], Char) => Boolean = {
case (Nil, _) => false
case (cs, c) if cs.head == c => true
case (cs, c) => contains(cs.tail, c)
}
Note that in this particular case you can merge the last two cases by just using boolean logic:
def contains: (List[Char], Char) => Boolean = {
case (Nil, _) => false
case (cs, c) => cs.head == c || contains(cs.tail, c)
}

Parsing parentheses on search expression - Scala parser combinator

I am writing a parser for a search expression.
Ex.
a = "zyx" and ( b < 5 or c > 9)
I wrote this parser but it's not able to match the parentheses, getting this error:
failure: identifier expected
a = "zyx" and ( b < 5 or c > 9)
^
What can I do to be able to match the paretheses
class SearchQueryParser extends StandardTokenParsers {
def expr: Parser[Expression] = orExp | "(" ~> orExp ~ ")"
def orExp: Parser[Expression] = {
andExp *("or" ^^^ {(a: Expression, b: Expression) => BoolExp("OR", (a, b))})
}
def andExp: Parser[Expression] = {
compareExp *("and" ^^^ {(a: Expression, b: Expression) => BoolExp("AND", (a, b))})
}
def compareExp: Parser[Expression] = {
identifier ~ rep(
("=" | "!=" | "<" | ">") ~ literal ^^ {
case op ~ rhs => (op, rhs)
}
) ^^ {
case (acc, ("=", rhs: Expression)) => Binomial("=", acc, rhs)
case (acc, ("!=", rhs: Expression)) => Binomial("!=", acc, rhs)
case (acc, ("<", rhs: Expression)) => Binomial("<", acc, rhs)
case (acc, (">", rhs: Expression)) => Binomial(">", acc, rhs)
}
}
}
Your current grammar only allows parentheses in the expr rule, which I assume is your main rule, and the expr rule is never used by any other rule. So parentheses are only allowed around the entire expression.
What you want to do is to put "(" ~ expr ~ ")" in the lowest place where parenthesized expressions would be allowed. As I understand your grammar, that would probably meaning allowing it as an alternative to compareExp in andExp (assuming you don't want to allow parentheses inside compareExps).
As #sepp2k mentioned you need to put your parentheses on the lowest place where parentheses is allowed. In your case it should be in the compareExp:
def compareExp: Parser[Expression] = {
"(" ~> expr <~ ")" | identifier ~ rep(
("=" | "!=" | "<" | ">") ~ literal ^^ {
case op ~ rhs => (op, rhs)
}
) ^^ {
case (acc, ("=", rhs: Expression)) => Binomial("=", acc, rhs)
case (acc, ("!=", rhs: Expression)) => Binomial("!=", acc, rhs)
case (acc, ("<", rhs: Expression)) => Binomial("<", acc, rhs)
case (acc, (">", rhs: Expression)) => Binomial(">", acc, rhs)
}
}
and exp method should not handle the parentheses
def expr: Parser[Expression] = orExp

Scala pattern matching for tuple with options still needs unwrapping for the some case

f(Int) is an function that returns an Option[Int].
def findIntPair(x: Int, y: Int): (Int, Int) = {
(f(x), f(y)) match {
case (None, None) || (None, _) || (_, None) => fail("Unable to find the pair" )
case (a, b) => (a.get, b.get) // why I still need to unwrap by get
}
}
Why does the last case(a, b) not unwrap them into Int but still keep them as Option[Int]?
FYI: I'm using intelliJ IDEA.
You need to pattern match with Some:
def findIntPair(x: Int, y: Int): (Int, Int) = {
(f(x), f(y)) match {
case (None, None) || (None, _) || (_, None) => fail("Unable to find the pair" )
case (Some(a), Some(b)) => (a, b)
}
}
A bit cleaner using a catch-all case _:
def findIntPair(x: Int, y: Int): (Int, Int) = {
(f(x), f(y)) match {
case (Some(a), Some(b)) => (a, b)
case _ => fail("Unable to find the pair" )
}
}
I think the right solution is that you do that by this way:
(f(x), f(y)) match {
case (None, None) | (None, _) | (_, None) => fail("Unable to find the pair" )
case (Some(a), Some(b)) => (a, b)
}
}
Thats because:
def findIntPair(x: Int, y: Int): (Int, Int) = {
(f(x), f(y)) match {
case (None, None) || (None, _) || (_, None) => fail("Unable to find the pair" )
case (a, b) => (a.get, b.get) //here f(x) = a and f(y) = b
}
}
You want something like this:
def findIntPair(x: Int, y: Int): (Int, Int) = {
(f(x), f(y)) match {
case (None, None) || (None, _) || (_, None) => fail("Unable to find the pair" )
case (Some(a), Some(b)) => (a, b)
}
}
But thats not the best way to do that i think this is better:
def findIntPair(x: Int, y: Int): (Int, Int) = {
if(f(x).isDefined && f(y).isDefined) (f(x).get,f(y).get)
else fail("Unable to find the pair" )
}
Here is an alternative way of expressing this logic:
def findIntPair(x: Int, y: Int): Option[(Int, Int)] =
for {
a <- f(x)
b <- f(y)
} yield (a,b)
This has the advantage that it will not compute f(y) if f(x) returns None. This version returns an Option so that the error can be processed later, but you can process it inside the function like this:
def findIntPair(x: Int, y: Int): (Int, Int) =
(
for {
a <- f(x)
b <- f(y)
} yield (a, b)
).getOrElse(fail("Unable to find the pair"))
Note that this assumes that fail returns (Int, Int), which is what would be necessary for the code in the question to work.

Inline function ambiguity in Scala

When passing an operator lifted to be a function to one of defined higher order functions, Scala allows for very concise syntax, e.g (please ignore the fact that it can be simplified to .product()):
List(1,2,3).fold(1)(_ * _)
To the above I can just pass _ \* _
However having defined my own toy function zipWith(), I need to be very explicit when passing a function:
implicit class EnrichedList[A](val self: List[A]) extends AnyVal {
def zipWith[B, C](that: List[B])
(implicit zipper: A => B => C): List[C] = {
def zipWithHelper(zipper: A => B => C)
(as: List[A])
(bs: List[B]): List[C] = {
(as, bs) match {
case (_, Nil) => Nil
case (Nil, _) => Nil
case (a :: restOfA, b :: restOfB) =>
zipper(a)(b) :: zipWithHelper(zipper)(restOfA)(restOfB)
}
}
zipWithHelper(zipper)(self)(that)
}
}
This: List(1, 3, 4).zipWith(List(3, 4, 5))(_ * _) will not work, saying
Error:(60, 46) missing parameter type for expanded function ((x$1: , x$2) => x$1.$times(x$2))
List(1, 3, 4).zipWith(List(3, 4, 5))(_ * _)
I need to say what type of arguments function takes:
List(1, 3, 4).zipWith(List(3, 4, 5))((x: Int) => (y: Int) => x * y)
Why won't the compiler allow me just to pass in a shorthand version _ * _?
The expression _ * _ is not shorthand for (x: Int) => (y: Int) => x * y. It's a shorthand for (x: Int, y: Int) => x * y. If you change the type of zipper to (A, B) => C instead of A => B => C, it should work. Currying is a thing, it's not just a fancy name for an identity function.
This here compiles:
implicit class EnrichedList[A](val self: List[A]) {
def zipWith[B, C](that: List[B])
(implicit zipper: (A, B) => C): List[C] = {
def zipWithHelper(zipper: (A, B) => C)
(as: List[A])
(bs: List[B]): List[C] = {
(as, bs) match {
case (_, Nil) => Nil
case (Nil, _) => Nil
case (a :: restOfA, b :: restOfB) =>
zipper(a, b) :: zipWithHelper(zipper)(restOfA)(restOfB)
}
}
zipWithHelper(zipper)(self)(that)
}
}
println( List(1, 3, 4).zipWith(List(3, 4, 5))(_ * _) )
and prints
List(3, 12, 20)

scala type matching for Tuple2

I was going through some exercises and noticed the following behavior for matching of tuple2. Is there a particular reason for this?
def test(x: Any): Unit= x match{
case i: Int => println("int")
case b: Boolean => println("bool")
case ti: (_, Int) => println("tuple2 with int")
case tb: (_, Boolean)=> println("tuple2 with boolean")
case _ => println("other")
}
test(false) //prints bool
test(3) ///prints int
test((1,3)) //prints tuple with int
test((1,true)) //prints tuple with int
If i exchange the ti and tb cases, then the (1,3) prints tuple2 with boolean. I assume there is some type casting going on here, but I'm unclear why.
Can someone give me a quick explanation.
Thanks
Type erasure. It can't tell what the types are inside the Tuple at runtime. It will compile fine, but it should emit a warning. This is what happens when I do it in :paste mode in the REPL:
scala> :paste
// Entering paste mode (ctrl-D to finish)
def test(x: Any): Unit= x match{
case i: Int => println("int")
case b: Boolean => println("bool")
case ti: (_, Int) => println("tuple2 with int")
case tb: (_, Boolean)=> println("tuple2 with boolean")
case _ => println("other")
}
// Exiting paste mode, now interpreting.
<console>:10: warning: non-variable type argument Int in type pattern (_, Int) is unchecked since it is eliminated by erasure
case ti: (_, Int) => println("tuple2 with int")
^
<console>:11: warning: non-variable type argument Boolean in type pattern (_, Boolean) is unchecked since it is eliminated by erasure
case tb: (_, Boolean)=> println("tuple2 with boolean")
^
<console>:11: warning: unreachable code
case tb: (_, Boolean)=> println("tuple2 with boolean")
^
test: (x: Any)Unit
Notice the last warning, it says the (_, Boolean) is unreachable because the (_, Int) will match on every Tuple2, courtesy of type erasure.
def test(x: Any) {
x match {
case xi: Int => println("int [" + xi + "]")
case yb: Boolean => println("boolean [" + yb + "]")
case (x, y # (y1: Int)) => println("[" + x + ", int(" + y + ")]")
case (x, y # (y1: Boolean)) => println("[" + x + ", boolean(" + y + ")]")
case _ => println("anything else")
}
}
test(1);
test(true);
test("hello", 1);
test("hello", false);
it seems to work this way.
However, just case (x, y # Int) doesn't work, even thought it complies.
You can try this, it works fine with minimal changes to your code. By unpacking and typing, the function works fine.
def test: Any => Unit = _ match{
case i: Int => println("int")
case b: Boolean => println("bool")
case (x:Any, y: Boolean)=> println("tuple2 with boolean")
case (x:Any, y: Int) => println("tuple2 with int")
case _ => println("other")
}