ArgExpr

Method

Retrieves an argument passed by expression, allowing for lazy evaluation within a callback.

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.

Syntax

ArgExpr(int)

Parameters

index
int
The 1-based index of the argument to retrieve.

Return

Expression

Returns the unevaluated Expression object passed as an argument. The callback can then choose to evaluate it, inspect it, or ignore it.

Remarks

⚙️ How It Works

The ArgExpr method is the key to lazy evaluation in uCalc. It retrieves an argument that was passed not as a final value, but as a raw, unevaluated Expression object. This is accomplished by marking a parameter with the ByExpr modifier in its DefineFunction signature.

uc.DefineFunction("MyFunc(ByExpr formula)", MyCallback);

Inside the MyCallback function, calling cb.ArgExpr(1) retrieves the parsed expression tree for whatever argument was passed to formula. The callback then has complete control over when and if that expression is ever evaluated by calling .Evaluate() on the returned object.

💡 The Power of Lazy Evaluation

Passing expressions instead of values unlocks several advanced capabilities:

  • Short-Circuiting Logic: You can create functions that only evaluate the arguments they need. The most common example is a custom IIf function, which evaluates either the then or the else expression, but never both. This prevents side effects and errors (like division by zero) in the unevaluated branch.

  • Custom Control Structures: ByExpr enables you to build your own looping and aggregation functions. The callback can evaluate the same expression multiple times with different variable values, effectively creating custom for loops or sum functions directly within the uCalc engine.

  • Metaprogramming: Because you receive an Expression object, your callback can inspect the argument's raw text via myExpr.Str() before deciding to execute it. This allows for powerful pre-evaluation validation, transformation, or logging.

🆚 Comparative Analysis

  • vs. C# Delegates/Lambdas: Passing an expression with ByExpr is conceptually similar to passing a Func<T> delegate in C#. Both techniques pass a piece of code to be executed later. However, uCalc's approach is designed for dynamic, string-based scripting environments where you can't compile a lambda ahead of time. The ability to inspect the expression's text also gives it a metaprogramming advantage over standard delegates.

  • vs. C++ Function Pointers: While also a way to pass executable logic, ByExpr is a much higher-level and safer construct. The entire lifecycle of the Expression object is managed by the uCalc engine, avoiding the complexities of manual memory management associated with raw function pointers.

Examples

Passing arg ByExpr (delayed lazy eval) and ByHandle

ID: 14

				
					using uCalcSoftware;

var uc = new uCalc();

static void MySum(uCalc.Callback cb) {
   var Total = 0.0;
   var Expr = cb.ArgExpr(1);
   var Start = cb.Arg(2);
   var Finish = cb.Arg(3);
   var Variable = cb.ArgItem(4);

   for (double x = Start; x <= Finish; x++) {
      Variable.Value(x);
      Total += Expr.Evaluate();
   }
   cb.Return(Total);
}

uc.DefineVariable("x");
uc.DefineFunction("Sum(ByExpr Expr, Start, Finish, ByHandle Var)", MySum);
Console.WriteLine(uc.Eval("Sum(x ^ 2, 1, 10, x)"));

				
			
385
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

void ucalc_call MySum(uCalcBase::Callback cb) {
   auto Total = 0.0;
   auto Expr = cb.ArgExpr(1);
   auto Start = cb.Arg(2);
   auto Finish = cb.Arg(3);
   auto Variable = cb.ArgItem(4);

   for (double x = Start; x <= Finish; x++) {
      Variable.Value(x);
      Total += Expr.Evaluate();
   }
   cb.Return(Total);
}
int main() {
   uCalc uc;
   uc.DefineVariable("x");
   uc.DefineFunction("Sum(ByExpr Expr, Start, Finish, ByHandle Var)", MySum);
   cout << uc.Eval("Sum(x ^ 2, 1, 10, x)") << endl;

}
				
			
385
				
					Imports System
