Skip to content

Instantly share code, notes, and snippets.

@sofoklis
Last active December 9, 2020 12:40
Show Gist options
  • Save sofoklis/3343973 to your computer and use it in GitHub Desktop.
Save sofoklis/3343973 to your computer and use it in GitHub Desktop.
Parser Combinators - A logical expression parser
package org.papasofokli
import scala.util.parsing.combinator.JavaTokenParsers
/**
* <b-expression>::= <b-term> [<orop> <b-term>]*
* <b-term> ::= <not-factor> [AND <not-factor>]*
* <not-factor> ::= [NOT] <b-factor>
* <b-factor> ::= <b-literal> | <b-variable> | (<b-expression>)
*/
case class LogicalExpression(variableMap: Map[String, Boolean]) extends JavaTokenParsers {
private lazy val b_expression: Parser[Boolean] = b_term ~ rep("or" ~ b_term) ^^ { case f1 ~ fs ⇒ (f1 /: fs)(_ || _._2) }
private lazy val b_term: Parser[Boolean] = (b_not_factor ~ rep("and" ~ b_not_factor)) ^^ { case f1 ~ fs ⇒ (f1 /: fs)(_ && _._2) }
private lazy val b_not_factor: Parser[Boolean] = opt("not") ~ b_factor ^^ (x ⇒ x match { case Some(v) ~ f ⇒ !f; case None ~ f ⇒ f })
private lazy val b_factor: Parser[Boolean] = b_literal | b_variable | ("(" ~ b_expression ~ ")" ^^ { case "(" ~ exp ~ ")" ⇒ exp })
private lazy val b_literal: Parser[Boolean] = "true" ^^ (x ⇒ true) | "false" ^^ (x ⇒ false)
// This will construct the list of variables for this parser
private lazy val b_variable: Parser[Boolean] = variableMap.keysIterator.map(Parser(_)).reduceLeft(_ | _) ^^ (x ⇒ variableMap(x))
def sparse(expression: String) = this.parseAll(b_expression, expression)
}
object LogicalExpression {
def sparse(variables: Map[String, Boolean])(value: String) {
println(LogicalExpression(variables).sparse(value))
}
}
object Sofoklis {
def main(args: Array[String]) {
println("testing parser")
val variables = Map("a" -> true, "b" -> false, "c" -> true)
val variableParser = LogicalExpression.sparse(variables) _
variableParser("a or b")
variableParser("a and b")
variableParser("a or b or c and a")
variableParser("a and b or ((a and b) or a)")
variableParser("a or b and c or (a and c)or a")
variableParser("a and b and a and c or b")
variableParser("a or b or d")
variableParser("a and b and c")
variableParser("")
}
}
@weickmanna
Copy link

Thanks, very helpful :-)

@yavuzmester
Copy link

Hello, i have a test case, which fails:
val variables = Map("a" -> true, "ax" -> true)
val variableParser = LogicalExpression.sparse(variables) _

variableParser("a")
variableParser("ax")

The output is:
testing parser
[1.2] parsed: true
[1.2] failure: string matching regex \z' expected butx' found

ax
^

It fails the parse "ax", because there is also a variable named "a". It gets "a" first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment