Lemma (lemma_engine v0.8.15)

Copy Markdown View Source

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 JSON

Engine 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

engine()

@type engine() :: reference()

limits_map()

@type limits_map() :: %{required(String.t()) => pos_integer()} | nil

spec_name()

@type spec_name() :: String.t()

Functions

execution_plan(engine, spec, opts \\ [])

@spec execution_plan(engine(), spec_name(), keyword()) ::
  {:ok, map()} | {:error, term()}

Returns the serialized execution plan for a spec as a map.

Options: :effective (datetime string or nil).

format(code)

@spec format(String.t()) :: {:ok, String.t()} | {:error, term()}

Formats Lemma source code. Does not require an engine instance.

Example

{:ok, formatted} = Lemma.format("spec foo\nfact   x:  1\nrule y: x +  1")

format_repository(engine, repository)

@spec format_repository(engine(), String.t()) :: {:ok, String.t()} | {:error, term()}

Returns canonical Lemma source for a loaded repository (formatted from the in-engine AST).

Use "lemma" for the embedded SI stdlib (spec si).

invert(engine, spec_name, effective, rule_name, target, values \\ %{})

@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.

list(engine)

@spec list(engine()) :: {:ok, [map()]} | {:error, term()}

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_from is nil when the spec has no declared start date (the first version is unbounded at the start).
  • :effective_to is nil when the spec has no later version (this row is the latest and stays valid forward indefinitely).
  • Otherwise :effective_to equals 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).

load(engine, code, source_label \\ "inline")

@spec load(engine(), String.t(), String.t()) :: :ok | {:error, [map()]}

Loads a spec from a string. Source label is used for error reporting (e.g. "my_spec.lemma"). Use "inline" when no path.

load_batch(engine, sources, dependency \\ nil)

@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).

load_from_paths(engine, paths)

@spec load_from_paths(engine(), [String.t()]) :: :ok | {:error, [map()]}

Loads specs from paths (files and/or directories). Directories are expanded one level; only .lemma files are loaded.

new(limits \\ nil)

@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 load
  • max_source_size_bytes - max single source text size in bytes
  • max_total_expression_count - max expression nodes
  • max_expression_depth - max nesting depth
  • max_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})

remove_spec(engine, spec_name, effective)

@spec remove_spec(engine(), spec_name(), String.t()) :: :ok | {:error, term()}

Removes a spec from the engine by name and effective datetime.

repositories(engine)

@spec repositories(engine()) :: {:ok, [map()]} | {:error, term()}

Loaded repositories (workspace and dependencies). Each map has string keys "name" and "dependency".

run(engine, spec, opts \\ [])

@spec run(engine(), spec_name(), keyword()) :: {:ok, map()} | {:error, term()}

Runs a spec. Options: :effective (datetime string or nil), :data (map).

Returns decoded JSON response.

schema(engine, spec, opts \\ [])

@spec schema(engine(), spec_name(), keyword()) :: {:ok, map()} | {:error, term()}

Returns the schema for a spec.

Options: :effective (datetime string or nil).