Rust API Reference: Expression System

This chapter documents the core types and functions of the oCAS expression system: the Arena allocator, symbols, expression nodes, construction contexts, parsing, normalization, and pattern matching.


Core Type Overview

The core type hierarchy of the expression system is:

Arena (bump allocator)
 └── AtomArena (hash-consing constructor)
      └── Atom (Copy handle)
           └── AtomNode (enum: Num/Var/Fun/Add/Mul/Pow)
                └── Symbol (interned string)

Design invariants: Atom is a Copy arena handle pointing to an AtomNode allocated in the Arena. Because AtomArena uses hash-consing (structurally identical subexpressions return the same pointer within the same AtomArena), structural equality is equivalent to pointer equality — the result of == agrees with pointer comparison. Note the implementation detail: Atom's PartialEq is a derived structural comparison (recursively comparing AtomNodes); hash-consing guarantees its truth value agrees with pointer equality rather than comparing pointers directly. Subtraction and division are desugared at parse time: $x - y$ becomes Add([x, Mul([Num(-1), y])]), and $x / y$ becomes Mul([x, Pow(y, Num(-1))]).


Arena

#![allow(unused)]
fn main() {
use ocas_core::arena::Arena;
}

Arena is a bump allocator that provides bulk memory management for expression nodes. All expression nodes are allocated in an Arena, and the entire memory is freed at once when the Arena is dropped. The current version does not run destructors, so it is only safe to store Copy types.

Arena::new

#![allow(unused)]
fn main() {
pub fn new() -> Self
}

Function: Creates an Arena using the default block size (64 KiB).

Parameters: None.

Return value: Arena.

Example:

#![allow(unused)]
fn main() {
use ocas_core::arena::Arena;

let arena = Arena::new();
let value = arena.allocate_with(|| 42);
assert_eq!(*value, 42);
}

Arena::with_capacity

#![allow(unused)]
fn main() {
pub fn with_capacity(block_size: usize) -> Self
}

Function: Creates an Arena using the given block size.

Parameters:

ParameterTypeDescription
block_sizeusizeSize of each memory block, in bytes.

Return value: Arena.

Arena::allocate_with

#![allow(unused)]
fn main() {
pub fn allocate_with<T>(&self, init: impl FnOnce() -> T) -> &mut T
}

Function: Allocates a value in the Arena, constructed via the closure, and returns a mutable reference bound to the Arena's lifetime.

Parameters:

ParameterTypeDescription
initimpl FnOnce() -> TClosure that constructs the value.

Return value: &mut T.

Errors: Panics if T has zero size.

Example:

#![allow(unused)]
fn main() {
use ocas_core::arena::Arena;

let arena = Arena::new();
let value = arena.allocate_with(|| "hello");
assert_eq!(*value, "hello");
}

Arena::allocate_slice

#![allow(unused)]
fn main() {
pub fn allocate_slice<T: Copy>(&self, values: &[T]) -> &[T]
}

Function: Copies a slice into the Arena and returns a slice reference bound to the Arena's lifetime. An empty slice returns &[].

Parameters:

ParameterTypeDescription
values&[T]Slice to allocate, T: Copy.

Return value: &[T].

Example:

#![allow(unused)]
fn main() {
use ocas_core::arena::Arena;

let arena = Arena::new();
let data = [1, 2, 3];
let slice = arena.allocate_slice(&data);
assert_eq!(slice, &[1, 2, 3]);
}

Arena::reset

#![allow(unused)]
fn main() {
pub fn reset(&self)
}

Function: Resets the Arena — keeps the first memory block and resets the offset, freeing the remaining blocks. After a reset, any references allocated previously must not be used.

Parameters: None.

Return value: None.

Example:

#![allow(unused)]
fn main() {
use ocas_core::arena::Arena;

let arena = Arena::new();
let _ = arena.allocate_with(|| 1);
arena.reset();
let value = arena.allocate_with(|| 2);
assert_eq!(*value, 2);
}

Arena::chunk_count

#![allow(unused)]
fn main() {
pub fn chunk_count(&self) -> usize
}

Function: Returns the number of memory blocks currently held by the Arena.

Parameters: None.

Return value: usize.


Symbol

#![allow(unused)]
fn main() {
use ocas_atom::Symbol;
}
#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Symbol(&'static str);
}

