prover API
The prover package (Luna-Flow/QED/prover) runs theorem scripts. It parses a script or a file of scripts, installs the propositional prelude if asked, lowers each goal, runs the steps with the tactics package (scheduling { ... } branch blocks itself), and returns either a kernel theorem, a structured failure, or an unfinished-proof report for a script with a hole. It also publishes the regression corpus that ties the user manual to the tests.
The package orchestrates and reports; it holds no logical authority. The prover design explains the result model, and the prover tutorial runs scripts from MoonBit.
Options
ProverOptions, default_prover_options and prover_options
ProverOptions has one switch: whether to install the propositional prelude into the state before running. default_prover_options() turns it on; prover_options(b) sets it explicitly.
pub struct ProverOptions {
auto_install_prelude : Bool
}
pub fn default_prover_options() -> ProverOptions
pub fn prover_options(Bool) -> ProverOptions
Without the prelude, T, F and the catalog theorems are unavailable unless the state already defines them.
Running scripts
prove_theorem_script_detailed
prove_theorem_script_detailed(state, src, opts) runs one theorem script and returns a three-way result.
pub fn prove_theorem_script_detailed(@kernel.KernelState, String, ProverOptions) -> ProverRunResult
Header binders (x : A) become locals of the goal. The steps run in order; a step followed by branch blocks runs, and then each block runs on its subgoal in its own frame. The first failing step stops the script. A hole stops it as unfinished. If all steps succeed but goals remain, the result is a failure with UnsolvedGoals.
prove_theorem_script and prove_theorem_script_with_diagnostics
These functions run one script and return the theorem and the state, or the failure or unfinished report as an error.
pub fn prove_theorem_script(@kernel.KernelState, String, ProverOptions) -> Result[(@kernel.KernelState, @kernel.Thm), ProverScriptError]
pub fn prove_theorem_script_with_diagnostics(@kernel.KernelState, String, ProverOptions) -> Result[(@kernel.KernelState, @kernel.Thm), ProverScriptError]
The two functions currently behave identically; both carry the full diagnostics in ProverScriptError. The returned state is the state the proof ran in, with the prelude installed when requested; proved theorems are not added to it.
prove_theorem_file_results_detailed
prove_theorem_file_results_detailed(state, src, opts) runs every theorem of a file whose theorems are separated by qed lines, and returns one result per theorem.
pub fn prove_theorem_file_results_detailed(@kernel.KernelState, String, ProverOptions) -> ProverFileRunResult
A failing or unfinished theorem does not stop the file: later theorems are still checked. The whole file fails (FileFailed) only when it cannot be parsed or the prelude cannot be installed.
prove_theorem_file_detailed
prove_theorem_file_detailed runs a file and summarises it as one result: the first failure if any, else the first unfinished theorem if any, else the last success.
pub fn prove_theorem_file_detailed(@kernel.KernelState, String, ProverOptions) -> ProverRunResult
Results
ProverRunResult
ProverRunResult is the outcome of one script.
pub enum ProverRunResult {
Proved(ProverSuccess)
Failed(ProverFailure)
Unfinished(ProverUnfinished)
}
ProverSuccess
ProverSuccess carries the theorem, the name of the script and the state it was proved in.
pub struct ProverSuccess {
state : @kernel.KernelState
theorem_name : String
thm : @kernel.Thm
}
ProverFailure
ProverFailure describes where and why a script failed.
pub struct ProverFailure {
theorem_name : String?
kind : ProverDiagnosticKind
detail : String
raw_offset : Int?
goal_src : String?
goal_span : @parser.SourceSpan?
step_index : Int?
branch_path : Array[Int]
step_span : @parser.SourceSpan?
step_src : String?
current_goal : @tactics.Goal?
local_hyps : Array[ProverLocalHyp]
error : ProverError
}
kind and error say which layer failed; detail renders the error. The position fields locate the failure in the source: raw_offset for parse errors, the goal span for lowering errors, and the step index, branch path, step span and step text for tactic errors, together with the goal and locals at that point. Fields that do not apply are None or empty.
ProverUnfinished
ProverUnfinished reports a script that reached a hole.
pub struct ProverUnfinished {
theorem_name : String
detail : String
raw_offset : Int?
goal_src : String?
goal_span : @parser.SourceSpan?
step_index : Int?
branch_path : Array[Int]
step_span : @parser.SourceSpan?
step_src : String?
current_goal : @tactics.Goal
local_hyps : Array[ProverLocalHyp]
hole_name : String?
}
It has the same position fields as a failure, the goal the hole stands for (always present) and the hole’s name if it has one. It is not a theorem.
ProverLocalHyp and prover_local_hyp
ProverLocalHyp is a named local hypothesis in a report; prover_local_hyp builds one.
pub struct ProverLocalHyp {
name : String
term : @kernel.Term
}
pub fn prover_local_hyp(String, @kernel.Term) -> ProverLocalHyp
prover_unfinished
prover_unfinished builds an Unfinished result from its fields, in the order theorem name, goal source, goal span, step index, branch path, step span, step source, current goal, locals, hole name, detail and raw offset.
pub fn prover_unfinished(String, String?, @parser.SourceSpan?, Int?, Array[Int], @parser.SourceSpan?, String?, @tactics.Goal, Array[ProverLocalHyp], String?, String, Int?) -> ProverRunResult
It exists for tests and renderers that need a fixed unfinished report.
ProverError and ProverDiagnosticKind
ProverError wraps the error of the layer that failed; ProverDiagnosticKind names the layer.
pub enum ProverError {
Parse(@parser.ParseError)
Bridge(@parser.ParseBridgeError)
Tactic(@tactics.TacticExecError)
Sig(@kernel.SigError)
Logic(@kernel.LogicError)
}
pub enum ProverDiagnosticKind {
Parse
Bridge
Tactic
Sig
Logic
}
ProverScriptError
ProverScriptError is the error side of prove_theorem_script.
pub enum ProverScriptError {
Failure(ProverFailure)
Unfinished(ProverUnfinished)
}
ProverFileRunResult, ProverFileReport and ProverFileItemResult
A file run either fails as a whole or yields one item per theorem, in order.
pub enum ProverFileRunResult {
FileChecked(ProverFileReport)
FileFailed(ProverFailure)
}
pub struct ProverFileReport {
state : @kernel.KernelState
items : Array[ProverFileItemResult]
}
pub enum ProverFileItemResult {
ProverItemProved(ProverSuccess)
ProverItemFailed(ProverFailure)
ProverItemUnfinished(ProverUnfinished)
}
test "run scripts" {
let st = @kernel.empty_kernel_state()
let opts = @prover.default_prover_options()
let ok = @prover.prove_theorem_script_detailed(st, "theorem truth_file : ⊢ T := by exact truth", opts)
inspect(ok is @prover.Proved({ theorem_name: "truth_file", .. }), content="true")
// the manual's bad_branch example: a structured failure
let bad = @prover.prove_theorem_script_detailed(
st,
"theorem bad_branch (x : bool) : ⊢ x -> x ∨ x := by\n intro h\n left { exact truth }",
opts,
)
guard bad is @prover.Failed(f) else { fail("expected a failure") }
inspect(f.detail, content="GoalShapeMismatch(exact witness does not directly close current goal)")
assert_eq((f.step_index, f.branch_path, f.step_src), (Some(3), [1], Some("exact truth")))
// a hole is reported, not proved
let unf = @prover.prove_theorem_script_detailed(
st,
"theorem unfinished_branch (x : bool) : ⊢ x -> x ∨ (x ∨ x) := by\n intro h\n right { right { hole h1 } }",
opts,
)
guard unf is @prover.Unfinished(u) else { fail("expected an unfinished proof") }
assert_eq((u.hole_name, u.step_index, u.branch_path), (Some("h1"), Some(4), [1, 1]))
}
test "run a file" {
let src =
#|theorem before_hole : ⊢ T := by exact truth
#|qed
#|
#|theorem unfinished_demo (x : bool) : ⊢ x -> x ∨ (x ∨ x) := by
#| intro h
#| right { right { hole h1 } }
#|qed
#|
#|theorem after_hole : ⊢ T := by exact truth
#|qed
let st = @kernel.empty_kernel_state()
guard @prover.prove_theorem_file_results_detailed(st, src, @prover.default_prover_options())
is @prover.FileChecked(report) else {
fail("expected the file to parse")
}
let kinds = report.items.map(item => match item {
@prover.ProverItemProved(_) => "ok"
@prover.ProverItemFailed(_) => "error"
@prover.ProverItemUnfinished(_) => "unfinished"
})
assert_eq(kinds, ["ok", "unfinished", "ok"])
}
The file is examples/multi_with_hole.qed.
Regression corpus
The corpus is the single list of scripts that the tests run and that the documentation may quote. Each case has an identifier, a script, the layout of its steps, and the capability or failure it demonstrates.
positive_corpus_cases, negative_corpus_cases, unfinished_corpus_cases and quantifier_corpus_cases
These functions return the cases of each kind.
pub fn positive_corpus_cases() -> Array[PositiveCorpusCase]
pub fn negative_corpus_cases() -> Array[NegativeCorpusCase]
pub fn unfinished_corpus_cases() -> Array[UnfinishedCorpusCase]
pub fn quantifier_corpus_cases() -> Array[QuantifierCorpusCase]
PositiveCorpusCase
A positive case must prove expected_goal after declaring the boolean constants const_names.
pub struct PositiveCorpusCase {
case_id : String
const_names : Array[String]
expected_goal : String
script : String
layout : ScriptLayout
catalog_class : PositiveCatalogClass
capability : PositiveTacticCapability
}
NegativeCorpusCase
A negative case must fail with the error shape expected_error. The constants are declared as booleans, unary or binary boolean functions.
pub struct NegativeCorpusCase {
case_id : String
bool_consts : Array[String]
unary_bool_consts : Array[String]
binary_bool_consts : Array[String]
script : String
layout : ScriptLayout
failure_class : NegativeFailureClass
expected_error : NegativeErrorShape
}
UnfinishedCorpusCase
An unfinished case must stop at the given step, branch path and hole, with the given detail.
pub struct UnfinishedCorpusCase {
case_id : String
const_names : Array[String]
expected_goal : String
script : String
layout : ScriptLayout
expected_step_index : Int
expected_branch_path : Array[Int]
expected_hole_name : String?
expected_detail : String
}
QuantifierCorpusCase and QuantifierSurfaceOutcome
A quantifier case covers the binder and raw forall surface and may succeed, fail or be unfinished.
pub struct QuantifierCorpusCase {
case_id : String
script : String
layout : ScriptLayout
kind : MappingCaseKind
outcome : QuantifierSurfaceOutcome
capability_label : String
manual_anchor : String
expected_step_index : Int?
expected_branch_path : Array[Int]
expected_hole_name : String?
expected_detail : String?
}
pub enum QuantifierSurfaceOutcome {
QuantifierSuccess
QuantifierFailure
QuantifierUnfinished
}
Classification enums
These enums classify cases; the label functions below render them as the stable strings used in tests and documentation.
pub enum ScriptLayout {
InlineSequential
BlockSequential
BranchStructured
}
pub enum PositiveCatalogClass {
LocalFact
DirectClose
ImplicationBacked
ContextDerived
StructuralOnly
}
pub enum PositiveTacticCapability {
IntroExact
Assumption
ExactNamedDirectClose
ExactContextDerived
ApplyLocalImp
ApplyNamedImp
Split
Left
Right
Mixed
}
pub enum NegativeFailureClass {
FailureUnknownName
FailureWrongModeTheoremUsage
FailureGoalShapeMismatch
FailureNonBoolConnector
FailureUnsupportedHonestFailure
FailureShadowingOrScopeDrift
}
pub enum NegativeErrorShape {
ErrorTacticUnknownName
ErrorTacticApplyMismatch
ErrorTacticGoalShapeMismatch
ErrorTacticUnsolvedGoals
ErrorLogicNotBoolTerm
}
script_layout_label, catalog_label, capability_label, negative_failure_class_label, negative_error_shape_label and quantifier_surface_outcome_label
These functions return the stable snake-case label of a classification value, such as inline_sequential, local_fact or intro_exact.
pub fn script_layout_label(ScriptLayout) -> String
pub fn catalog_label(PositiveCatalogClass) -> String
pub fn capability_label(PositiveTacticCapability) -> String
pub fn negative_failure_class_label(NegativeFailureClass) -> String
pub fn negative_error_shape_label(NegativeErrorShape) -> String
pub fn quantifier_surface_outcome_label(QuantifierSurfaceOutcome) -> String
Mapping matrix
The mapping matrix links each corpus case to the documentation anchor where it may appear, such as manual:runnable_examples, and says whether it is a public example.
MappingMatrixEntry and mapping_matrix_entries
pub struct MappingMatrixEntry {
case_id : String
kind : MappingCaseKind
script : String
catalog_or_failure_label : String
capability_or_error_label : String
doc_anchor : String
doc_visibility : MappingDocVisibility
}
pub fn mapping_matrix_entries() -> Array[MappingMatrixEntry]
MappingCaseKind, mapping_case_positive, mapping_case_negative and mapping_case_unfinished
MappingCaseKind says whether a case is positive, negative or unfinished; the three functions return its constructors.
pub enum MappingCaseKind {
PositiveCase
NegativeCase
UnfinishedCase
}
pub fn mapping_case_positive() -> MappingCaseKind
pub fn mapping_case_negative() -> MappingCaseKind
pub fn mapping_case_unfinished() -> MappingCaseKind
MappingDocVisibility and its constructor functions
MappingDocVisibility says whether a case may be quoted in the documentation as an example, as a failure example, or not at all. mapping_doc_visibility_public_unfinished_example returns PublicExample: unfinished examples are public examples.
pub enum MappingDocVisibility {
PublicExample
PublicFailureExample
InternalOnly
}
pub fn mapping_doc_visibility_public_example() -> MappingDocVisibility
pub fn mapping_doc_visibility_public_failure_example() -> MappingDocVisibility
pub fn mapping_doc_visibility_public_unfinished_example() -> MappingDocVisibility
pub fn mapping_doc_visibility_internal_only() -> MappingDocVisibility
test "corpus" {
let first = @prover.positive_corpus_cases()[0]
inspect(first.case_id, content="pos_intro_exact_identity")
inspect(first.script, content="theorem pos_intro_exact_identity : ⊢ P -> P := by intro h; exact h")
inspect(@prover.capability_label(first.capability), content="intro_exact")
// every corpus case appears in the mapping matrix with an anchor
let entry = @prover.mapping_matrix_entries().search_by(e => e.case_id == first.case_id).unwrap()
inspect(@prover.mapping_matrix_entries()[entry].doc_anchor, content="manual:runnable_examples")
}