Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Nonanti/mathcore/llms.txt
Use this file to discover all available pages before exploring further.
MathCore includes a built-in recursive-descent parser (powered by nom) that converts human-readable mathematical expression strings into Expr trees. The entry point is MathCore::parse(), a static method that you can call without constructing a MathCore instance. The parser handles the full range of expressions: arithmetic, exponentiation, factorials, absolute values, function calls, and complex number literals — all in one pass, with correct operator precedence baked in.
MathCore::parse()
pub fn parse(expression: &str) -> Result<Expr, MathError>
Parses expression and returns the root Expr node on success, or a MathError::ParseError if the input is malformed or contains trailing characters that cannot be consumed.
use mathcore::MathCore;
// Parse a simple arithmetic expression
let expr = MathCore::parse("2 + 3 * 4")?;
println!("{}", expr); // ((2 + (3 * 4)))
// Parse a symbolic polynomial
let poly = MathCore::parse("x^3 - 2*x + 1")?;
// Parse a function call
let trig = MathCore::parse("sin(pi / 2)")?;
// Parse a complex literal
let z = MathCore::parse("3+4i")?;
Leading and trailing whitespace is stripped automatically. Spaces around operators are optional: "x+1", "x + 1", and "x + 1" all produce the same tree.
Operator Precedence
The parser implements standard mathematical precedence, from lowest to highest:
| Level | Operators | Associativity |
|---|
| 1 | +, - | Left |
| 2 | *, /, % | Left |
| 3 | ^ | Right |
| 4 | ! (postfix) | — |
| 5 | Unary - | — |
| 6 | Primaries (literals, function calls, parentheses, |…|) | — |
Because ^ is right-associative, 2^3^4 is parsed as 2^(3^4), matching conventional mathematical notation.
use mathcore::MathCore;
// Precedence: * binds tighter than +
let expr = MathCore::parse("2 + 3 * 4").unwrap();
// Tree: (2 + (3 * 4)) → evaluates to 14
// Precedence: ^ binds tighter than *
let expr = MathCore::parse("2 * 3 ^ 4").unwrap();
// Tree: (2 * (3 ^ 4)) → evaluates to 162
// Parentheses override precedence
let expr = MathCore::parse("(2 + 3) * 4").unwrap();
// Tree: ((2 + 3) * 4) → evaluates to 20
Supported Operators
Arithmetic Operators
| Operator | Name | Example | Notes |
|---|
+ | Addition | x + y | |
- | Subtraction | x - y | |
* | Multiplication | x * y | |
/ | Division | x / y | Runtime DivisionByZero if denominator is zero |
^ | Exponentiation | x^2 | Right-associative |
% | Modulo | n % 3 | Real numbers only |
! | Factorial | 5! | Postfix; non-negative integers only |
Absolute Value
Absolute value is written with vertical bars, mirroring standard mathematical notation:
let expr = MathCore::parse("|x - 3|").unwrap();
// Produces: Expr::Unary { op: UnaryOp::Abs, expr: (x - 3) }
The bars may contain any expression, including nested function calls:
let expr = MathCore::parse("|sin(x) + cos(x)|").unwrap();
abs(x) (function form) and |x| (bar form) are both supported and produce the same Unary { op: UnaryOp::Abs, … } node.
Supported Functions
Trigonometric
| Syntax | Description | Arity |
|---|
sin(x) | Sine | 1 |
cos(x) | Cosine | 1 |
tan(x) | Tangent | 1 |
let math = mathcore::MathCore::new();
let val = math.evaluate("sin(pi / 6)")?; // Expr::Number(0.5)
sec(x) is parsed as an Expr::Function { name: "sec", … } node, but the engine has no built-in evaluation rule for it. Calling evaluate or calculate on an expression containing sec will return MathError::UndefinedFunction("sec"). Parsing alone succeeds.
Exponential and Logarithmic
| Syntax | Description | Arity |
|---|
exp(x) | Natural exponential eˣ | 1 |
ln(x) | Natural logarithm | 1 |
log(x, b) | Logarithm base b | 2 |
sqrt(x) | Square root | 1 |
MathCore::parse("exp(1)")?; // e¹ ≈ 2.71828
MathCore::parse("ln(1)")?; // 0.0
MathCore::parse("log(100, 10)")?; // 2.0
MathCore::parse("sqrt(9)")?; // 3.0
sqrt of a negative number does not error — instead, the engine promotes the result to a complex value. For example, sqrt(-4) evaluates to Expr::Complex(0 + 2i).
Utility
| Syntax | Description | Arity |
|---|
abs(x) | Absolute value / complex norm | 1 |
min(a, b) | Minimum of numeric arguments | ≥ 1 |
max(a, b) | Maximum of numeric arguments | ≥ 1 |
MathCore::parse("min(3, 1, 4, 1, 5)")?; // parses successfully; evaluates to 1.0
MathCore::parse("max(a, b)")?; // parses successfully; evaluation errors if a or b are symbolic
min and max require all arguments to evaluate to Expr::Number. Passing symbolic arguments returns MathError::InvalidOperation("min requires numeric arguments") at evaluation time.
Mathematical Constants
The following identifiers are resolved as numeric constants by the engine (via Context::with_defaults()):
| Name | Value | Source constant |
|---|
pi | 3.141592653589793 | f64::consts::PI |
e | 2.718281828459045 | f64::consts::E |
tau | 6.283185307179586 | f64::consts::TAU |
The parser treats these as ordinary Symbol nodes; the engine resolves them against the context at evaluation time.
use mathcore::MathCore;
let math = MathCore::new();
let circle = math.calculate("2 * pi * 5").unwrap(); // circumference, r=5
let euler = math.calculate("e^(pi)").unwrap(); // e^π ≈ 23.1407
let turn = math.calculate("tau / 2").unwrap(); // π ≈ 3.14159
Complex Number Literals
Complex numbers are written as <real><sign><imag>i, where both the real and imaginary parts are numeric literals:
MathCore::parse("3+4i")?; // Complex64 { re: 3.0, im: 4.0 }
MathCore::parse("1-2i")?; // Complex64 { re: 1.0, im: -2.0 }
MathCore::parse("0+1i")?; // Complex64 { re: 0.0, im: 1.0 }
The parser checks for the complex form before the plain number form, so 3+4i is consumed as a single token rather than 3 followed by +4i.
Complex literals are strictly <number><+|-><number>i with no spaces. 3 + 4i is not a complex literal — it is parsed as the addition of 3 and the multiplication of 4 by the symbol i.
Expression Syntax Examples
| Input string | Resulting Expr structure |
|---|
"42" | Number(42.0) |
"3.14" | Number(3.14) |
"-2.5e-3" | Number(-0.0025) |
"x" | Symbol("x") |
"var_123" | Symbol("var_123") |
"3+4i" | Complex(3.0 + 4.0i) |
"x + 1" | Binary { Add, Symbol("x"), Number(1.0) } |
"x^2" | Binary { Power, Symbol("x"), Number(2.0) } |
"sin(x)" | Function { "sin", [Symbol("x")] } |
"log(x, 10)" | Function { "log", [Symbol("x"), Number(10.0)] } |
"5!" | Unary { Factorial, Number(5.0) } |
"|x - 1|" | Unary { Abs, Binary { Subtract, Symbol("x"), Number(1.0) } } |
"-(x + 1)" | Unary { Negate, Binary { Add, Symbol("x"), Number(1.0) } } |
"2 + 3 * 4" | Binary { Add, Number(2.0), Binary { Multiply, Number(3.0), Number(4.0) } } |
Variables and Identifiers
Any identifier that is not recognised as a built-in function name becomes an Expr::Symbol. Identifier syntax follows standard programming conventions: it must start with a letter or underscore, and may contain letters, digits, and underscores.
MathCore::parse("x")?; // Symbol("x")
MathCore::parse("alpha")?; // Symbol("alpha")
MathCore::parse("_temp")?; // Symbol("_temp")
MathCore::parse("x_1")?; // Symbol("x_1")
Symbols are resolved at evaluation time, not parse time. You can parse any expression with free variables and inspect the tree, simplify it, differentiate it, and so on. Values are only required when you call evaluate or calculate.
use mathcore::MathCore;
use std::collections::HashMap;
let math = MathCore::new();
let mut vars = HashMap::new();
vars.insert("x".to_string(), 3.0);
let result = math.evaluate_with_vars("x^2 + 2*x + 1", &vars).unwrap();
// → 16.0 (since (3)^2 + 2*(3) + 1 = 16)
Because pi, e, and tau are just Symbol nodes resolved in the default context, you can shadow them by inserting your own values into a custom Context. This is intentional, but use it carefully.