SkillAgentSearch skills...

Luaxp

Luaxp is a simple arithmetic infix expression parser/evaluator written in Lua

Install / Use

/learn @toggledbits/Luaxp
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

luaxp

Luaxp is a simple arithmetic expression parser for Lua.

Luaxp supports simple mathemtical expressions for addition, subtraction, multiplication, division, modulus, bitwise operations, and logical operations. It has a small library of built-in functions (abs, cos, sin, floor, ceil, round, etc.).

Through a passed-in context table, Luaxp supports named variables, and custom functions. See the documentation below for how to implement these.

Luaxp is offered under MIT License as of October 29, 2018 (beginning with version 0.9.7).

Github

There are three branches in the Github repository:

  • master - The current released version; this is the version to use/track if you are incorporating LuaXP into other projects;
  • develop - The current development version, which may contain work in progress, partial implementations, debugging code, etc. ("the bleeding edge");
  • stable - The current stable development code, which contains only completed and tested functionality, but may still contain debug messages and lack some optimizations and refinement.

Code moves from the develop branch to the stable branch to the master branch. There is no release schedule. Releases are done as needed.

Installation

Some day. This is all very new.

Grab it. Put in your shared Lua directory (/usr/share/lua/...?) or keep it where you use it. Try out the free-form test program try_luaxp.lua. This lets you enter expressions and see the results.

TO-DO: Install with LuaRocks

Known Issues

As of version 0.9.7, the following are known issues or enhancement that are currently being considered:

None

Bug Reports and Contributions

I wrote this library as a port of a similar library I wrote for JavaScript called lexp.js (Lightweight Expression Parser). It differs slightly in operation, but the underlying approach is fundamentally the same and it's a very close port. I did this mainly for fun. I use lexp.js in a dashboard system that I wrote (I wanted something simpler to set up and manage than dashing, which is great, but has way too high a setup and learning curve, but I digress), and figured that somebody might make use of it in Lua as well.

I like bug reports. I like help. I like making things better. If you have suggestions or bug reports please use use GitHub Issues. If you have a contribution, have at it! Please try to follow the coding style to keep it consistent, and use spaces rather than tabs (4 space indenting).

Also, if you're making a feature enhancement contribution, consider looking at my lexp project as well, and see if the same enhancement would be appropriate there. Since the Lua implementation is born of the JavaScript one, I think it would be an interesting exercise to try and keep them as close functionally as possible.

Syntax

This is a very rough BNF for the parser:

<expression> ::= <number>
               | <string>
               | <variable-name>
               | <variable-name> "[" <array-subscript> "]"
               | <function-name> "(" <argument-list> ")"
               | <expression> <binary-operator> <expression>
               | <unary-operator> <expression>
               | "(" <expression> ")"
               
<argument-list> ::= "" | <expression-list>
                  
<expression-list> ::= <expression> [ "," <expression-list> ]

<unary-operator> ::= "-" | "+" | "!"

<binary-operator> ::= "+" | "-" | "*" | "/" | "%"
                    | "&" | "|" | "^"
                    | "<" | "<=" | ">" | ">=" | "==" | "=" | "<>" | "!="
                    
<array-subscript> :== <number> | <expression> /* must eval to number */

<number> ::= <decimal-integer>
           | "0x" <hexadecimal-integer>
           | "0b" <binary-integer>
           | "0" <octal-integer>
           | <decimal-rational-number>
         
<string> ::= "'" <characters> "'"
           | '"' <characters> '"'
           
<variable-name> ::= <letter> { <letter> | <digit> | "_" | "." }

<function-name> ::= <letter> { <letter> | <digit> | "_" }

This is intentionally simplified and doesn't exhaustively convey the full syntax, which would be too detailed to convey the concept quickly. Specific elements of the syntax such are array and dot notation for traversal of trees/structures is not shown (e.g. expressions forms "weather.current" and "weather['current'], which are equivalent).

The Basics

To load the library, use a require() statement:

luaxp = require "luaxp"

compile( expressionString )

The compile() function accepts a single argument, the string the containing the expression to be parsed. If parsing of the expression succeeds, the function returns a table containing the parse tree that is used as input to run() later. If parsing fails, the function returns two values: nil and a table containing information about the error.

Example:

luaxp = require('luaxp')

