Xpression
Expression parser written in Go
Install / Use
/learn @bhmj/XpressionREADME
Expression parser/evaluator in Go
What is it?
This project is a renewed version of expression parser/evaluator used in jsonslice. It allows you to evaluate simple arithmetic expressions with variable support.
Check it out
git clone https://github.com/bhmj/xpression.git
cd xpression
make build
./bin/xpression "1+2"
Expression examples:
1+2
2**1**2
3 + 4 * 2 / (1-5) ** 2 ** 3
5 + -5
1/(3 & 5)
'a' > 'b'
'abc' =~ /a.c/i
!((false))
Usage
// simple expression evaluation (error handling skipped)
result, _ := xpression.EvalStr(`5-3*(6-12)`)
fmt.Println(result.String())
// external data in expression (aka variables)
foobar := 123
varMapper := map[string]*int{
`foobar`: &foobar,
}
varFunc := func(name []byte, result *xpression.Operand) error {
ref := varMapper[string(name)]
if ref == nil {
return errors.New("unknown variable")
}
result.SetNumber(float64(*ref))
return nil
}
result, _ = xpression.EvalVarStr(`27 / foobar`, varFunc)
fmt.Println(result.String())
Functions
func Eval(expression []byte) (*Operand, error)
Eval evaluates expression and returns the result. No external variables used. See EvalVar for more.
Returns a pointer to Operand or error.
Use Operand's method .String() to get a serializable value or see Operand's .OperandType field to determine the result type and get the value from .Str, .Number or .Bool field.
func EvalVar(expression []byte, varFunc VariableFunc) (*Operand, error)
EvalVar evaluates expression and returns the result. External variables can be used via varFunc.
func EvalStr(expression string) (*Operand, error)
Same as Eval but receives a string instead of []byte.
func EvalVarStr(expression string, varFunc VariableFunc) (*Operand, error)
Same as EvalVar but receives a string instead of []byte.
xpression CLI
You can find a simple and dumb expression evaluation CLI tool in cmd/xpression.
Build it with make build and run with ./bin/xpression.
Feel free to make your own, better implementation.
Remember that this evaluator complies with JavaScript rules, so the following example is legitimate and behaves JavaScript-like:
> a = 123
123
> ("0x" + a) * 2
582
Operators and data types supported
Operators |
--- | ---
Arithmetic | + - * / ** %
Bitwise | \| & ^ ~ << >>
Logical | && \|\| !
Comparison | == != === !== >= > <= <
Regexp | =~ !=~ !~
Parentheses | ( )
<b>Data types</b> |
--- | ---
String constants | 'string' or "string"
Numeric | 64-bit integers or floats in decimal or hexadecimal form: 123 or 0.123 or 1.2e34 or 0x12a or 0x12A
Boolean | true or false. Comparison results in boolean value.
Regexp | /expression/ with modifiers:<br>i (case-insensitive), m (multiline), s (single-line), U (ungreedy)
Other | null
Test coverage
Tests cover the majority of cases described in ECMAScript Language definition (specifically ECMAScript Language: Expressions reference and Testing and Comparison Operations).
Benchmarks
Evaluate (2) + (2) == (4)
$ go test -bench=. -benchmem -benchtime=4s
goos: darwin
goarch: amd64
pkg: github.com/bhmj/xpression
cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
Benchmark_ModifiedNumericLiteral_WithParsing-16 2622770 1811 ns/op 1272 B/op 26 allocs/op
Benchmark_ModifiedNumericLiteral_WithoutParsing-16 77455698 57.55 ns/op 0 B/op 0 allocs/op
PASS
ok github.com/bhmj/xpression 11.548s
The same expression evaluated with github.com/Knetic/govaluate :
$ go test -bench='LiteralModifiers' -benchmem -benchtime=4s
goos: darwin
goarch: amd64
pkg: github.com/Knetic/govaluate
cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
BenchmarkEvaluationLiteralModifiers_WithParsing-16 1000000 4019 ns/op 2208 B/op 43 allocs/op
BenchmarkEvaluationLiteralModifiers-16 30173640 147.2 ns/op 8 B/op 1 allocs/op
PASS
ok github.com/Knetic/govaluate 9.810s
Changelog
0.9.4 (2022-11-28) -- Go 1.19 support. EvalStr and EvalVarStr helper functions added.
0.9.3 (2022-07-06) -- bugfixes: string to hex conversion "0x123"*2, closing bracket after a variable (0+a).
0.9.2 (2022-02-23) -- Minor code refactoring. One-liner functions added (Eval, EvalVar).
0.9.1 (2022-01-02) -- Variable bounds refined.
0.9.0 (2021-11-19) -- Memory allocation reduced. Speed optimization.
0.8.0 (2021-11-11) -- hex numbers support. Production ready.
0.7.x (2021-11-11) -- WIP
0.7.0 (2021-11-10) -- project renamed to xpression
0.6.0 (2021-11-05) -- a remainder operator % added. Benchmarks added. Some optimization done.
0.5.0 (2021-11-04) -- Tests added. Multiple bugs fixed.
0.4.0 (2021-11-02) -- Expression evaluation.
0.3.0 (2021-11-01) -- MVP.
Roadmap
- [x] arithmetic operators:
+ - * / **% - [x] bitwise operators:
| & ^ ~ - [x] logical operators:
|| && ! - [x] comparison operators:
> < >= <= == === != !== - [x] full support of parentheses
- [x] regular expressions for strings
- [x] unary minus supported
- [x] expression evaluation
- [x] parser test coverage
- [x] evaluator test coverage
- [x] add external reference type aka variable (node reference in jsonslice)
- [x] optimize memory allocations
- [ ] Unicode support
- [ ] add math functions support (?)
- [ ] add math/big support (?)
Contributing
- Fork it!
- Create your feature branch:
git checkout -b my-new-feature - Commit your changes:
git commit -am 'Add some feature' - Push to the branch:
git push origin my-new-feature - Submit a pull request :)
Licence
Author
Michael Gurov aka BHMJ
Related Skills
node-connect
342.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
xurl
342.5kA CLI tool for making authenticated requests to the X (Twitter) API. Use this skill when you need to post tweets, reply, quote, search, read posts, manage followers, send DMs, upload media, or interact with any X API v2 endpoint.
frontend-design
85.3kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
342.5kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
