Hassan Abedi e68a4b0dee WIP
2026-06-05 13:37:45 +02:00

76 lines
3.3 KiB
Markdown

## Plan Exporter
Haskell tool that turns a hand-authored scenario into the JSON IR consumed by
[`crates/plan-runner`](../../crates/plan-runner).
Each scenario carries a small schema, a set of ground facts, a list of conjunctive-query atoms, and an expected-bindings oracle.
The exporter parses the scenario, runs `Geolog.DB.Plan.planConjunction` from
[`external/geolog`](../../external/geolog) to produce a Yannakakis-style plan,
self-checks the planned bindings against the oracle, then writes the runner IR to stdout.
This is the only producer of runner IR today;
any other frontend that emits the same JSON shape can drive the runner instead.
### Layout
```text
tools/exporter/
├── plan-exporter.cabal # cabal package: executable `plan-export`
├── cabal.project # points at geolog-lang and its siblings in external/geolog
├── src/Main.hs # the exporter itself; rustdoc-style header explains the IR
└── examples/ # hand-authored scenario inputs
├── cartesian.scenario.json
├── self_loop.scenario.json
├── three_atom_chain.scenario.json
└── two_atom_join.scenario.json
```
### Run It
The exporter needs GHC 9.12 and Cabal.
The repository's Nix dev shell provides both;
enter it with `make shell` (or `nix develop`) before running the commands below.
```sh
# Build the executable:
cd tools/exporter && cabal build plan-export
# Export one scenario to runner IR JSON:
cabal run -v0 plan-export -- examples/two_atom_join.scenario.json
# Regenerate every fixture in crates/plan-runner/fixtures/ from these scenarios:
make export-fixtures # run from the repository root
```
### Scenario Format
Each `.scenario.json` carries four blocks:
| Block | Maps to |
|---------------------|----------------------------------------------------------------------|
| `schema` | `FlatTheory` from `geolog-lang`: relation paths and column types. |
| `facts` | Ground tuples per relation, in the shape `InMemory` ingests. |
| `atoms` | The conjunctive-query body, as a list of `QAtom`. |
| `expected_bindings` | Variables and rows the planner is expected to produce. Used as a self-check; lifted through to the runner-side oracle. |
See the four committed examples for the exact JSON shape.
The runner-side IR shape (what the exporter emits) is documented in
[`crates/plan-runner/src/lib.rs`](../../crates/plan-runner/src/lib.rs).
### Self-Check
Before emitting JSON, the exporter runs the planned query through
`Geolog.DB.InMemory.evalConjunctionPlanned` and asserts the bindings match
the scenario's `expected_bindings`.
A mismatched scenario fails at export time, so the Rust side never sees a broken fixture.
### Notes
- **Cross-language contract.**
The JSON shape is the contract between this tool and `plan-runner`.
Changing it means editing both sides.
- **Subset of the source language.**
The exporter only handles conjunctive queries that fit `planConjunction`.
Negation, recursion, and laws are out of scope here; they belong to a different planner pass.
- **No durable references to ignored paths.**
This tool depends on `external/geolog` (a submodule), not on any ad-hoc clone or generated copy in an ignored path.