MtxVec Expression Parser (TMtxExpression)

TMtxExpression is a math expression parser that evaluates Matlab-like syntax at runtime. It is designed for zero-allocation evaluation: memory is allocated once at parse time, then reused across repeated evaluations. This makes it faster than most scripting engines for numerical workloads.

Location: MtxParseExpr.pas (TMtxExpression). Related units: MtxParseClass.pas,

In C++ the headers are MtxParseExpr.h (TMtxExpression), MtxParseClass.h (TValueRec, TExprGridVariable, TExprContext), MtxParseOperator.h and MtxParseProbabilities.h. TMtxExpression has a public constructor and destructor, so a local variable needs no new and no delete, and a custom function is a plain function void MyFunction(TExprRec* Param).

Key design principles:

  • No memory allocation during evaluation — all buffers allocated at parse time
  • Statically typed variables (type set on first assignment, immutable until undefine())
  • 0-based arrays and matrices (row-major storage)
  • Matlab-like syntax with some differences (see below)

Differences from Matlab:

  1. Arrays are 0-based (not 1-based)
  2. Integer division is strict: 5./6 = 0 (per-element / returns integer for integer operands)
  3. Concatenation [1, 2] requires commas between elements
  4. * on two vectors is treated as .* (element-wise)
  5. Case-sensitive (but many aliases exist: Tan() = tan(), False = false)
  6. Colon : has higher precedence than +,-,*,/: 2:3+1 = 3:4
  7. Explicit type conversions required (no implicit integer→double promotion in loops)

Built-in types:

TypeDescription
double64-bit float (or 32-bit depending on build)
integer64-bit integer (overflow/division-by-zero checked)
complexDouble-precision real + imaginary (struct)
stringString value
booleanTrue/False (stored as 32-bit integer)
vectorDouble-precision 1D array (can hold complex)
matrixDouble-precision 2D array (can hold complex, row-major)
integer vector/matrix32/16/8-bit integer arrays
boolean vector/matrix32-bit boolean arrays
rangeTwo or three values: 1:10 or 10:-1:0
customArbitrary object types (for grid integration, etc.)

Variable declaration by example:

a = 0.0              // double
a = 0                // 64-bit integer
a = [1.0, 2, 3, 4]  // double vector (at least one element must be double)
a = [1, 2, 3, 4]    // integer vector
a = [1+1i, 2, 3]    // complex vector
A = [1, 2; 3, 4]    // 2x2 matrix (semicolon separates rows)
b = Boolean(a)       // explicit conversion
d = double(a)        // explicit conversion
undefine(a)          // reset type, allows reassignment to new type

Control flow:

// If-else (keywords must be on their own lines)
if b == 2
   a = 2
else
   a = 3
end

// While loop
while (b < 10)
   b = b + 1
end

// For loop with range
for k = 1:10
   j = j + 1
end

// For loop over vector elements
a = [4, 5, 6]
for k = a
   j = j + k    // j = 15 after loop
end

// Break and continue
for k = a
   if k > 2
      continue
   end
   j = j + k
end

Operators (by priority, lowest number = highest priority):

PriorityOperatorDescription
10!x, ~x, not xLogical/bitwise NOT
10-x, +xUnary minus/plus
10x'Transpose (adjungate)
15x:y, x:step:yRange operator
20x ^ yPower
30x * yMatrix multiply
30x *. y, x .* yElement-wise multiply
30x / yMatrix division
30x /. y, x ./ yElement-wise division
30A \ yBack-division: x = A^(-1)*y
30x div yInteger-only division
10x % y, x mod yRemainder
40x + yAdd (also string concatenation)
40x - ySubtract
40x +. y, x .- yElement-wise add/subtract
45x >> y, x shr yBit shift right
45x << y, x shl yBit shift left
50<, <=, >, >=Comparison (returns bool/mask)
55==, !=, <>, ~=Equality/inequality
70x & y, x and yLogical/bitwise AND
70x xor yLogical/bitwise XOR
80x or y, `x \y`Logical/bitwise OR
200x = yAssignment

