# `Predicator.Conformance.AnalysisGenerator`
[🔗](https://github.com/riddler/predicator-ex/blob/v9.5.0/lib/predicator/conformance/analysis_generator.ex#L1)

The pure half of the corpus's analysis surface.

`generate/1` takes decoded authored analysis cases - the maps in
`conformance/analysis/cases.json` - and completes each one from
`Predicator.Analysis`: the instructions a `source` compiles to (through
`Predicator.compile_program/1`), and the `references`, `calls` and
`assignments` the analysis lists for it. `mix corpus.generate` is the I/O
shell that writes the result to `conformance/analysis/corpus.json`.

The expected values are always computed, never authored: a case names a
program, and the reference implementation says what it reads, calls and
stores. A case authored as `source` is analysed in both forms - the source
string and its compiled instructions - and generation fails, naming the
case, when the two forms disagree on anything but positions, since the
instruction form carries none. That keeps one expected set per case honest
for both forms a runner may read.

Only cases the analysis answers `{:ok, list}` for are shipped: a source that
does not parse, or an instruction the analysis cannot read, fails
generation rather than becoming a case.

The JSON spelling of an answer (`conformance/README.md`, "The analysis
surface"): a segment is a string, an integer, or `{"$type": "dynamic"}`; a
position is `[line, column]` or `null`.

## Examples

    iex> Predicator.Conformance.AnalysisGenerator.generate([
    ...>   %{"id" => "analysis/example", "source" => "items[k] > 1"}
    ...> ])
    {:ok,
     [
       %{
         "id" => "analysis/example",
         "source" => "items[k] > 1",
         "instructions" => [
           ["load", "items"],
           ["load", "k"],
           ["bracket_access"],
           ["lit", 1],
           ["compare", "GT"],
           ["pop"]
         ],
         "references" => [
           %{"path" => ["items", %{"$type" => "dynamic"}], "position" => [1, 1]},
           %{"path" => ["k"], "position" => [1, 7]}
         ],
         "calls" => [],
         "assignments" => []
       }
     ]}

# `authored_case`

```elixir
@type authored_case() :: %{optional(String.t()) =&gt; term()}
```

An authored analysis case, already JSON-decoded.

# `case_error`

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

One per-case generation failure, in `Predicator.Conformance.Generator`'s shape.

# `completed_case`

```elixir
@type completed_case() :: %{optional(String.t()) =&gt; term()}
```

A completed analysis case, as one line of `conformance/analysis/corpus.json`.

# `generate`

```elixir
@spec generate([authored_case()]) ::
  {:ok, [completed_case()]} | {:error, [case_error()]}
```

Completes every authored analysis case, sorted by id.

Returns `{:ok, cases}` when every case generates cleanly, or
`{:error, errors}` naming every failing case, not just the first.

## Examples

    iex> Predicator.Conformance.AnalysisGenerator.generate([%{"id" => "analysis/bad", "source" => "limit >"}])
    {:error, [%{id: "analysis/bad", problem: "source \"limit >\" has no analysis: Expected number, string, boolean, date, datetime, identifier, function call, list, object, or '(' but found end of input"}]}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