Symbol is a globally interned symbol name used for variable names, function names, and constant names. Symbols with identical contents point to the same static memory within the process, making comparisons $O(1)$ pointer comparisons. Symbol implements Copy.

Symbol::new

#![allow(unused)]
fn main() {
pub fn new(name: &str) -> Self
}

Function: Creates or looks up an existing Symbol. The string is interned on first call; subsequent calls return the existing Symbol.

Parameters:

ParameterTypeDescription
name&strThe symbol name.

Return value: Symbol.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::Symbol;

let x = Symbol::new("x");
let also_x = Symbol::new("x");
assert_eq!(x, also_x);
assert_eq!(x.as_str(), "x");
}

Symbol::as_str

#![allow(unused)]
fn main() {
pub fn as_str(&self) -> &str
}

Function: Returns the string slice of the symbol.

Parameters: None.

Return value: &str.

See also: Atom, AtomArena


Atom

#![allow(unused)]
fn main() {
use ocas_atom::Atom;
}
#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Atom<'a>(&'a AtomNode<'a>);
}

Atom is a lightweight Copy handle to an AtomNode in the Arena. Copying an Atom only copies a pointer, not the underlying data. Thanks to hash-consing, the truth value of a == b agrees with pointer comparison (within the same AtomArena); the implementation is a derived structural comparison.

Atom implements Display, with the following output formats:

  • Num(42) → 42
  • Var("x") → x
  • Fun("sin", [x]) → sin(x)
  • Add([x, y]) → x + y (subexpressions are parenthesized unless they are leaf nodes)
  • Mul([x, y]) → x*y
  • Pow(x, n) → x^n (base/exponent parenthesized unless they are leaves)

Atom::node

#![allow(unused)]
fn main() {
pub fn node(&self) -> &'a AtomNode<'a>
}

Function: Returns a reference to the underlying node data. Used for pattern-matching the expression structure.

Parameters: None.

Return value: &'a AtomNode<'a>.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::{AtomArena, AtomNode};
use ocas_core::arena::Arena;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let x = ctx.var("x");
assert!(matches!(x.node(), AtomNode::Var(_)));
}

Atom::children

#![allow(unused)]
fn main() {
pub fn children(&self) -> &'a [Atom<'a>]
}

Function: Returns the slice of direct child expressions (left to right). Num and Var return an empty slice; Fun, Add, and Mul return their argument slices; Pow returns an empty slice (use binary_children to get the two operands).

Parameters: None.

Return value: &'a [Atom<'a>].

Example:

#![allow(unused)]
fn main() {
use ocas_atom::AtomArena;
use ocas_core::arena::Arena;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let x = ctx.var("x");
let y = ctx.var("y");
let sum = ctx.add(&[x, y, ctx.num(1)]);
assert_eq!(sum.children().len(), 3);
assert_eq!(x.children().len(), 0);
}

Atom::binary_children

#![allow(unused)]
fn main() {
pub fn binary_children(&self) -> Option<(Atom<'a>, Atom<'a>)>
}

Function: If this Atom is a Pow node, returns (base, exp); otherwise returns None.

Parameters: None.

Return value: Option<(Atom<'a>, Atom<'a>)>.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::AtomArena;
use ocas_core::arena::Arena;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let x = ctx.var("x");
let y = ctx.var("y");
let power = ctx.pow(x, y);
let (base, exp) = power.binary_children().unwrap();
assert_eq!(base.to_string(), "x");
assert_eq!(exp.to_string(), "y");
}

See also: Atom::children, AtomNode


AtomNode

#![allow(unused)]
fn main() {
use ocas_atom::AtomNode;
}
#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum AtomNode<'a> {
    Num(i64),
    Var(Symbol),
    Fun(Symbol, &'a [Atom<'a>]),
    Add(&'a [Atom<'a>]),
    Mul(&'a [Atom<'a>]),
    Pow(Atom<'a>, Atom<'a>),
}
}

The concrete data of each node in the expression tree. An Atom handle obtains an AtomNode reference via the node() method.

Variant descriptions

