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.

Error handling and control flow

Product: 

Class: 

Explains uCalc's state-based error handling and its mechanisms for creating custom control-flow structures.

Remarks

💣 Error Handling & Control Flow: A Different Approach

uCalc's architecture for handling errors and implementing control flow differs from what developers in traditional compiled languages like C# or C++ might expect. Instead of relying on language-level try/catch blocks and if/for statements, uCalc provides an integrated, engine-level system designed for the dynamic nature of a parsing environment. This approach prioritizes performance, flexibility, and, most importantly, the ability to recover from errors gracefully.

This topic covers the two pillars of this system: uCalc's state-based error model and its lazy-evaluation mechanism for building custom control structures.


1. Error Handling: Recover, Don't Just Fail

In a parsing context, especially one dealing with user input, errors are not "exceptional" events; they are a normal part of operation. A user might type an incomplete formula or misspell a variable name. A traditional try/catch block, which unwinds the call stack, is a heavyweight solution for such common occurrences. uCalc uses a more lightweight, state-based model.

The Two Methods of Error Handling

A. Reactive: Checking the Error State

The simplest way to handle errors is to check the engine's state after an operation. Every uCalc instance has an Error property that holds information about the last error. If an operation like EvalStr fails, it doesn't throw an exception; it returns an error message as a string and sets the error state.

var result = uc.EvalStr("5 * (10 +"); // Invalid syntax// Reactively check if an error occurredif (uc.Error.Code != ErrorCode.None) {{    Console.WriteLine($"An error occurred: {uc.Error.Message}");}

This approach is simple and effective for many use cases, but you only learn about the error after the operation has already aborted.

B. Proactive: The Error Handler Callback

For more advanced control, you can register an error handler using AddHandler. This is a callback function that uCalc invokes the moment an error occurs, giving you the power to intercept, inspect, and even recover from it.

The Power of Recovery with Resume

The most powerful feature of an error handler is the ability to control what happens next by setting the Error.Response property. While the default is to Abort, you can set it to ErrorHandlerResponse::Resume.

This instructs the engine to continue execution as if the error never happened. This is the key to building fault-tolerant and "intelligent" parsers. For example, an error handler can catch an Undefined_Identifier error, automatically define the missing variable, and then Resume the evaluation, which will now succeed. This is a level of dynamic recovery that is very difficult to achieve with standard try/catch blocks.

For a full tutorial, see Handling Errors.


2. Control Flow: Lazy Evaluation with ByExpr

Just as uCalc avoids language-level try/catch, it also provides its own mechanism for control flow structures like if statements and loops. This is necessary because of a fundamental concept in expression evaluation: eager evaluation.

The Problem with Eager Evaluation

In most languages, when you call a function like MyFunc(arg1, arg2), both arg1 and arg2 are fully calculated before MyFunc begins to run. This makes it impossible to create a custom if function. A call like MyIf(1 > 0, 100, 1/0) would crash because the 1/0 in the "else" branch is evaluated eagerly, even though it should never be executed.

The Solution: ByExpr

uCalc solves this with the ByExpr parameter modifier. When used in a function signature, it tells the engine not to pass the argument's final value, but to pass an unevaluated Expression object instead.

The callback function then has complete control over this Expression object. It can:

  • Evaluate it: Call .Evaluate() to get its result.
  • Ignore it: Choose not to evaluate it at all (the key to short-circuiting).
  • Evaluate it multiple times: The foundation for creating custom loops.

This mechanism is the core of all control flow in uCalc. It is used to implement the built-in IIf function (a ternary operator), as well as looping constructs like ForLoop and DoLoop. It also empowers you to build your own custom control structures, elevating uCalc from a simple calculator to a lightweight scripting engine.

For a detailed guide, see Lazy Evaluation (ByExpr).

Examples

Checking the error code for a simple syntax error
				
					using uCalcSoftware;

var uc = new uCalc();
var result = uc.Eval("MyVar * 10");
Console.WriteLine("An error has occurred!");
Console.WriteLine($"Error #: {(int)uc.Error.Code}");
Console.WriteLine($"Error Message: {uc.Error.Message}");
Console.WriteLine($"Error Location: {uc.Error.Location}");
Console.WriteLine($"Error Expression: {uc.Error.Expression}");
				
			
An error has occurred!
Error #: 258
Error Message: Undefined identifier
Error Location: 0
Error Expression: MyVar * 10
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