Imports uCalcSoftware
Public Module Program
   
   Public Sub MySum(ByVal cb As uCalc.Callback)
      Dim Total = 0.0
      Dim Expr = cb.ArgExpr(1)
      Dim Start = cb.Arg(2)
      Dim Finish = cb.Arg(3)
      Dim Variable = cb.ArgItem(4)
      
      For x  As Double = Start To Finish
         Variable.Value(x)
         Total += Expr.Evaluate()
      Next
      cb.Return(Total)
   End Sub
   Public Sub Main()
      Dim uc As New uCalc()
      uc.DefineVariable("x")
      uc.DefineFunction("Sum(ByExpr Expr, Start, Finish, ByHandle Var)", AddressOf MySum)
      Console.WriteLine(uc.Eval("Sum(x ^ 2, 1, 10, x)"))
      
   End Sub
End Module
				
			
385
Building an Equation Solver with the Parser and Transformer

ID: 1461

				
					using uCalcSoftware;

var uc = new uCalc();

static void EqSolveCb(uCalc.Callback cb) { // Callback based on the Bisection Method
   var expr = cb.ArgExpr(1);     // ByExpr: Unevaluated Expression object (lazy evaluation)
   var a = cb.Arg(2);            // Argument 2: Range Minimum
   var b = cb.Arg(3);            // Argument 3: Range Maximum
   var variable = cb.ArgItem(4); // ByHandle: The variable Item object

   // Helper to update the variable in the uCalc engine and evaluate the expression
   double EvaluateAt(double val) {
      variable.Value(val);   // Push the new test value to the variable
      return expr.Evaluate(); // Evaluate the pre-parsed expression
   }

   // Ensure f(a) < f(b) so we always know which direction to slide the bounds; swap a & b if necessary
   if (EvaluateAt(b) < EvaluateAt(a)) (a, b) = (b, a);

   var midpoint = 0.0;
   var fMidpoint = 0.0;

   // Bisection loop
   for (int i = 0; i <= 100; i++) {
      midpoint = (a + b) / 2;
      fMidpoint = EvaluateAt(midpoint);

      if (Math.Abs(fMidpoint) < 1e-7) break; // Stop if close enough to 0

      // Narrow the bounds (compact logic!)
      if (fMidpoint < 0) a = midpoint; else b = midpoint;
   }

   if (Math.Abs(fMidpoint) > 1e-5) cb.Error.Raise("No solution found in the given range.");
   cb.Return(Math.Round(midpoint, 7)); // Return the final solved value
}

// 1. Define variables that might be used by the end-user
uc.DefineVariable("x");
uc.DefineVariable("MyVar");

// 2. Transformer converts `EqSolve(L = R)` into `EqSolve(L - (R))` & sets defaults before it hits the parser
var t = uc.ExpressionTransformer;
t.FromTo("EqSolve({L} = {R} [[,]for {var}][, {min}, {max}])",
"EqSolve({L} - ({R}), {min}{!min:-10000}, {max}{!max: 10000}, {var}{!var: x})");

// 3. Define the custom function signature
uc.DefineFunction("EqSolve(ByExpr eq, min, max, ByHandle variable)", EqSolveCb);

// --- Demo Executions ---
System.Collections.Generic.List<string> eqList = new() {
   "EqSolve(x + 5 = 125)", // Using default range [-10000,10000]
   "EqSolve(x^2 + 5 = 105)", // Picks one result from the default range
   "EqSolve(x^2 + 5 = 105, 0, 100)", // Restricts to positive root
   "EqSolve(x^2 + 5 = 105, -100, 0)", // Restricts to negative root
   "EqSolve(x^2 + 1000 = 5)", // No existing solution
   "EqSolve(40 + MyVar * 6 = 88, for MyVar)" // Uses custom variable 'MyVar' instead of 'x'
};

foreach(var eq in eqList) {
   Console.WriteLine(uc.ExpressionTransformer.Transform(eq)); // Displays transformed expression
   Console.WriteLine($"Result: {uc.EvalStr(eq)}"); // Returns result
}
				
			
EqSolve(x + 5 - (125), -10000,  10000,  x)
Result: 120
EqSolve(x^2 + 5 - (105), -10000,  10000,  x)
Result: 10
EqSolve(x^2 + 5 - (105), 0, 100,  x)
Result: 10
EqSolve(x^2 + 5 - (105), -100, 0,  x)
Result: -10
EqSolve(x^2 + 1000 - (5), -10000,  10000,  x)
Result: No solution found in the given range.
EqSolve(40 + MyVar * 6 - (88), -10000,  10000, MyVar)
Result: 8
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

