Skip to content

guamoko995/expr-cls

 
 

Repository files navigation

expr-cls

expr-cls is a minimal, experimental implementation of a high-performance string expression compiler and runtime for Go.
The core idea is to build expressions as chains of strictly-typed Go closures (functions), rather than interpreting bytecode on a virtual machine.
This architecture enables extremely fast compilation and execution, zero allocations during evaluation, and a flexible, extensible environment system.


Key Concepts

Environment:
At compile time, you define an environment describing:

  • Supported unary and binary operators for the parser (not implemented yet; currently a fixed set is used).
  • Complex constructs for the parser (e.g., ternary operator, arbitrary typed literals; planned).
  • Overload registrations for unary and binary operators.
  • Overload registrations for functions.
  • Constant registrations.
  • Variable type registrations.
  • Variable source type registrations (struct fields as variables).

Extensibility:
You can create separate packages with ready-made environments for domain-specific tasks (e.g., matrices, complex numbers, statistics, geospatial, etc). See example for a current example of environment definition and usage.

Performance:
Expressions compile and execute extremely quickly at runtime.
Compiled expressions are strictly typed.


Usage

package example_test

import (
	"fmt"

	exprcls "github.com/guamoko995/expr-cls"

	// Using the example environment
	_ "github.com/guamoko995/expr-cls/tests/example/def_env"
)

// CompileAndCalcExample demonstrates how to compile and evaluate expressions
// using the expr-cls package.
func Example() {
	// Define a data structure containing input variables for our expression.
	type InputData struct {
		A int
		B int
	}

	// Parse and compile the expression "3 + A * B".
	prog, err := exprcls.Compile[InputData, float64]("3 + A * -B + sin(pi/2)")
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	// Provide input data for evaluating the expression.
	input := InputData{A: 7, B: 10}

	// Evaluate the expression using the provided input data.
	result := prog(input)

	// Print the computed result.
	fmt.Println(result)

	// Output: -66
}

Features

  • Struct fields as variables in expressions (via registration).
  • Operator overloading for custom types (operator set is fixed by parser, not extendable yet).
  • Function overloading and custom function registration.
  • Constant registration.
  • Strict typing of compiled expressions.
  • Extremely fast compilation and evaluation (see benchmarks below).
  • MVP: Some features and built-ins from original expr-lang/expr are disabled or not implemented.

Benchmarks

You can find the benchmark source in tests/benchmarks.

Compilation Speed

Measures the time and resource usage to compile an expression (benchmark code):

goos: linux
goarch: amd64
pkg: github.com/guamoko995/expr-cls/tests/benchmarks
cpu: AMD Ryzen 5 5600H with Radeon Graphics         
BenchmarkCompile/expr-cls-12         	  661381	      1802 ns/op	    1344 B/op	      31 allocs/op
BenchmarkCompile/expr-12             	  107227	     10928 ns/op	   10351 B/op	      76 allocs/op
BenchmarkCompile/cel-go-12           	   33639	     36255 ns/op	   24404 B/op	     355 allocs/op
PASS
ok  	github.com/guamoko995/expr-cls/tests/benchmarks	3.591s

expr-cls compiles more than 6x faster and with 8x less memory allocation than expr-lang/expr.


Evaluation Speed

Measures the time and resource usage to repeatedly evaluate a compiled expression (benchmark code):

expression: "X+(6.0*Y)"
params:
	X=3
	Y=5
expr-cls result: 33
expr result: 33
cel-go result: 33

goos: linux
goarch: amd64
pkg: github.com/guamoko995/expr-cls/tests/benchmarks
cpu: AMD Ryzen 5 5600H with Radeon Graphics         
BenchmarkСalc/expr-cls-12          	132511530	         9.015 ns/op	       0 B/op	       0 allocs/op
BenchmarkСalc/expr-12              	 6529965	       172.0 ns/op	     136 B/op	       6 allocs/op
BenchmarkСalc/cel-go-12            	 4484371	       267.9 ns/op	     368 B/op	       6 allocs/op
PASS
ok  	github.com/guamoko995/expr-cls/tests/benchmarks	3.529s

expr-cls evaluates expressions ~18x faster and with zero allocations.


Architecture

  • Parsing: Expressions are parsed into AST nodes. The set of operators/constructs is currently fixed.
  • Environment: Holds operator/function/constant/variable builders. Easily extended. See example for practical setup.
  • Building: AST is compiled into a closure chain (Go functions), not bytecode.
  • Evaluation: The compiled closure chain receives strictly-typed input (struct) and returns strictly-typed output.

Extending expr-cls

To see how to register types, constants, functions, and operator overloads, refer to the example.


Limitations and Roadmap

  • Only struct variable sources supported (for now).
  • Operators cannot be extended (only overloaded).
  • Some complex constructs (e.g. ternary operator, custom literals) are planned but not implemented.
  • Error handling and reporting are minimal.
  • More tests and environments are planned.
  • Parser extensibility: declarative operator/construct definition is a future goal.

This README summarizes the ideas and experimental architecture of expr-cls.
Feedback and contributions are welcome.

About

Efficient evaluation of expressions specified in the GO runtime

Resources

License

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Go 100.0%