int main() {
   uCalc uc;
   auto result = uc.Eval("MyVar * 10");
   cout << "An error has occurred!" << endl;
   cout << "Error #: " << (int)uc.Error().Code() << endl;
   cout << "Error Message: " << uc.Error().Message() << endl;
   cout << "Error Location: " << uc.Error().Location() << endl;
   cout << "Error Expression: " << uc.Error().Expression() << endl;
}
				
			
An error has occurred!
Error #: 258
Error Message: Undefined identifier
Error Location: 0
Error Expression: MyVar * 10
				
					Imports System
Imports uCalcSoftware
Public Module Program
   Public Sub Main()
      Dim uc As New uCalc()
      Dim result = uc.Eval("MyVar * 10")
      Console.WriteLine("An error has occurred!")
      Console.WriteLine($"Error #: {CInt(uc.Error.Code)}")
      Console.WriteLine($"Error Message: {uc.Error.Message}")
      Console.WriteLine($"Error Location: {uc.Error.Location}")
      Console.WriteLine($"Error Expression: {uc.Error.Expression}")
   End Sub
End Module
				
			
An error has occurred!
Error #: 258
Error Message: Undefined identifier
Error Location: 0
Error Expression: MyVar * 10
A practical example demonstrating all four related floating-point error configuration methods.
				
					using uCalcSoftware;

var uc = new uCalc();
Console.WriteLine($"Divide by Zero (Default): {uc.EvalStr("1/0")}");
uc.Error.TrapOnDivideByZero = true;
Console.WriteLine($"Divide by Zero (Error Enabled): {uc.EvalStr("1/0")}");

Console.WriteLine("");
Console.WriteLine($"Invalid Operation (Default): {uc.EvalStr("Sqrt(-1)")}");
uc.Error.TrapOnInvalid = true;
Console.WriteLine($"Invalid Operation (Error Enabled): {uc.EvalStr("Sqrt(-1)")}");

Console.WriteLine("");
Console.WriteLine($"Overflow (Default): {uc.EvalStr("5*10^308")}");
uc.Error.TrapOnOverflow = true;
Console.WriteLine($"Overflow (Error Enabled): {uc.EvalStr("5*10^308")}");

Console.WriteLine("");
Console.WriteLine($"Underflow (Default): {uc.EvalStr("10^-308/10000")}");
uc.Error.TrapOnUnderflow = true;
Console.WriteLine($"Underflow (Error Enabled): {uc.EvalStr("10^-308/10000")}");
				
			
Divide by Zero (Default): inf
Divide by Zero (Error Enabled): Division by 0

Invalid Operation (Default): nan
Invalid Operation (Error Enabled): Invalid operation

Overflow (Default): inf
Overflow (Error Enabled): Floating point overflow

Underflow (Default): 0
Underflow (Error Enabled): Floating point underflow
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

int main() {
   uCalc uc;
   cout << "Divide by Zero (Default): " << uc.EvalStr("1/0") << endl;
   uc.Error().TrapOnDivideByZero(true);
   cout << "Divide by Zero (Error Enabled): " << uc.EvalStr("1/0") << endl;

   cout << "" << endl;
   cout << "Invalid Operation (Default): " << uc.EvalStr("Sqrt(-1)") << endl;
   uc.Error().TrapOnInvalid(true);
   cout << "Invalid Operation (Error Enabled): " << uc.EvalStr("Sqrt(-1)") << endl;

   cout << "" << endl;
   cout << "Overflow (Default): " << uc.EvalStr("5*10^308") << endl;
   uc.Error().TrapOnOverflow(true);
   cout << "Overflow (Error Enabled): " << uc.EvalStr("5*10^308") << endl;

   cout << "" << endl;
   cout << "Underflow (Default): " << uc.EvalStr("10^-308/10000") << endl;
   uc.Error().TrapOnUnderflow(true);
   cout << "Underflow (Error Enabled): " << uc.EvalStr("10^-308/10000") << endl;
}
				
			
Divide by Zero (Default): inf
Divide by Zero (Error Enabled): Division by 0

Invalid Operation (Default): nan
Invalid Operation (Error Enabled): Invalid operation

Overflow (Default): inf
Overflow (Error Enabled): Floating point overflow

Underflow (Default): 0
Underflow (Error Enabled): Floating point underflow
				
					Imports System
