Version: dev

evaluate

Stable function · Expressions

Tier: stable · Kind: function · Topic: Expressions

JuliaTypeScriptPythonRustGo
not exportednot exportedevaluateevaluateEvaluate

Julia

Julia does not export this function.

TypeScript

TypeScript does not export this function.

Python

evaluate(expr: Expr, bindings: dict[str, float]) -> float

Evaluate a scalar AST expression against a dict of float variable bindings.

This is the official ESS Python runner entry point (the public API imported as from earthsci_ast import evaluate). It wraps eval_expr with an empty-state EvalContext so callers don’t need to construct one themselves.

bindings maps free-variable names to their numeric values. The special key "t" supplies the simulation time (defaults to 0.0 if absent). Returns the scalar result as a Python float. Raises NumpyInterpreterError if any variable in expr is not in bindings. The whole expression is walked first (refuse_unevaluable_operators), so an operator this interpreter cannot evaluate is refused before any of it is evaluated.

Source

Example (evaluate)

line = ExprNode(op="+", args=[ExprNode(op="*", args=[2, "x"]), 1])
assert earthsci_ast.evaluate(line, {"x": 3.0}) == 7.0

From docs/api/examples/python/expressions.py

Rust

pub fn evaluate(expr: &Expr, bindings: &HashMap<String, f64>) -> Result<f64, Vec<String>>

Evaluate a scalar AST expression against a map of float variable bindings.

This is the official ESS Rust runner entry point (the public API exported as earthsci_ast::evaluate). The array runtime’s per-cell oracle — the binding’s one interpreter — does the evaluation, so a single expression means here exactly what it means inside a simulated model.

bindings maps free-variable names to their f64 values. The special key "t" supplies the simulation time (defaults to 0.0 if absent). Returns Ok(f64) on success, or Err(Vec<String>) listing unbound variable names if any variable in expr is missing from bindings, or carrying the diagnostic (unlowered_operator / unevaluable_operator, with the op named) for an operator this evaluator cannot evaluate — including the array, tensor and geometry ops, which have no value over scalar bindings. That check runs over the whole expression before any of it is evaluated (esm-spec §9.6.6). Genuine MATH errors (division by zero, the log of a non-positive number) are not errors: they come back as NaN or ±inf in the Ok branch, which is the answer.

Examples

use earthsci_ast::{Expr, evaluate};
use std::collections::HashMap;

let expr = Expr::Variable("x".to_string());
let mut bindings = HashMap::new();
bindings.insert("x".to_string(), 3.14_f64);
let result = evaluate(&expr, &bindings).unwrap();
assert!((result - 3.14).abs() < 1e-10);

Source · Rust docs

Example (evaluate)

let expr = parse_expression("k * A - x")?;
let bindings = HashMap::from([
    ("k".to_string(), 2.0),
    ("A".to_string(), 3.0),
    ("x".to_string(), 1.0),
]);
assert_eq!(evaluate(&expr, &bindings), Ok(5.0));

// A missing binding is reported by name, not guessed.
let partial = HashMap::from([("k".to_string(), 2.0)]);
let mut unbound = evaluate(&expr, &partial).unwrap_err();
unbound.sort();
assert_eq!(unbound, ["A", "x"]);

assert!(contains(&expr, "A") && !contains(&expr, "B"));
assert_eq!(to_ascii(&simplify(&parse_expression("x * 1 + 0")?)), "x");

From docs/api/examples/rust/expressions.rs

Go

func Evaluate(expr Expression, bindings map[string]float64) (float64, error)

Evaluate numerically evaluates an expression with variable bindings.

A boolean leaf reduces to the evaluator’s float encoding (1.0 / 0.0), so a literal predicate such as and(true, false) is evaluable. A typed-nil *ExprNode is reported as an error rather than dereferenced (no audit ID; found while fixing G3).

The whole expression is walked BEFORE any of it is evaluated (esm-spec §9.6.6): an operator this evaluator cannot evaluate is refused up front with unlowered_operator or unevaluable_operator, including one sitting in an ifelse branch that evaluation would not have taken.

Source · Go docs

Example (expressions)

parsed, err := esm.ParseExpression("k * (T_wall - T_air)")
if err != nil {
	log.Fatal(err)
}
fmt.Println(sortedKeys(esm.FreeVariables(parsed)))

// The same tree, built directly: an operator node is an ExprNode, a
// variable is a string and a number is a float64.
built := esm.ExprNode{Op: "*", Args: []any{
	"k",
	esm.ExprNode{Op: "-", Args: []any{"T_wall", "T_air"}},
}}
fmt.Println(esm.ToASCII(built) == esm.ToASCII(parsed))

substituted, err := esm.Substitute(parsed, map[string]esm.Expression{
	"T_wall": 290.0,
	"k":      esm.ExprNode{Op: "/", Args: []any{1.0, "tau"}},
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(esm.ToASCII(substituted))
fmt.Println(sortedKeys(esm.FreeVariables(substituted)))

value, err := esm.Evaluate(substituted, map[string]float64{"tau": 20, "T_air": 280})
if err != nil {
	log.Fatal(err)
}
fmt.Println(value)

From docs/api/examples/go/expressions_test.go