Dynamic Compilation Pipeline
Helix compiles declarative rule definitions into executable JVM bytecode using a 5-stage compilation lifecycle:
1. Native Recursive-Descent Lexer & Operator-Precedence AST Parser
Helix features a zero-dependency lexical analyzer and operator-precedence parser (AstBuilder) built directly into engine-core. Unlike legacy engines that rely on external expression interpreters or reflection-heavy runtimes, Helix tokenizes infix expressions into a strict stream of typed tokens (IDENTIFIER, LITERAL, BINARY_OP, LOGICAL_OP, PAREN) and constructs the Abstract Syntax Tree using Dijkstra's shunting-yard and precedence climbing.
For example, the expression:
amount > 10000 && country != "US"
is parsed into the following strongly-typed node tree:
2. Type Checking & Semantic Validation
Before generating bytecode, the TypeChecker verifies that:
- Every referenced variable in the expression exists in the
inputSchema. - Operands have compatible types (e.g. comparing
intwithint,doublewithdouble, orStringwithString). - Logical operations (
&&,||,!) operate exclusively on boolean expressions. - Division by zero on literal constants is caught at compile-time before bytecode emission.
3. Multi-Pass AST Optimization
The AstOptimizer runs iterative passes until tree convergence:
- Constant Folding: Expressions like
10 + 20 > 5are computed at compile-time totrue. - Dead Code Elimination: Branches like
false && (x > 100)are reduced to literalfalse. - Identity Simplification: Expressions like
x && truereduce tox. - Algebraic Reductions: Operations like
x * 0orx + 0are simplified to zero orx.
4. Bytecode Generation & ClassLoader Emission
The optimized AST is transformed into raw bytecode implementing the CompiledRule interface:
package com.helix.generated;
import com.helix.api.CompiledRule;
import com.helix.api.ExecutionContext;
public class FraudDetectionRule_v1 implements CompiledRule {
@Override
public boolean eval(ExecutionContext ctx) {
int amount = ctx.getInt("amount");
String country = ctx.getString("country");
return (amount > 10000) && (!"US".equals(country));
}
}
The resulting .class bytes are dynamically injected into an isolated RuleClassLoader instance, ready for instant invocation.
5. Non-Intrusive Dynamic Debug Probing & Disassembly
During development or active diagnosis, the compiled bytecode can be instrumented on-the-fly without changing the original source rule:
DebugClassVisitor: Uses ASM method adapters to inject non-intrusive probe callbacks (DebugProbe) at bytecode entry, branching points, and exit.FrameInspector: Captures the operand stack and local variable slots (amount,country) without halting the JVM thread.- Bytecode Disassembler: Decompiles generated
.classbyte arrays into human-readable Java bytecode opcodes directly in the CLI (helix repl:disasm), allowing engineers to verify JIT inlining friendliness and stack depth.