Imports uCalcSoftware
Public Module Program
   Public Sub Main()
      Dim uc As New uCalc()
      Console.WriteLine($"Divide by Zero (Default): {uc.EvalStr("1/0")}")
      uc.Error.TrapOnDivideByZero = true
      Console.WriteLine($"Divide by Zero (Error Enabled): {uc.EvalStr("1/0")}")
      
      Console.WriteLine("")
      Console.WriteLine($"Invalid Operation (Default): {uc.EvalStr("Sqrt(-1)")}")
      uc.Error.TrapOnInvalid = true
      Console.WriteLine($"Invalid Operation (Error Enabled): {uc.EvalStr("Sqrt(-1)")}")
      
      Console.WriteLine("")
      Console.WriteLine($"Overflow (Default): {uc.EvalStr("5*10^308")}")
      uc.Error.TrapOnOverflow = true
      Console.WriteLine($"Overflow (Error Enabled): {uc.EvalStr("5*10^308")}")
      
      Console.WriteLine("")
      Console.WriteLine($"Underflow (Default): {uc.EvalStr("10^-308/10000")}")
      uc.Error.TrapOnUnderflow = true
      Console.WriteLine($"Underflow (Error Enabled): {uc.EvalStr("10^-308/10000")}")
   End Sub
End Module
				
			
Divide by Zero (Default): inf
Divide by Zero (Error Enabled): Division by 0

Invalid Operation (Default): nan
Invalid Operation (Error Enabled): Invalid operation

Overflow (Default): inf
Overflow (Error Enabled): Floating point overflow

Underflow (Default): 0
Underflow (Error Enabled): Floating point underflow
Adding an error handler callback
				
					using uCalcSoftware;

var uc = new uCalc();

static void MyErrorHandler(Handle_uCalc h) {
   var uc = new uCalc(h);
   Console.WriteLine("An error has occurred!");
   Console.WriteLine($"Error #: {(int)uc.Error.Code}");
   Console.WriteLine($"Error Message: {uc.Error.Message}");
   Console.WriteLine($"Error Symbol: {uc.Error.Symbol}");
   Console.WriteLine($"Error Location: {uc.Error.Location}");
   Console.WriteLine($"Error Expression: {uc.Error.Expression}");
}


uc.Error.AddHandler(MyErrorHandler);
Console.WriteLine(uc.EvalStr("123+"));
Console.WriteLine("");

uc.Error.TrapOnDivideByZero = true;
Console.WriteLine(uc.EvalStr("5/0"));
				
			
An error has occurred!
Error #: 257
Error Message: Syntax error
Error Symbol: +
Error Location: 3
Error Expression: 123+
Syntax error

An error has occurred!
Error #: 8
Error Message: Division by 0
Error Symbol: 
Error Location: 0
Error Expression: 
Division by 0
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

void ucalc_call MyErrorHandler(Handle_uCalc h) {
   auto uc = uCalc(h);
   cout << "An error has occurred!" << endl;
   cout << "Error #: " << (int)uc.Error().Code() << endl;
   cout << "Error Message: " << uc.Error().Message() << endl;
   cout << "Error Symbol: " << uc.Error().Symbol() << endl;
   cout << "Error Location: " << uc.Error().Location() << endl;
   cout << "Error Expression: " << uc.Error().Expression() << endl;
}

int main() {
   uCalc uc;
   uc.Error().AddHandler(MyErrorHandler);
   cout << uc.EvalStr("123+") << endl;
   cout << "" << endl;

   uc.Error().TrapOnDivideByZero(true);
   cout << uc.EvalStr("5/0") << endl;
}
				
			
An error has occurred!
Error #: 257
Error Message: Syntax error
Error Symbol: +
Error Location: 3
Error Expression: 123+
Syntax error

An error has occurred!
Error #: 8
Error Message: Division by 0
Error Symbol: 
Error Location: 0
Error Expression: 
Division by 0
				
					Imports System
Imports uCalcSoftware
Public Module Program
   
   Public Sub MyErrorHandler(ByVal h As Handle_uCalc)
      Dim uc As New uCalc(h)
      Console.WriteLine("An error has occurred!")
      Console.WriteLine($"Error #: {CInt(uc.Error.Code)}")
      Console.WriteLine($"Error Message: {uc.Error.Message}")
      Console.WriteLine($"Error Symbol: {uc.Error.Symbol}")
      Console.WriteLine($"Error Location: {uc.Error.Location}")
      Console.WriteLine($"Error Expression: {uc.Error.Expression}")
   End Sub
   
   Public Sub Main()
      Dim uc As New uCalc()
      uc.Error.AddHandler(AddressOf MyErrorHandler)
      Console.WriteLine(uc.EvalStr("123+"))
      Console.WriteLine("")
      
      uc.Error.TrapOnDivideByZero = true
      Console.WriteLine(uc.EvalStr("5/0"))
   End Sub
End Module
				
			
An error has occurred!
Error #: 257
Error Message: Syntax error
Error Symbol: +
Error Location: 3
Error Expression: 123+
Syntax error