VariantDescription
Num(i64)64-bit signed integer literal.
Var(Symbol)Named variable or constant.
Fun(Symbol, &'a [Atom<'a>])Function application. The first argument is the function name; the second is the argument list (at least one element).
Add(&'a [Atom<'a>])Addition. The argument list has at least one element.
Mul(&'a [Atom<'a>])Multiplication. The argument list has at least one element.
Pow(Atom<'a>, Atom<'a>)Exponentiation. The first argument is the base; the second is the exponent.

Design notes:

  • Subtraction/division are desugared at parse time: $x - y$ becomes Add([x, Mul([Num(-1), y])]), and $x / y$ becomes Mul([x, Pow(y, Num(-1))]). There are no standalone subtraction or division nodes in the AST.
  • The argument lists of Add and Mul are sorted after normalize, ensuring that structurally equal expressions produce the same AST.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::{AtomArena, AtomNode};
use ocas_core::arena::Arena;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let x = ctx.var("x");
match x.node() {
    AtomNode::Var(s) => assert_eq!(s.as_str(), "x"),
    _ => panic!("expected variable"),
}
}

See also: Atom, Symbol


AtomArena

#![allow(unused)]
fn main() {
use ocas_atom::AtomArena;
}
#![allow(unused)]
fn main() {
pub struct AtomArena<'a> {
    arena: &'a Arena,
    cons_table: RefCell<FastHashMap<AtomNode<'a>, Atom<'a>>>,
}
}

AtomArena is the only entry point for constructing Atoms. It wraps an Arena reference and a hash-consing table (interior mutability via RefCell). All construction methods are immutable from the caller's perspective. Structurally identical subexpressions always return the same Atom handle within the same AtomArena — this makes structural equality equivalent to pointer equality.

AtomArena::new

#![allow(unused)]
fn main() {
pub fn new(arena: &'a Arena) -> Self
}

Function: Creates an AtomArena backed by the given Arena.

Parameters:

ParameterTypeDescription
arena&'a ArenaReference to the bump allocator.

Return value: AtomArena<'a>.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::AtomArena;
use ocas_core::arena::Arena;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let n = ctx.num(42);
assert_eq!(n.to_string(), "42");
}

AtomArena::num

#![allow(unused)]
fn main() {
pub fn num(&self, value: i64) -> Atom<'a>
}

Function: Creates an integer literal Atom.

Parameters:

ParameterTypeDescription
valuei64The integer value.

Return value: Atom<'a> — a Num(value) node.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::AtomArena;
use ocas_core::arena::Arena;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let n = ctx.num(7);
assert_eq!(n.to_string(), "7");
}

AtomArena::var

#![allow(unused)]
fn main() {
pub fn var(&self, name: &str) -> Atom<'a>
}

Function: Creates a variable Atom.

Parameters:

ParameterTypeDescription
name&strThe variable name.

Return value: Atom<'a> — a Var(Symbol) node.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::AtomArena;
use ocas_core::arena::Arena;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let x = ctx.var("x");
assert_eq!(x.to_string(), "x");
}

AtomArena::fun

#![allow(unused)]
fn main() {
pub fn fun(&self, name: &str, args: &[Atom<'a>]) -> Atom<'a>
}

Function: Creates a function-application Atom.

Parameters:

ParameterTypeDescription
name&strThe function name.
args&[Atom<'a>]Argument list. Panics on an empty list in debug mode.

Return value: Atom<'a> — a Fun(Symbol, &[Atom]) node.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::AtomArena;
use ocas_core::arena::Arena;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let x = ctx.var("x");
let f = ctx.fun("sin", &[x]);
assert_eq!(f.to_string(), "sin(x)");
}

AtomArena::add

#![allow(unused)]
fn main() {
pub fn add(&self, args: &[Atom<'a>]) -> Atom<'a>
}

Function: Creates an addition Atom.

Parameters:

ParameterTypeDescription
args&[Atom<'a>]Operand list. Panics on an empty list in debug mode.

Return value: Atom<'a> — an Add(&[Atom]) node.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::AtomArena;
use ocas_core::arena::Arena;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let x = ctx.var("x");
let y = ctx.var("y");
let sum = ctx.add(&[x, y]);
assert_eq!(sum.to_string(), "x + y");
}

AtomArena::mul

#![allow(unused)]
fn main() {
pub fn mul(&self, args: &[Atom<'a>]) -> Atom<'a>
}

Function: Creates a multiplication Atom.

Parameters:

ParameterTypeDescription
args&[Atom<'a>]Operand list. Panics on an empty list in debug mode.

Return value: Atom<'a> — a Mul(&[Atom]) node.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::AtomArena;
use ocas_core::arena::Arena;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let x = ctx.var("x");
let y = ctx.var("y");
let product = ctx.mul(&[x, y]);
assert_eq!(product.to_string(), "x*y");
}

AtomArena::pow

#![allow(unused)]
fn main() {
pub fn pow(&self, base: Atom<'a>, exp: Atom<'a>) -> Atom<'a>
}

Function: Creates an exponentiation Atom.

Parameters:

ParameterTypeDescription
baseAtom<'a>The base.
expAtom<'a>The exponent.

Return value: Atom<'a> — a Pow(base, exp) node.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::AtomArena;
use ocas_core::arena::Arena;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let x = ctx.var("x");
let p = ctx.pow(x, ctx.num(3));
assert_eq!(p.to_string(), "x^3");
}

AtomArena::slice

#![allow(unused)]
fn main() {
pub fn slice(&self, atoms: &[Atom<'a>]) -> &'a [Atom<'a>]
}

Function: Allocates a slice of Atoms into the Arena and returns the reference. Used for multi-result scenarios that share the Arena lifetime (e.g., component solutions of ODE systems).

Parameters:

ParameterTypeDescription
atoms&[Atom<'a>]Slice of Atoms to allocate.

Return value: &'a [Atom<'a>].

See also: Arena, Atom


Functions

parse

#![allow(unused)]
fn main() {
use ocas_parse::parse;
}
#![allow(unused)]
fn main() {
pub fn parse<'a>(ctx: &'a AtomArena<'a>, input: &str) -> Result<Atom<'a>, ParseError>
}

Function: Parses a mathematical expression string into an Atom tree. Supports integers, variables, function calls (f(x)), addition, subtraction, multiplication, division, exponentiation, and parentheses. Subtraction desugars to addition with a negative coefficient; division desugars to multiplication with a negative exponent.

Parameters:

ParameterTypeDescription
ctx&'a AtomArena<'a>The arena context used to allocate nodes.
input&strThe expression string to parse.

Return value: Result<Atom<'a>, ParseError>.

Errors:

VariantDescription
ParseError::Lex(LexError)The input contains an illegal character.
ParseError::UnexpectedEofThe input ends unexpectedly (e.g., "x +").
ParseError::UnexpectedTokenAn unexpected token is encountered (e.g., "*x").

Example:

#![allow(unused)]
fn main() {
use ocas_atom::AtomArena;
use ocas_core::arena::Arena;
use ocas_parse::parse;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let expr = parse(&ctx, "x^2 + 2*x + 1").unwrap();
assert_eq!(expr.to_string(), "((x^2) + (2*x)) + 1");
}

See also: normalize

normalize

#![allow(unused)]
fn main() {
use ocas_atom::normalize::normalize;
}
#![allow(unused)]
fn main() {
pub fn normalize<'a>(ctx: &AtomArena<'a>, atom: Atom<'a>) -> Atom<'a>
}

Function: Normalizes an expression into a deterministic canonical form. Specifically:

  • Flattens nested Add and Mul (e.g., Add([Add([x, y]), z]) → Add([x, y, z]))
  • Sorts the operands
  • Combines numeric coefficients (e.g., Add([x, Num(2), Num(3)]) → Add([Num(5), x]))

The result is allocated in the same arena as the input.

Parameters:

ParameterTypeDescription
ctx&AtomArena<'a>The arena context used to allocate the result.
atomAtom<'a>The expression to normalize.

Return value: Atom<'a> — the normalized expression.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::normalize::normalize;
use ocas_atom::AtomArena;
use ocas_core::arena::Arena;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let x = ctx.var("x");
let y = ctx.var("y");
let z = ctx.var("z");
let inner = ctx.add(&[x, y]);
let outer = ctx.add(&[inner, z, ctx.num(2), ctx.num(3)]);
let result = normalize(&ctx, outer);
assert_eq!(result.to_string(), "5 + x + y + z");
}

See also: parse, transform

substitute

#![allow(unused)]
fn main() {
use ocas_calc::series::substitute;
}
#![allow(unused)]
fn main() {
pub fn substitute<'a>(
    ctx: &'a AtomArena<'a>,
    expr: Atom<'a>,
    var: Symbol,
    replacement: Atom<'a>,
) -> Atom<'a>
}

Function: Replaces every occurrence of the variable var in the expression with replacement. A convenience function implemented on top of transform.

Parameters:

ParameterTypeDescription
ctx&'a AtomArena<'a>The arena context.
exprAtom<'a>The expression to substitute into.
varSymbolThe variable name to replace.
replacementAtom<'a>The replacement expression.

Return value: Atom<'a> — the substituted expression.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::{AtomArena, Symbol};
use ocas_calc::series::substitute;
use ocas_core::arena::Arena;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let x = ctx.var("x");
let y = ctx.var("y");
let expr = ctx.add(&[ctx.pow(x, ctx.num(2)), ctx.num(1)]);
let result = substitute(&ctx, expr, Symbol::new("x"), y);
assert_eq!(result.to_string(), "(y^2) + 1");
}

See also: transform

transform

#![allow(unused)]
fn main() {
use ocas_rewrite::transform;
}
#![allow(unused)]
fn main() {
pub fn transform<'a, F>(ctx: &'a AtomArena<'a>, atom: Atom<'a>, mut f: F) -> Atom<'a>
where
    F: FnMut(Atom<'a>) -> Option<Atom<'a>>,
}

Function: Traverses the expression tree bottom-up and applies the transform function f to each node. f is called after the node's children have been transformed. Returning Some(atom) replaces the node; returning None keeps the node (with its transformed children). This is the standard rewrite traversal pattern used by the oCAS rule engine and simplifier.

Parameters:

ParameterTypeDescription
ctx&'a AtomArena<'a>The arena context.
atomAtom<'a>The expression to transform.
fFnMut(Atom<'a>) -> Option<Atom<'a>>The transform function.

Return value: Atom<'a> — the transformed expression.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::{Atom, AtomArena, AtomNode};
use ocas_core::arena::Arena;
use ocas_rewrite::transform;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let x = ctx.var("x");
let y = ctx.var("y");
let sum = ctx.add(&[x, y]);

let result = transform(&ctx, sum, |a| {
    if let AtomNode::Add(args) = a.node() {
        if args.len() == 2 && args[0] == x && args[1] == y {
            return Some(ctx.add(&[y, x]));
        }
    }
    None
});

assert_eq!(result.to_string(), "y + x");
}

See also: substitute, normalize

collect_funs

#![allow(unused)]
fn main() {
use ocas_atom::walk::collect_funs;
}
#![allow(unused)]
fn main() {
pub fn collect_funs<'a>(atom: Atom<'a>) -> Vec<(Symbol, Atom<'a>)>
}

Function: Collects all function applications in the expression in post-order (innermost first), deduplicated (hash-consing guarantees that structurally identical applications appear only once).

Parameters:

ParameterTypeDescription
atomAtom<'a>The expression to traverse.

Return value: Vec<(Symbol, Atom<'a>)> — a list of (function name, function application node).

Example:

#![allow(unused)]
fn main() {
use ocas_atom::{AtomArena, walk::collect_funs};
use ocas_core::arena::Arena;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let x = ctx.var("x");
let sin_x = ctx.fun("sin", &[x]);
let expr = ctx.fun("cos", &[sin_x]);
let funs = collect_funs(expr);
assert_eq!(funs.len(), 2);
assert_eq!(funs[0].0.as_str(), "sin");
assert_eq!(funs[1].0.as_str(), "cos");
}

See also: collect_vars

collect_vars

#![allow(unused)]
fn main() {
use ocas_atom::walk::collect_vars;
}
#![allow(unused)]
fn main() {
pub fn collect_vars(atom: Atom) -> Vec<Symbol>
}

Function: Collects all distinct variable names in the expression, in order of first appearance (depth-first, left to right).

Parameters:

ParameterTypeDescription
atomAtomThe expression to traverse.

Return value: Vec<Symbol> — the list of variable names.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::{AtomArena, walk::collect_vars};
use ocas_core::arena::Arena;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let x = ctx.var("x");
let y = ctx.var("y");
let expr = ctx.add(&[ctx.mul(&[x, y]), x]);
let vars = collect_vars(expr);
assert_eq!(vars.len(), 2);
assert_eq!(vars[0].as_str(), "x");
assert_eq!(vars[1].as_str(), "y");
}

See also: collect_funs


Pattern Matching

The pattern-matching system matches Atom expression trees against patterns with wildcards (Pattern), with support for associative/commutative (AC) matching and backtracking-budget control.

Pattern

#![allow(unused)]
fn main() {
use ocas_rewrite::pattern::Pattern;
}
#![allow(unused)]
fn main() {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Pattern<'a> {
    Literal(Atom<'a>),
    Wildcard(Symbol, WildcardLevel),
    Add(Vec<Pattern<'a>>),
    Mul(Vec<Pattern<'a>>),
    Pow(Box<(Pattern<'a>, Pattern<'a>)>),
    Fun(Symbol, Vec<Pattern<'a>>),
}
}

Patterns mirror the structure of AtomNode but add wildcard nodes. Matching of Add and Mul supports associative-commutative (AC) matching: subpatterns can match any subset of the argument list.

VariantDescription
Literal(Atom)Matches the given Atom exactly.
Wildcard(Symbol, WildcardLevel)Wildcard match; the name and level determine the binding behavior.
Add(Vec<Pattern>)Matches an Add node; the argument list is matched AC-wise.
Mul(Vec<Pattern>)Matches a Mul node; the argument list is matched AC-wise.
Pow(Box<(Pattern, Pattern)>)Matches a Pow node; base and exponent are matched separately.
Fun(Symbol, Vec<Pattern>)Matches a Fun node; function name and argument list are matched separately.

Pattern::from_atom

#![allow(unused)]
fn main() {
pub fn from_atom(_ctx: &'a impl PatternAlloc<'a>, atom: Atom<'a>) -> Pattern<'a>
}

Function: Converts an Atom into a Pattern. Variables whose names end in _ (or begin with _) are treated as wildcards:

  • x_ → Wildcard(Symbol("x"), Single)
  • x__ → Wildcard(Symbol("x"), Sequence)
  • x___ → Wildcard(Symbol("x"), NullSequence)

Parameters:

ParameterTypeDescription
_ctx&'a impl PatternAlloc<'a>The pattern allocator (usually &()).
atomAtom<'a>The expression to convert.

Return value: Pattern<'a>.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::AtomArena;
use ocas_core::arena::Arena;
use ocas_rewrite::pattern::{Pattern, WildcardLevel};

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let x = ctx.var("x_");
let pat = Pattern::from_atom(&(), x);
assert!(matches!(pat, Pattern::Wildcard(s, WildcardLevel::Single) if s.as_str() == "x"));
}

See also: WildcardLevel, match_pattern

WildcardLevel

#![allow(unused)]
fn main() {
use ocas_rewrite::pattern::WildcardLevel;
}
#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum WildcardLevel {
    Single,
    Sequence,
    NullSequence,
}
}

The matching scope of a wildcard.

VariantNaming conventionDescription
SingleOne trailing _ in the name (e.g., x_; a leading _ also works, e.g. _x)Matches exactly one Atom.
SequenceTwo trailing _ in the name (e.g., x__; two leading _ also work, e.g. __x)Matches one or more Atoms in an Add/Mul/Fun argument list.
NullSequenceThree trailing _ in the name (e.g., x___; three leading _ also work, e.g. ___x)Matches zero or more Atoms.

Example:

#![allow(unused)]
fn main() {
use ocas_rewrite::pattern::WildcardLevel;

assert!(matches!(WildcardLevel::Single, WildcardLevel::Single));
assert!(matches!(WildcardLevel::Sequence, WildcardLevel::Sequence));
assert!(matches!(WildcardLevel::NullSequence, WildcardLevel::NullSequence));
}

match_pattern

#![allow(unused)]
fn main() {
use ocas_rewrite::matcher::match_pattern;
}
#![allow(unused)]
fn main() {
pub fn match_pattern<'a>(pattern: Pattern<'a>, atom: Atom<'a>) -> Result<Bindings<'a>, MatchError>
}

Function: Attempts to match pattern against atom, returning the binding set on success. Uses the default backtracking budget (DEFAULT_MAX_BACKTRACKS = 10_000). Add/Mul use AC matching (full backtracking search); Fun uses ordered matching (with sequence wildcard support).

Parameters:

ParameterTypeDescription
patternPattern<'a>The matching pattern.
atomAtom<'a>The expression to match against.

Return value: Result<Bindings<'a>, MatchError>.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::{AtomArena, Symbol};
use ocas_core::arena::Arena;
use ocas_rewrite::pattern::{Pattern, WildcardLevel};
use ocas_rewrite::matcher::match_pattern;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let x = ctx.var("x");
let sum = ctx.add(&[x, ctx.num(1)]);

let pat = Pattern::Add(vec![
    Pattern::Wildcard(Symbol::new("a"), WildcardLevel::Single),
    Pattern::Literal(ctx.num(1)),
]);
let bindings = match_pattern(pat, sum).unwrap();
}

match_pattern_with_budget

#![allow(unused)]
fn main() {
use ocas_rewrite::matcher::match_pattern_with_budget;
}
#![allow(unused)]
fn main() {
pub fn match_pattern_with_budget<'a>(
    pattern: Pattern<'a>,
    atom: Atom<'a>,
    max_backtracks: usize,
) -> Result<Bindings<'a>, MatchError>
}

Function: Same as match_pattern, but allows a custom backtracking budget to prevent exponential-time blowups on pathological inputs.

Parameters:

ParameterTypeDescription
patternPattern<'a>The matching pattern.
atomAtom<'a>The expression to match against.
max_backtracksusizeThe maximum number of backtracks.

Return value: Result<Bindings<'a>, MatchError>.

Errors: Returns MatchError::BudgetExhausted if the backtracking limit is exceeded.

See also: match_pattern

Bindings

#![allow(unused)]
fn main() {
use ocas_rewrite::matcher::Bindings;
}
#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Default)]
pub struct Bindings<'a> { /* private fields */ }
}

The set of wildcard bindings produced by a successful match.

Bindings::new

#![allow(unused)]
fn main() {
pub fn new() -> Self
}

Function: Creates an empty binding set.

Bindings::get

#![allow(unused)]
fn main() {
pub fn get(&self, name: Symbol) -> Option<&MatchValue<'a>>
}

Function: Looks up the bound value by wildcard name.

Parameters:

ParameterTypeDescription
nameSymbolThe wildcard name (without trailing underscores).

Return value: Option<&MatchValue<'a>>.

Example:

#![allow(unused)]
fn main() {
use ocas_atom::{AtomArena, Symbol};
use ocas_core::arena::Arena;
use ocas_rewrite::pattern::{Pattern, WildcardLevel};
use ocas_rewrite::matcher::match_pattern;

let arena = Arena::new();
let ctx = AtomArena::new(&arena);
let x = ctx.var("x");
let y = ctx.var("y");
let sum = ctx.add(&[x, y]);

let pat = Pattern::Add(vec![
    Pattern::Wildcard(Symbol::new("a"), WildcardLevel::Single),
    Pattern::Wildcard(Symbol::new("b"), WildcardLevel::Single),
]);
let bindings = match_pattern(pat, sum).unwrap();
let val = bindings.get(Symbol::new("a")).unwrap();
}

MatchValue

#![allow(unused)]
fn main() {
use ocas_rewrite::matcher::MatchValue;
}
#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MatchValue<'a> {
    Single(Atom<'a>),
    Sequence(&'a [Atom<'a>]),
}
}

The value type of a wildcard binding.

VariantDescription
Single(Atom<'a>)A Single-level wildcard bound to a single Atom.
Sequence(&'a [Atom<'a>])A Sequence- or NullSequence-level wildcard bound to a slice of Atoms.

MatchError

#![allow(unused)]
fn main() {
use ocas_rewrite::matcher::MatchError;
}
#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MatchError {
    NoMatch,
    InconsistentBinding,
    BudgetExhausted,
}
}

Pattern-matching errors.

VariantDisplayDescription
NoMatch"pattern did not match"The pattern does not match the target expression.
InconsistentBinding"inconsistent wildcard binding"The same wildcard name is bound to different values at different positions.
BudgetExhausted"backtrack budget exhausted"The backtracking budget of AC matching is exhausted. Indicates the input may cause exponential-time matching; increase the budget or simplify the pattern.

See also