Skip to content

Make ?- list tuples and handle typos and arity - #20

Merged
ajwdev merged 1 commit into
mainfrom
ajw-repl-query-listing
Oct 2, 2026
Merged

ajwdev merged 1 commit into
mainfrom
ajw-repl-query-listing

Conversation

@ajwdev

@ajwdev ajwdev commented Oct 2, 2026

Copy link
Copy Markdown
Owner

Summary

Reworks the REPL's query UX around ?-.

  • ?- rel, ?- rel() and ?- rel("c", _, ...) (a single atom with no named variables) now list whole matching tuples. No _N rule is created. _ and missing trailing arguments match anything.
  • Queries with named variables or several atoms keep the existing binding path (X = v), including _ padding for partial atoms.
  • Typos and arity mistakes are caught before evaluation. An unknown relation (not declared, not defined by a rule or fact, no EDB rows, not in the current store) prints Unknown relation 'x'. Did you mean: a, b? (up to 3 suggestions by edit distance, skipping _N results and x__rel snapshot copies). More arguments than a relation has columns prints rel has N columns, got M arguments.
  • New output formats: compact (bare rel(a, b) tuples) and table (aligned columns under a header row; cells clipped at 64 characters, newlines escaped). table also applies to binding results, with the variable names as headers.
  • The default listing names each column (Principal = "...", Namespace = ...), falling back to c0, c1 when a relation has no Decl.
  • Help text updated.

Engine additions: CompiledProgram::defined_relations and Engine::known_relations.

Behavior changes

  • The bare pred(args) / pred query form is removed. Non-command input prints Not a command. Queries start with ?-, e.g. ?- <line>, and an unknown ::cmd prints Unknown command: .... Use ?- pred(args).
  • The plain output format is renamed default, with no alias. \format plain and --format plain are now rejected. The --format default is spelled default. (tests/smt_ndjson.rs is updated for the rename.)
  • \format and --format accept compact and table in addition to default, pretty and ndjson.
  • A ?- query naming an unknown relation, or passing too many arguments, is now an error instead of an empty result (interpreter) or an opaque error (DD).
  • A ?- conjunction with no named variables prints No variables to bind. Name one (e.g. X). (previously a zero-column rule: No results. or a blank row).
  • ::smt behavior and --format ndjson output are unchanged.

Testing

  • cargo fmt --check, cargo clippy --all-targets --locked -- -D warnings, cargo test --all-targets --locked all pass on stable.
  • Unit tests for body_relations, suggest, split_top_level_args, single_atom, render_table.
  • New tests/repl_query.rs drives the binary over testdata/: default listing names columns, \format compact, \format table (header and rule row), unknown relation did-you-mean, too many arguments, plain rejected, bare direct_perm(X) prints the hint.
  • Manual smoke on both the interpreter and --backend dd against testdata/: listing, partial atom, _ wildcard, binding query with :string:contains, typo'd relation, too many arguments, conjunction with no variables, every \format value. Output is identical on both backends.

Before / after

Listing (before, ?- direct_perm("developer@example.com") created a zero-column rule and printed a blank row):

before:
  
Found 1 result(s): (-> _0)

after:
  Principal = "developer@example.com", Namespace = "kube-system", ApiGroup = "*", Resource = "*", Verb = "*"
Found 1 result(s).

Typo and arity:

before:
?- direct_permz
No variables found - use uppercase names for variables.
?- direct_perm("a","b","c","d","e","f")
No variables found - use uppercase names for variables.

after:
?- direct_permz
Unknown relation 'direct_permz'. Did you mean: direct_perm, indirect_perm?
?- direct_perm("a","b","c","d","e","f")
direct_perm has 5 columns, got 6 arguments.

Formats:

\format compact
?- direct_perm("admin@example.com")
  direct_perm("admin@example.com", "", "*", "*", "*")

\format table
?- direct_perm("admin@example.com")
  Principal            Namespace  ApiGroup  Resource  Verb
  -------------------  ---------  --------  --------  ----
  "admin@example.com"  ""         "*"       "*"       "*"

\format ndjson   (before: {} ; after: keyed by column)
{"Principal":"admin@example.com","Namespace":"","ApiGroup":"*","Resource":"*","Verb":"*"}

\format plain
before: Output format: plain.
after:  Unknown format 'plain'. Use: default | compact | pretty | table | ndjson.

Removed bare form:

before:  direct_perm(X)   -> lists every direct_perm tuple
after:   direct_perm(X)   -> Not a command. Queries start with ?-, e.g. ?- direct_perm(X)

Binding queries are unchanged:

?- direct_perm(P, NS, R, V, G), :string:contains(P, "admin")
  P = "admin@example.com", NS = "", R = "*", V = "*", G = "*"
  P = "system:serviceaccount:pallograph-test:admin-sa", NS = "", R = "*", V = "*", G = "*"
Found 2 result(s): (-> _0)

🤖 Generated with Claude Code

A single plain atom with no named variables (`?- rel`, `?- rel()`,
`?- rel("c", _)`) now lists whole matching tuples instead of
creating a `_N` rule. `_` and missing trailing arguments match
anything. Queries with named variables, or with several atoms, keep
the existing binding path (`X = v`), including `_` padding for
partial atoms.

Typos used to be silent: an unknown relation was an empty result on
the interpreter and an opaque error on DD. Before running any `?-`
query the REPL now rejects relations the engine does not know
(declared, defined by a rule or fact, EDB rows, or in the current
store) and suggests up to three near names. Giving more arguments
than a relation has columns is also an error. This adds
CompiledProgram::defined_relations and Engine::known_relations.

Output formats gain `compact` (bare `rel(a, b)` tuples) and `table`
(aligned columns under a header, cells clipped at 64 characters,
newlines escaped). The default listing now names each column
(`Principal = "...", Namespace = ...`), falling back to c0, c1 when
there is no Decl. `table` also applies to binding results, using
the variable names as headers.

Behavior changes:

* The bare `pred(args)` and `pred` query forms are removed. Input
  that is not a command prints "Not a command. Queries start with
  ?-, e.g. ?- <line>" and an unknown `::cmd` prints "Unknown
  command: ...". Use `?- pred(args)` instead.
* The `plain` output format is renamed `default`, with no alias.
  `\format plain` and `--format plain` are now rejected. The
  default for `--format` is now spelled `default`.
* `\format` accepts `compact` and `table`.
* A `?-` query naming an unknown relation, or passing too many
  arguments to one, now errors instead of returning nothing.
* A `?-` conjunction with no named variables now prints "No
  variables to bind. Name one (e.g. X)." It previously built a
  zero-column rule and printed "No results." or a blank row.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@ajwdev
ajwdev merged commit a760568 into main Oct 2, 2026
2 checks passed
@ajwdev
ajwdev deleted the ajw-repl-query-listing branch October 2, 2026 18:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant