[][src]Crate arithmetic_eval

Simple interpreter for ASTs produced by arithmetic-parser.

Assumptions

  • There is only one numeric type, which is complete w.r.t. all arithmetic operations. This is expressed via type constraints, in Interpreter.
  • Arithmetic operations are assumed to be infallible; panics during their execution are not caught by the interpreter.
  • Grammar literals are directly parsed to the aforementioned numeric type.

These assumptions do not hold for some grammars parsed by the crate. For example, finite cyclic groups have two types (scalars and group elements) and thus cannot be effectively interpreted.

Semantics

  • All variables are immutable. Re-declaring a var shadows the previous declaration.
  • Functions are first-class (in fact, a function is just a variant of the Value enum).
  • Functions can capture variables (including other functions). All captures are by value.
  • Arithmetic operations are defined on primitive vars and tuples. With tuples, operations are performed per-element. Binary operations require tuples of the same size, or a tuple and a primitive value. As an example, (1, 2) + 3 and (2, 3) / (4, 5) are valid, but (1, 2) * (3, 4, 5) isn't.
  • Methods are considered syntactic sugar for functions, with the method receiver considered the first function argument. For example, (1, 2).map(sin) is equivalent to map((1, 2), sin).
  • No type checks are performed before evaluation.
  • Type annotations are completely ignored. This means that the interpreter may execute code that is incorrect with annotations (e.g., assignment of a tuple to a variable which is annotated to have a numeric type).

Examples

use arithmetic_parser::{grammars::F32Grammar, Grammar, GrammarExt, Span};
use arithmetic_eval::{fns, Interpreter, Value};

const MIN: fns::Binary<fn(f32, f32) -> f32> =
    fns::Binary::new(|x, y| if x < y { x } else { y });
const MAX: fns::Binary<fn(f32, f32) -> f32> =
    fns::Binary::new(|x, y| if x > y { x } else { y });

let mut context = Interpreter::new();
// Add some native functions to the interpreter.
context
    .insert_native_fn("min", MIN)
    .insert_native_fn("max", MAX)
    .insert_native_fn("assert", fns::Assert);

let program = r#"
    # The interpreter supports all parser features, including
    # function definitions, tuples and blocks.
    order = |x, y| (min(x, y), max(x, y));
    assert(order(0.5, -1) == (-1, 0.5));
    (_, M) = order(3^2, { x = 3; x + 5 });
    M
"#;
let program = F32Grammar::parse_statements(Span::new(program)).unwrap();
let ret = context.evaluate(&program).unwrap();
assert_eq!(ret, Value::Number(9.0));

Modules

fns

Standard functions for the interpreter.

Structs

Backtrace

Call backtrace.

BacktraceElement

Function call.

CallContext

Opaque context for native calls.

ErrorWithBacktrace

Error with the associated backtrace.

ExecutableModule

Executable module together with its imports.

InterpretedFn

Function defined within the interpreter.

Interpreter

Simple interpreter for arithmetic expressions.

SpannedEvalError

Evaluation error together with one or more relevant code spans.

Enums

AuxErrorInfo

Auxiliary information about error.

EvalError

Errors that can occur during interpreting expressions and statements.

Function

Function definition. Functions can be either native (defined in the Rust code) or defined in the interpreter.

RepeatedAssignmentContext

Context for EvalError::RepeatedAssignment.

TupleLenMismatchContext

Context for EvalError::TupleLenMismatch.

Value

Values produced by expressions during their interpretation.

Traits

NativeFn

Function on zero or more Values.

Type Definitions

EvalResult

Result of an expression evaluation.

SpannedValue

Value together with a span that has produced it.