local parsedExp,err = luaxp.compile("abs(355/113-pi)")
if parsedExp == nil then
    -- Parsing failed
    print("Expression parsing failed. Reason: " .. luaxp.dump(err))
else
    -- Parsing succeeded, on to other work...
	...
end

This example uses the LuaXP public function dump() to display the contents of the err table returned.

run( parsedExp [, executionContext ] )

The run() function executes the parsed expression. It takes an optional executionContext argument, which is a table containing variable names and functions.

run() returns the result of the expression evaluation. If the evaluation succeeds, the first return value will always be non-nil. If it fails, two values are returned: nil and a string containing the error message (i.e. same semantics as compile()). You should always check for evaluation errors, as these are errors that were not or could not be detected in parsing (e.g. a sub-expression used as a divisor evaluates to zero, thus an attempt to divide by zero).

luaxp = require "luaxp" 

local parsedExp, cerr = luaxp.compile("abs(355 / 113 - pi)" )
if parsedExp == nil then error("Parsing failed: " .. cerr.message) end

local context = { pi = math.pi }

local resultValue, rerr = luaxp.run( parsedExp, context )
if resultValue == nil then
    error("Evaluation failed: " .. rerr.message)
else
    print("Result:", luaxp.isNull(resultValue) and "NULL" or tostring(resultValue) )
end

In the above example, a context is created to define the value of "pi" that is used in the parsed expression. This context is then passed to run(), which uses it to dereference the value on the fly.

The code also checks the return value for the special "null" value. If the result of an expression results in "no value", LuaXP does not use Lua nil, it has its own indicator, and your code should check for this as shown above.

As of this version, Luaxp does not allow you to modify variables or create new ones during evaluation.

evaluate( expressionString [, executionContext ] )

The evaluate() function performs the work of compile() and run() in one step. The function result is the value of the parsed and evaluated expression, unless a parsing or evaluation error occurs, in which case the function will return two values: nil and an error message.

luaxp = require "luaxp"

local context = { pi = math.pi }
local resultValue,err = luaxp.evaluate("abs(355/113-pi)", context)
if resultValue == nil then
    error("Error in evaluation of expression: " .. err.message)
else
	print("The difference between the two approximations of pi is " .. tostring(result))
end

Other Functions and Values

The LuaXP dump() function will return a string containing a safely-printable representation of the passed value. If the value passed is a table, for example, dump() will display it in a Lua-like table initialization syntax (tuned for readability, not for re-use as actual Lua code).

The isNull() function returns a boolean indicating if the passed argument is LuaXP's null value.

The null and NULL constants (synonyms) are the represtations of LuaXP's null value. Thus the test returnValue==luaxp.null in Lua is equivalent to isNull(returnvalue). The constants can also be used to initialize values when creating the execution context.

Reserved Words

The words true and false are reserved and evaluate to their respective boolean values. The words null, NULL, and nil evaluate to the LuaXP null value.

The reserved words pi and PI (synonyms) are provided as a convenience and evaluate to the underyling Lua Math library implementation of math.pi.

Error Returns

If a LuaXP call results in an error (nil first return value), the error table (second return value) contains the following elements:

  • type - Always included, the string "compile" or "evaluation" to indicate the stage at which the error was detected.
  • message - Always included, text describing the error.
  • location - Sometimes included, the character position at which the error was detected, if available.

The try_luaxp.lua example included with LuaXP shows how the location value can be used to provide feedback to the user when errors occur. Try entering "0b2" and "max(1,2,nosuchname)" into this example program.

Context Variables

The context passed to evaluate() and run() is used to define named variables and custom functions that can be used in expressions. We've seen in the above examples for these functions how that works. For variables, it's simple a matter of defining a table element with the value to be used:

local context = {}
context.minrange = 0
context.maxrange = 100

-- or the more streamlined:

local context = { minrange=0, maxrange=100 }

These may be referred to in expressions simply by their names as defined (case sensitive):

$ lua try_luaxp.lua
Running with Luaxp version 0.9.2
Context variables defined:
    minrange=0 
    maxrange=100

EXP>
View on GitHub
GitHub Stars27
CategoryDevelopment
Updated11mo ago
Forks5

Languages

Lua

Security Score

67/100

Audited on Apr 16, 2025

No findings