Expression evaluator

Over the last couple days, I finally got around to implementing an expression evaulator I’ve needed in some projects. In case it’s useful to anybody: link

This is reasonably feature-complete (but see below), but needs some more stress-testing. Docs are getting there, except for the general anatomy of an expression (though the tests in main.lua should give a good idea).

This allows you to define a little grammar, then evaluate Lua-ish expressions based on it, but in something of a sandbox rather than just outright executing code.

A toy example, given a numerical grammar, might go something like:

local expression = require("expression") -- declare some opts (see main.lua in link for some examples) local numbers = ... expression.DefineGrammar(opts) local result = expression.Evaluate("X + Y \* ceil(4.5 + Z)", { X = 2, Y = 3, Z = 1 }) print(result) -- prints 20

In my own cases (which I’ll see about posting in video form once I’ve integrated this and fleshed out some dependent bits), I have textboxes for entering expressions and the variables are gathered from a bunch of nodes you can link together. The grammar is an implementation detail and the expression is what the end user provides.

Some math-specific projects along the same lines (in C and C++ respectively):

tinyexpr I even customized this quite a bit, considering it for a plugin, but found doing so becoming hard to maintain and so set it aside.

exprtk This one looks quite impressive, but I believe it was under a more restrictive license when I first checked it out. Being now under MIT I might give it a whirl again later.

Future plans :

At the moment all values in the expression are assumed to be of the same type, e.g. all booleans or all numbers. This precludes adding stuff like C’s ternary operator or Lua’s select(), which basically always expect a boolean or non-negative integer, respectively, as first argument whereas they would be useful in general. (Notes about this can be found here and there in expression.lua.)

To be able to verify these more general expressions, my “version 2” will almost certainly entail adding types across the board. But that will probably not be for some time.

UPDATE :

Link updated with minor fixes:

  • Was reporting an error by concatenating term to a string, but term could be a function.

  • Was reading optional sign and digits after an “e” or “E” in numbers to detect exponents, but of course that maybe you could just have the “e” and then get a false positive, even when the rest was missing. Float parsing is now split up into two patterns, the second being run if that “e” / “E” is detected.

  • Flushed out by that: forgot to detect an error when no operator was found, e.g. when two variables are mashed together or you forgot to define it.