Common Gotchas

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

Product: 

Class: 

Warning

uCalc API Preview Release Notice:The uCalc engine has successfully transitioned to modern cross-platform environments.The next phase envolves some structural changes, performance optimizations, and API refinements.The API is subject to breaking changes prior to the stable release. Please evaluate the preview version thoroughly before production use.

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.

using uCalcSoftware;

var uc = new uCalc();

// Correct, High-Performance Way
var 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.

using uCalcSoftware;

var uc = new uCalc();

// Incorrect Assumption
var 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.

using uCalcSoftware;

var uc = new uCalc();

// Correct Way
var t = new uCalc.Transformer();
t.FromTo("An {item}", "ITEM");  // General rule first
t.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.
using uCalcSoftware;

var uc = new uCalc();


// Correct C# Way
using (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().

using uCalcSoftware;

var uc = new uCalc();

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.

using uCalcSoftware;

var uc = new uCalc();

// Incorrect Order
var 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.

using uCalcSoftware;

var uc = new uCalc();

// Correct Order
var 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

This page last modified on: 

8/21/2026