An error has occurred!
Error #: 8
Error Message: Division by 0
Error Symbol: 
Error Location: 0
Error Expression: 
Division by 0
Creating an error handler that automatically defines variables on the fly by checking for an 'Undefined Identifier' error.
				
					using uCalcSoftware;

var uc = new uCalc();

static void AutoDefineHandler(Handle_uCalc h) {
   var uc = new uCalc(h);
   // Check if the error is specifically an undefined identifier
   if (uc.Error.Code == ErrorCode.Undefined_Identifier) {
      // If so, define the missing variable and instruct uCalc to resume
      Console.WriteLine($"Auto-defining variable: '{uc.Error.Symbol}'");
      uc.DefineVariable(uc.Error.Symbol);
      uc.Error.Response = ErrorHandlerResponse.Resume;
   }
}


uc.Error.AddHandler(AutoDefineHandler);

// 'x' doesn't exist, but the handler will intercept the error and create it.
var Result = uc.EvalStr("x = 10; x * 5");
Console.WriteLine($"Result: {Result}");
				
			
Auto-defining variable: 'x'
Result: 50
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

void ucalc_call AutoDefineHandler(Handle_uCalc h) {
   auto uc = uCalc(h);
   // Check if the error is specifically an undefined identifier
   if (uc.Error().Code() == ErrorCode::Undefined_Identifier) {
      // If so, define the missing variable and instruct uCalc to resume
      cout << "Auto-defining variable: '" << uc.Error().Symbol() << "'" << endl;
      uc.DefineVariable(uc.Error().Symbol());
      uc.Error().Response(ErrorHandlerResponse::Resume);
   }
}

int main() {
   uCalc uc;
   uc.Error().AddHandler(AutoDefineHandler);

   // 'x' doesn't exist, but the handler will intercept the error and create it.
   auto Result = uc.EvalStr("x = 10; x * 5");
   cout << "Result: " << Result << endl;
}
				
			
Auto-defining variable: 'x'
Result: 50
				
					Imports System
Imports uCalcSoftware
Public Module Program
   
   Public Sub AutoDefineHandler(ByVal h As Handle_uCalc)
      Dim uc As New uCalc(h)
      '// Check if the error is specifically an undefined identifier
      If uc.Error.Code = ErrorCode.Undefined_Identifier Then
         '// If so, define the missing variable and instruct uCalc to resume
         Console.WriteLine($"Auto-defining variable: '{uc.Error.Symbol}'")
         uc.DefineVariable(uc.Error.Symbol)
         uc.Error.Response = ErrorHandlerResponse.Resume
      End If
   End Sub
   
   Public Sub Main()
      Dim uc As New uCalc()
      uc.Error.AddHandler(AddressOf AutoDefineHandler)
      
      '// 'x' doesn't exist, but the handler will intercept the error and create it.
      Dim Result = uc.EvalStr("x = 10; x * 5")
      Console.WriteLine($"Result: {Result}")
   End Sub
End Module
				
			
Auto-defining variable: 'x'
Result: 50
Error handler order
				
					using uCalcSoftware;

var uc = new uCalc();

static void ErrorHandlerA(Handle_uCalc h) {
   var uc = new uCalc(h);
   Console.WriteLine("Handler A called");
}

static void ErrorHandlerB(Handle_uCalc h) {
   var uc = new uCalc(h);
   Console.WriteLine("Handler B called");
}

static void ErrorHandlerC(Handle_uCalc h) {
   var uc = new uCalc(h);
   Console.WriteLine("Handler C called");
}

static void ErrorHandlerD(Handle_uCalc h) {
   var uc = new uCalc(h);
   Console.WriteLine("Handler D called");
}

static void ErrorHandlerE(Handle_uCalc h) {
   var uc = new uCalc(h);
   Console.WriteLine("Handler E called");
}


uc.Error.AddHandler(ErrorHandlerA);
uc.Error.AddHandler(ErrorHandlerB);
uc.Error.AddHandler(ErrorHandlerC);
uc.Error.AddHandler(ErrorHandlerD, -1);
uc.Error.AddHandler(ErrorHandlerE, 3);

Console.WriteLine(uc.EvalStr("10 / "));
				
			
Handler C called
Handler B called
Handler A called
Handler E called
Handler D called
Syntax error
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

void ucalc_call ErrorHandlerA(Handle_uCalc h) {
   auto uc = uCalc(h);
   cout << "Handler A called" << endl;
}

void ucalc_call ErrorHandlerB(Handle_uCalc h) {
   auto uc = uCalc(h);
   cout << "Handler B called" << endl;
}

void ucalc_call ErrorHandlerC(Handle_uCalc h) {
   auto uc = uCalc(h);
   cout << "Handler C called" << endl;
}

