ProCalc.NET 1.2.0

dotnet add package ProCalc.NET --version 1.2.0
                    
NuGet\Install-Package ProCalc.NET -Version 1.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ProCalc.NET" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ProCalc.NET" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="ProCalc.NET" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ProCalc.NET --version 1.2.0
                    
#r "nuget: ProCalc.NET, 1.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ProCalc.NET@1.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ProCalc.NET&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=ProCalc.NET&version=1.2.0
                    
Install as a Cake Tool

ProCalc.NET

ProCalc.NET is a math expression language evaluation engine for the .NET platform. The package lets you compile textual expressions into an execution tree and then run them many times, mixing symbolic computation, exact integer arithmetic, exact fractions, floating-point numbers, complex numbers, "big numbers" (BigNum), vectors, matrices, lists and strings. The engine also supports:

  • automatic type promotion on overflow (e.g. Int -> LargeNumber, Int^(-n) -> IntFraction),
  • defining variables and user functions directly inside an expression,
  • call parameters (_0, _1, ...) – for passing data from the host into the expression,
  • external variables ($name) resolved at runtime by a resolver supplied by the application,
  • saving and loading state (variables + user functions) to BinaryWriter/BinaryReader.

The library is cross-platform and targets: .NET Framework 4.7.2, .NET Framework 4.8, .NET 6, .NET 8, .NET 10.


Table of contents

  1. Simple usage example
  2. Feature list
  3. Defining custom functions and macros
  4. Parameters and variables
  5. Host integration – runtime parameters

1. Simple usage example

using ProCalc.NET;
using ProCalc.NET.Numerics;

var core = new ProCalcCore();

CompiledExpression expr = core.Compile("2 + 3 * 4");
BaseNumeric result = core.Execute(expr);

Console.WriteLine(result.AsString()); // "14"

Compilation is done once, and execution (Execute) can be invoked any number of times – including with different sets of call parameters and different external variable resolvers.


2. Feature list

2.1 Supported types

Type .NET class Syntax / Example expression Description
Integer IntNumeric 42, -7 Signed 64-bit integer.
Integer in other base IntNumeric 0b1010, 0o17, 0d99, 0hFF Binary, octal, decimal, hexadecimal literal.
Floating-point FloatNumeric 3.14, 1.5e-3, 5f double; the f suffix forces float type for an integer literal.
Integer fraction IntFractionNumeric result of 1/3, 0.25 (parsed exactly) Exact numerator/denominator fraction (e.g. 1/3 instead of 0.3333…).
Complex number ComplexNumeric 2 + 3i, 1.5e2i Real + imaginary part.
Big number (BigNum) LargeNumberNumeric 12345678901234567890L Arbitrary-size integer; result of automatic promotion on overflow.
Boolean BoolNumeric true, false Logical constant.
Degrees/minutes/seconds (DMS) 12:30, 12:30:45.5 Angular syntax.
String StringNumeric "hello", "a""b" A quote inside a literal is doubled.
List ListNumeric {1, 2, 3} A list of values of any type.
Vector VectorNumeric (1, 2, 3) A vector of scalar values.
Matrix MatrixNumeric [1, 2; 3, 4] A matrix; rows separated by ;, columns by ,.

Indexing uses the value[index, ...] syntax, e.g. "abcdef"[2] or m[1, 2].

Type promotion

The engine does not return an overflow where the result can be represented exactly:

  • Int + Int, Int - Int, Int * Int outside the Int64 range → LargeNumberNumeric,
  • Int \ Int (integer division) outside the range → LargeNumberNumeric,
  • Int ^ Int with a negative exponent → IntFractionNumeric (when representable),
  • Int ^ Int with a large result → LargeNumberNumeric.

2.2 Operators

Operator Meaning
+ Addition / string concatenation
- Subtraction / unary minus
* Multiplication
/ Division (preserves exactness when possible)
\ Integer division
% Modulo
^ Exponentiation
<< >> Bitwise shift
& \| # Bitwise / logical AND, OR, XOR
! Logical negation (unary)
< > <= >= == != Comparisons

