uCalc API Version: 2.1.3-preview.2 Released: 6/17/2026

Warning

uCalc API Preview Release Notice:The documentation describes the intended behavior of the API. The current preview build contains incomplete features, unoptimized performance, and is subject to breaking changes.

Common Gotchas

Product: 

Class: 

A guide to the most common mistakes and misunderstandings developers encounter when using uCalc, with solutions and best practices.

Remarks

⚠️ Common uCalc Gotchas & Pitfalls

This guide covers the most frequent mistakes developers make when working with uCalc. Understanding these points will save you significant debugging time and help you write more efficient, robust code.


1. 🐌 Performance: Using Eval() Inside a Loop

The Pitfall: Calling Eval() or EvalStr() inside a high-frequency loop.

Why it's a problem: Every call to Eval() performs two steps: parsing the string into an executable plan (slow) and then evaluating that plan (fast). In a loop, you're repeating the slow parsing step unnecessarily for every iteration.

The Solution: Use the "Parse-Once, Evaluate-Many" pattern.

// Correct, High-Performance Wayvar x_var = uc.DefineVariable("x");// 1. Parse ONCE, outside the loop.var expr = uc.Parse("x * x + 2");// 2. Evaluate MANY times, inside the loop. This is very fast.for ( i = 1; i <= 1000; i++) {    x_var.Value(i);    Console.WriteLine(expr.Evaluate());}

For more details, see the Optimizing Performance tutorial.


2. 🧠 Transformer Rules: Precedence is LIFO

The Pitfall: Assuming transformer rules are applied in the order they are defined.

Why it's a problem: uCalc uses a LIFO (Last-In, First-Out) stack for rule precedence. The last rule defined is checked first. This is often counter-intuitive.

// Incorrect Assumptionvar t = new uCalc.Transformer();t.FromTo("An apple", "FRUIT"); // Defined first (lower priority)t.FromTo("An {item}", "ITEM");  // Defined last (higher priority)// This will output "ITEM" because the general rule was defined last.Console.WriteLine(t.Transform("An apple"));

The Solution: Always define your most specific rules last to give them the highest priority.

// Correct Wayvar t = new uCalc.Transformer();t.FromTo("An {item}", "ITEM");  // General rule firstt.FromTo("An apple", "FRUIT"); // Specific rule last// This will correctly output "FRUIT".Console.WriteLine(t.Transform("An apple"));

3. 💧 Memory Management: Forgetting to Release()

The Pitfall: Not releasing uCalc objects in non-RAII environments.

Why it's a problem: The uCalc core is C++. In managed languages like C#, the garbage collector only cleans up the .NET handle, not the underlying C++ engine instance. This causes a memory leak.

The Solution: Use language-idiomatic resource management.

  • C# / VB.NET: Always wrap object creation in a using block.
  • C++: Use stack-allocated objects and call Owned() to enable RAII.
  • Manual: If an object's lifetime is not scoped, you must call Release() when you are done with it.
// Correct C# Wayusing (var t = uc.NewTransformer()){    // ... use t ...} // t.Release() is called automatically here.

See the Memory Management guide for more information.


4. 🔢 Types in {@Eval}: Forgetting String Conversion

The Pitfall: Trying to perform math on a captured variable inside an {@Eval} block without converting it to a number.

Why it's a problem: All variables captured from a pattern, even from {@Number:val}, are strings. An expression like val * 2 will result in string concatenation, not multiplication.

The Solution: Explicitly cast the string to a numeric type using functions like Double() or Int().

var t = new uCalc.Transformer();// Incorrect: "10" * 2 would result in repeating the string twice resulting in "1010".// Correct: Use Double() to convert the string '10' to a number.t.FromTo("{@Number:val}", "{@Eval: Double(val) * 2}");Console.WriteLine(t.Transform("Value: 10"));

For details, see Executing Logic in Replacements.


5. 🔀 Alternation Order: Longest Match First

The Pitfall: In an alternation |, placing a shorter, partial match before a longer, more specific one.

Why it's a problem: The engine checks alternatives from left to right and stops at the first successful match.

// Incorrect Ordervar t = new uCalc.Transformer();// "Apple" will match first, so "Apple Pie" is never checked.t.FromTo("{ Apple | Apple Pie }", "MATCH");Console.WriteLine(t.Transform("An Apple Pie")); // Incorrectly matches only "Apple"

The Solution: Always place the longest, most specific patterns first in an alternation.

// Correct Ordervar t = new uCalc.Transformer();t.FromTo("{ Apple Pie | Apple }", "MATCH");Console.WriteLine(t.Transform("An Apple Pie")); // Correctly matches "Apple Pie"

6. 📝 Indexing: 1-based vs. 0-based

The Pitfall: Confusing 1-based and 0-based indexing.

Why it's a problem: Different parts of the uCalc API use different conventions.

  • 1-based: Arguments in a Callback (e.g., cb.Arg(1)).
  • 0-based: Most collections, like Matches (myMatches[0]) and Tokens (myTokens.At(0)).

The Solution: Be mindful of the context. When in a callback interacting with arguments, think 1-based. When iterating a collection of results, think 0-based.

Examples