Lemma rules engine for Elixir.
Wraps the Lemma engine (Rust) via NIFs. Create an engine, load specs from string or paths, run evaluations, and introspect schemas.
Example
{:ok, engine} = Lemma.new()
:ok = Lemma.load(engine, "spec foo\nfact x: 1\nrule y: x + 1", "my_spec.lemma")
{:ok, response} = Lemma.run(engine, "foo", [])
# response is a map from decoded JSONEngine lifecycle
Each engine is an opaque resource. Do not share the same engine ref across processes unless you serialize access (e.g. via a GenServer).
Summary
Functions
Returns the serialized execution plan for a spec as a map.
Formats Lemma source code. Does not require an engine instance.
Returns canonical Lemma source for a loaded repository (formatted from the in-engine AST).
Inverts a rule to find input domains that produce a desired outcome.
Lists loaded specs grouped by repository (same order as the engine: workspace first, then dependencies).
Loads a spec from a string. Source label is used for error reporting (e.g. "my_spec.lemma"). Use "inline" when no path.
Loads multiple Lemma sources in one planning pass.
Loads specs from paths (files and/or directories). Directories are expanded one level; only .lemma files are loaded.
Creates a new engine. Optionally pass a map of resource limits; omitted keys use defaults.
Removes a spec from the engine by name and effective datetime.
Loaded repositories (workspace and dependencies). Each map has string keys "name" and "dependency".
Runs a spec. Options: :effective (datetime string or nil), :data (map).
Returns the schema for a spec.
Types
@type engine() :: reference()
@type limits_map() :: %{required(String.t()) => pos_integer()} | nil
@type spec_name() :: String.t()
Functions
Returns the serialized execution plan for a spec as a map.
Options: :effective (datetime string or nil).
Formats Lemma source code. Does not require an engine instance.
Example
{:ok, formatted} = Lemma.format("spec foo\nfact x: 1\nrule y: x + 1")
Returns canonical Lemma source for a loaded repository (formatted from the in-engine AST).
Use "lemma" for the embedded SI stdlib (spec si).
@spec invert(engine(), spec_name(), String.t() | nil, String.t(), map(), map()) :: {:ok, map()} | {:error, term()}
Inverts a rule to find input domains that produce a desired outcome.
effective is a datetime string or nil.
Target is a map with :outcome ("value" | "veto" | "any_value" | "any_veto"), |
optionally :op ("eq" | "neq" | "lt" | etc.), and for "value"/"veto": :value or :message. |
Lists loaded specs grouped by repository (same order as the engine: workspace first, then dependencies).
Each element is %{repository: %{name: ..., dependency: ..., start_line: ..., attribute: ...}, specs: [...]}
where :name / :dependency are strings or nil (workspace has :name nil),
:start_line is a non-negative integer, and :attribute is the load-source label string
or nil (same display as SourceType in Rust — path, volatile, …).
Each entry in :specs has :name, :effective_from, :effective_to, :start_line,
:attribute, and :schema (decoded JSON object).
Temporal versions form a half-open [effective_from, effective_to) range:
:effective_fromisnilwhen the spec has no declared start date (the first version is unbounded at the start).:effective_toisnilwhen the spec has no later version (this row is the latest and stays valid forward indefinitely).- Otherwise
:effective_toequals the next version's:effective_from(exclusive end of this row's validity).
:schema is the decoded [Lemma.schema/3] envelope for this version so
callers never need a second round-trip.
Always includes the embedded lemma repository (spec si) on a fresh engine.
Returns {:ok, []} only when no repositories have specs (should not happen on Lemma.new/1).
Loads a spec from a string. Source label is used for error reporting (e.g. "my_spec.lemma"). Use "inline" when no path.
@spec load_batch(engine(), %{required(String.t()) => String.t()}, String.t() | nil) :: :ok | {:error, [map()]}
Loads multiple Lemma sources in one planning pass.
sources is a map of path label (for errors) to source text. Use "" as key for volatile/inline.
dependency is optional; when non-empty, repositories in this batch are tagged with that dependency id (same as the Rust Engine.load_batch/2 second argument).
Loads specs from paths (files and/or directories). Directories are expanded one level; only .lemma files are loaded.
@spec new(limits_map()) :: {:ok, engine()} | {:error, term()}
Creates a new engine. Optionally pass a map of resource limits; omitted keys use defaults.
Options (limits map keys)
max_sources- max sources per load_from_paths (after expanding paths)max_loaded_bytes- max total bytes to loadmax_source_size_bytes- max single source text size in bytesmax_total_expression_count- max expression nodesmax_expression_depth- max nesting depthmax_expression_count- max expressions per source (parser)max_data_value_bytes- max data value size
Examples
{:ok, engine} = Lemma.new()
{:ok, engine} = Lemma.new(%{max_sources: 100})
Removes a spec from the engine by name and effective datetime.
Loaded repositories (workspace and dependencies). Each map has string keys "name" and "dependency".
Runs a spec. Options: :effective (datetime string or nil), :data (map).
Returns decoded JSON response.
Returns the schema for a spec.
Options: :effective (datetime string or nil).