⬆️ ⬇️

Calculation of logical expressions in a string inside Java / Scala / Kotlin code

I needed to calculate the truth of type expressions in runtime.



a>10 && b<c+5 && (a+b)<c*4 


located in the row of Rocks.



I myself have the code on Scala, but for this I evaluated the different libraries on Kotlin, just to play with it. I get the expression itself from the client, but from the internal one, so I didn’t have to worry about the fact that in the expression I didn’t erase the files from the disk.

')

I evaluated different libraries for that 1) can they do the right thing 2) speed of execution



Have been tested





results



Travel time in ms for 1000 expressions (or rather, the same expression for 1000 different sets of 3 variables):



js239 ms
mxParser56713 ms
evalex35 ms
groovy118 ms
Jexl62 ms


The remaining methods / libraries did not work.



Under the cut details:







1. Interpolation of lines.



At first I wondered if it would be possible to adjust the interpolation of the Scala lines for my purposes.

I can write in the rock
 s"{a>10}" 
but I could not find a way to turn a regular string into a string for interpolation.

In Scala, there is a StringContext that alternates regular strings with variables:

 s"You are ${age / 10} decades old, $name!" 
it really

 StringContext ("You are ", " decades old, ", "!").s (age / 10, name) 
and turns into
 "You are " + (age / 10) + " decades old, " + (name) + "!" 


but I didn't want to mess with parsing the string and dividing it into parts



2. Use the JavaScript Engine inside Java.



worked without problems



 val e = ScriptEngineManager().getEngineByName("js") fun jsEvaluate(a: Double, b: Double, c: Double): Boolean { e.context.setAttribute("a", a, ScriptContext.ENGINE_SCOPE) e.context.setAttribute("b", b, ScriptContext.ENGINE_SCOPE) e.context.setAttribute("c", c, ScriptContext.ENGINE_SCOPE) return e.eval(expr) as Boolean } 


3. Javaluator Library



Without writing your extensions, only floating point expressions are supported.

It can easily be expanded for Boolean expressions, and, probably, it can be extended for expressions of the type "(a + b)> 5"



4. mxParser library



It does everything, but about 1000 times slower than JavaScript.

The result always returns as Double.



 val mxExpr = org.mariuszgromada.math.mxparser.Expression(expr) fun mxParserEvaluate(a: Double, b: Double, c: Double): Boolean { val v1 = Argument("a = $a") val v2 = Argument("b = $b") val v3 = Argument("c = $c") mxExpr.addArguments(v1, v2, v3) return mxExpr.calculate() == 1.0 } 


5. evalEx library



Everything is done, the fastest, the result returns as BigDecimal



  val evalExpression = com.udojava.evalex.Expression(expr) fun evalexEvaluate(a: Double, b: Double, c: Double): Boolean { val eval = evalExpression.with("a", BigDecimal.valueOf(a)).and("b", BigDecimal.valueOf(b)).and("c", BigDecimal.valueOf(c)).eval() return eval == BigDecimal.ONE } 


6. Library exp4j



There is no support for logical expressions, only floating point expressions, but can be extended.



7. MathEval Library



No support for boolean expressions, only floating point expressions



8. Groovy





 val template = groovy.text.GStringTemplateEngine().createTemplate(expr) fun groovyEvaluate(a: Double, b: Double, c: Double): Boolean { val binding = HashMap<String, Double>() binding.put("a", a) binding.put("b", b) binding.put("c", c) val template = template.make(binding) return template.toString().toBoolean() } 




9. Jexl





 val jexl = JexlBuilder().create() val jexlExp = jexl.createExpression(expr) fun jexlEvaluate(a: Double, b: Double, c: Double): Boolean { val jc = MapContext() jc.set("a", a) jc.set("b", b) jc.set("c", c) return jexlExp.evaluate(jc) as Boolean } 




The code that I checked can be taken on github

Source: https://habr.com/ru/post/354692/



All Articles