Learn how uCalc's grammar can be extended at runtime by defining new operators, keywords, and literal formats.
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.
Most programming languages and parsers have a fixed, unchangeable grammar. The meaning of +, if, or 123 is defined by the language specification and cannot be altered. uCalc is different. Its syntax is dynamic, meaning you can teach the engine new tricks—new operators, new keywords, and even new ways to write numbers—at runtime, without recompiling your application.
This capability transforms uCalc from a simple expression evaluator into a lightweight language workbench, allowing you to create expressive, human-readable Domain-Specific Languages (DSLs) tailored to your specific needs.
There are two primary ways to create custom syntax, depending on your needs:
DefineOperatorThe DefineOperator method is for creating new operators that consist of a single alphanumeric word (like and) or a single symbol (like ##).
For example, let's create a percentof operator:
using uCalcSoftware;
var uc = new uCalc();
// Define a 'percentof' operator with the same precedence as multiplication.
uc.DefineOperator("{percentage} percentof {total} = (percentage / 100) * total", 60);
// The new syntax is immediately available.
Console.WriteLine($"15 percentof 200 is {uc.Eval("15 percentof 200")}");
For more complex, multi-word syntax like 100 USD to EUR, the correct tool is the ExpressionTransformer. It allows you to define flexible, token-aware patterns that are transformed into standard expressions before evaluation.
using uCalcSoftware;
var uc = new uCalc();
// Get the transformer that pre-processes expressions.
var t = uc.ExpressionTransformer;
// Define a rule to handle the multi-word syntax.
t.FromTo("{@Number:amount} USD to EUR", "({@Eval: Double(amount) * 0.92})");
// The new syntax is now available in any expression.
Console.WriteLine($"100 USD is {uc.EvalStr("100 USD to EUR")} EUR");
Extending the syntax to support new literal formats, like C-style hexadecimal numbers (0xFF), is a more advanced process that showcases the power of uCalc's two-stage parsing pipeline. It involves two steps:
0x... Hexadecimal Supportusing uCalcSoftware;
var uc = new uCalc();
// Step 1: Define the lexical rule.
// TokenType::TokenTransform tells the parser that this token needs pre-processing.
uc.ExpressionTokens.Add("0x[0-9a-fA-F]+", TokenType.TokenTransform);
// Step 2: Define the transformation rule.
// This captures the hex digits ('val') and replaces the whole token (e.g., "0xFF")
// with a standard function call that the engine already understands.
uc.TokenTransformer.FromTo("{'0x'}{val:'[0-9a-fA-F]+'}", "BaseConvert('{val}', 16)");
// Step 3: Use the new literal format directly in any expression.
Console.WriteLine(uc.Eval("0xFF + 1")); // Evaluates 255 + 1
This two-step process allows you to integrate complex, custom syntax seamlessly into the uCalc engine.
vs. Parser Generators (ANTLR, Flex/Bison): These are powerful tools, but their grammars are static. To add a new operator or literal, you must modify a grammar file, run a code generator, and recompile your entire application. With uCalc, these changes can be made programmatically at runtime, allowing your application to adapt its own syntax on the fly.
vs. Statically-Compiled Languages (C#, C++): In these languages, operator sets and precedence rules are fixed by the language specification. You cannot invent a multi-word USD to EUR syntax. uCalc gives you complete control over the grammar, allowing you to build languages that are far more expressive and domain-specific.
ID: 1225
using uCalcSoftware;
var uc = new uCalc();
// Use the ExpressionTransformer for multi-word syntax.
var t = uc.ExpressionTransformer;
// Note: Captured variables are passed to @Eval as text
// Double() converts the text to Double a precision value
uc.Format("Result = Format('{:.2f}', Double(Result))", uc.DataTypeOf("Double"));
t.FromTo("{@Number:amount} USD to EUR", "({@Eval: Double(amount) * 0.92})");
t.FromTo("{@Number:amount} EUR to USD", "({@Eval: Double(amount) / 0.92})");
Console.WriteLine($"100 USD is approx. {uc.EvalStr("100 USD to EUR")} EUR");
Console.WriteLine($"120 EUR is approx. {uc.EvalStr("120 EUR to USD")} USD");
100 USD is approx. 92.00 EUR
120 EUR is approx. 130.43 USD using uCalcSoftware; var uc = new uCalc(); // Use the ExpressionTransformer for multi-word syntax. var t = uc.ExpressionTransformer; // Note: Captured variables are passed to @Eval as text // Double() converts the text to Double a precision value uc.Format("Result = Format('{:.2f}', Double(Result))", uc.DataTypeOf("Double")); t.FromTo("{@Number:amount} USD to EUR", "({@Eval: Double(amount) * 0.92})"); t.FromTo("{@Number:amount} EUR to USD", "({@Eval: Double(amount) / 0.92})"); Console.WriteLine($"100 USD is approx. {uc.EvalStr("100 USD to EUR")} EUR"); Console.WriteLine($"120 EUR is approx. {uc.EvalStr("120 EUR to USD")} USD");
#include
#include "uCalc.h"
using namespace std;
using namespace uCalcSoftware;
int main() {
uCalc uc;
// Use the ExpressionTransformer for multi-word syntax.
auto t = uc.ExpressionTransformer();
// Note: Captured variables are passed to @Eval as text
// Double() converts the text to Double a precision value
uc.Format("Result = Format('{:.2f}', Double(Result))", uc.DataTypeOf("Double"));
t.FromTo("{@Number:amount} USD to EUR", "({@Eval: Double(amount) * 0.92})");
t.FromTo("{@Number:amount} EUR to USD", "({@Eval: Double(amount) / 0.92})");
cout << "100 USD is approx. " << uc.EvalStr("100 USD to EUR") << " EUR" << endl;
cout << "120 EUR is approx. " << uc.EvalStr("120 EUR to USD") << " USD" << endl;
}
100 USD is approx. 92.00 EUR
120 EUR is approx. 130.43 USD #include <iostream> #include "uCalc.h" using namespace std; using namespace uCalcSoftware; int main() { uCalc uc; // Use the ExpressionTransformer for multi-word syntax. auto t = uc.ExpressionTransformer(); // Note: Captured variables are passed to @Eval as text // Double() converts the text to Double a precision value uc.Format("Result = Format('{:.2f}', Double(Result))", uc.DataTypeOf("Double")); t.FromTo("{@Number:amount} USD to EUR", "({@Eval: Double(amount) * 0.92})"); t.FromTo("{@Number:amount} EUR to USD", "({@Eval: Double(amount) / 0.92})"); cout << "100 USD is approx. " << uc.EvalStr("100 USD to EUR") << " EUR" << endl; cout << "120 EUR is approx. " << uc.EvalStr("120 EUR to USD") << " USD" << endl; }
Imports System
Imports uCalcSoftware
Public Module Program
Public Sub Main()
Dim uc As New uCalc()
'// Use the ExpressionTransformer for multi-word syntax.
Dim t = uc.ExpressionTransformer
'// Note: Captured variables are passed to @Eval as text
'// Double() converts the text to Double a precision value
uc.Format("Result = Format('{:.2f}', Double(Result))", uc.DataTypeOf("Double"))
t.FromTo("{@Number:amount} USD to EUR", "({@Eval: Double(amount) * 0.92})")
t.FromTo("{@Number:amount} EUR to USD", "({@Eval: Double(amount) / 0.92})")
Console.WriteLine($"100 USD is approx. {uc.EvalStr("100 USD to EUR")} EUR")
Console.WriteLine($"120 EUR is approx. {uc.EvalStr("120 EUR to USD")} USD")
End Sub
End Module
100 USD is approx. 92.00 EUR
120 EUR is approx. 130.43 USD Imports System Imports uCalcSoftware Public Module Program Public Sub Main() Dim uc As New uCalc() '// Use the ExpressionTransformer for multi-word syntax. Dim t = uc.ExpressionTransformer '// Note: Captured variables are passed to @Eval as text '// Double() converts the text to Double a precision value uc.Format("Result = Format('{:.2f}', Double(Result))", uc.DataTypeOf("Double")) t.FromTo("{@Number:amount} USD to EUR", "({@Eval: Double(amount) * 0.92})") t.FromTo("{@Number:amount} EUR to USD", "({@Eval: Double(amount) / 0.92})") Console.WriteLine($"100 USD is approx. {uc.EvalStr("100 USD to EUR")} EUR") Console.WriteLine($"120 EUR is approx. {uc.EvalStr("120 EUR to USD")} USD") End Sub End Module
ID: 1224
using uCalcSoftware;
var uc = new uCalc();
// Define the variables that the operator's expression will use.
uc.DefineVariable("i");
uc.DefineVariable("total");
// Define a new 'sum_to' operator at runtime, with precedence level of 50.
// It uses the built-in ForLoop function to sum numbers into the 'total' variable.
uc.DefineOperator("{start} sum_to {end} = total = 0; ForLoop(i, start, end, 1, total = total + i)", 50);
// Use the new operator. The result is stored in the 'total' variable.
uc.Eval("1 sum_to 5");
Console.WriteLine($"The sum from 1 to 5 is: {uc.Eval("total")}");
The sum from 1 to 5 is: 15 using uCalcSoftware; var uc = new uCalc(); // Define the variables that the operator's expression will use. uc.DefineVariable("i"); uc.DefineVariable("total"); // Define a new 'sum_to' operator at runtime, with precedence level of 50. // It uses the built-in ForLoop function to sum numbers into the 'total' variable. uc.DefineOperator("{start} sum_to {end} = total = 0; ForLoop(i, start, end, 1, total = total + i)", 50); // Use the new operator. The result is stored in the 'total' variable. uc.Eval("1 sum_to 5"); Console.WriteLine($"The sum from 1 to 5 is: {uc.Eval("total")}");
#include
#include "uCalc.h"
using namespace std;
using namespace uCalcSoftware;
int main() {
uCalc uc;
// Define the variables that the operator's expression will use.
uc.DefineVariable("i");
uc.DefineVariable("total");
// Define a new 'sum_to' operator at runtime, with precedence level of 50.
// It uses the built-in ForLoop function to sum numbers into the 'total' variable.
uc.DefineOperator("{start} sum_to {end} = total = 0; ForLoop(i, start, end, 1, total = total + i)", 50);
// Use the new operator. The result is stored in the 'total' variable.
uc.Eval("1 sum_to 5");
cout << "The sum from 1 to 5 is: " << uc.Eval("total") << endl;
}
The sum from 1 to 5 is: 15 #include <iostream> #include "uCalc.h" using namespace std; using namespace uCalcSoftware; int main() { uCalc uc; // Define the variables that the operator's expression will use. uc.DefineVariable("i"); uc.DefineVariable("total"); // Define a new 'sum_to' operator at runtime, with precedence level of 50. // It uses the built-in ForLoop function to sum numbers into the 'total' variable. uc.DefineOperator("{start} sum_to {end} = total = 0; ForLoop(i, start, end, 1, total = total + i)", 50); // Use the new operator. The result is stored in the 'total' variable. uc.Eval("1 sum_to 5"); cout << "The sum from 1 to 5 is: " << uc.Eval("total") << endl; }
Imports System
Imports uCalcSoftware
Public Module Program
Public Sub Main()
Dim uc As New uCalc()
'// Define the variables that the operator's expression will use.
uc.DefineVariable("i")
uc.DefineVariable("total")
'// Define a new 'sum_to' operator at runtime, with precedence level of 50.
'// It uses the built-in ForLoop function to sum numbers into the 'total' variable.
uc.DefineOperator("{start} sum_to {end} = total = 0; ForLoop(i, start, end, 1, total = total + i)", 50)
'// Use the new operator. The result is stored in the 'total' variable.
uc.Eval("1 sum_to 5")
Console.WriteLine($"The sum from 1 to 5 is: {uc.Eval("total")}")
End Sub
End Module
The sum from 1 to 5 is: 15 Imports System Imports uCalcSoftware Public Module Program Public Sub Main() Dim uc As New uCalc() '// Define the variables that the operator's expression will use. uc.DefineVariable("i") uc.DefineVariable("total") '// Define a new 'sum_to' operator at runtime, with precedence level of 50. '// It uses the built-in ForLoop function to sum numbers into the 'total' variable. uc.DefineOperator("{start} sum_to {end} = total = 0; ForLoop(i, start, end, 1, total = total + i)", 50) '// Use the new operator. The result is stored in the 'total' variable. uc.Eval("1 sum_to 5") Console.WriteLine($"The sum from 1 to 5 is: {uc.Eval("total")}") End Sub End Module
ID: 1223
using uCalcSoftware;
var uc = new uCalc();
// 1. Define the lexical rule.
// The regex matches '0x' followed by hex digits.
// The TokenType::TokenTransform tells the parser to pre-process this token.
uc.ExpressionTokens.Add("0x[0-9a-fA-F]+", TokenType.TokenTransform);
// 2. Define the transformation rule.
// This captures the hex digits and replaces the whole token with a call to BaseConvert.
uc.TokenTransformer.FromTo("{'0x'}{val:'[0-9a-fA-F]+'}", "BaseConvert('{val}', 16)");
// 3. Now, the new literal format can be used in expressions.
Console.WriteLine(uc.Eval("0xFF + 0xA")); // 255 + 10
Console.WriteLine(uc.EvalStr("Hex(0x100)")); // Hex(256)
265
100 using uCalcSoftware; var uc = new uCalc(); // 1. Define the lexical rule. // The regex matches '0x' followed by hex digits. // The TokenType::TokenTransform tells the parser to pre-process this token. uc.ExpressionTokens.Add("0x[0-9a-fA-F]+", TokenType.TokenTransform); // 2. Define the transformation rule. // This captures the hex digits and replaces the whole token with a call to BaseConvert. uc.TokenTransformer.FromTo("{'0x'}{val:'[0-9a-fA-F]+'}", "BaseConvert('{val}', 16)"); // 3. Now, the new literal format can be used in expressions. Console.WriteLine(uc.Eval("0xFF + 0xA")); // 255 + 10 Console.WriteLine(uc.EvalStr("Hex(0x100)")); // Hex(256)
#include
#include "uCalc.h"
using namespace std;
using namespace uCalcSoftware;
int main() {
uCalc uc;
// 1. Define the lexical rule.
// The regex matches '0x' followed by hex digits.
// The TokenType::TokenTransform tells the parser to pre-process this token.
uc.ExpressionTokens().Add("0x[0-9a-fA-F]+", TokenType::TokenTransform);
// 2. Define the transformation rule.
// This captures the hex digits and replaces the whole token with a call to BaseConvert.
uc.TokenTransformer().FromTo("{'0x'}{val:'[0-9a-fA-F]+'}", "BaseConvert('{val}', 16)");
// 3. Now, the new literal format can be used in expressions.
cout << uc.Eval("0xFF + 0xA") << endl; // 255 + 10
cout << uc.EvalStr("Hex(0x100)") << endl; // Hex(256)
}
265
100 #include <iostream> #include "uCalc.h" using namespace std; using namespace uCalcSoftware; int main() { uCalc uc; // 1. Define the lexical rule. // The regex matches '0x' followed by hex digits. // The TokenType::TokenTransform tells the parser to pre-process this token. uc.ExpressionTokens().Add("0x[0-9a-fA-F]+", TokenType::TokenTransform); // 2. Define the transformation rule. // This captures the hex digits and replaces the whole token with a call to BaseConvert. uc.TokenTransformer().FromTo("{'0x'}{val:'[0-9a-fA-F]+'}", "BaseConvert('{val}', 16)"); // 3. Now, the new literal format can be used in expressions. cout << uc.Eval("0xFF + 0xA") << endl; // 255 + 10 cout << uc.EvalStr("Hex(0x100)") << endl; // Hex(256) }
Imports System
Imports uCalcSoftware
Public Module Program
Public Sub Main()
Dim uc As New uCalc()
'// 1. Define the lexical rule.
'// The regex matches '0x' followed by hex digits.
'// The TokenType::TokenTransform tells the parser to pre-process this token.
uc.ExpressionTokens.Add("0x[0-9a-fA-F]+", TokenType.TokenTransform)
'// 2. Define the transformation rule.
'// This captures the hex digits and replaces the whole token with a call to BaseConvert.
uc.TokenTransformer.FromTo("{'0x'}{val:'[0-9a-fA-F]+'}", "BaseConvert('{val}', 16)")
'// 3. Now, the new literal format can be used in expressions.
Console.WriteLine(uc.Eval("0xFF + 0xA")) '// 255 + 10
Console.WriteLine(uc.EvalStr("Hex(0x100)")) '// Hex(256)
End Sub
End Module
265
100 Imports System Imports uCalcSoftware Public Module Program Public Sub Main() Dim uc As New uCalc() '// 1. Define the lexical rule. '// The regex matches '0x' followed by hex digits. '// The TokenType::TokenTransform tells the parser to pre-process this token. uc.ExpressionTokens.Add("0x[0-9a-fA-F]+", TokenType.TokenTransform) '// 2. Define the transformation rule. '// This captures the hex digits and replaces the whole token with a call to BaseConvert. uc.TokenTransformer.FromTo("{'0x'}{val:'[0-9a-fA-F]+'}", "BaseConvert('{val}', 16)") '// 3. Now, the new literal format can be used in expressions. Console.WriteLine(uc.Eval("0xFF + 0xA")) '// 255 + 10 Console.WriteLine(uc.EvalStr("Hex(0x100)")) '// Hex(256) End Sub End Module
This page last modified on: