Expression is a basic expression parser and compiler, implemented in Golang for handling simple sytanxes such as mathematical operations (+, -, *, /, %), logical operations (==, <, >, <=, >=, !=, &&, ||), unary operations (-, !), and bitwise operations (>>, <<, ^, |, &).
To use the package inside your golang project, simply run the following command:
go get github.com/techerfan/expression
To parse a string expression, first you need a to make a sytanx tree. During this process, lexer will tokenize the expression and makes tree for you:
tree := syntax.Parse("1 + 2")
The result of the parsing, contains an array named Diagnostics
that helps you to diagnose errors that happens during the process. So basically when the length of the Diagnostics
array is greater than zero, the provided expression is not valid and has syntax issues. Otherwise, we are good to continue:
if len(tree.Diagnostics) > 0 {
for _, d := range tree.Diagnostics {
fmt.Println(d.Message, d.Span, d.Span.Length, d.Span.Start)
}
panic(1)
}
If any variable is used in the expression, the parser will return them to us:
// Imagine the expression was "variable1 + variable2":
for _, v := range tree.Variables() {
fmt.Println(v)
}
// The result would be:
// variable1
// variable2
After parsing the expression, it needs to be compiled:
compilationResult := expression.NewCompilation(tree)
Lastly, the compilation result must be evaluated. If variables are used inside the express, we must tell the evaluator. Otherwise, pass an empty map:
// No variables used
result := compilationResult.Evaluate(map[*contracts.VariableSymbol]interface{}{})
// Variables used:
var variables = map[*contracts.VariableSymbol]interface{}{
// for each variable, you must specify the type of it:
contracts.NewVariableSymbol("variable1", reflect.Float64): 10.0,
contracts.NewVariableSymbol("variable2", reflect.Float64): 11.0,
}
result := compilationResult.Evaluate(variables)
It is possible to face errors in the evaluation process. Just like the syntax tree, the result of the evaluation contains a Diagnostics
array (if it is empty, we are good to go). To get the result value, there are mainly two ways, get it as aninterface{}
or casted as a float64
:
if len(result.Diagnostics) > 0 {
for _, d := range tree.Diagnostics {
fmt.Println(d.Message, d.Span, d.Span.Length, d.Span.Start)
}
panic(1)
}
// Printing the result as an interface{}
fmt.Println(result.Value)
// Printing the result as a float64
fmt.Println(result.FloatCastedValue)
The whole code with the using of variables:
tree := syntax.Parse("variable1 + variable2")
// Check if parsing is done without any error
if len(tree.Diagnostics) > 0 {
for _, d := range tree.Diagnostics {
fmt.Println(d.Message, d.Span, d.Span.Length, d.Span.Start)
}
panic(1)
}
variables := make(map[*contracts.VariableSymbol]interface{})
for _, v := range tree.Variables() {
variables[contracts.NewVariableSymbol(v, reflect.Float64)] = 10.0
}
result := compilationResult.Evaluate(variables)
// Check if evaluation is done without any error
if len(result.Diagnostics) > 0 {
for _, d := range tree.Diagnostics {
fmt.Println(d.Message, d.Span, d.Span.Length, d.Span.Start)
}
panic(1)
}
// Printing the result as an interface{}
fmt.Println(result.Value)
// Printing the result as a float64
fmt.Println(result.FloatCastedValue)
Name | Operator | Sample |
---|---|---|
Addition | + |
var1 + var2 |
Substraction | - |
var1 - var2 |
Multiplication | * |
var1 * var2 |
Division | / |
var1 / var2 |
Name | Operator | Sample |
---|---|---|
Shift right | >> |
var1 >> var2 |
Shift Left | << |
var1 << var2 |
AND | & |
var1 & var2 |
OR | | |
var1 | var2 |
XOR | ^ |
var1 ^ var2 |
Name | Operator | Sample |
---|---|---|
Equal | == |
var1 == var2 |
Not Equal | != |
var1 != var2 |
Logical And | && |
var1 && var2 |
Logical Or | || |
var1 || var2 |
Greater Than or Equal | >= |
var1 >= var2 |
Lesser Than or Equal | <= |
var1 <= var2 |
Greater Than | > |
var1 > var2 |
Lesser Than | < |
var1 < var2 |
Name | Operator | Sample |
---|---|---|
Logical Negation | ! |
!bool_var1 |
Negation | - |
-var1 |
Name | Operator | Sample |
---|---|---|
Parenthesis | () |
((var1 + var2) * (var1 - var2)) / 2 |
Name | Operator | Sample |
---|---|---|
Assignment | = |
var3 = var1 + var2 |
This package is built based on the Immo Landwerth's tutorial on youtube. Many thanks to him for making such a great content.
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.