Operator precedence (from highest): & | # << >> > ^ > / \ % > * > + - > comparisons.

2.3 Built-in functions

The built-in function set (Arithmetics.Functions):

  • Trigonometry: sin, cos, tan, ctg, arcsin, arccos, arctan, arcctg
  • Arithmetic: abs, round, trunc, frac, sqrt, sqr, ln
  • Strings / lists / matrices: length, copy, pos, insert, delete, uppercase, lowercase, strtoint, inttostr
  • Vectors: dot (dot product), distance, project

Example:

core.Execute(core.Compile("sin(pi / 2) + sqrt(2)^2"));   // 3
core.Execute(core.Compile("uppercase(\"hello\")"));      // "HELLO"
core.Execute(core.Compile("dot((1,2,3), (4,5,6))"));     // 32

2.4 Built-in constants

Constant Value
pi System.Math.PI
e System.Math.E
true logical true value
false logical false value

3. Defining custom functions and macros

ProCalc.NET lets you define user functions directly in the expression language. A definition is just an assignment: the function header is on the left-hand side, and the expression body is on the right.

var core = new ProCalcCore();

// Definition: f(x, y) = x^2 + y^2
core.Execute(core.Compile("f(x, y) = x^2 + y^2"));

// Usage:
var result = core.Execute(core.Compile("f(3, 4)"));
Console.WriteLine(result.AsString()); // "25"

Functions are stored in an internal function table and persist between Execute calls within the same ProCalcCore instance. Registered functions (and variables) can be persisted:

using (var fs = File.Create("state.bin"))
using (var bw = new BinaryWriter(fs))
{
    core.SaveState(bw);
}

using (var fs = File.OpenRead("state.bin"))
using (var br = new BinaryReader(fs))
{
    core.LoadState(br);
}

core.ClearState(); // remove all definitions

If evaluation of an expression containing a function/variable definition is interrupted by an exception, the definition is not persisted (the engine uses a pending/accept/reject mechanism).

Disabling definitions in a given call is done via the allowControlStatements: false parameter:

core.Execute(core.Compile("f(x) = x + 1"), allowControlStatements: false);
// -> RuntimeException: control statements not allowed

This allows you to expose the engine as a safe "expression calculator" without the ability to modify its state (e.g. for expressions entered by an end user).


4. Parameters and variables

ProCalc.NET distinguishes four kinds of "named values", each with a different lifetime:

Kind Expression syntax Value source Lifetime
Built-in constant pi, e, true Arithmetics built-in, immutable
Engine variable x core.SetVariableValue(...) or x = ... in an expression persisted in ProCalcCore
Call parameter (runtime) _0, _1, ... callParams passed from code to core.Execute(...) only during Execute
External variable $name BaseExternalVariableResolver.ResolveVariable(name) only during Execute

4.1 Variables inside an expression

Variable value is stored in ProCalcCore state, can be persisted and also used in later expressions or statements.

Their value can be set using a control statement.

core.Execute(core.Compile("a = 10"));
core.Execute(core.Compile("b = a * 2"));
var v = core.Execute(core.Compile("a + b")); // 30

4.2 Variables set from the host

Alternative way of setting variable value, from code.

core.SetVariableValue("radius", new FloatNumeric(2.5));
var area = core.Execute(core.Compile("pi * radius^2"));

BaseNumeric current = core.GetVariableValue("radius");

4.3 Call parameters (_0, _1, …)

Parameters named _0, _1, … are data passed from the host into the expression during an Execute call. Inside a user function the same names refer to the function call arguments.

var expr = core.Compile("_0 * _1 + _2");
var result = core.Execute(expr, new List<BaseNumeric>
{
    new IntNumeric(3),
    new IntNumeric(4),
    new IntNumeric(1),
}); // 13

4.4 External variables ($name)

A variable prefixed with $ is resolved at runtime by an object inheriting from BaseExternalVariableResolver. This lets you dynamically plug in any host data (e.g. spreadsheet cells, form fields, database values) – without having to pre-register them in the engine.