void ucalc_call ErrorHandlerD(Handle_uCalc h) {
   auto uc = uCalc(h);
   cout << "Handler D called" << endl;
}

void ucalc_call ErrorHandlerE(Handle_uCalc h) {
   auto uc = uCalc(h);
   cout << "Handler E called" << endl;
}

int main() {
   uCalc uc;
   uc.Error().AddHandler(ErrorHandlerA);
   uc.Error().AddHandler(ErrorHandlerB);
   uc.Error().AddHandler(ErrorHandlerC);
   uc.Error().AddHandler(ErrorHandlerD, -1);
   uc.Error().AddHandler(ErrorHandlerE, 3);

   cout << uc.EvalStr("10 / ") << endl;
}
				
			
Handler C called
Handler B called
Handler A called
Handler E called
Handler D called
Syntax error
				
					Imports System
Imports uCalcSoftware
Public Module Program
   
   Public Sub ErrorHandlerA(ByVal h As Handle_uCalc)
      Dim uc As New uCalc(h)
      Console.WriteLine("Handler A called")
   End Sub
   
   Public Sub ErrorHandlerB(ByVal h As Handle_uCalc)
      Dim uc As New uCalc(h)
      Console.WriteLine("Handler B called")
   End Sub
   
   Public Sub ErrorHandlerC(ByVal h As Handle_uCalc)
      Dim uc As New uCalc(h)
      Console.WriteLine("Handler C called")
   End Sub
   
   Public Sub ErrorHandlerD(ByVal h As Handle_uCalc)
      Dim uc As New uCalc(h)
      Console.WriteLine("Handler D called")
   End Sub
   
   Public Sub ErrorHandlerE(ByVal h As Handle_uCalc)
      Dim uc As New uCalc(h)
      Console.WriteLine("Handler E called")
   End Sub
   
   Public Sub Main()
      Dim uc As New uCalc()
      uc.Error.AddHandler(AddressOf ErrorHandlerA)
      uc.Error.AddHandler(AddressOf ErrorHandlerB)
      uc.Error.AddHandler(AddressOf ErrorHandlerC)
      uc.Error.AddHandler(AddressOf ErrorHandlerD, -1)
      uc.Error.AddHandler(AddressOf ErrorHandlerE, 3)
      
      Console.WriteLine(uc.EvalStr("10 / "))
   End Sub
End Module
				
			
Handler C called
Handler B called
Handler A called
Handler E called
Handler D called
Syntax error
Raises an error with a dynamic message if a validation check fails within a callback.
				
					using uCalcSoftware;

var uc = new uCalc();

static void ValidateValue(uCalc.Callback cb) {
   var val = cb.Arg(1);
   if (val > 100) {
      // The error message includes the problematic value, making it dynamic.
      cb.Error.Raise("Value exceeds maximum of 100. Got: " + val.ToString());
   } else {
      cb.Return(val);
   }
}

uc.DefineFunction("CheckValue(val)", ValidateValue);
Console.WriteLine(uc.EvalStr("CheckValue(50)"));
Console.WriteLine(uc.EvalStr("CheckValue(123)"));
				
			
50
Value exceeds maximum of 100. Got: 123
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

void ucalc_call ValidateValue(uCalcBase::Callback cb) {
   auto val = cb.Arg(1);
   if (val > 100) {
      // The error message includes the problematic value, making it dynamic.
      cb.Error().Raise("Value exceeds maximum of 100. Got: " + to_string((int)val));
   } else {
      cb.Return(val);
   }
}
int main() {
   uCalc uc;
   uc.DefineFunction("CheckValue(val)", ValidateValue);
   cout << uc.EvalStr("CheckValue(50)") << endl;
   cout << uc.EvalStr("CheckValue(123)") << endl;
}
				
			
50
Value exceeds maximum of 100. Got: 123
				
					Imports System
Imports uCalcSoftware
Public Module Program
   
   Public Sub ValidateValue(ByVal cb As uCalc.Callback)
      Dim val = cb.Arg(1)
      If val > 100 Then
         '// The error message includes the problematic value, making it dynamic.
         cb.Error.Raise("Value exceeds maximum of 100. Got: " + val.ToString())
      Else
         cb.Return(val)
      End If
   End Sub
   Public Sub Main()
      Dim uc As New uCalc()
      uc.DefineFunction("CheckValue(val)", AddressOf ValidateValue)
      Console.WriteLine(uc.EvalStr("CheckValue(50)"))
      Console.WriteLine(uc.EvalStr("CheckValue(123)"))
   End Sub
End Module
				
			
50
Value exceeds maximum of 100. Got: 123