Predicator.Conformance.Generator (predicator v8.0.0)

Copy Markdown View Source

The pure half of conformance corpus generation (px-35i.4).

generate/1 takes decoded authored cases - already-JSON.decode/1-ed maps from conformance/cases/*.json - and runs each through the real compiler and evaluator, deriving everything the authored case does not spell out: instructions (when a case authors source), expected_result or expected_error, tier, and features.

An authored expected or tier is an assertion, not data: if either disagrees with what the real pipeline computes, generation fails naming the case and both values, so a sibling author's belief about semantics gets checked against Elixir's behavior rather than silently overwritten.

Failures accumulate rather than halting on the first one - a contributor fixing five broken cases should see five errors in one run, not one at a time.

This module writes nothing to disk and knows nothing about conformance/cases/*.json as files; mix corpus.generate (px-35i.4 Phase 4) is the thin I/O shell around it.

Summary

Types

An authored case, already JSON-decoded: string keys, JSON-shaped values.

One per-case generation failure: the case id (nil if unknown) and a human-readable problem.

A completed case: the authored fields plus everything the generator derived.

Functions

Runs every authored case through the real compiler and evaluator.

Types

authored_case()

@type authored_case() :: %{optional(String.t()) => term()}

An authored case, already JSON-decoded: string keys, JSON-shaped values.

case_error()

@type case_error() :: %{id: String.t() | nil, problem: String.t()}

One per-case generation failure: the case id (nil if unknown) and a human-readable problem.

completed_case()

@type completed_case() :: %{optional(String.t()) => term()}

A completed case: the authored fields plus everything the generator derived.

Functions

generate(cases, opts \\ [])

@spec generate(
  [authored_case()],
  keyword()
) ::
  {:ok,
   %{tiers: %{required(pos_integer()) => [completed_case()]}, manifest: map()}}
  | {:error, [case_error()]}

Runs every authored case through the real compiler and evaluator.

Returns {:ok, %{tiers: %{1 => [case], ...}, manifest: manifest}} when every case generates cleanly, or {:error, [case_error, ...]} with every failing case's problem, not just the first.

Accepts an options keyword list. The only option is :retired_opcodes, a MapSet of opcode names this build's reference evaluator can no longer run (docs/isa.md section 4, "Retired opcodes"). It defaults to the opcodes present in Instructions.opcodes/0 but absent from Instructions.opcode_set(Instructions.isa_version()) - today that default is empty, since no opcode carries :removed_in yet. A case that uses a retired opcode is classified in generate_case/3 and takes a frozen- expectation path instead of running the pipeline below.

Examples

iex> {:ok, result} = Predicator.Conformance.Generator.generate([
...>   %{"id" => "core/literal-true", "source" => "true", "expected" => %{"result" => true}}
...> ])
iex> result.tiers[1]
[
  %{
    "id" => "core/literal-true",
    "source" => "true",
    "instructions" => [["lit", true]],
    "context" => %{},
    "expected_result" => true,
    "tier" => 1,
    "features" => []
  }
]

iex> Predicator.Conformance.Generator.generate([%{"id" => "bad", "source" => "score >"}])
{:error, [%{id: "bad", problem: "source \"score >\" failed to compile: Expected number, string, boolean, date, datetime, identifier, function call, list, object, or '(' but found end of input"}]}