using ProCalc.NET.Resolvers;
using ProCalc.NET.Numerics;

class DictionaryVariableResolver : BaseExternalVariableResolver
{
    private readonly IDictionary<string, BaseNumeric> values;
    public DictionaryVariableResolver(IDictionary<string, BaseNumeric> values) => this.values = values;

    public override BaseNumeric ResolveVariable(string externalVariableName)
        => values.TryGetValue(externalVariableName, out var v) ? v : null;
}

5. Host integration – runtime parameters

The following example shows a typical integration scenario: an expression is compiled once and then executed many times with different call parameters and with an external variable resolver supplying host data.

using System;
using System.Collections.Generic;
using ProCalc.NET;
using ProCalc.NET.Numerics;
using ProCalc.NET.Resolvers;

// 1. External variable resolver – plugs in host data.
public class HostVariableResolver : BaseExternalVariableResolver
{
    private readonly Dictionary<string, BaseNumeric> data;
    public HostVariableResolver(Dictionary<string, BaseNumeric> data) => this.data = data;

    public override BaseNumeric ResolveVariable(string name)
        => data.TryGetValue(name, out var v) ? v : null;
}

public static class PricingEngine
{
    public static void Run()
    {
        var core = new ProCalcCore();

        // 2. Business constants as engine variables.
        core.SetVariableValue("vatRate", new FloatNumeric(0.23));

        // 3. User function – compiled once, used many times.
        core.Execute(core.Compile("gross(net, rate) = net * (1 + rate)"));

        // 4. The expression uses:
        //    - call parameters:   _0 (net price), _1 (quantity)
        //    - external variable: $discount (fetched from the host on demand)
        //    - engine variable:   vatRate
        //    - user function:     gross(...)
        CompiledExpression formula = core.Compile(
            "gross(_0 * _1 * (1 - $discount), vatRate)");

        // 5. The host provides runtime data per call.
        var hostData = new Dictionary<string, BaseNumeric>
        {
            ["discount"] = new FloatNumeric(0.10), // 10% discount
        };
        var resolver = new HostVariableResolver(hostData);

        // 6. Multiple executions of the same compiled formula.
        foreach (var order in new[]
        {
            (price: 100.0, qty: 2),
            (price:  49.99, qty: 5),
            (price: 250.0, qty: 1),
        })
        {
            var args = new List<BaseNumeric>
            {
                new FloatNumeric(order.price),
                new IntNumeric(order.qty),
            };

            // allowControlStatements: false -> user expressions cannot
            // modify the engine state (define variables/functions).
            BaseNumeric total = core.Execute(
                formula,
                callParams: args,
                allowControlStatements: false,
                externalVariableResolver: resolver);

            Console.WriteLine($"{order.qty} x {order.price} => {total.AsString()}");
        }
    }
}

In this pattern the role of each mechanism is as follows:

  • Compile is invoked once – the AST is reusable.
  • callParams (_0, _1, …) pass input data for a specific call.
  • BaseExternalVariableResolver lets the engine reach into the host for $xxx names, without having to copy data into the engine.
  • SetVariableValue / function definitions in expressions let you keep business state (rates, formulas) inside a ProCalcCore instance.
  • allowControlStatements: false protects the call against unwanted modification of the engine state (typical for expressions originating from end users).
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Framework net472 is compatible.  net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.7.2

  • .NETFramework 4.8

  • net10.0

  • net8.0

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.0 91 5/13/2026
1.1.3 253 8/13/2024
1.1.2 389 6/14/2023
1.1.1 303 6/13/2023
1.1.0 510 11/22/2022
1.0.6 577 5/5/2021
1.0.5 508 4/29/2021
1.0.4 508 4/29/2021
1.0.3 509 4/29/2021
1.0.1 518 4/28/2021
1.0.0 481 4/28/2021

Bugfixes, calculation quality improvements, arithmetics is fully unit-tested.