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
Related
I have to write a function to find if the parenthesises are balanced in a string.
The problem is that in line 1, the if condition is always skipped.
def balance(chars: List[Char]): Boolean = {
def butil(chars: List[Char], l: Int, r: Int): Boolean ={
if (chars.isEmpty) l==r
val c = chars.head
val ret = c match {
case '(' => butil(chars.tail, l+1, r)
case ')' => if(l<=r) false else butil(chars.tail, l, r+1)
case _ => butil(chars.tail, l, r)
}
ret
}
butil(chars, 0, 0)
}
Even the IntelliJ idea shows it in a faded text(meaning that it is never evaluated).
The faded code is evaluated, it is just ignored because the if does nothing with it. You need to move the rest of the code into the else part of the first if and remove the spurious recursive call at the end:
def balance(chars: List[Char]): Boolean = {
def butil(chars: List[Char], l: Int, r: Int): Boolean = {
if (chars.isEmpty) {
l==r
} else {
println(chars.toString())
val c = chars.head
c match {
case '(' => butil(chars.tail, l+1, r)
case ')' => if(l<=r) false else butil(chars.tail, l, r+1)
case _ => butil(chars.tail, l, r)
}
}
}
butil(chars, 0, 0)
}
A cleaner way to do with is to use match to test and extract head/tail at the same time:
def balance(chars: String): Boolean = {
def butil(chars: List[Char], l: Int, r: Int): Boolean =
chars match {
case Nil => l == r
case '(' :: tail => butil(tail, l + 1, r)
case ')' :: tail => if (l <= r) false else butil(tail, l, r + 1)
case _ :: tail => butil(tail, l, r)
}
butil(chars.toList, 0, 0)
}
I've also changed the input type to String as that seems more natural.
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)
...
I came across this piece code of parsing string like 5*5+5
def add_expr: Parser[Expr] =
mult_expr * (
"+" ^^^ { (a: Expr, b: Expr) => Plus(a, b) } |
"-" ^^^ { (a: Expr, b: Expr) => Minus(a, b) } )
According to document, ^^^ says "if the left operand parses successfully, ignore the result and use the value from the right". Then why this piece of code work? The value from the right of ^^^ is a function, not a Expr value.
I was misled by IDEA. The "*" right after mult_expr is not
def * = rep(this)
but
def *[U >: T](sep: => Parser[(U, U) => U]) = chainl1(this, sep)
Indeed "+" ^^^ { (a: Expr, b: Expr) => Plus(a, b) } generates a parser whose type is Parser[(Expr, Expr) => Expr]. Then this parser is used in foldLeft to generate Expr value
I am a bit new to scala and I have a program here that does everything I would like it to, except one thing I cannot seem to figure out. I Have expressions and I simplify the expression based on a few simple math rules.Then I prompt the user for bindings to variables and substitutes the variables with integers the user types in and then the expression is evaluated. That all works like I want it to. However I still want to simplify/evaluate the expression when I do not substitute a specific variable for an integer. I just simply evaluate the expression without asking the user for a value for x and I remove the statement for replacing x in my Environment for example. The problem is I get a scala.Match error when the eval method calls the env when it hits the x in my expression. To maybe make it clearer what I am trying to do is with the expression in my example.. Expression: (x + (x * (y - (z / 2)))) by only substituting the variables y and z with 2, the result I would like to see is (x + x). I would appreciate any advice/help possible! My program at this point looks like this:
var n1 = 0
var n2 = 0
var n3 = 0
type Environment = String => Int
lazy val exp: Tree = Sum(Var("x"), Times(Var("x"), Minus(Var("y"), Divide(Var("z"), Const(2)))))
lazy val env: Environment = {
case "x" => n1 //take this line out to not bind x
case "y" => n2
case "z" => n3
}
abstract class Tree
case class Sum(l: Tree, r: Tree) extends Tree {
override def toString = "(" + l + " + " + r + ")"
}
case class Minus(l: Tree, r: Tree) extends Tree {
override def toString = "(" + l + " - " + r + ")"
}
case class Times(l: Tree, r: Tree) extends Tree {
override def toString = "(" + l + " * " + r + ")"
}
case class Divide(l: Tree, r: Tree) extends Tree {
override def toString = "(" + l + " / " + r + ")"
}
case class Var(n: String) extends Tree {
override def toString = n
}
case class Const(v: Int) extends Tree {
override def toString = v.toString
}
def simplify(t: Tree): Tree = t match {
case Times(Const(1), r) => simplify(r)
case Times(l, Const(1)) => simplify(l)
case Times(Const(0), r) => Const(0)
case Times(l, Const(0)) => Const(0)
case Sum(Const(0), r) => simplify(r)
case Sum(l, Const(0)) => simplify(l)
case Minus(l, Const(0)) => simplify(l)
case Minus(l, r) if l == r => Const(0)
case Divide(Const(0), r) => Const(0)
case Divide(l, Const(1)) => simplify(l)
case Divide(l, r) if l == r => Const(1)
case Times(l, r) => Times(simplify(l), simplify(r))
case Sum(l, r) => Sum(simplify(l), simplify(r))
case Minus(l, r) => Minus(simplify(l), simplify(r))
case Divide(l, r) => Divide(simplify(l), simplify(r))
case _ => t
}
def eval(t: Tree): Int = t match {
case Sum(l, r) => eval(l) + eval(r)
case Minus(l, r) => eval(l) - eval(r)
case Times(l, r) => eval(l) * eval(r)
case Divide(l, r) => eval(l) / eval(r)
case Var(n) => env(n)
case Const(v) => v
}
println("Expression: " + exp)
println("Simplified: " + simplify(exp))
println("Enter the binding for x.") //take this line out to not bind x
n1 = readInt() //take this line out to not bind x
println("Enter the binding for y.")
n2 = readInt()
println("Enter the binding for z.")
n3 = readInt()
println("Evaluation: " + eval(exp))
Here is a relatively clean solution. I've only included the pieces I modified.
type Environment = String => Option[Int]
lazy val env: Environment = {
case "y" => Some(n2)
case "z" => Some(n3)
case _ => None
}
def simplify(t: Tree): Tree = {
val reducedTree = t match {
case Times(l, r) => Times(simplify(l), simplify(r))
case Sum(l, r) => Sum(simplify(l), simplify(r))
case Minus(l, r) => Minus(simplify(l), simplify(r))
case Divide(l, r) => Divide(simplify(l), simplify(r))
case Var(n) => env(n).map(Const).getOrElse(t)
case _ => t
}
reducedTree match {
case Times(Const(1), r) => r
case Times(l, Const(1)) => l
case Times(Const(0), r) => Const(0)
case Times(l, Const(0)) => Const(0)
case Times(Const(l), Const(r)) => Const(l * r)
case Sum(Const(0), r) => r
case Sum(l, Const(0)) => l
case Sum(Const(l), Const(r)) => Const(l + r)
case Minus(l, Const(0)) => l
case Minus(l, r) if l == r => Const(0)
case Minus(Const(l), Const(r)) => Const(l - r)
case Divide(Const(0), r) => Const(0)
case Divide(l, Const(1)) => l
case Divide(l, r) if l == r => Const(1)
case Divide(Const(l), Const(r)) => Const(l / r)
case _ => reducedTree
}
}
I changed your code a little bit to make it work as you want:
abstract class Tree
case class Sum(l: Tree, r: Tree) extends Tree {
override def toString = "(" + l + " + " + r + ")"
}
case class Minus(l: Tree, r: Tree) extends Tree {
override def toString = "(" + l + " - " + r + ")"
}
case class Times(l: Tree, r: Tree) extends Tree {
override def toString = "(" + l + " * " + r + ")"
}
case class Divide(l: Tree, r: Tree) extends Tree {
override def toString = "(" + l + " / " + r + ")"
}
case class Var(n: String) extends Tree {
override def toString = n
}
case class Const(v: Int) extends Tree {
override def toString = v.toString
}
object ExprEval {
lazy val exp: Tree = Sum(Var("x"), Times(Var("x"), Minus(Var("y"), Divide(Var("z"), Const(2)))))
var env = Map[String, Int]()
def simplify(t: Tree, recursive : Boolean = true): Tree = {
t match {
case Sum(Const(0), r) => simplify(r)
case Sum(l, Const(0)) => simplify(l)
case Sum(Const(l), Const(r)) => Const(l + r)
case Sum(Var(l), Var(r)) if l == r => Times(Const(2), Var(l))
case Sum(l, r) if recursive => simplify(Sum(simplify(l), simplify(r)), recursive = false)
case Minus(l, Const(0)) => simplify(l)
case Minus(l, r) if l == r => Const(0)
case Minus(Const(l), Const(r)) => Const(l - r)
case Minus(l, r) if recursive => simplify(Minus(simplify(l), simplify(r)), recursive = false)
case Times(Const(1), r) => simplify(r)
case Times(l, Const(1)) => simplify(l)
case Times(Const(0), r) => Const(0)
case Times(l, Const(0)) => Const(0)
case Times(Const(l), Const(r)) => Const(l * r)
case Times(l, r) if recursive => simplify(Times(simplify(l), simplify(r)), recursive = false)
case Divide(Const(0), r) => Const(0)
case Divide(l, Const(1)) => simplify(l)
case Divide(l, r) if l == r => Const(1)
case Divide(Const(l), Const(r)) => Const(l / r)
case Divide(l, r) if recursive => simplify(Divide(simplify(l), simplify(r)), recursive = false)
case Var(n) => env.get(n).map(Const).getOrElse(Var(n))
case _ => t
}
}
def eval(t: Tree): Int = t match {
case Sum(l, r) => eval(l) + eval(r)
case Minus(l, r) => eval(l) - eval(r)
case Times(l, r) => eval(l) * eval(r)
case Divide(l, r) => eval(l) / eval(r)
case Var(n) => env(n)
case Const(v) => v
}
def main(args: Array[String]) {
env = Map()
println(s"env: $env, exp : $exp, simplified : ${simplify(exp)}")
env = Map("y" -> 2, "z" -> 2)
println(s"env: $env, exp : $exp, simplified : ${simplify(exp)}")
env = Map("z" -> 4)
println(s"env: $env, exp : $exp, simplified : ${simplify(exp)}")
env = Map("x" -> 3, "y" -> 2, "z" -> 2)
println(s"env: $env, exp : $exp, simplified : ${simplify(exp)}")
}
}
If no variables are bound, it returns a simplified original expression tree. If all variables are bound, it produces the same result as eval().
Output:
env: Map(), exp : (x + (x * (y - (z / 2)))), simplified : (x + (x * (y - (z / 2))))
env: Map(y -> 2, z -> 2), exp : (x + (x * (y - (z / 2)))), simplified : (2 * x)
env: Map(z -> 4), exp : (x + (x * (y - (z / 2)))), simplified : (x + (x * (y - 2)))
env: Map(x -> 3, y -> 2, z -> 2), exp : (x + (x * (y - (z / 2)))), simplified : 6
A message class:
case class Message(username:String, content:String)
A message list:
val list = List(
Message("aaa", "111"),
Message("aaa","222"),
Message("bbb","333"),
Message("aaa", "444"),
Message("aaa", "555"))
How to group the messages by name and get the following result:
List( "aaa"-> List(Message("aaa","111"), Message("aaa","222")),
"bbb" -> List(Message("bbb","333")),
"aaa" -> List(Message("aaa","444"), Message("aaa", "555")) )
That means, if a user post several messages, then group them together, until another user posted. The order should be kept.
I can't think of an easy way to do this with the provided Seq methods, but you can write your own pretty concisely with a fold:
def contGroupBy[A, B](s: List[A])(p: A => B) = (List.empty[(B, List[A])] /: s) {
case (((k, xs) :: rest), y) if k == p(y) => (k, y :: xs) :: rest
case (acc, y) => (p(y), y :: Nil) :: acc
}.reverse.map { case (k, xs) => (k, xs.reverse) }
Now contGroupBy(list)(_.username) gives you what you want.
I tried to create such a code which works not only with Lists and can be written in operator notation. I came up with this:
object Grouper {
import collection.generic.CanBuildFrom
class GroupingCollection[A, C, CC[C]](ca: C)(implicit c2i: C => Iterable[A]) {
def groupBySep[B](f: A => B)(implicit
cbf: CanBuildFrom[C,(B, C),CC[(B,C)]],
cbfi: CanBuildFrom[C,A,C]
): CC[(B, C)] =
if (ca.isEmpty) cbf().result
else {
val iter = c2i(ca).iterator
val outer = cbf()
val inner = cbfi()
val head = iter.next()
var olda = f(head)
inner += head
for (a <- iter) {
val fa = f(a)
if (olda != fa) {
outer += olda -> inner.result
inner.clear()
}
inner += a
olda = fa
}
outer += olda -> inner.result
outer.result
}
}
implicit def GroupingCollection[A, C[A]](ca: C[A])(
implicit c2i: C[A] => Iterable[A]
): GroupingCollection[A, C[A], C] =
new GroupingCollection[A, C[A],C](ca)(c2i)
}
Can be used (with Lists, Seqs, Arrays, ...) as:
list groupBySep (_.username)
def group(lst: List[Message], out: List[(String, List[Message])] = Nil)
: List[(String, List[Message])] = lst match {
case Nil => out.reverse
case Message(u, c) :: xs =>
val (same, rest) = lst span (_.username == u)
group(rest, (u -> same) :: out)
}
Tail recursive version. Usage is simply group(list).
(List[Tuple2[String,List[Message]]]() /: list) {
case (head :: tail, msg) if msg.username == head._1 =>
(msg.username -> (msg :: head._2)) :: tail
case (xs, msg) =>
(msg.username -> List(msg)) :: xs
} map { t => t._1 -> t._2.reverse } reverse
Here's another method using pattern matching and tail recursion. Probably not as efficient as those above though due to the use of both takeWhile and dropWhile.
def groupBy(msgs: List[Message]): List[(String,List[Message])] = msgs match {
case Nil => List()
case head :: tail => (head.username ->
(head :: tail.takeWhile(m => m.username == head.username))) +:
groupBy(tail.dropWhile(m => m.username == head.username))
}