Alectryon

repository·main·Indexed 43 days ago

https://github.com/cpitclaudel/alectryon

A library and toolkit for processing Rocq (Coq) and Lean 4 snippets embedded in text documents. Version 2.0.0 allows developers to create literate programming documents, such as textbooks and blog posts, by annotating code segments with goals and messages from a prover. It supports a pipeline-based architecture for extracting snippets from HTML, LaTeX, and Typst, and generating outputs in formats including HTML, LaTeX, and JSON.

Tokens
49.1K
Snippets
174
Records
224
Agent score
55%

What's inside Alectryon

  1. Use the .. coq:: directive in reStructuredText

    main

    To include Coq code fragments within a reStructuredText document, use the .. coq:: directive. These fragments are processed by Alectryon and sent to the Coq prover. You can also nest these directives inside other reST directives like .. list-table::.

    .. coq::
    
       Lemma le_l : forall y x, S x <= y -> x <= y.
         induction y; inversion 1; subst.
         all: info_eauto.
       Qed.
  2. Use Alectryon markers and references (Experimental)

    main

    Alectryon provides tools to link to specific parts of the prover's output (goals, hypotheses, or sentences) using a marker-placement mini-language.

    Roles:

    • :mref: (Marker Reference): Creates a link to a specific object (e.g., a goal or hypothesis).
    • :mquote: (Marker Quote): Inserts a copy of the target (e.g., a hypothesis type) instead of a link.

    Targeting Blocks: By default, markers refer to the most recent .. coq:: block. To target a specific block, prepend .io#name to the argument.

    Example Syntax:

    • :mref:".s(Induction).h#IHm"
    • :mquote:".s(Induction).h#IHm.type" (prints the type of the hypothesis)

    Output Assertions: Use the .. massert:: directive to perform "poor lad's unit tests". It checks that every line in its body is a valid reference to a previous goal.

  3. Extend Lean using metaprogramming

    main

    Lean allows you to write its own tactics and procedures using its internal API. This is known as metaprogramming.

    • Monadic Interface: Most internal data structures and procedures are exposed via a monadic interface.
    • The meta Keyword: Definitions that use the metaprogramming API (which access points outside the formal framework) must be annotated with the meta keyword. This informs Lean that these definitions are part of the metaprogramming layer rather than the trusted kernel.
    • Safety: Even if a tactic is buggy, the resulting proof is always checked by the Lean kernel. If the tactic produces an invalid term, the kernel will reject it.
    open expr tactic
    
    private meta def contra_aux : list expr → list expr → tactic unit
    | []         hs := failed
    | (h₁ :: rs) hs := 
        do t₀ ← infer_type h₁,
           t  ← whnf t₀,
           (do a ← match_not t,
                h₂ ← find_same_type a hs,
                tgt ← target,
                pr ← mk_app `absurd [tgt, h₂, h₁],
                exact pr)
           <|> contra_aux rs hs
    
    meta def contra : tactic unit := 
    do ctx ← local_context,
       contra_aux ctx ctx
  4. Configure MathJax via processHtmlClass

    main

    Instead of manually wrapping text in delimiters via JavaScript, you can configure MathJax to look for specific HTML classes using the processHtmlClass option. This is more robust but requires careful setup to ensure definitions are processed before MathJax loads.

    1. Set the configuration:
      MathJax = { options: { processHtmlClass: 'mathjax_process|alectryon-io' } };
    2. Customize the startup: Use MathJax.startup.pageReady to inject the necessary \( ... \) delimiters into the page before the default pageReady runs.
    3. Handle loading order: Ensure this configuration is loaded before the MathJax script. In Alectryon's standalone mode, the defer option is already configured, making this reliable. In Sphinx, you must use mathjax_loading_method = "defer" or a separate JS file.
    MathJax = {};
    MathJax.options = { processHtmlClass: 'mathjax_process|alectryon-io' };
    
    MathJax.startup = {
        pageReady: function () {
            // ... Custom code to add \( ... \) delimiters
            return MathJax.startup.defaultPageReady();
        }
    }
  5. Document Lean3 proofs and capture goals

    main

    Alectryon captures goals and hypotheses as Lean3 proofs progress. This allows you to document the state of a proof step-by-step.

    Similar to the Coq integration, you can use the following features to reference or quote the captured proof state:

    • References (:mref:): Link to specific steps or hypotheses (e.g., :mref:.s(intro H)`).
    • Quotes (:mquote:): Quote specific parts of the proof state (e.g., :mquote:.s(and.intro).h#H.type`).
    • Assertions (.. massert::): Assert properties about the proof state at a specific goal or step.
    example (p q r : Prop) : p ∧ q ↔ q ∧ p :=
    begin /- .none -/
      apply iff.intro, {
        intro H,
        apply and.intro, /- .unfold -/
        apply (and.elim_right H),
        apply (and.elim_left H),
      }, {
        intro H,
        apply and.intro,
        apply (and.elim_right H),
        apply (and.elim_left H),
      }
    end

    Usage in reST:

    .. massert:: .s(apply iff.intro).g#2
    .. mquote:: .s(apply iff.intro).g#2.ccl
  6. Capture Lean4 proofs and goals

    main

    When writing Lean4 code in Alectryon, the tool automatically captures goals and hypotheses as proofs progress. This allows you to document the step-by-step state of a proof within your literate document.

    .. lean4::
    
       theorem test (p q : Prop) (hp : p) (hq : q): p ∧ q ↔ q ∧ p := by
         apply Iff.intro
         . intro h
           apply And.intro
           . exact hq
           . exact hp
         . intro h
           apply And.intro
           . exact hp
           . exact hq
  7. Write proofs using tactics

    main

    Lean supports two primary styles of writing proofs:

    1. Term Style

    Proofs are constructed as explicit expressions in the formal language. For existential statements, you can use Unicode angle brackets ⟨ ... ⟩ (typed as \< and \>) or ASCII equivalents (| ... |) to package a witness and a proof of the property.

    2. Tactic Style

    Tactic-style proofs use instructions (tactics) that tell Lean how to construct the required expression. This is often more readable and allows for automation. Common tactics include:

    • intros: Introduces variables into the local context.
    • cases: Performs case analysis on a hypothesis.
    • unfold: Expands a definition.
    • existsi: Provides a witness for an existential quantifier.
    • simp: Invokes the built-in simplifier to prove assertions using provided facts.
    • by <tactic>: The by keyword transitions from term mode to tactic mode.
    theorem even_add : ∀ m n, even m → even n → even (n + m) :=
    begin
      intros m n hm hn,
      cases hm with k hk,
      cases hn with l hl,
      unfold even,
      existsi (k + l),
      simp [hk, hl, mul_add, add_sym]
    end
  8. Use math and nested markup in MyST

    main

    When writing MyST documents for Alectryon, you can use the following math and markup conventions:

    • Math: Use Docutils math roles like {math}e^{i\pi} = -1or use$signs if thedollarmathoption is enabled in your configuration (e.g.,$\cos(\pi) = -1$`).
    • Nested Markup: Unlike reStructuredText, MyST supports nested built-in inline markup. You can nest code blocks within other roles, such as coqid for Coq identifiers or standard Markdown links.

    Example of nested roles: {coqid}references <Coq.Init.Nat#even>orlinks`

  9. Customize proof rendering (Experimental)

    main

    Alectryon allows you to filter or hide parts of the proof output using annotations within the marker-placement syntax. This is useful for cleaning up the display of goals and hypotheses.

    Filtering Syntax

    • Exclude by name: Use -.h#<name> to hide hypotheses starting with a certain pattern.
    • Exclude by pattern: Use -.h#[aA] to hide hypotheses named 'a' or 'A'.
    • Limit output: Use -.s(selector).msg(key) to reduce a command's output to only a specific message/warning.
    • Hide lines: Use -.s{<text>} to completely hide a specific line (e.g., -.s{Proof.}).
    • Goal selection: You can exclude all goals and then re-include specific ones using -.g#* followed by specific indices.

    Note: This feature is currently experimental.

    .. coq:: -.h#l* -.h#[aA] -.s(Check let).msg(let) -.s{Proof.}
       :name: pr
    
       Proof. ... Qed.
  10. Use the marker-placement mini-language to reference proof parts

    main

    Alectryon allows you to selectively show, hide, or link to specific parts of a proof (sentences, goals, messages, hypotheses, conclusions) using CSS-inspired paths.

    Path Syntax:

    • .s(*pattern*): Search for a sentence matching pattern. Use (pattern) for literal matching or {pattern} for fnmatch style.
    • .msg(*str*): Match a message containing str.
    • .g#id: Select goal by number (e.g., .g#1).
    • .g(*str*): Select goal whose conclusion includes str.
    • .h#name: Match hypothesis by name (e.g., .h#IHn).
    • .h(*str*): Match hypothesis by content.
    • .ccl: Match the conclusion of a goal.
    • .type / .body / .name: Match hypothesis properties.

    Example Paths:

    • .s(Induction).h#IHm: Find sentence matching 'Induction', then find hypothesis 'IHm' in its first goal.
    • .g#2.h#*: Hide all hypotheses in goal 2.

    Experimental Features:

    • Exclusion: Use -. prefix (e.g., -.h#IHn to hide hypothesis IHn).
    • Key-Value Attributes: Append [key]=value to a path (e.g., .msg[lang]=haskell to use the Haskell lexer for message bodies).
    ## Example Pathing
    
    - `.s(Goal ∀)` -> Search for sentence matching content
    - `.s(Base case).ccl` -> Sentence matching 'Base case', then its first goal's conclusion
    - `.s(Induction).h#IHm` -> Sentence matching 'Induction', then hypothesis 'IHm' in its first goal
  11. Insert numbered references using the marker-placement mini-language

    main

    Alectryon allows you to reference specific sentences, goals, and hypotheses within code fragments using a mini-language. This creates links to the corresponding parts of the proof output.

    Reference Types

    • By Text Search: Use :literal::mref:`search-term```. Alectryon searches for the text and automatically labels the first matching sentence.
    • By Manual Label: Use :literal::mref:`label <target>`` to pick a specific label.
    • By Path (Granular Access):
      • .s(sentence_name): A specific sentence.
      • .g#index: A specific goal (e.g., .g#1).
      • .h#name: A specific hypothesis.
      • .h(type_or_name): A hypothesis by its type or name.
      • .ccl: The conclusion of a goal.

    Example Usage

    .. coq::
       :name: setup
    
       Fixpoint plus_comm (n m: nat) : n + m = m + n.
       Proof.
         destruct n eqn:Heq.
         - (* Base case *) 
           rewrite <- plus_n_O; reflexivity.
    
    # Reference the Fixpoint command
    Starting at :mref:`Fixpoint plus_comm`.
    
    # Reference a specific goal/hypothesis path
    In the first case (:mref:`.s(destruct n).g#1`), we see the variable in the context (:mref:`.s(destruct n).g#1.h#n`).
    :mref:`Fixpoint plus_comm"
    :mref:`.s(destruct n).g#1"
    :mref:`.s(destruct n).g#1.h#n"
  12. Compile reST files to HTML with Alectryon

    main

    To compile a reStructuredText (.rst) file into an HTML document using Alectryon, use the following command in your terminal:

    alectryon <filename>.rst

    This will process the Coq directives within the file and produce an HTML output (e.g., filename.html).

    alectryon display_flags.rst