Scala expression evaluator (perhaps using placeholders?)

I am reading something like this from my configuration file:

metric1.critical = "<2000 || >20000"
metric1.okay = "=1"
metric1.warning = "<=3000"
metric2.okay = ">0.9 && < 1.1 "
metric3.warning ="( >0.9 && <1.5) || (<500 &&>200)"

and i have

metric1.value =  //have some value

My goal is mainly to evaluate

    if(metric1.value<2000 || metric1.value > 20000)
     metric1.setAlert("critical");
    else if(metric1.value=1)
     metric.setAlert("okay");
    //and so on

I am not very familiar with regex, so I will try not to use it. I am coding in Scala and wanted to know if some existing library can help with this. Maybe I need to put placeholders to fill in the blanks and then evaluate the expression? But how can I evaluate an expression most efficiently and with less overhead?

EDIT: java, , , - . , , ,?? metric1.value( ), ? - ? !

+4
4

, .

scala . scala , , https://github.com/scala/scala-parser-combinators.

: , , scala -parser-combinators, fastparse. . , scala -parser-combinators.

, Scala, 33, " ".

, , . , , .. . , scala -, .

import scala.util.parsing.combinator.{JavaTokenParsers, PackratParsers}

sealed abstract class AST
sealed abstract class BooleanExpression extends AST
case class BooleanOperation(op: String, lhs: BooleanExpression, rhs:BooleanExpression) extends BooleanExpression
case class Comparison(op:String, rhs:Constant) extends BooleanExpression
case class Constant(value: Double) extends AST

object ConditionParser extends JavaTokenParsers with PackratParsers {

  val booleanOperator : PackratParser[String] = literal("||") | literal("&&")
  val comparisonOperator : PackratParser[String] = literal("<=") | literal(">=") | literal("==") | literal("!=") | literal("<") | literal(">")
  val constant : PackratParser[Constant] = floatingPointNumber.^^ { x => Constant(x.toDouble) }
  val comparison : PackratParser[Comparison] = (comparisonOperator ~ constant) ^^ { case op ~ rhs => Comparison(op, rhs) }
  lazy val p1 : PackratParser[BooleanExpression] = booleanOperation | comparison
  val booleanOperation = (p1 ~ booleanOperator ~ p1) ^^ { case lhs ~ op ~ rhs => BooleanOperation(op, lhs, rhs) }
}

object Evaluator {

  def evaluate(expression:BooleanExpression, value:Double) : Boolean = expression match {
    case Comparison("<=", Constant(c)) => value <= c
    case Comparison(">=", Constant(c)) => value >= c
    case Comparison("==", Constant(c)) => value == c
    case Comparison("!=", Constant(c)) => value != c
    case Comparison("<", Constant(c)) => value < c
    case Comparison(">", Constant(c)) => value > c
    case BooleanOperation("||", a, b) => evaluate(a, value) || evaluate(b, value)
    case BooleanOperation("&&", a, b) => evaluate(a, value) && evaluate(b, value)
  }
}

object Test extends App {

  def parse(text:String) : BooleanExpression = ConditionParser.parseAll(ConditionParser.p1, text).get

  val texts = Seq(
    "<2000",
    "<2000||>20000",
    "==1",
    "<=3000",
    ">0.9&&<1.1")

  val xs = Seq(0.0, 1.0, 100000.0)

  for {
    text <- texts
    expression = parse(text)
    x <- xs
    result = Evaluator.evaluate(expression, x)
  } {
    println(s"$text $expression $x $result")
  }
}
+6

Scala Interpreter, . , eval() . Scala String .interpret, .

import scala.tools.nsc.{ Interpreter, Settings }

val settings = new Settings   
settings.usejavacp.value = true
val in = new Interpreter(settings) 
val lowerCritical = "<2000" // set the value from config
val value = 200   
in.interpret(s"$value $lowerCritical")                    //> res0: Boolean = true
val value1 = 20000                               //> value1  : Int = 20000
in.interpret(s"$value1 $lowerCritical")          //> res1: Boolean = false
+2

.

Scala, , .

Parboiled2, , , Scala - , , , , . Parboiled2 200 , Scala.

( 2.11, ), Parboiled2.

+2

I recently ran into the same problem and ended up writing my own scalexpr expression evaluation library . This is a simple library, but it can check / evaluate expressions similar to those indicated in the question. You can do things like:

val ctx = Map("id" -> 10L, "name" -> "sensor1")
val parser = ExpressionParser()
val expr = parser.parseBooleanExpression(""" id == 10L || name == "sensor1" """).get
println(expr3.resolve(ctx3)) // prints true

If you do not want to use the library, I recommend the fastparse parser ... This is much faster than parser combinators, a little slower than steam, but much easier to use than both.

0
source

All Articles