Index operations (gather/scatter):

a = [1, 0, 3, 4]
mask = a <> 0              // boolean mask: [true, false, true, true]
d = a(mask)                // gather by mask: [1, 3, 4]
d = d + 2                  // modify: [3, 5, 6]
a(mask) = d                // scatter back

A(0:2, 2:3) = B(2:4, 5:6) // sub-matrix copy
a(2:) = b                  // copy b into a starting at index 2
a(2:) = 3                  // fill a from index 2 to end with 3

// Multiple return values
(mag, phase) = CartToPolar(FFT([0:511]))

Complete function reference (from TMtxExpression.GetFuncList runtime v6.3.10):

Trigonometric: Abs, Sin, Cos, Tan, Sec, Csc, Cot, Sinh, Cosh, Tanh, Sech, Csch, Coth, ArcSin, ArcCos, ArcTan, ArcTan(x,y), ArcSec, ArcCsc, ArcCot, ArcSinh, ArcCosh, ArcTanh, ArcSech, ArcCsch, ArcCoth, SinCos, SinhCosh, FixAngle, DegToRad, RadToDeg

Exponential/logarithmic: Exp, Exp2, Exp10, Ln, Log, Log2, Log10, LogN, Sqrt, Sqr, Cbrt, Power, Pow, IntPow, IntPower, Root(x,k,n)

Complex: Cplx(x), Cplx(re,im), Conj, Flip, Imag, Real, Arg, Expj, Cis, IsComplex, CartToPolar, PolarToCart, Norm (squared norm)

Rounding/conversion: Ceil, Floor, Frac, Round, RoundToInt, Trunc, TruncToInt, Double(x), Integer(x), Boolean(x), Sgn

Checks: IsNaN, IsInf, IsInfNan, IsNanInf, IsEqual, IfThen(a,b,c)

Vector statistics: Sum, SumOfSqr, Product, Mean, MeanCols, MeanRows, Median, StdDev, StdDevCols, StdDevRows, Rms, Min, Max, Kurtosis, Skewness, Normalize, NormL1, NormL2, NormC, ZScore, Length

Vector operations: CumSum, Difference, Reverse, Rotate, SortAscend, SortDescend, ThreshBottom, ThreshTop, Ones, Zeros, Ramp, Integrate

Random: Random (uniform -1..1), RandG/RandomG (Gaussian mean=0, stddev=1)

Signal processing: FFT, IFFT, IFFTToReal, FFT2D, IFFT2D, IFFT2DToReal, DCT, IDCT, Convolve, Deconv, Hilbert, DotProd, AutoCorrBiased, AutoCorrNormal, AutoCorrUnbiased, CrossCorr

Polynomial: PolyCoeff, PolyEval, PolyRoots

Matrix operations: Rows, Cols, Eye, Eig, SVD, SVDSolve, LQR, Cholesky, Determinant, Trace, Diag, MtxIntPower, MtxPower, MtxSqrt, Rotate90, FlipHor, FlipVer, LowerTriangle, UpperTriangle, Hankel, Toeplitz, Vandermonde, Kron, Kac, Pascl, Norm1, NormFro, NormInf

Special functions: Erf, Erfc, ErfInv, Gamma, LnGamma, Pythag, Primes, Rem, RemDiv, Lcm, Gcd

String: CompareStr, CompareText, CplxToStr, FormatCplx, FormatSample, SampleToStr, IntToStr, Pos, ReplaceStr, ReplaceText, Lower, Upper, Trim

File I/O: csvRead, csvWrite, FileOpen, FileClose, FileRead, FileWrite, FilePosition, FileSetPosition, FileSize, FileCopy, FileMove, FileDelete, FileExists, DirectoryCreate, DirectoryDelete, DirectoryExists

Variable management: Undefine(x,...), Assign(Dst, Range, Src)

Aliases: Ln = Log, Expj = Cis, Integer() = TruncToInt(), Pow = Power

API — interactive evaluation (console/REPL style):

TMtxExpression expr;   // a local object: freed at the end of the scope