void ucalc_call EqSolveCb(uCalcBase::Callback cb) { // Callback based on the Bisection Method
   auto expr = cb.ArgExpr(1);     // ByExpr: Unevaluated Expression object (lazy evaluation)
   auto a = cb.Arg(2);            // Argument 2: Range Minimum
   auto b = cb.Arg(3);            // Argument 3: Range Maximum
   auto variable = cb.ArgItem(4); // ByHandle: The variable Item object

   // Helper to update the variable in the uCalc engine and evaluate the expression
   auto EvaluateAt = [&](double val) -> double {
      variable.Value(val);
      return expr.Evaluate();
   };

   // Ensure f(a) < f(b) so we always know which direction to slide the bounds; swap a & b if necessary
   if (EvaluateAt(b) < EvaluateAt(a)) swap(a, b);

   auto midpoint = 0.0;
   auto fMidpoint = 0.0;

   // Bisection loop
   for (int i = 0; i <= 100; i++) {
      midpoint = (a + b) / 2;
      fMidpoint = EvaluateAt(midpoint);

      if (abs(fMidpoint) < 1e-7) break; // Stop if close enough to 0

      // Narrow the bounds (compact logic!)
      if (fMidpoint < 0) a = midpoint; else b = midpoint;
   }

   if (abs(fMidpoint) > 1e-5) cb.Error().Raise("No solution found in the given range.");
   cb.Return(round(midpoint * 10000000.0) / 10000000.0); // Return the final solved value
}
int main() {
   uCalc uc;
   // 1. Define variables that might be used by the end-user
   uc.DefineVariable("x");
   uc.DefineVariable("MyVar");

   // 2. Transformer converts `EqSolve(L = R)` into `EqSolve(L - (R))` & sets defaults before it hits the parser
   auto t = uc.ExpressionTransformer();
   t.FromTo("EqSolve({L} = {R} [[,]for {var}][, {min}, {max}])",
   "EqSolve({L} - ({R}), {min}{!min:-10000}, {max}{!max: 10000}, {var}{!var: x})");

   // 3. Define the custom function signature
   uc.DefineFunction("EqSolve(ByExpr eq, min, max, ByHandle variable)", EqSolveCb);

   // --- Demo Executions ---
   vector<string> eqList = {
      "EqSolve(x + 5 = 125)", // Using default range [-10000,10000]
      "EqSolve(x^2 + 5 = 105)", // Picks one result from the default range
      "EqSolve(x^2 + 5 = 105, 0, 100)", // Restricts to positive root
      "EqSolve(x^2 + 5 = 105, -100, 0)", // Restricts to negative root
      "EqSolve(x^2 + 1000 = 5)", // No existing solution
      "EqSolve(40 + MyVar * 6 = 88, for MyVar)" // Uses custom variable 'MyVar' instead of 'x'
   };

   for(auto eq : eqList) {
      cout << uc.ExpressionTransformer().Transform(eq) << endl; // Displays transformed expression
      cout << "Result: " << uc.EvalStr(eq) << endl; // Returns result
   }
}
				
			
EqSolve(x + 5 - (125), -10000,  10000,  x)
Result: 120
EqSolve(x^2 + 5 - (105), -10000,  10000,  x)
Result: 10
EqSolve(x^2 + 5 - (105), 0, 100,  x)
Result: 10
EqSolve(x^2 + 5 - (105), -100, 0,  x)
Result: -10
EqSolve(x^2 + 1000 - (5), -10000,  10000,  x)
Result: No solution found in the given range.
EqSolve(40 + MyVar * 6 - (88), -10000,  10000, MyVar)
Result: 8
				
					Imports System
