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.
Handling Errors
Product:
Class:
Learn to detect, handle, and recover from parsing and evaluation errors using uCalc's state-based and callback-driven systems.
Remarks
💣 Handling Errors
No parser is complete without robust error handling. uCalc offers a powerful and flexible system that differs from the traditional try/catch exception model. It provides two primary methods for dealing with errors: reactive state checking and proactive callback handling.
This tutorial covers the two main types of errors and how to manage them.
1. Types of Errors: Parse-Time vs. Evaluation-Time
Errors in uCalc fall into two categories:
- Parse-Time Errors: These are syntax errors caught before any calculation happens. Examples include mismatched parentheses, unrecognized keywords, or invalid operators. When these occur, you can get detailed information about the location (Error.Location) and symbol (Error.Symbol) that caused the problem.
- Evaluation-Time Errors: These occur after an expression has been successfully parsed, during the actual calculation. Examples include division by zero, invalid arguments passed to a function, or numeric overflow.
2. 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.
After calling a method like EvalStr, you can check if an error occurred:
var result = uc.EvalStr("5 * (10 +"); // Invalid syntax// Check if an error was flaggedif (uc.Error.Code() != ErrorCode.None) {{ Console.WriteLine($"An error occurred: {uc.Error.Message()}");}This approach is simple and effective for many use cases, but it's reactive—you only know about the error after it has already happened and aborted the operation.
3. Proactive: Using an Error Handler Callback
For more advanced control, you can register an error handler using AddHandler. This callback function is automatically invoked the moment an error occurs, giving you the power to intercept, inspect, and even recover from it.
Inside the Handler
Your callback receives a uCalc instance (implicitly via uc in examples), giving you access to the full suite of error-reporting methods:
- Error.Code(): The numeric ErrorCode.
- Error.Message(): The human-readable error string.
- Error.Symbol(): The specific token that caused a parse-time error.
- Error.Location(): The character position of the parse-time error.
Controlling the Outcome with Error.Response
The most powerful feature of an error handler is the ability to control what happens next by setting the Error.Response:
ErrorHandlerResponse::Abort(Default): Halts execution immediately.ErrorHandlerResponse::ReRaise: Passes the error to the next handler in the chain.ErrorHandlerResponse::Resume: Instructs the engine to continue execution as if the error never happened.
4. Recovering from Errors with Resume
The Resume response is what makes uCalc's error handling truly unique. It allows you to fix a problem on the fly and continue.
A common use case is to automatically define variables that a user forgot to declare. The practical example below demonstrates this perfectly.
⚠️ Important: When you Resume, you must resolve the underlying cause of the error. If you don't, the engine will re-encounter the same error, leading to an infinite loop. uCalc has a built-in safety limit to prevent a true infinite loop, which will raise a ReParseOverflow error.
💡 Why uCalc? (Comparative Analysis)
vs.
try/catchExceptions: Traditional exception handling interrupts the program flow and unwinds the call stack, which can be computationally expensive. uCalc's callback system is lightweight and does not unwind the stack. This makes it highly suitable for parsing user input where errors are common and expected, not "exceptional."vs. C-Style Error Codes: Simply returning an error code after a function call requires the developer to write boilerplate
if (error) { ... }checks everywhere. uCalc centralizes this logic into a single, clean handler.
The ability to recover from an error with Resume is the most significant advantage. It elevates error handling from a simple failure-reporting mechanism to a dynamic, interactive recovery system, which is difficult to achieve with standard try/catch blocks.
Examples
A succinct example showing how to reactively check for an error after an operation fails, and how a subsequent successful operation clears the error state.
using uCalcSoftware;
var uc = new uCalc();
// Trigger a syntax error
uc.EvalStr("1 +");
// Check the error state after the fact
if (uc.Error.Code != ErrorCode.None) {
Console.WriteLine($"Error detected: {uc.Error.Message}");
} else {
Console.WriteLine("Success!");
}
// Perform a successful operation, which clears the error state
uc.EvalStr("1 + 1");
if (uc.Error.Code == ErrorCode.None) {
Console.WriteLine("Previous error was cleared by successful operation.");
}
Error detected: Syntax error
Previous error was cleared by successful operation. using uCalcSoftware; var uc = new uCalc(); // Trigger a syntax error uc.EvalStr("1 +"); // Check the error state after the fact if (uc.Error.Code != ErrorCode.None) { Console.WriteLine($"Error detected: {uc.Error.Message}"); } else { Console.WriteLine("Success!"); } // Perform a successful operation, which clears the error state uc.EvalStr("1 + 1"); if (uc.Error.Code == ErrorCode.None) { Console.WriteLine("Previous error was cleared by successful operation."); }
#include
#include "uCalc.h"
using namespace std;
using namespace uCalcSoftware;
int main() {
uCalc uc;
// Trigger a syntax error
uc.EvalStr("1 +");
// Check the error state after the fact
if (uc.Error().Code() != ErrorCode::None) {
cout << "Error detected: " << uc.Error().Message() << endl;
} else {
cout << "Success!" << endl;
}
// Perform a successful operation, which clears the error state
uc.EvalStr("1 + 1");
if (uc.Error().Code() == ErrorCode::None) {
cout << "Previous error was cleared by successful operation." << endl;
}
}
Error detected: Syntax error
Previous error was cleared by successful operation. #include <iostream> #include "uCalc.h" using namespace std; using namespace uCalcSoftware; int main() { uCalc uc; // Trigger a syntax error uc.EvalStr("1 +"); // Check the error state after the fact if (uc.Error().Code() != ErrorCode::None) { cout << "Error detected: " << uc.Error().Message() << endl; } else { cout << "Success!" << endl; } // Perform a successful operation, which clears the error state uc.EvalStr("1 + 1"); if (uc.Error().Code() == ErrorCode::None) { cout << "Previous error was cleared by successful operation." << endl; } }
Imports System
Imports uCalcSoftware
Public Module Program
Public Sub Main()
Dim uc As New uCalc()
'// Trigger a syntax error
uc.EvalStr("1 +")
'// Check the error state after the fact
If uc.Error.Code <> ErrorCode.None Then
Console.WriteLine($"Error detected: {uc.Error.Message}")
Else
Console.WriteLine("Success!")
End If
'// Perform a successful operation, which clears the error state
uc.EvalStr("1 + 1")
If uc.Error.Code = ErrorCode.None Then
Console.WriteLine("Previous error was cleared by successful operation.")
End If
End Sub
End Module
Error detected: Syntax error
Previous error was cleared by successful operation. Imports System Imports uCalcSoftware Public Module Program Public Sub Main() Dim uc As New uCalc() '// Trigger a syntax error uc.EvalStr("1 +") '// Check the error state after the fact If uc.Error.Code <> ErrorCode.None Then Console.WriteLine($"Error detected: {uc.Error.Message}") Else Console.WriteLine("Success!") End If '// Perform a successful operation, which clears the error state uc.EvalStr("1 + 1") If uc.Error.Code = ErrorCode.None Then Console.WriteLine("Previous error was cleared by successful operation.") End If End Sub End Module
A practical example of a proactive error handler that automatically defines undeclared variables on the fly, allowing the expression to successfully resume execution.
using uCalcSoftware;
var uc = new uCalc();
// This handler automatically defines variables when they are first used.
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) {
Console.WriteLine($"Handler: '{uc.Error.Symbol}' is undefined. Defining it now.");
uc.DefineVariable(uc.Error.Symbol);
// Tell the engine to resume the operation
uc.Error.Response = ErrorHandlerResponse.Resume;
}
}
uc.Error.AddHandler(AutoDefineHandler);
// 'my_var' doesn't exist yet, but the handler will create it.
var result = uc.EvalStr("my_var = 100; my_var = my_var * 2");
Console.WriteLine($"Final result: {result}");
Handler: 'my_var' is undefined. Defining it now.
Final result: 200 using uCalcSoftware; var uc = new uCalc(); // This handler automatically defines variables when they are first used. 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) { Console.WriteLine($"Handler: '{uc.Error.Symbol}' is undefined. Defining it now."); uc.DefineVariable(uc.Error.Symbol); // Tell the engine to resume the operation uc.Error.Response = ErrorHandlerResponse.Resume; } } uc.Error.AddHandler(AutoDefineHandler); // 'my_var' doesn't exist yet, but the handler will create it. var result = uc.EvalStr("my_var = 100; my_var = my_var * 2"); Console.WriteLine($"Final result: {result}");
#include
#include "uCalc.h"
using namespace std;
using namespace uCalcSoftware;
// This handler automatically defines variables when they are first used.
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) {
cout << "Handler: '" << uc.Error().Symbol() << "' is undefined. Defining it now." << endl;
uc.DefineVariable(uc.Error().Symbol());
// Tell the engine to resume the operation
uc.Error().Response(ErrorHandlerResponse::Resume);
}
}
int main() {
uCalc uc;
uc.Error().AddHandler(AutoDefineHandler);
// 'my_var' doesn't exist yet, but the handler will create it.
auto result = uc.EvalStr("my_var = 100; my_var = my_var * 2");
cout << "Final result: " << result << endl;
}
Handler: 'my_var' is undefined. Defining it now.
Final result: 200 #include <iostream> #include "uCalc.h" using namespace std; using namespace uCalcSoftware; // This handler automatically defines variables when they are first used. 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) { cout << "Handler: '" << uc.Error().Symbol() << "' is undefined. Defining it now." << endl; uc.DefineVariable(uc.Error().Symbol()); // Tell the engine to resume the operation uc.Error().Response(ErrorHandlerResponse::Resume); } } int main() { uCalc uc; uc.Error().AddHandler(AutoDefineHandler); // 'my_var' doesn't exist yet, but the handler will create it. auto result = uc.EvalStr("my_var = 100; my_var = my_var * 2"); cout << "Final result: " << result << endl; }
Imports System
Imports uCalcSoftware
Public Module Program
'// This handler automatically defines variables when they are first used.
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
Console.WriteLine($"Handler: '{uc.Error.Symbol}' is undefined. Defining it now.")
uc.DefineVariable(uc.Error.Symbol)
'// Tell the engine to resume the operation
uc.Error.Response = ErrorHandlerResponse.Resume
End If
End Sub
Public Sub Main()
Dim uc As New uCalc()
uc.Error.AddHandler(AddressOf AutoDefineHandler)
'// 'my_var' doesn't exist yet, but the handler will create it.
Dim result = uc.EvalStr("my_var = 100; my_var = my_var * 2")
Console.WriteLine($"Final result: {result}")
End Sub
End Module
Handler: 'my_var' is undefined. Defining it now.
Final result: 200 Imports System Imports uCalcSoftware Public Module Program '// This handler automatically defines variables when they are first used. 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 Console.WriteLine($"Handler: '{uc.Error.Symbol}' is undefined. Defining it now.") uc.DefineVariable(uc.Error.Symbol) '// Tell the engine to resume the operation uc.Error.Response = ErrorHandlerResponse.Resume End If End Sub Public Sub Main() Dim uc As New uCalc() uc.Error.AddHandler(AddressOf AutoDefineHandler) '// 'my_var' doesn't exist yet, but the handler will create it. Dim result = uc.EvalStr("my_var = 100; my_var = my_var * 2") Console.WriteLine($"Final result: {result}") End Sub End Module