// Evaluate one expression at a time
expr.AddExpr("j = -2");
TValueRec* a = expr.Evaluate();
DewString varName = expr.EvaluatedVarName(0);
TValueRec* val = expr.GetVarByName(varName);  // inspect result

// Multi-line: vectorized math
expr.AddExpr("x = j + [10:15]");       // [ ] turns the range into a vector
expr.Evaluate();
expr.AddExpr("y = double(x)");
expr.Evaluate();

// List all defined variables
tStringList varNames;
expr.GetVarList(&varNames);

// Get help: list all functions and operators
// (each call clears the lists it fills, so use one pair per call)
tStringList funcList;
tStringList funcHelp;
expr.GetFuncList(&funcList, &funcHelp, true);   // functions with help text
tStringList operList;
tStringList operHelp;
expr.GetOperList(&operList, &operHelp);         // operators with help text

// Reset everything
expr.ClearAll();

API — scalar evaluation (fast repeated evaluation):

TMtxExpression expr;
expr.ClearAll();
TDoubleValue* x1 = expr.DefineDouble("x");       // define scalar variable
expr.SetExpressions("x *. Sin( x / pi )");       // set formula

// Evaluate many times with different x values (zero allocation per call)
for (int i = 0; i < values.GetLength(); i++)
{
    x1->SetDoubleValue(values.Values(i));        // set input
    results.Values(i) = expr.EvaluateDouble();   // fast scalar result
}

API — vectorized evaluation (10-20x faster than scalar loop):

TMtxExpression expr;
expr.ClearAll();
expr.DefineVector("x", xVector);               // bind vector variable
expr.SetExpressions("Sin(x) + Cos(x)");        // set formula
TVec* yResult = expr.EvaluateVector();         // entire vector at once (owned by expr)

API — compiled scripts (multi-line):

TMtxExpression expr;
expr.ClearExpressions();
for (int i = 0; i < scriptLines.GetCount(); i++)
    expr.AddExpr(scriptLines.GetString(i));
expr.Compile();                                // compile once
TValueRec* result = expr.EvaluateCompiled();   // run compiled script

// Step-by-step execution (debugger)
int line = expr.EvaluateStep(currentLine);     // execute one line, returns next

API — custom functions and grid integration:

struct MyGrid                   // stands in for your grid control
{
    DewString Cells[10][10];
};

// Custom function: reads its arguments from Param
void MyCallback(TExprRec* Param)
{
    // ... draw the values ...
}

// Grid callbacks: the void* argument is the object bound with the event
void GridSetValue(void* Grid, TExprGridVariable* Sender, const DewString& Src, const int Row, const int Col)
{
    ((MyGrid*)Grid)->Cells[Row][Col] = Src;
}

void GridGetValue(void* Grid, TExprGridVariable* Sender, DewString& Dst, const int Row, const int Col)
{
    Dst = ((MyGrid*)Grid)->Cells[Row][Col];
}

void Register(TMtxExpression& expr, MyGrid& grid, TExprGridVariable& gridVar)
{
    // Register custom function
    expr.DefineFunction("drawvalues", MyCallback, 5, 0,
        "drawvalues(Y, series, xOffset, xStep, downsample): Draws Y to chart series.");

    // Grid integration (spreadsheet-like access from expressions)
    gridVar.SetRows(10);
    gridVar.SetCols(10);
    gridVar.SetOnSetValue(TExprGridSetProc(&grid, GridSetValue));
    gridVar.SetOnGetValue(TExprGridGetProc(&grid, GridGetValue));
    expr.DefineCustomValue("grid1", &gridVar);
    // Now in expressions: grid1(0, 0) = "Test"; a = grid1(1:3, 0:2)
}

// Workspace save/load (persist all variables)
void SaveWorkspace(TMtxExpression& expr, TExprContext& workspace)
{
    expr.SaveContext(&workspace);   // custom values such as grid1 are not saved
}

void LoadWorkspace(TMtxExpression& expr, TExprContext& workspace)
{
    expr.LoadContext(&workspace);   // clears expr first: define custom values again
}