Last active
December 9, 2020 12:40
-
-
Save sofoklis/3343973 to your computer and use it in GitHub Desktop.
Parser Combinators - A logical expression parser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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("") | |
} | |
} |
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 but
x' 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
Thanks, very helpful :-)