Imports uCalcSoftware
Public Module Program
   
   Public Sub EqSolveCb(ByVal cb As uCalc.Callback)REM // Callback based on the Bisection Method
      Dim expr = cb.ArgExpr(1)     '// ByExpr: Unevaluated Expression object (lazy evaluation)
      Dim a = cb.Arg(2)            '// Argument 2: Range Minimum
      Dim b = cb.Arg(3)            '// Argument 3: Range Maximum
      Dim variable = cb.ArgItem(4) '// ByHandle: The variable Item object
      
      '// Helper to update the variable in the uCalc engine and evaluate the expression
      Dim EvaluateAt = Function (val as Double) As Double
         variable.Value(val)   '// Push the new test value to the variable
         return expr.Evaluate() '// Evaluate the pre-parsed expression
      End Function
      
      '// Ensure f(a) < f(b) so we always know which direction to slide the bounds; swap a & b if necessary
      If EvaluateAt(b) < EvaluateAt(a) Then Dim temp = a : a = b : b = temp
         
         Dim midpoint = 0.0
         Dim fMidpoint = 0.0
         
         '// Bisection loop
         For i  As Integer = 0 To 100
            midpoint = (a + b) / 2
            fMidpoint = EvaluateAt(midpoint)
            
            If Math.Abs(fMidpoint) < 1e-7 Then Exit For REM // Stop if close enough to 0

      REM// Narrow the bounds (compact logic!)
               If fMidpoint < 0 Then a = midpoint Else b = midpoint
                  Next
                  
                  If Math.Abs(fMidpoint) > 1e-5 Then cb.Error.Raise("No solution found in the given range.")
                     cb.Return(Math.Round(midpoint, 7)) '// Return the final solved value 
                  End Sub
                  Public Sub Main()
                     Dim uc As New uCalc()
                     '// 1. Define variables that might be used by the end-user
                     uc.DefineVariable("x")
                     uc.DefineVariable("MyVar")
                     
                     '// 2. Transformer converts `EqSolve(L = R)` into `EqSolve(L - (R))` & sets defaults before it hits the parser
                     Dim t = uc.ExpressionTransformer
                     t.FromTo("EqSolve({L} = {R} [[,]for {var}][, {min}, {max}])",
                     "EqSolve({L} - ({R}), {min}{!min:-10000}, {max}{!max: 10000}, {var}{!var: x})")
                     
                     '// 3. Define the custom function signature
                     uc.DefineFunction("EqSolve(ByExpr eq, min, max, ByHandle variable)", AddressOf EqSolveCb)
                     
                     '// --- Demo Executions ---
                     Dim eqList As New List(Of String) From {
                     "EqSolve(x + 5 = 125)", '// Using default range [-10000,10000]
                     "EqSolve(x^2 + 5 = 105)", '// Picks one result from the default range
                     "EqSolve(x^2 + 5 = 105, 0, 100)", '// Restricts to positive root
                     "EqSolve(x^2 + 5 = 105, -100, 0)", '// Restricts to negative root
                     "EqSolve(x^2 + 1000 = 5)", '// No existing solution
                     "EqSolve(40 + MyVar * 6 = 88, for MyVar)" '// Uses custom variable 'MyVar' instead of 'x'
                     }
                     
                     For Each eq In eqList
                        Console.WriteLine(uc.ExpressionTransformer.Transform(eq)) '// Displays transformed expression
                        Console.WriteLine($"Result: {uc.EvalStr(eq)}") '// Returns result
                     Next
                  End Sub
               End Module
				
			
EqSolve(x + 5 - (125), -10000,  10000,  x)
Result: 120
EqSolve(x^2 + 5 - (105), -10000,  10000,  x)
Result: 10
EqSolve(x^2 + 5 - (105), 0, 100,  x)
Result: 10
EqSolve(x^2 + 5 - (105), -100, 0,  x)
Result: -10
EqSolve(x^2 + 1000 - (5), -10000,  10000,  x)
Result: No solution found in the given range.
EqSolve(40 + MyVar * 6 - (88), -10000,  10000, MyVar)
Result: 8

This page last modified on: 

8/20/2026