├── .gitattributes ├── .gitconfig ├── .github └── workflows │ └── pypi.yml ├── .gitignore ├── CHANGES.rst ├── CITATION.bib ├── LICENSE ├── Makefile ├── README.rst ├── alectryon.py ├── alectryon ├── __init__.py ├── __main__.py ├── assets │ ├── alectryon.css │ ├── alectryon.js │ ├── alectryon.sty │ └── docutils_basic.css ├── cli.py ├── coq.py ├── coqc_time.py ├── core.py ├── docutils.py ├── html.py ├── json.py ├── latex.py ├── lean3.py ├── lean4.py ├── literate.py ├── markers.py ├── minimal.py ├── myst.py ├── pygments.py ├── pygments_lexer.py ├── pygments_style.py ├── serapi.py ├── sexp.py ├── sphinx.py └── transforms.py ├── deps └── requirements.txt ├── etc ├── dumptables.py ├── elisp │ ├── .gitignore │ ├── Cask │ ├── alectryon.el │ └── screenshot │ │ ├── .gitignore │ │ ├── capture.el │ │ └── example.v ├── lint_changes.py ├── regen_makefile.py ├── screenshot.svg ├── svg │ ├── bubble.svg │ ├── square-bubble-xl-wide.svg │ ├── square-bubble-xl.svg │ ├── square-bubble.svg │ └── wireframe-bubble.svg └── vm │ └── provision.sh ├── pyproject.toml ├── recipes ├── .gitignore ├── Makefile ├── _output │ ├── alectryon_custom_driver.out │ ├── api.out │ ├── api.rst.out │ ├── caching.html │ ├── caching.v.cache.xz │ ├── coq_drivers.coqc.html │ ├── coq_drivers.html │ ├── coq_drivers.noexec.html │ ├── coqdoc.html │ ├── custom_highlighting.html │ ├── custom_highlighting.with_driver.html │ ├── custom_stylesheet.html │ ├── fragments.snippets.html │ ├── fragments.snippets.tex │ ├── fragments.v.io.json │ ├── lean3-tutorial.html │ ├── lean3-tutorial.lean3 │ ├── literate_MyST.html │ ├── literate_coq.html │ ├── literate_coq.min.rst │ ├── literate_coq.min.stdin.rst │ ├── literate_coq.tex │ ├── literate_coq.v.rst │ ├── literate_lean3.html │ ├── literate_lean3.lean3.rst │ ├── literate_lean3.xe.tex │ ├── literate_lean4.html │ ├── literate_lean4.lean.rst │ ├── literate_lean4.xe.tex │ ├── literate_reST.html │ ├── literate_reST.min.stdin.v │ ├── literate_reST.min.v │ ├── literate_reST.tex │ ├── literate_reST.v │ ├── mathjax.html │ ├── minification.html │ ├── minimal.html │ ├── minimal.no-alectryon.html │ ├── plain-lean3.lean.html │ ├── plain-lean4.lean.html │ ├── plain.v.html │ ├── polyglot.html │ ├── references.html │ ├── references.xe.tex │ └── tests │ │ ├── alternative_clis.out │ │ ├── auto_toggle.html │ │ ├── cache_v1.html │ │ ├── cache_v2.html │ │ ├── cli_flags.txt │ │ ├── coqc_time_error.out │ │ ├── corner_cases.html │ │ ├── corner_cases.lean3.html │ │ ├── corner_cases.xe.tex │ │ ├── dialects.4.html │ │ ├── dialects.5.html │ │ ├── dialects.lua.tex │ │ ├── dialects.tex │ │ ├── dialects.xe.tex │ │ ├── directive-options.html │ │ ├── directive-options.xe.tex │ │ ├── display-flags.html │ │ ├── docinfo_flags.txt │ │ ├── doctests.out │ │ ├── errors.lint.json │ │ ├── errors.py.out │ │ ├── errors.sh.out │ │ ├── errors.txt │ │ ├── excepthook.v.out │ │ ├── fatal.v.out │ │ ├── fatal_transform.v.out │ │ ├── frontend_warnings.json.out │ │ ├── latex_formatting.tex │ │ ├── lean3_error.out │ │ ├── lists.lean3.html │ │ ├── literate.marked-empty.rst │ │ ├── literate.marked-end.rst │ │ ├── literate.v │ │ ├── literate.v.rst │ │ ├── minification.v.html │ │ ├── misc.html │ │ ├── plain_cli.noext.html │ │ ├── plain_cli.stdin.html │ │ ├── plain_cli.tmp.html │ │ ├── recording.snippets.html │ │ ├── recording.snippets.tex │ │ ├── recording.v.html │ │ ├── recording.v.io.json │ │ ├── screenshot.html │ │ ├── stylesheets.html │ │ ├── stylesheets.part.tex │ │ ├── syntax_highlighting.html │ │ ├── unit.py.out │ │ ├── unterminated.html │ │ ├── unterminated.rst.out │ │ └── unterminated.v.out ├── alectryon_custom_driver.py ├── api.py ├── api.rst ├── backstop │ ├── .gitignore │ ├── Makefile │ ├── backstop.config.js │ ├── backstop.sh │ ├── backstop_data │ │ └── engine_scripts │ │ │ └── puppet │ │ │ ├── clickAndHoverHelper.js │ │ │ └── onReady.js │ ├── package-lock.json │ ├── pdfjs.html │ └── screenshot.js ├── caching.v ├── coq_drivers.v ├── coqdoc.v ├── custom_highlighting.v ├── custom_stylesheet.css ├── custom_stylesheet.docutils.conf ├── custom_stylesheet.rst ├── docutils.conf ├── fragments.v.json ├── lean3-tutorial.rst ├── literate.docutils.conf ├── literate_MyST.md ├── literate_coq.v ├── literate_lean3.lean ├── literate_lean4.lean ├── literate_reST.rst ├── mathjax.rst ├── minification.rst ├── minimal.rst ├── plain-lean3.lean ├── plain-lean4.lean ├── plain.v ├── polyglot.rst ├── recipes.mk ├── references.docutils.conf ├── references.rst ├── sphinx │ ├── .gitignore │ ├── Makefile │ ├── MyST.md │ ├── _build │ │ ├── alectryon │ │ │ ├── MyST.md.cache │ │ │ ├── coqchapter.v.cache │ │ │ ├── index.rst.cache │ │ │ └── math.rst.cache │ │ ├── html │ │ │ ├── MyST.html │ │ │ ├── coqchapter.html │ │ │ ├── index.html │ │ │ └── math.html │ │ └── latex │ │ │ └── alectryon-demo.tex │ ├── _static │ │ └── mathjax_config.js │ ├── conf.py │ ├── coqchapter.v │ ├── docutils.conf │ ├── index.rst │ └── math.rst ├── src │ └── imported.v ├── tests.mk └── tests │ ├── alternative_clis.py │ ├── auto_toggle.rst │ ├── cache_v1.v │ ├── cache_v1.v.cache │ ├── cache_v2.v │ ├── cache_v2.v.cache │ ├── cli_flags.rst │ ├── coqc_time_error.rst │ ├── corner_cases.lean3 │ ├── corner_cases.rst │ ├── dialects.rst │ ├── directive-options.rst │ ├── display-flags.rst │ ├── docinfo_flags.rst │ ├── doctests.py │ ├── errors.py │ ├── errors.rst │ ├── errors.sh │ ├── excepthook.v │ ├── fatal.v │ ├── fatal_transform.v │ ├── frontend_warnings.json │ ├── latex_formatting.v │ ├── lean3_error.rst │ ├── lists.lean3 │ ├── literate.rst │ ├── literate.v │ ├── minification.v │ ├── misc.rst │ ├── plain_cli.rst │ ├── recording.v │ ├── recording.v.io.json │ ├── screenshot.v │ ├── stylesheets.docutils.conf │ ├── stylesheets.v │ ├── syntax_highlighting.v │ ├── unit.py │ ├── unterminated.rst │ └── unterminated.v ├── setup.cfg └── setup.py /.gitattributes: -------------------------------------------------------------------------------- 1 | *.cache.xz diff=xz 2 | -------------------------------------------------------------------------------- /.gitconfig: -------------------------------------------------------------------------------- 1 | # Include into repo config after checkout using `git config --local include.path ../.gitconfig` 2 | [diff "xz"] 3 | binary = true 4 | textconv = xzcat 5 | -------------------------------------------------------------------------------- /.github/workflows/pypi.yml: -------------------------------------------------------------------------------- 1 | name: Upload Python Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Set up Python 13 | uses: actions/setup-python@v2 14 | with: 15 | python-version: '3.x' 16 | - name: Install dependencies 17 | run: | 18 | python -m pip install --upgrade pip 19 | pip install build twine 20 | - name: Build and publish 21 | env: 22 | TWINE_USERNAME: __token__ 23 | TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} 24 | run: | 25 | make upload -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dist/ 2 | __pycache__/ 3 | alectryon.egg-info/* 4 | deps/.venv -------------------------------------------------------------------------------- /CITATION.bib: -------------------------------------------------------------------------------- 1 | @InProceedings{Alectryon+SLE2020, 2 | author = {Pit-Claudel, Clément}, 3 | title = {Untangling Mechanized Proofs}, 4 | booktitle = {Proceedings of the 13th {ACM} {SIGPLAN} International 5 | Conference on Software Language Engineering}, 6 | year = 2020, 7 | series = {{SLE} 2020}, 8 | pages = {155–174}, 9 | address = {New York, NY, USA}, 10 | publisher = {Association for Computing Machinery}, 11 | isbn = 9781450381765, 12 | url = {https://pit-claudel.fr/clement/papers/alectryon-SLE20.pdf}, 13 | doi = {10.1145/3426425.3426940}, 14 | abstract = {Proof assistants like Coq, Lean, or HOL4 rely heavily on 15 | stateful meta-programs called scripts to assemble 16 | proofs. Unlike pen-and-paper proofs, proof scripts only 17 | describe the steps to take (induct on x, apply a theorem, …), 18 | not the states that these steps lead to; as a result, plain 19 | proof scripts are essentially incomprehensible without the 20 | assistance of an interactive user interface able to run the 21 | script and show the corresponding proof states. Until now, the 22 | standard process to communicate a proof without forcing 23 | readers to execute its script was to manually copy-paste 24 | intermediate proof states into the script, as source code 25 | comments — a tedious and error-prone exercise. Additional 26 | prose (such as for a book or tutorial) was likewise embedded 27 | in comments, preserving executability at the cost of a 28 | mediocre text-editing experience. This paper describes a new 29 | approach to the development and dissemination of literate 30 | proof scripts, with a focus on the Coq proof 31 | assistant. Specifically, we describe two contributions: a 32 | compiler that interleaves Coq’s output with the original proof 33 | script to produce interactive webpages that are complete, 34 | self-contained presentations of Coq proofs; and a new literate 35 | programming toolkit that allows authors to switch seamlessly 36 | between prose- and code-oriented views of the same sources, by 37 | translating back and forth between reStructuredText documents 38 | and literate Coq source files. In combination, these tools 39 | offer a new way to write, communicate, and preserve proofs, 40 | combining the flexibility of procedural proof scripts and the 41 | intelligibility of declarative proofs.}, 42 | numpages = 20, 43 | keywords = {formal verification, proof browsing, literate programming, 44 | proof presentation}, 45 | location = {Virtual, USA} 46 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2019-2020 Clément Pit-Claudel 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | python_venv := deps/.venv 2 | python_bin := $(python_venv)/bin 3 | export PATH := $(abspath $(python_bin)):$(PATH) 4 | 5 | binaries := $(python_bin)/pip 6 | dependencies := $(binaries) 7 | 8 | .PHONY: test coverage develop dist upload lint-changes lint 9 | 10 | ## Main targets 11 | 12 | test: $(dependencies) 13 | +$(MAKE) -C recipes clean 14 | +$(MAKE) -C recipes 15 | 16 | dist: $(dependencies) 17 | python -m build 18 | 19 | upload: dist 20 | python -m twine upload dist/* 21 | 22 | FORCE: 23 | recipes/%: FORCE 24 | +$(MAKE) -C recipes --always-make "$*" 25 | 26 | ## Dependencies 27 | 28 | ifeq ($(MAKECMDGOALS), init) 29 | 30 | $(python_venv): 31 | python3 -m venv $(python_venv) 32 | 33 | init: $(python_venv) 34 | pip install -r deps/requirements.txt 35 | 36 | else 37 | 38 | $(dependencies): 39 | $(error Dependency $(notdir $@) not set up; try `make init`?) 40 | 41 | endif 42 | 43 | ## Local development 44 | 45 | lint-changes: $(dependencies) 46 | etc/lint_changes.py CHANGES.rst 47 | 48 | lint: $(dependencies) 49 | pylint --rcfile=setup.cfg alectryon 50 | mypy alectryon/ 51 | pyright --project . 52 | 53 | coverage: $(dependencies) 54 | +$(MAKE) -C recipes coverage 55 | 56 | develop: $(dependencies) 57 | (which opam || { echo "OPAM not found; please install it"; exit 1; }) 58 | eval $$(opam env); opam install coq-serapi 59 | pip install coverage[toml] 60 | pip install -e .[full] 61 | -------------------------------------------------------------------------------- /alectryon.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright © 2019 Clément Pit-Claudel 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | from alectryon.cli import main 24 | 25 | if __name__ == '__main__': 26 | main() 27 | -------------------------------------------------------------------------------- /alectryon/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2019 Clément Pit-Claudel 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in all 11 | # copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | # SOFTWARE. 20 | 21 | """Annotate segments of Coq code with responses and goals.""" 22 | 23 | __version__ = "1.5.0-dev" 24 | __author__ = 'Clément Pit-Claudel' 25 | GENERATOR = "Alectryon" 26 | -------------------------------------------------------------------------------- /alectryon/__main__.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Clément Pit-Claudel 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in all 11 | # copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | # SOFTWARE. 20 | 21 | from .cli import main 22 | main() 23 | -------------------------------------------------------------------------------- /alectryon/coqc_time.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Clément Pit-Claudel 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in all 11 | # copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | # SOFTWARE. 20 | 21 | import tempfile 22 | import re 23 | from pathlib import Path 24 | 25 | from .core import CLIDriver, EncodedDocument, Positioned, Position, Sentence, Text 26 | from .serapi import CoqIdents 27 | 28 | class CoqcTime(CLIDriver): 29 | BIN = "coqc" 30 | NAME = "Coq+coqc-time" 31 | CLI_ARGS = ("-time", "-color", "no", "-quiet", "-q") 32 | 33 | ID = "coqc_time" 34 | LANGUAGE = "coq" 35 | 36 | COQ_TIME_RE = re.compile(r"^Chars (?P[0-9]+) - (?P[0-9]+) ", 37 | re.MULTILINE) 38 | 39 | def _find_sentences(self, document): 40 | with tempfile.TemporaryDirectory(prefix="alectryon_coqc-time") as wd: 41 | source = Path(wd) / CoqIdents.topfile_of_fpath(self.fpath) 42 | source.write_bytes(document.contents) 43 | stdout = self.run_cli(more_args=[str(source)]) 44 | for m in self.COQ_TIME_RE.finditer(stdout): 45 | beg, end = int(m.group("beg")), int(m.group("end")) 46 | yield Positioned(beg, end, Sentence(document[beg:end], [], [])) 47 | 48 | def partition(self, contents): 49 | return EncodedDocument.intersperse_text_fragments(contents, self._find_sentences(contents)) 50 | 51 | def annotate(self, chunks): 52 | r"""Use ``coqc -time`` to fragment multiple chunks of Coq code. 53 | 54 | >>> CoqcTime().annotate(["Check 1. (* … *) ", "Print nat."]) 55 | [[Sentence(contents='Check 1.', messages=[], goals=[]), 56 | Text(contents=' (* … *) ')], 57 | [Sentence(contents='Print nat.', messages=[], goals=[])]] 58 | >>> CoqcTime().annotate(["Check (* … *)", "1."]) 59 | [[Sentence(contents='Check (* … *)', messages=[], goals=[])], 60 | [Sentence(contents='1.', messages=[], goals=[])]] 61 | """ 62 | document = EncodedDocument(chunks, "\n", encoding="utf-8") 63 | try: 64 | fragments = self.partition(document) 65 | return list(document.recover_chunks(fragments)) 66 | except ValueError as e: 67 | self.observer.notify(None, str(e), Position(self.fpath, 0, 1).as_range(), level=3) 68 | return [[Text(c)] for c in chunks] 69 | -------------------------------------------------------------------------------- /alectryon/myst.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Clément Pit-Claudel 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in all 11 | # copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | # SOFTWARE. 20 | 21 | """MyST support for Alectryon. 22 | 23 | This file extends ``docutils.py`` to expose a convenient interface for 24 | connecting with MyST. 25 | 26 | This is mostly useful through Alectryon's command line. For integration with 27 | Sphinx, use the ``myst_parser`` and the ``alectryon.sphinx`` plugins. 28 | """ 29 | 30 | from typing import Type 31 | 32 | import docutils.parsers 33 | 34 | try: 35 | from myst_parser.docutils_ import Parser as MystParser 36 | 37 | # https://github.com/executablebooks/MyST-Parser/issues/347 38 | # https://github.com/executablebooks/MyST-Parser/pull/419 39 | class RealParser(MystParser): 40 | def get_transforms(self): 41 | from .docutils import ActivateMathJaxTransform 42 | return super().get_transforms() + [ActivateMathJaxTransform] 43 | 44 | Parser: Type[docutils.parsers.Parser] = RealParser 45 | 46 | except ImportError as err: 47 | class FallbackParser(docutils.parsers.Parser): 48 | def parse(self, inputstring, document): 49 | document.append(document.reporter.severe( 50 | 'Cannot parse Markdown input without Python package `myst_parser`.')) 51 | Parser = FallbackParser 52 | -------------------------------------------------------------------------------- /alectryon/sexp.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | SEXP_SPECIAL = re.compile(rb'[ ()"]') 4 | STR_SPECIAL = re.compile(rb'[\\"]') 5 | OPEN, CLOSE, ESCAPE, QUOTE = map(ord, r'()\"') 6 | 7 | STRING_QUOTES = [(b'\\', b'\\'), (b'"', b'"'), (b'\r', b'r'), (b'\n', b'n'), 8 | (b'\b', b'b'), (b'\f', b'f'), (b'\t', b't')] 9 | 10 | STRING_QUOTE_RE = re.compile(rb'[\\"\r\n\b\f\t]') 11 | STRING_UNQUOTE_RE = re.compile(rb'\\[\\"rnbft]') 12 | STRING_QUOTE_TBL = {raw[0]: b"\\" + quoted for raw, quoted in STRING_QUOTES} 13 | STRING_UNQUOTE_TBL = {quoted[0]: raw for raw, quoted in STRING_QUOTES} 14 | 15 | class ParseError(ValueError): 16 | pass 17 | 18 | def unescape_1(m): 19 | return STRING_UNQUOTE_TBL[m.string[m.start() + 1]] 20 | 21 | def unescape(bs): 22 | return STRING_UNQUOTE_RE.sub(unescape_1, bs) 23 | 24 | def escape_1(m): 25 | return STRING_QUOTE_TBL[m.string[m.start()]] 26 | 27 | def escape(bs): 28 | return STRING_QUOTE_RE.sub(escape_1, bs) 29 | 30 | def tostr(bs): 31 | return unescape(bs).decode('utf-8') 32 | 33 | def tokenize_str(view, start, str_special=STR_SPECIAL): 34 | pos = start 35 | while True: 36 | m = str_special.search(view, pos) 37 | if m is None: 38 | raise ParseError("Unterminated string: {!r}@{}.".format(view, start)) 39 | if view[m.start()] == ESCAPE: 40 | pos = m.end() + 1 41 | else: 42 | yield view[start:m.start()] 43 | return m.end() 44 | 45 | def tokenize(view, sexp_special=SEXP_SPECIAL): 46 | pos = 0 47 | while True: 48 | m = sexp_special.search(view, pos) 49 | if m is None: 50 | break 51 | mstart, mend = m.span() 52 | if mstart > pos: 53 | yield view[pos:mstart] 54 | pos = mend 55 | special = view[mstart] 56 | if special in (OPEN, CLOSE): 57 | yield special 58 | elif special == QUOTE: 59 | pos = yield from tokenize_str(view, pos) 60 | if len(view) > pos: 61 | yield view[pos:] 62 | 63 | def parse(tokens): 64 | top = [] 65 | stack = [] 66 | for tok in tokens: 67 | if tok is OPEN: 68 | new = [] 69 | top.append(new) 70 | stack.append(top) 71 | top = new 72 | elif tok is CLOSE: 73 | top = stack.pop() 74 | else: 75 | top.append(tok) 76 | return top[0] 77 | 78 | def load(bs): 79 | try: 80 | return parse(tokenize(bs)) 81 | except IndexError as e: 82 | raise ParseError("Unbalanced input") from e 83 | 84 | def unparse(sexp, buf): 85 | stack = [sexp] 86 | while stack: 87 | top = stack.pop() 88 | if isinstance(top, list): 89 | buf.append(OPEN) 90 | stack.append(CLOSE) 91 | stack.extend(reversed(top)) 92 | elif isinstance(top, bytes): 93 | buf.append(QUOTE) 94 | buf.extend(top) 95 | buf.append(QUOTE) 96 | else: 97 | assert isinstance(top, int) 98 | buf.append(top) 99 | 100 | def dump(sexp): 101 | buf = bytearray() 102 | unparse(sexp, buf) 103 | return buf 104 | -------------------------------------------------------------------------------- /deps/requirements.txt: -------------------------------------------------------------------------------- 1 | #coq==8.18.0 2 | pygments==2.14.0 3 | dominate==2.7.0 4 | beautifulsoup4==4.11.2 5 | docutils==0.19 6 | myst_parser==1.0.0 7 | sphinx==6.1.3 8 | alabaster==0.7.16 9 | -------------------------------------------------------------------------------- /etc/elisp/.gitignore: -------------------------------------------------------------------------------- 1 | /.cask/ -------------------------------------------------------------------------------- /etc/elisp/Cask: -------------------------------------------------------------------------------- 1 | (source gnu) 2 | (source melpa) 3 | 4 | (package-file "alectryon.el") 5 | 6 | (depends-on "proof-general") 7 | (depends-on "polymode") 8 | 9 | (development 10 | (depends-on "company-coq") 11 | (depends-on "adaptive-wrap")) 12 | -------------------------------------------------------------------------------- /etc/elisp/screenshot/.gitignore: -------------------------------------------------------------------------------- 1 | *.svg 2 | *.png -------------------------------------------------------------------------------- /etc/elisp/screenshot/example.v: -------------------------------------------------------------------------------- 1 | (*| 2 | Conjunction 3 | =========== 4 | 5 | To prove that `P /\ Q` holds, we must present evidence for both `P` and `Q`. Thus, it makes sense to define a proof object for `P /\ Q` as consisting of a **pair** of two proofs: one for `P` and another one for `Q`. This leads to the following definition. 6 | |*) 7 | 8 | Inductive and (P Q : Prop) : Prop := 9 | | conj : P -> Q -> and P Q. 10 | 11 | (*| 12 | Notice the similarity with the definition of the `prod` type, given in chapter `Poly `_; the only difference is that `prod` takes `Type` arguments, whereas `and` takes `Prop` arguments. 13 | |*) 14 | 15 | Print prod. (* .unfold *) 16 | 17 | (*| 18 | This similarity should clarify why `destruct` and `intros` patterns can be used on a conjunctive hypothesis. Case analysis allows us to consider all possible ways in which `P /\ Q` was proved -- here just one (the `conj` constructor). […] 19 | |*) 20 | 21 | Lemma and_comm : 22 | forall P Q : Prop, P /\ Q <-> Q /\ P. 23 | Proof. 24 | intros P Q. split. 25 | - (* → *) intros [HP HQ]. split. 26 | { apply HQ. } { apply HP. } 27 | - (* ← *) intros [HP HQ]. split. 28 | { apply HQ. } { apply HP. } 29 | Qed. 30 | 31 | (*| 32 | .. exercise:: conj_fact 33 | :difficulty: 2 34 | :optional: 35 | 36 | Construct a *proof of the following proposition: 37 | |*) 38 | 39 | Definition conj_fact : 40 | forall P Q R, P /\ Q -> Q /\ R -> P /\ R 41 | (* REPLACE THIS LINE WITH ":= …." *). 42 | Admitted. 43 | -------------------------------------------------------------------------------- /etc/lint_changes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Check for incorrect commit IDs and update automatically 3 | 4 | # FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch -f --tree-filter "make lint-changes; git add CHANGES.rst" START..HEAD 5 | 6 | import re 7 | import subprocess 8 | import shlex 9 | import sys 10 | 11 | SINGLE_HASH = re.compile(r"[a-f0-9]{6,}") 12 | ANNOT = re.compile(r"\[{}(, *{})*\]".format( 13 | SINGLE_HASH.pattern, SINGLE_HASH.pattern)) 14 | 15 | def shell(cmd): 16 | with subprocess.Popen(shlex.split(cmd), encoding="utf-8", 17 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p: 18 | stdout, stderr = p.communicate() 19 | if p.returncode != 0: 20 | print(stderr, file=sys.stderr, end='') 21 | return None 22 | return stdout 23 | 24 | def sub1(m): 25 | sha = m.group() 26 | if shell(f'git merge-base --is-ancestor "{sha}" HEAD') is None: 27 | print(f"{sha} is not an ancestor of HEAD", file=sys.stderr) 28 | if shell(f'git cat-file -e "{sha}^{{commit}}"') is not None: 29 | msg = shell(f'git log --format=%s -n 1 "{sha}"').strip() 30 | print(f" {msg}", file=sys.stderr) 31 | for line in shell('git log --format="%h %s"').splitlines(): 32 | if msg in line: 33 | print(f" {line}", file=sys.stderr) 34 | return next(SINGLE_HASH.finditer(line)).group() 35 | return sha 36 | 37 | def subn(m): 38 | return SINGLE_HASH.sub(sub1, m.group()) 39 | 40 | def main(): 41 | # changes = 0 42 | with open(sys.argv[1]) as f: 43 | contents = f.read() 44 | with open(sys.argv[1], mode="w") as f: 45 | f.write(ANNOT.sub(subn, contents)) 46 | 47 | if __name__ == '__main__': 48 | main() 49 | -------------------------------------------------------------------------------- /etc/regen_makefile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import re 5 | from pathlib import Path 6 | from fnmatch import fnmatch 7 | 8 | CMD_RE = re.compile(r""" 9 | (?:^[ ]+(?=alectryon)|To[ ]compile:|[$])[ ]*(?P[^\s]+)[ ]+ 10 | (?P(?:.|\\\n)*?) # Allow escaped newlines in arguments 11 | \s+[#][ ]+ # Allows newline before "#" 12 | (?P.+?)[;,][\n ]+produces[ ]+ 13 | ‘(?P.+?)’ 14 | """, re.VERBOSE | re.MULTILINE) 15 | 16 | def parse_rules(path: str): 17 | with open(path) as f: 18 | contents = f.read() 19 | for m in CMD_RE.finditer(contents): 20 | yield m.groupdict() 21 | 22 | RULE_TEMPLATE = """\ 23 | # {comment} 24 | {out}: {fpath}{dir_deps} 25 | {cmd} 26 | {prefix}_targets += {out}\ 27 | """ 28 | 29 | def escape(s): 30 | return s.replace("$", "$$").replace("#", "##") 31 | 32 | CUSTOM_DRIVER_RE = re.compile(r"\b(alectryon_[a-z_]+[.]py)\b") 33 | 34 | def gen_rule(fpath, prefix, outdir, params): 35 | params = { k: escape(v) for (k, v) in params.items() } 36 | 37 | # Remove escaped newlines 38 | params["args"] = re.sub(r"\s+\\\n\s+", " ", params["args"]) 39 | 40 | params["cmd"] = (params["cmd"] + " " + params["args"]) \ 41 | .replace("alectryon ", "$(alectryon) ") \ 42 | .replace("python ", "$(PYTHON) ") \ 43 | .replace(params["out"], "$@") \ 44 | .replace(fpath.name, "$<") 45 | 46 | params["cmd"] = CUSTOM_DRIVER_RE.sub(r"\1 $(alectryon_opts)", params["cmd"]) 47 | 48 | params["out"] = str(outdir / params["out"]) 49 | 50 | needs_outdir = "$(alectryon)" not in params["cmd"] 51 | params["dir_deps"] = " | {}/".format(outdir) if needs_outdir else "" 52 | 53 | return params["out"], RULE_TEMPLATE.format(fpath=fpath, prefix=prefix, **params) 54 | 55 | HEADER = """\ 56 | # -*- makefile -*- 57 | ### Auto-generated with etc/regen_makefile.py ### 58 | ### Do not edit! ### 59 | 60 | {outdir}/: 61 | mkdir -p $@ 62 | 63 | {prefix}_targets := 64 | """ 65 | 66 | FOOTER = """\ 67 | $({prefix}_targets): out_dir := {outdir} 68 | targets += $({prefix}_targets)\ 69 | """ 70 | 71 | EXCLUDED_SOURCES = { 72 | "*docutils.conf", 73 | "*.v.cache", 74 | "flycheck_*.py", 75 | "custom_stylesheet.css" 76 | } 77 | 78 | def main(): 79 | prefix = sys.argv[1] 80 | outdir = Path(sys.argv[2]) 81 | print(HEADER.format(prefix=prefix, outdir=outdir)) 82 | 83 | all_targets = [] 84 | for fname in sorted(sys.argv[3:]): 85 | src, targets = Path(fname), [] 86 | if any(fnmatch(src.name, pattern) for pattern in EXCLUDED_SOURCES): 87 | continue 88 | for params in parse_rules(src): 89 | dst, rule = gen_rule(src, prefix, outdir, params) 90 | targets.append(dst) 91 | print(rule) 92 | if targets: 93 | all_targets.extend(targets) 94 | print() 95 | else: 96 | print("Not sure how to compile {}".format(fname), file=sys.stderr) 97 | 98 | print(FOOTER.format(prefix=prefix, outdir=outdir)) 99 | 100 | if __name__ == '__main__': 101 | main() 102 | -------------------------------------------------------------------------------- /etc/svg/bubble.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /etc/svg/square-bubble-xl-wide.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /etc/svg/square-bubble-xl.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /etc/svg/square-bubble.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /etc/svg/wireframe-bubble.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=42", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [tool.pyright] 6 | include = ["alectryon"] 7 | reportOptionalMemberAccess = false 8 | reportWildcardImportFromLibrary = false 9 | 10 | [tool.mypy] 11 | ignore_missing_imports = true 12 | 13 | [tool.coverage.run] 14 | # branch = True 15 | 16 | [tool.coverage.report] 17 | exclude_lines = [ 18 | "pragma: no cover", 19 | "def __repr__", 20 | "assert False", 21 | "raise AssertionError", 22 | "raise NotImplementedError", 23 | "raise UnexpectedError", 24 | ] -------------------------------------------------------------------------------- /recipes/.gitignore: -------------------------------------------------------------------------------- 1 | /_output/**/*.css 2 | /_output/**/*.js 3 | /_output/**/*.pdf 4 | /_output/**/*.sty 5 | /_output/latex.aux/ 6 | **/sphinx/_build/doctrees 7 | **/sphinx/_build/html/[._]* 8 | **/sphinx/_build/html/*index* 9 | **/sphinx/_build/html/*search* 10 | **/sphinx/_build/html/*.inv 11 | /coverage/ 12 | -------------------------------------------------------------------------------- /recipes/Makefile: -------------------------------------------------------------------------------- 1 | default: all 2 | 3 | SHELL := bash -o pipefail 4 | 5 | export PYTHON ?= python3 -Wd # -X tracemalloc 6 | export SPHINXBUILD ?= $(PYTHON) "$(shell which sphinx-build)" 7 | export COVERAGE_FILE := $(abspath coverage/.coverage) 8 | export COVERAGE_RCFILE := $(abspath ../pyproject.toml) 9 | 10 | COVERAGE ?= 11 | ifneq (, $(COVERAGE)) 12 | PYTHON := $(PYTHON) -m coverage run --source=$(abspath ../alectryon) --parallel-mode 13 | endif 14 | 15 | PYTHONPATH ?= 16 | export PYTHONPATH:=$(realpath ../):$(PYTHONPATH) 17 | 18 | ALECTRYON_OPTS ?= 19 | 20 | alectryon_opts = --copy-assets hardlink --output-directory $(out_dir) --no-version-numbers $(ALECTRYON_OPTS) 21 | alectryon = $(PYTHON) ../alectryon.py $(alectryon_opts) 22 | 23 | targets := 24 | other := 25 | 26 | %/: 27 | mkdir -p $@ 28 | .PRECIOUS: %/ 29 | 30 | ######################## 31 | # Auto-generated rules # 32 | ######################## 33 | 34 | recipes.mk: ../etc/regen_makefile.py $(filter-out %.mk,$(wildcard *.*)) 35 | ./$< recipes _output $(filter-out $<,$^) > $@ 36 | 37 | tests.mk: ../etc/regen_makefile.py $(filter-out %.mk,$(wildcard tests/*.*)) 38 | ./$< tests _output/tests/ $(filter-out $<,$^) > $@ 39 | 40 | include recipes.mk tests.mk 41 | 42 | # TODO 43 | # _output/mathjax.tex: mathjax.rst | _output/ 44 | # $(alectryon) --backend latex $< 45 | 46 | ########## 47 | # Sphinx # 48 | ########## 49 | 50 | sphinx_html := sphinx/_build/html/index.html 51 | sphinx_pdf := sphinx/_build/latex/alectryon-demo.pdf 52 | 53 | $(sphinx_html) $(sphinx_pdf): export SPHINXBUILD := $(PYTHON) "$(shell which sphinx-build)" 54 | 55 | $(sphinx_html): sphinx/index.rst 56 | +$(MAKE) -C sphinx html 57 | 58 | $(sphinx_pdf): sphinx/index.rst 59 | +$(MAKE) -C sphinx latexpdf 60 | 61 | targets += $(sphinx_html) $(sphinx_pdf) 62 | 63 | ############## 64 | # Screenshot # 65 | ############## 66 | 67 | _output/tests/screenshot.pdf: backstop/screenshot.js _output/tests/screenshot.html 68 | ./$^ $@ 69 | ../etc/screenshot.svg: _output/tests/screenshot.pdf 70 | pdf2svg $< $@ 71 | svgcleaner --multipass --indent 2 $@ $@ 72 | 73 | ################ 74 | # LaTeX → PDF # 75 | ################ 76 | 77 | _output/%.pdf: _output/latex.aux/%.pdf 78 | mv $< $@ 79 | 80 | .SECONDEXPANSION: 81 | 82 | _output/latex.aux/%.xe.pdf: _output/%.xe.tex | $$(dir $$@) 83 | texfot --tee=/dev/null --no-stderr latexmk -cd -pdfxe -output-directory=$(shell realpath --relative-to $(dir $<) $(dir $@)) $< 84 | 85 | _output/latex.aux/%.lua.pdf: _output/%.lua.tex | $$(dir $$@) 86 | texfot --tee=/dev/null --no-stderr latexmk -cd -pdflua -output-directory=$(shell realpath --relative-to $(dir $<) $(dir $@)) $< 87 | 88 | _output/latex.aux/%.pdf: _output/%.tex | $$(dir $$@) 89 | texfot --tee=/dev/null --no-stderr latexmk -cd -pdf -output-directory=$(shell realpath --relative-to $(dir $<) $(dir $@)) $< 90 | 91 | other += $(patsubst %.tex,%.pdf,$(filter-out %.snippets.tex %.part.tex,$(filter %.tex,$(targets)))) 92 | 93 | ############### 94 | # Entry point # 95 | ############### 96 | 97 | alectryon: $(targets) 98 | 99 | all: $(targets) $(other) 100 | 101 | coverage: 102 | mkdir -p coverage 103 | +$(MAKE) clean 104 | +$(MAKE) COVERAGE=1 alectryon 105 | cd coverage && coverage combine && coverage html 106 | .PHONY: coverage 107 | 108 | ########### 109 | # Cleanup # 110 | ########### 111 | 112 | clean: 113 | rm -rf *.mk coverage/ _output/ sphinx/_build/ 114 | 115 | .PHONY: clean 116 | FORCE: 117 | -------------------------------------------------------------------------------- /recipes/_output/alectryon_custom_driver.out: -------------------------------------------------------------------------------- 1 | Alectryon 2 | -------------------------------------------------------------------------------- /recipes/_output/api.out: -------------------------------------------------------------------------------- 1 | [[Sentence(contents='Check 1.', messages=[Message(contents='1\n : nat')], goals=[])]] 2 | ================================================================================ 3 | [[{"_type": "sentence", "contents": "Goal True /\\ True.", "messages": [], "goals": [{"_type": "goal", "name": null, "conclusion": "True /\\ True", "hypotheses": []}]}, {"_type": "text", "contents": " "}, {"_type": "sentence", "contents": "split.", "messages": [], "goals": [{"_type": "goal", "name": null, "conclusion": "True", "hypotheses": []}, {"_type": "goal", "name": null, "conclusion": "True", "hypotheses": []}]}, {"_type": "text", "contents": " "}], [{"_type": "sentence", "contents": "all: eauto.", "messages": [], "goals": []}]] 4 | ================================================================================ 5 | 6 | \begin{alectryon} 7 | % Generator: Alectryon 8 | \sep 9 | \begin{sentence} 10 | \begin{input} 11 | \PY{k+kn}{Goal}~\PY{k+kt}{True}~\PY{o}{/\PYZbs{}}~\PY{k+kt}{True}\PY{o}{.} 12 | \end{input} 13 | \sep 14 | \begin{output} 15 | \begin{goals} 16 | \begin{goal} 17 | \begin{hyps}\end{hyps} 18 | \sep 19 | \infrule{} 20 | \sep 21 | \begin{conclusion} 22 | \PY{k+kt}{True}~\PY{o}{/\PYZbs{}}~\PY{k+kt}{True} 23 | \end{conclusion} 24 | \end{goal} 25 | \end{goals} 26 | \end{output} 27 | \end{sentence} 28 | \sep 29 | \begin{sentence} 30 | \begin{input} 31 | \PY{n+nb}{split}\PY{o}{.} 32 | \end{input} 33 | \sep 34 | \begin{output} 35 | \begin{goals} 36 | \begin{goal} 37 | \begin{hyps}\end{hyps} 38 | \sep 39 | \infrule{} 40 | \sep 41 | \begin{conclusion} 42 | \PY{k+kt}{True} 43 | \end{conclusion} 44 | \end{goal} 45 | \sep 46 | \begin{extragoals} 47 | \begin{goal} 48 | \begin{hyps}\end{hyps} 49 | \sep 50 | \infrule{} 51 | \sep 52 | \begin{conclusion} 53 | \PY{k+kt}{True} 54 | \end{conclusion} 55 | \end{goal} 56 | \end{extragoals} 57 | \end{goals} 58 | \end{output} 59 | \end{sentence} 60 | \end{alectryon} 61 | 62 | 63 | \begin{alectryon} 64 | % Generator: Alectryon 65 | \sep 66 | \begin{sentence} 67 | \begin{input} 68 | \PY{k+kp}{all}\PY{o}{:}~\PY{n+nb}{eauto}\PY{o}{.} 69 | \end{input} 70 | \end{sentence} 71 | \end{alectryon} 72 | 73 | -------------------------------------------------------------------------------- /recipes/_output/caching.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Caching results for faster execution 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
Built with Alectryon, running Coq+SerAPI. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑ Ctrl+↓ to navigate, Ctrl+🖱️ to focus. On Mac, use instead of Ctrl.
18 |

Caching results for faster execution

19 | 20 |

Alectryon can generate cache files to memoize Coq's output, yielding faster compilation when Coq fragments embedded in a document have not changed:

21 |
22 | alectryon --cache-directory _output/ --cache-compression=xz caching.v
23 | # Coq+reST → HTML, cached to _output/caching.v.cache; produces ‘caching.html’
24 | 
25 |

(The --cache-compression flag is option; the default is to not compress caches.)

26 |
Inductive nat : Set := O : nat | S : nat -> nat. 27 | 28 | Arguments S _%nat_scope
29 |
30 |
31 | 32 | -------------------------------------------------------------------------------- /recipes/_output/caching.v.cache.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpitclaudel/alectryon/5a35676636766d2c6de8f1856a4bc168a3515ecb/recipes/_output/caching.v.cache.xz -------------------------------------------------------------------------------- /recipes/_output/custom_stylesheet.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Using a custom CSS stylesheet with Alectryon 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
Built with Alectryon, running . Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑ Ctrl+↓ to navigate, Ctrl+🖱️ to focus. On Mac, use instead of Ctrl.
19 |

Using a custom CSS stylesheet with Alectryon

20 | 21 |

There are multiple ways to add custom stylesheets to an Alectryon document:

22 |
    23 |
  1. Use a .. raw:: html block:

    24 |
    25 | .. raw:: html
    26 | 
    27 |    <link rel="stylesheet" href="custom.css" />
    28 | 
    29 |
  2. 30 |
  3. Use a custom Alectryon driver and modify alectyron.html.ASSETS.DOCUTILS_CSS:

    31 |
    32 | alectryon.html.ASSETS.DOCUTILS_CSS += ("custom.css",)
    33 | 
    34 |
  4. 35 |
  5. If using Sphinx, add your stylesheet in conf.py:

    36 |
    37 | html_css_files = ['css/custom.css']
    38 | 
    39 |
  6. 40 |
  7. If using plain Docutils, use the stylesheet option:

    41 |
    42 | [html writers]
    43 | stylesheet=custom.css
    44 | 
    45 |

    As of this writing, the stylesheet_path option does not work with Alectryon.

    46 |
  8. 47 |
48 |

To compile this file using the last option:

49 |
50 |
51 |
$ DOCUTILSCONFIG=custom_stylesheet.docutils.conf alectryon
52 |
53 |
custom_stylesheet.rst
54 |
# Coq+reST → HTML; produces ‘custom_stylesheet.html’
55 |
56 |
57 |
58 |
59 |
60 |
61 | 62 | -------------------------------------------------------------------------------- /recipes/_output/fragments.snippets.html: -------------------------------------------------------------------------------- 1 |
(* Alectyron can process individual chunks of Coq Code fed to in in a JSON file. *)
2 |
(* The output is a new JSON file in which each sentence has been annotated with Coq's output. *)
3 |
(* To compile: *)
4 |
(* $ alectryon fragments.v.json # JSON → JSON; produces ‘fragments.v.io.json’ *)
5 |
(* $ alectryon fragments.v.json --backend snippets-html # JSON → HTML; produces ‘fragments.snippets.html’ *)
6 |
(* $ alectryon fragments.v.json --backend snippets-latex # JSON → LaTeX; produces ‘fragments.snippets.tex’ *)
7 |
H: False

True
exact I. Qed.
8 |
xyz = fun _ : False => I 9 | : False -> True 10 | 11 | Arguments xyz H
12 | -------------------------------------------------------------------------------- /recipes/_output/fragments.snippets.tex: -------------------------------------------------------------------------------- 1 | \begin{alectryon} 2 | % Generator: Alectryon 3 | \sep 4 | \begin{txt} 5 | \PY{c}{(*~Alectyron~can~process~individual~chunks~of~Coq~Code~fed~to~in~in~a~JSON~file.~*)} 6 | \end{txt} 7 | \end{alectryon} 8 | %% alectryon-block-end 9 | \begin{alectryon} 10 | % Generator: Alectryon 11 | \sep 12 | \begin{txt} 13 | \PY{c}{(*~The~output~is~a~new~JSON~file~in~which~each~sentence~has~been~annotated~with~Coq\PYZsq{}s~output.~*)} 14 | \end{txt} 15 | \end{alectryon} 16 | %% alectryon-block-end 17 | \begin{alectryon} 18 | % Generator: Alectryon 19 | \sep 20 | \begin{txt} 21 | \PY{c}{(*~To~compile:~*)} 22 | \end{txt} 23 | \end{alectryon} 24 | %% alectryon-block-end 25 | \begin{alectryon} 26 | % Generator: Alectryon 27 | \sep 28 | \begin{txt} 29 | \PY{c}{(*~\PYZdl{}~alectryon~fragments.v.json~\PYZsh{}~JSON~→~JSON;~produces~‘fragments.v.io.json’~*)} 30 | \end{txt} 31 | \end{alectryon} 32 | %% alectryon-block-end 33 | \begin{alectryon} 34 | % Generator: Alectryon 35 | \sep 36 | \begin{txt} 37 | \PY{c}{(*~\PYZdl{}~alectryon~fragments.v.json~\PYZhy{}\PYZhy{}backend~snippets\PYZhy{}html~\PYZsh{}~JSON~→~HTML;~produces~‘fragments.snippets.html’~*)} 38 | \end{txt} 39 | \end{alectryon} 40 | %% alectryon-block-end 41 | \begin{alectryon} 42 | % Generator: Alectryon 43 | \sep 44 | \begin{txt} 45 | \PY{c}{(*~\PYZdl{}~alectryon~fragments.v.json~\PYZhy{}\PYZhy{}backend~snippets\PYZhy{}latex~\PYZsh{}~JSON~→~LaTeX;~produces~‘fragments.snippets.tex’~*)} 46 | \end{txt} 47 | \end{alectryon} 48 | %% alectryon-block-end 49 | \begin{alectryon} 50 | % Generator: Alectryon 51 | \sep 52 | \begin{sentence} 53 | \begin{input} 54 | \PY{k+kn}{Example}~\PY{n+nf}{xyz}~\PY{o}{(}\PY{n+nv}{H}\PY{o}{:}~\PY{k+kt}{False}\PY{o}{):}~\PY{k+kt}{True}\PY{o}{.}~\PY{c}{(*~...~*)} 55 | \end{input} 56 | \sep 57 | \begin{output} 58 | \begin{goals} 59 | \begin{goal} 60 | \begin{hyps} 61 | \hyp{H}{\PY{k+kt}{False}} 62 | \end{hyps} 63 | \sep 64 | \infrule{} 65 | \sep 66 | \begin{conclusion} 67 | \PY{k+kt}{True} 68 | \end{conclusion} 69 | \end{goal} 70 | \end{goals} 71 | \end{output} 72 | \end{sentence} 73 | \sep 74 | \begin{sentence} 75 | \begin{input} 76 | \PY{n+nb+bp}{exact}~\PY{n}{I}\PY{o}{.}~ 77 | \end{input} 78 | \end{sentence} 79 | \sep 80 | \begin{sentence} 81 | \begin{input} 82 | \PY{k+kn}{Qed}\PY{o}{.} 83 | \end{input} 84 | \end{sentence} 85 | \end{alectryon} 86 | %% alectryon-block-end 87 | \begin{alectryon} 88 | % Generator: Alectryon 89 | \sep 90 | \begin{sentence} 91 | \begin{input} 92 | \PY{k+kn}{Print}~\PY{n}{xyz}\PY{o}{.} 93 | \end{input} 94 | \sep 95 | \begin{output} 96 | \begin{messages} 97 | \begin{message} 98 | \PY{n}{xyz}~\PY{o}{=}~\PY{k+kr}{fun}~\PY{n+nv}{\PYZus{}}~\PY{o}{:}~\PY{k+kt}{False}~\PY{o}{=\PYZgt{}}~\PY{n}{I}\nl 99 | ~~~~~\PY{o}{:}~\PY{k+kt}{False}~\PY{o}{\PYZhy{}\PYZgt{}}~\PY{k+kt}{True}\nl 100 | \nl 101 | \PY{k+kn}{Arguments}~\PY{n}{xyz}~\PY{n}{H} 102 | \end{message} 103 | \end{messages} 104 | \end{output} 105 | \end{sentence} 106 | \end{alectryon} 107 | %% alectryon-block-end 108 | -------------------------------------------------------------------------------- /recipes/_output/fragments.v.io.json: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | { 4 | "_type": "text", 5 | "contents": "(* Alectyron can process individual chunks of Coq Code fed to in in a JSON file. *)" 6 | } 7 | ], 8 | [ 9 | { 10 | "_type": "text", 11 | "contents": "(* The output is a new JSON file in which each sentence has been annotated with Coq's output. *)" 12 | } 13 | ], 14 | [ 15 | { 16 | "_type": "text", 17 | "contents": "(* To compile: *)" 18 | } 19 | ], 20 | [ 21 | { 22 | "_type": "text", 23 | "contents": "(* $ alectryon fragments.v.json # JSON \u2192 JSON; produces \u2018fragments.v.io.json\u2019 *)" 24 | } 25 | ], 26 | [ 27 | { 28 | "_type": "text", 29 | "contents": "(* $ alectryon fragments.v.json --backend snippets-html # JSON \u2192 HTML; produces \u2018fragments.snippets.html\u2019 *)" 30 | } 31 | ], 32 | [ 33 | { 34 | "_type": "text", 35 | "contents": "(* $ alectryon fragments.v.json --backend snippets-latex # JSON \u2192 LaTeX; produces \u2018fragments.snippets.tex\u2019 *)" 36 | } 37 | ], 38 | [ 39 | { 40 | "_type": "sentence", 41 | "contents": "Example xyz (H: False): True.", 42 | "messages": [], 43 | "goals": [ 44 | { 45 | "_type": "goal", 46 | "name": null, 47 | "conclusion": "True", 48 | "hypotheses": [ 49 | { 50 | "_type": "hypothesis", 51 | "names": [ 52 | "H" 53 | ], 54 | "body": null, 55 | "type": "False" 56 | } 57 | ] 58 | } 59 | ] 60 | }, 61 | { 62 | "_type": "text", 63 | "contents": " (* ... *) " 64 | }, 65 | { 66 | "_type": "sentence", 67 | "contents": "exact I.", 68 | "messages": [], 69 | "goals": [] 70 | }, 71 | { 72 | "_type": "text", 73 | "contents": " " 74 | }, 75 | { 76 | "_type": "sentence", 77 | "contents": "Qed.", 78 | "messages": [], 79 | "goals": [] 80 | } 81 | ], 82 | [ 83 | { 84 | "_type": "sentence", 85 | "contents": "Print xyz.", 86 | "messages": [ 87 | { 88 | "_type": "message", 89 | "contents": "xyz = fun _ : False => I\n : False -> True\n\nArguments xyz H" 90 | } 91 | ], 92 | "goals": [] 93 | } 94 | ] 95 | ] -------------------------------------------------------------------------------- /recipes/_output/literate_lean3.lean3.rst: -------------------------------------------------------------------------------- 1 | ================================================== 2 | Literate programming with Alectryon (Lean3 input) 3 | ================================================== 4 | 5 | Alectryon supports literate programs and documents (combinations of code and prose) written in Lean3 and reStructuredText. Here is an example written in Lean3. It can be converted to reST, HTML, or LaTeX using the following commands:: 6 | 7 | alectryon --frontend lean3+rst literate_lean3.lean 8 | # Coq+reST → HTML; produces ‘literate_lean3.html’ 9 | alectryon --frontend lean3+rst literate_lean3.lean --backend latex \ 10 | --latex-dialect xelatex \ 11 | -o literate_lean3.xe.tex 12 | # Coq+reST → LaTeX; produces ‘literate_lean3.xe.tex’ 13 | alectryon --frontend lean3+rst literate_lean3.lean --backend rst 14 | # Coq+reST → reST; produces ‘literate_lean3.lean3.rst’ 15 | 16 | ----- 17 | 18 | .. default-role:: lean3 19 | 20 | Running queries 21 | =============== 22 | 23 | Alectryon captures the results of `#check`, `#eval`, and the like: 24 | 25 | .. lean3:: 26 | 27 | #reduce let x := 5 in x + 3 28 | 29 | By default, these results are folded and are displayed upon hovering or clicking. We can unfold them by default using annotations or directives: 30 | 31 | .. lean3:: 32 | 33 | #check nat /- .unfold -/ 34 | 35 | .. lean3:: unfold 36 | 37 | #check bool 38 | #eval 1 + 1 39 | 40 | Other flags can be used to control display, like ``.no-in``: 41 | 42 | .. lean3:: 43 | 44 | #print iff /- .unfold .no-in -/ 45 | 46 | Documenting proofs 47 | ================== 48 | 49 | Alectryon also captures goals and hypotheses as proofs progress: 50 | 51 | .. lean3:: 52 | 53 | example (p q r : Prop) : p ∧ q ↔ q ∧ p := 54 | begin /- .none -/ 55 | apply iff.intro, { 56 | intro H, 57 | apply and.intro, /- .unfold -/ 58 | apply (and.elim_right H), 59 | apply (and.elim_left H), 60 | }, { 61 | intro H, 62 | apply and.intro, 63 | apply (and.elim_right H), 64 | apply (and.elim_left H), 65 | } 66 | end 67 | 68 | Most features available for Coq are also available for Lean3; in particular, references (:mref:`.s(intro H)`, :mref:`.s(and.intro).h#H`), quotes (:mquote:`.s(and.intro).h#H.type`) and assertions should work. 69 | 70 | .. massert:: .s(apply iff.intro).g#2 71 | .. mquote:: .s(apply iff.intro).g#2.ccl 72 | 73 | For now, please refer to the main README and to the Coq examples for more information. 74 | -------------------------------------------------------------------------------- /recipes/_output/literate_lean4.lean.rst: -------------------------------------------------------------------------------- 1 | ================================================== 2 | Literate programming with Alectryon (Lean4 input) 3 | ================================================== 4 | 5 | Alectryon supports literate programs and documents (combinations of code and prose) written in Lean4 and reStructuredText. Here is an example written in Lean4. It can be converted to reST, HTML, or LaTeX using the following commands:: 6 | 7 | alectryon literate_lean4.lean 8 | # Lean4+reST → HTML; produces ‘literate_lean4.html’ 9 | alectryon literate_lean4.lean --backend latex \ 10 | --latex-dialect xelatex \ 11 | -o literate_lean4.xe.tex 12 | # Lean4+reST → LaTeX; produces ‘literate_lean4.xe.tex’ 13 | alectryon literate_lean4.lean --backend rst 14 | # Lean4+reST → reST; produces ‘literate_lean4.lean.rst’ 15 | 16 | ----- 17 | 18 | .. default-role:: lean4 19 | 20 | Running queries 21 | =============== 22 | 23 | Alectryon captures the results of `#check`, `#eval`, and the like: 24 | 25 | .. lean4:: 26 | 27 | def x : Nat := 5 28 | #reduce 5 + x 29 | 30 | By default, these results are folded and are displayed upon hovering or clicking. We can unfold them by default using annotations or directives: 31 | 32 | .. lean4:: 33 | 34 | #check Nat /- .unfold -/ 35 | 36 | .. lean4:: unfold 37 | 38 | #check Bool 39 | #eval 1 + 1 40 | 41 | Other flags can be used to control display, like ``.no-in``: 42 | 43 | .. lean4:: 44 | 45 | #print Iff /- .unfold .no-in -/ 46 | 47 | Documenting proofs 48 | ================== 49 | 50 | Alectryon also captures goals and hypotheses as proofs progress: 51 | 52 | .. lean4:: 53 | 54 | theorem test (p q : Prop) (hp : p) (hq : q): p ∧ q ↔ q ∧ p := by 55 | apply Iff.intro 56 | . intro h 57 | apply And.intro 58 | . exact hq 59 | . exact hp 60 | . intro h 61 | apply And.intro 62 | . exact hp 63 | . exact hq 64 | -------------------------------------------------------------------------------- /recipes/_output/literate_reST.min.stdin.v: -------------------------------------------------------------------------------- 1 | (*| 2 | ================================================== 3 | Literate programming with Alectryon (reST input) 4 | ================================================== 5 | 6 | .. raw:: latex 7 | 8 | \makeatletter\def\alectryon@nobreakspace{\alectryon@breakspace}\makeatother 9 | 10 | Alectryon supports literate programs and documents (combinations of code and prose) written in Coq and reStructuredText. Here is an example, written in reST. It can be converted to Coq, HTML, or LaTeX using the following commands:: 11 | 12 | alectryon literate_reST.rst 13 | # reST+Coq → HTML; produces ‘literate_reST.html’ 14 | $ DOCUTILSCONFIG=literate.docutils.conf alectryon \ 15 | literate_reST.rst --backend latex 16 | # reST+Coq → LaTeX; produces ‘literate_reST.tex’ 17 | alectryon literate_reST.rst --backend coq 18 | # reST+Coq → Coq; produces ‘literate_reST.v’ 19 | 20 | $ cd ..; python -m alectryon.literate \ 21 | recipes/literate_reST.rst > recipes/literate_reST.min.v 22 | # Minimal reST → Coq; produces ‘literate_reST.min.v’ 23 | $ cd ..; python -m alectryon.literate --rst2coq - \ 24 | < recipes/literate_reST.rst > recipes/literate_reST.min.stdin.v 25 | # Minimal reST → Coq; produces ‘literate_reST.min.stdin.v’ 26 | 27 | ---- 28 | 29 | Coq fragments are introduced with ``.. coq::``: 30 | |*) 31 | 32 | Lemma le_l : forall y x, S x <= y -> x <= y. 33 | induction y; inversion 1; subst. 34 | all: info_eauto. 35 | Qed. 36 | 37 | Goal forall x y z, x <= y <= z -> x <= z. 38 | 39 | (*| 40 | They can be nested nested into other reST directives, such as tables: 41 | 42 | .. list-table:: Coq commands 43 | :header-rows: 1 44 | :width: 90% 45 | 46 | - * Coq command 47 | * Description 48 | 49 | - * 50 | 51 | .. coq:: unfold 52 | |*) 53 | 54 | intros x y. 55 | 56 | (*| 57 | * Move the variables `x` and `y` into the context. 58 | 59 | - * 60 | 61 | .. coq:: unfold 62 | |*) 63 | 64 | revert x; induction y. 65 | 66 | (*| 67 | * Perform induction on `y`, generalizing `x`. 68 | 69 | - * 70 | 71 | .. coq:: unfold 72 | |*) 73 | 74 | all: intros x z (Hl & Hr). 75 | 76 | (*| 77 | * Move conjunction into the context, splitting it into `Hl` and `Hr`. 78 | 79 | - * 80 | 81 | .. coq:: unfold 82 | |*) 83 | 84 | - inversion Hl; assumption. 85 | 86 | (*| 87 | * Solve base case; `inversion` changes `x <= 0` into `x = 0`. 88 | 89 | - * 90 | 91 | .. coq:: unfold 92 | |*) 93 | 94 | - inversion Hl; subst; try congruence. 95 | apply IHy; split; info_eauto using le_l. 96 | 97 | (*| 98 | * Solve inductive case using the `le_l` lemma. 99 | |*) 100 | -------------------------------------------------------------------------------- /recipes/_output/literate_reST.min.v: -------------------------------------------------------------------------------- 1 | (*| 2 | ================================================== 3 | Literate programming with Alectryon (reST input) 4 | ================================================== 5 | 6 | .. raw:: latex 7 | 8 | \makeatletter\def\alectryon@nobreakspace{\alectryon@breakspace}\makeatother 9 | 10 | Alectryon supports literate programs and documents (combinations of code and prose) written in Coq and reStructuredText. Here is an example, written in reST. It can be converted to Coq, HTML, or LaTeX using the following commands:: 11 | 12 | alectryon literate_reST.rst 13 | # reST+Coq → HTML; produces ‘literate_reST.html’ 14 | $ DOCUTILSCONFIG=literate.docutils.conf alectryon \ 15 | literate_reST.rst --backend latex 16 | # reST+Coq → LaTeX; produces ‘literate_reST.tex’ 17 | alectryon literate_reST.rst --backend coq 18 | # reST+Coq → Coq; produces ‘literate_reST.v’ 19 | 20 | $ cd ..; python -m alectryon.literate \ 21 | recipes/literate_reST.rst > recipes/literate_reST.min.v 22 | # Minimal reST → Coq; produces ‘literate_reST.min.v’ 23 | $ cd ..; python -m alectryon.literate --rst2coq - \ 24 | < recipes/literate_reST.rst > recipes/literate_reST.min.stdin.v 25 | # Minimal reST → Coq; produces ‘literate_reST.min.stdin.v’ 26 | 27 | ---- 28 | 29 | Coq fragments are introduced with ``.. coq::``: 30 | |*) 31 | 32 | Lemma le_l : forall y x, S x <= y -> x <= y. 33 | induction y; inversion 1; subst. 34 | all: info_eauto. 35 | Qed. 36 | 37 | Goal forall x y z, x <= y <= z -> x <= z. 38 | 39 | (*| 40 | They can be nested nested into other reST directives, such as tables: 41 | 42 | .. list-table:: Coq commands 43 | :header-rows: 1 44 | :width: 90% 45 | 46 | - * Coq command 47 | * Description 48 | 49 | - * 50 | 51 | .. coq:: unfold 52 | |*) 53 | 54 | intros x y. 55 | 56 | (*| 57 | * Move the variables `x` and `y` into the context. 58 | 59 | - * 60 | 61 | .. coq:: unfold 62 | |*) 63 | 64 | revert x; induction y. 65 | 66 | (*| 67 | * Perform induction on `y`, generalizing `x`. 68 | 69 | - * 70 | 71 | .. coq:: unfold 72 | |*) 73 | 74 | all: intros x z (Hl & Hr). 75 | 76 | (*| 77 | * Move conjunction into the context, splitting it into `Hl` and `Hr`. 78 | 79 | - * 80 | 81 | .. coq:: unfold 82 | |*) 83 | 84 | - inversion Hl; assumption. 85 | 86 | (*| 87 | * Solve base case; `inversion` changes `x <= 0` into `x = 0`. 88 | 89 | - * 90 | 91 | .. coq:: unfold 92 | |*) 93 | 94 | - inversion Hl; subst; try congruence. 95 | apply IHy; split; info_eauto using le_l. 96 | 97 | (*| 98 | * Solve inductive case using the `le_l` lemma. 99 | |*) 100 | -------------------------------------------------------------------------------- /recipes/_output/literate_reST.v: -------------------------------------------------------------------------------- 1 | (*| 2 | ================================================== 3 | Literate programming with Alectryon (reST input) 4 | ================================================== 5 | 6 | .. raw:: latex 7 | 8 | \makeatletter\def\alectryon@nobreakspace{\alectryon@breakspace}\makeatother 9 | 10 | Alectryon supports literate programs and documents (combinations of code and prose) written in Coq and reStructuredText. Here is an example, written in reST. It can be converted to Coq, HTML, or LaTeX using the following commands:: 11 | 12 | alectryon literate_reST.rst 13 | # reST+Coq → HTML; produces ‘literate_reST.html’ 14 | $ DOCUTILSCONFIG=literate.docutils.conf alectryon \ 15 | literate_reST.rst --backend latex 16 | # reST+Coq → LaTeX; produces ‘literate_reST.tex’ 17 | alectryon literate_reST.rst --backend coq 18 | # reST+Coq → Coq; produces ‘literate_reST.v’ 19 | 20 | $ cd ..; python -m alectryon.literate \ 21 | recipes/literate_reST.rst > recipes/literate_reST.min.v 22 | # Minimal reST → Coq; produces ‘literate_reST.min.v’ 23 | $ cd ..; python -m alectryon.literate --rst2coq - \ 24 | < recipes/literate_reST.rst > recipes/literate_reST.min.stdin.v 25 | # Minimal reST → Coq; produces ‘literate_reST.min.stdin.v’ 26 | 27 | ---- 28 | 29 | Coq fragments are introduced with ``.. coq::``: 30 | |*) 31 | 32 | Lemma le_l : forall y x, S x <= y -> x <= y. 33 | induction y; inversion 1; subst. 34 | all: info_eauto. 35 | Qed. 36 | 37 | Goal forall x y z, x <= y <= z -> x <= z. 38 | 39 | (*| 40 | They can be nested nested into other reST directives, such as tables: 41 | 42 | .. list-table:: Coq commands 43 | :header-rows: 1 44 | :width: 90% 45 | 46 | - * Coq command 47 | * Description 48 | 49 | - * 50 | 51 | .. coq:: unfold 52 | |*) 53 | 54 | intros x y. 55 | 56 | (*| 57 | * Move the variables `x` and `y` into the context. 58 | 59 | - * 60 | 61 | .. coq:: unfold 62 | |*) 63 | 64 | revert x; induction y. 65 | 66 | (*| 67 | * Perform induction on `y`, generalizing `x`. 68 | 69 | - * 70 | 71 | .. coq:: unfold 72 | |*) 73 | 74 | all: intros x z (Hl & Hr). 75 | 76 | (*| 77 | * Move conjunction into the context, splitting it into `Hl` and `Hr`. 78 | 79 | - * 80 | 81 | .. coq:: unfold 82 | |*) 83 | 84 | - inversion Hl; assumption. 85 | 86 | (*| 87 | * Solve base case; `inversion` changes `x <= 0` into `x = 0`. 88 | 89 | - * 90 | 91 | .. coq:: unfold 92 | |*) 93 | 94 | - inversion Hl; subst; try congruence. 95 | apply IHy; split; info_eauto using le_l. 96 | 97 | (*| 98 | * Solve inductive case using the `le_l` lemma. 99 | |*) 100 | -------------------------------------------------------------------------------- /recipes/_output/tests/auto_toggle.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Test that a toggle is automatically inserted after a docinfo block 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
Built with Alectryon, running Coq+SerAPI. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑ Ctrl+↓ to navigate, Ctrl+🖱️ to focus. On Mac, use instead of Ctrl.
19 |

Test that a toggle is automatically inserted after a docinfo block

20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
Date:Test
28 |
29 |

To compile:

30 |
31 | alectryon auto_toggle.rst # reST → HTML; produces ‘auto_toggle.html’
32 | 
33 |
1 34 | : nat
35 |
36 |
37 | 38 | -------------------------------------------------------------------------------- /recipes/_output/tests/cache_v1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Cached reads 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
Built with Alectryon, running Coq+SerAPI. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑ Ctrl+↓ to navigate, Ctrl+🖱️ to focus. On Mac, use instead of Ctrl.
18 |

Cached reads

19 | 20 |

This file checks that reading from a cache works. To compile:

21 |
22 | alectryon cache_v1.v --cache-directory tests/
23 |   # Coq → HTML (cached); produces ‘cache_v1.html’
24 | 
25 |
1 26 | : nat
27 |
28 |
29 | 30 | -------------------------------------------------------------------------------- /recipes/_output/tests/cache_v2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Cached reads 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
Built with Alectryon, running Coq+SerAPI. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑ Ctrl+↓ to navigate, Ctrl+🖱️ to focus. On Mac, use instead of Ctrl.
18 |

Cached reads

19 | 20 |

This file checks that reading from a cache works. To compile:

21 |
22 | alectryon cache_v2.v --cache-directory tests/
23 |   # Coq → HTML (cached); produces ‘cache_v2.html’
24 | 
25 |
1 26 | : nat
27 |
28 |
29 | 30 | -------------------------------------------------------------------------------- /recipes/_output/tests/cli_flags.txt: -------------------------------------------------------------------------------- 1 | exit: 0 2 | -------------------------------------------------------------------------------- /recipes/_output/tests/coqc_time_error.out: -------------------------------------------------------------------------------- 1 | tests/coqc_time_error.rst:16: (ERROR/3) Driver Coq+coqc-time (coqc) exited with code 1: 2 | Error: 3 | There are pending proofs : Unnamed_thm. 4 | 5 | exit: 13 6 | -------------------------------------------------------------------------------- /recipes/_output/tests/dialects.lua.tex: -------------------------------------------------------------------------------- 1 | \documentclass[a4paper]{article} 2 | % generated by Docutils 3 | % rubber: set program xelatex 4 | \usepackage{fontspec} 5 | % \defaultfontfeatures{Scale=MatchLowercase} 6 | % straight double quotes (defined T1 but missing in TU): 7 | \ifdefined \UnicodeEncodingName 8 | \DeclareTextCommand{\textquotedbl}{\UnicodeEncodingName}{% 9 | {\addfontfeatures{RawFeature=-tlig,Mapping=}\char34}}% 10 | \fi 11 | \usepackage{ifthen} 12 | \usepackage{alltt} 13 | 14 | %%% Custom LaTeX preamble 15 | 16 | \setmainfont{Linux Libertine O} 17 | \setsansfont{Linux Biolinum O} 18 | \setmonofont[Scale=MatchLowercase]{Fira Code} 19 | 20 | %%% User specified packages and stylesheets 21 | \usepackage{alectryon} 22 | \usepackage{pygments} 23 | 24 | %%% Fallback definitions for Docutils-specific commands 25 | % hyperlinks: 26 | \ifthenelse{\isundefined{\hypersetup}}{ 27 | \usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref} 28 | \usepackage{bookmark} 29 | \urlstyle{same} % normal text font (alternatives: tt, rm, sf) 30 | }{} 31 | \hypersetup{ 32 | pdftitle={LaTeX and HTML dialects}, 33 | } 34 | 35 | \title{LaTeX and HTML dialects% 36 | \label{latex-and-html-dialects}} 37 | \author{} 38 | \date{} 39 | 40 | %%% Body 41 | \begin{document} 42 | \maketitle 43 | 44 | \let\oldalltt\alltt 45 | \def\alltt{\oldalltt\scriptsize} 46 | 47 | This simple file demos LaTeX and HTML dialect configuration: 48 | 49 | \begin{quote} 50 | \begin{alltt} 51 | alectryon --html-dialect=html4 -o dialects.4.html dialects.rst 52 | # HTML4; produces ‘dialects.4.html’ 53 | alectryon --html-dialect=html5 -o dialects.5.html dialects.rst 54 | # HTML5; produces ‘dialects.5.html’ 55 | 56 | alectryon --latex-dialect=pdflatex -o dialects.tex dialects.rst 57 | # LaTeX; produces ‘dialects.tex’ 58 | alectryon --latex-dialect=xelatex -o dialects.xe.tex dialects.rst 59 | # XeLaTeX; produces ‘dialects.xe.tex’ 60 | alectryon --latex-dialect=lualatex -o dialects.lua.tex dialects.rst 61 | # LuaLaTeX; produces ‘dialects.lua.tex’ 62 | \end{alltt} 63 | \end{quote} 64 | 65 | \begin{alectryon} 66 | % Generator: Alectryon 67 | \sep 68 | \begin{sentence} 69 | \begin{input} 70 | \PY{k+kn}{Goal}~\PY{k+kt}{True}\PY{o}{.} 71 | \end{input} 72 | \sep 73 | \begin{output} 74 | \begin{goals} 75 | \begin{goal} 76 | \begin{hyps}\end{hyps} 77 | \sep 78 | \infrule{} 79 | \sep 80 | \begin{conclusion} 81 | \PY{k+kt}{True} 82 | \end{conclusion} 83 | \end{goal} 84 | \end{goals} 85 | \end{output} 86 | \end{sentence} 87 | \sep 88 | \begin{sentence} 89 | \begin{input} 90 | ~~\PY{n+nb+bp}{exact}~\PY{n}{I}\PY{o}{.}\nl 91 | \end{input} 92 | \end{sentence} 93 | \sep 94 | \begin{sentence} 95 | \begin{input} 96 | ~~\PY{k+kn}{Show~Proof}\PY{o}{.} 97 | \end{input} 98 | \sep 99 | \begin{output} 100 | \begin{messages} 101 | \begin{message} 102 | \PY{n}{I} 103 | \end{message} 104 | \end{messages} 105 | \end{output} 106 | \end{sentence} 107 | \end{alectryon} 108 | 109 | \end{document} 110 | -------------------------------------------------------------------------------- /recipes/_output/tests/dialects.tex: -------------------------------------------------------------------------------- 1 | \documentclass[a4paper]{article} 2 | % generated by Docutils 3 | \usepackage{cmap} % fix search and cut-and-paste in Acrobat 4 | \usepackage{ifthen} 5 | \usepackage[T1]{fontenc} 6 | \usepackage[utf8]{inputenc} 7 | \usepackage{alltt} 8 | 9 | %%% Custom LaTeX preamble 10 | % PDF Standard Fonts 11 | \usepackage{mathptmx} % Times 12 | \usepackage[scaled=.90]{helvet} 13 | \usepackage{courier} 14 | 15 | %%% User specified packages and stylesheets 16 | \usepackage{alectryon} 17 | \usepackage{pygments} 18 | 19 | %%% Fallback definitions for Docutils-specific commands 20 | 21 | % hyperlinks: 22 | \ifthenelse{\isundefined{\hypersetup}}{ 23 | \usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref} 24 | \usepackage{bookmark} 25 | \urlstyle{same} % normal text font (alternatives: tt, rm, sf) 26 | }{} 27 | \hypersetup{ 28 | pdftitle={LaTeX and HTML dialects}, 29 | } 30 | 31 | %%% Body 32 | \begin{document} 33 | \title{LaTeX and HTML dialects% 34 | \label{latex-and-html-dialects}} 35 | \author{} 36 | \date{} 37 | \maketitle 38 | 39 | \let\oldalltt\alltt 40 | \def\alltt{\oldalltt\scriptsize} 41 | 42 | This simple file demos LaTeX and HTML dialect configuration: 43 | 44 | \begin{quote} 45 | \begin{alltt} 46 | alectryon -{}-html-dialect=html4 -o dialects.4.html dialects.rst 47 | # HTML4; produces ‘dialects.4.html’ 48 | alectryon -{}-html-dialect=html5 -o dialects.5.html dialects.rst 49 | # HTML5; produces ‘dialects.5.html’ 50 | 51 | alectryon -{}-latex-dialect=pdflatex -o dialects.tex dialects.rst 52 | # LaTeX; produces ‘dialects.tex’ 53 | alectryon -{}-latex-dialect=xelatex -o dialects.xe.tex dialects.rst 54 | # XeLaTeX; produces ‘dialects.xe.tex’ 55 | alectryon -{}-latex-dialect=lualatex -o dialects.lua.tex dialects.rst 56 | # LuaLaTeX; produces ‘dialects.lua.tex’ 57 | \end{alltt} 58 | \end{quote} 59 | 60 | \begin{alectryon} 61 | % Generator: Alectryon 62 | \sep 63 | \begin{sentence} 64 | \begin{input} 65 | \PY{k+kn}{Goal}~\PY{k+kt}{True}\PY{o}{.} 66 | \end{input} 67 | \sep 68 | \begin{output} 69 | \begin{goals} 70 | \begin{goal} 71 | \begin{hyps}\end{hyps} 72 | \sep 73 | \infrule{} 74 | \sep 75 | \begin{conclusion} 76 | \PY{k+kt}{True} 77 | \end{conclusion} 78 | \end{goal} 79 | \end{goals} 80 | \end{output} 81 | \end{sentence} 82 | \sep 83 | \begin{sentence} 84 | \begin{input} 85 | ~~\PY{n+nb+bp}{exact}~\PY{n}{I}\PY{o}{.}\nl 86 | \end{input} 87 | \end{sentence} 88 | \sep 89 | \begin{sentence} 90 | \begin{input} 91 | ~~\PY{k+kn}{Show~Proof}\PY{o}{.} 92 | \end{input} 93 | \sep 94 | \begin{output} 95 | \begin{messages} 96 | \begin{message} 97 | \PY{n}{I} 98 | \end{message} 99 | \end{messages} 100 | \end{output} 101 | \end{sentence} 102 | \end{alectryon} 103 | 104 | \end{document} 105 | -------------------------------------------------------------------------------- /recipes/_output/tests/dialects.xe.tex: -------------------------------------------------------------------------------- 1 | \documentclass[a4paper]{article} 2 | % generated by Docutils 3 | % rubber: set program xelatex 4 | \usepackage{fontspec} 5 | % \defaultfontfeatures{Scale=MatchLowercase} 6 | % straight double quotes (defined T1 but missing in TU): 7 | \ifdefined \UnicodeEncodingName 8 | \DeclareTextCommand{\textquotedbl}{\UnicodeEncodingName}{% 9 | {\addfontfeatures{RawFeature=-tlig,Mapping=}\char34}}% 10 | \fi 11 | \usepackage{ifthen} 12 | \usepackage{alltt} 13 | 14 | %%% Custom LaTeX preamble 15 | 16 | \setmainfont{Linux Libertine O} 17 | \setsansfont{Linux Biolinum O} 18 | \setmonofont[Scale=MatchLowercase]{Fira Code} 19 | 20 | %%% User specified packages and stylesheets 21 | \usepackage{alectryon} 22 | \usepackage{pygments} 23 | 24 | %%% Fallback definitions for Docutils-specific commands 25 | % hyperlinks: 26 | \ifthenelse{\isundefined{\hypersetup}}{ 27 | \usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref} 28 | \usepackage{bookmark} 29 | \urlstyle{same} % normal text font (alternatives: tt, rm, sf) 30 | }{} 31 | \hypersetup{ 32 | pdftitle={LaTeX and HTML dialects}, 33 | } 34 | 35 | \title{LaTeX and HTML dialects% 36 | \label{latex-and-html-dialects}} 37 | \author{} 38 | \date{} 39 | 40 | %%% Body 41 | \begin{document} 42 | \maketitle 43 | 44 | \let\oldalltt\alltt 45 | \def\alltt{\oldalltt\scriptsize} 46 | 47 | This simple file demos LaTeX and HTML dialect configuration: 48 | 49 | \begin{quote} 50 | \begin{alltt} 51 | alectryon --html-dialect=html4 -o dialects.4.html dialects.rst 52 | # HTML4; produces ‘dialects.4.html’ 53 | alectryon --html-dialect=html5 -o dialects.5.html dialects.rst 54 | # HTML5; produces ‘dialects.5.html’ 55 | 56 | alectryon --latex-dialect=pdflatex -o dialects.tex dialects.rst 57 | # LaTeX; produces ‘dialects.tex’ 58 | alectryon --latex-dialect=xelatex -o dialects.xe.tex dialects.rst 59 | # XeLaTeX; produces ‘dialects.xe.tex’ 60 | alectryon --latex-dialect=lualatex -o dialects.lua.tex dialects.rst 61 | # LuaLaTeX; produces ‘dialects.lua.tex’ 62 | \end{alltt} 63 | \end{quote} 64 | 65 | \begin{alectryon} 66 | % Generator: Alectryon 67 | \sep 68 | \begin{sentence} 69 | \begin{input} 70 | \PY{k+kn}{Goal}~\PY{k+kt}{True}\PY{o}{.} 71 | \end{input} 72 | \sep 73 | \begin{output} 74 | \begin{goals} 75 | \begin{goal} 76 | \begin{hyps}\end{hyps} 77 | \sep 78 | \infrule{} 79 | \sep 80 | \begin{conclusion} 81 | \PY{k+kt}{True} 82 | \end{conclusion} 83 | \end{goal} 84 | \end{goals} 85 | \end{output} 86 | \end{sentence} 87 | \sep 88 | \begin{sentence} 89 | \begin{input} 90 | ~~\PY{n+nb+bp}{exact}~\PY{n}{I}\PY{o}{.}\nl 91 | \end{input} 92 | \end{sentence} 93 | \sep 94 | \begin{sentence} 95 | \begin{input} 96 | ~~\PY{k+kn}{Show~Proof}\PY{o}{.} 97 | \end{input} 98 | \sep 99 | \begin{output} 100 | \begin{messages} 101 | \begin{message} 102 | \PY{n}{I} 103 | \end{message} 104 | \end{messages} 105 | \end{output} 106 | \end{sentence} 107 | \end{alectryon} 108 | 109 | \end{document} 110 | -------------------------------------------------------------------------------- /recipes/_output/tests/directive-options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Coq directive options 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
Built with Alectryon, running Coq+SerAPI. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑ Ctrl+↓ to navigate, Ctrl+🖱️ to focus. On Mac, use instead of Ctrl.
18 |

Coq directive options

19 | 20 |

This file checks that :class: and :name: attributes work on .. coq:: directives:

21 |
22 | alectryon directive-options.rst
23 |   # reST → Coq; produces ‘directive-options.html’
24 | alectryon directive-options.rst --latex-dialect xelatex -o directive-options.xe.tex
25 |   # reST → LaTeX; produces ‘directive-options.xe.tex’
26 | 
27 |
Definition test := " =: ʇsǝʇ uoıʇıuıɟǝᗡ".

Link to first Coq fragment. 28 | Another link.

29 |
30 |
31 | 32 | -------------------------------------------------------------------------------- /recipes/_output/tests/directive-options.xe.tex: -------------------------------------------------------------------------------- 1 | \documentclass[a4paper]{article} 2 | % generated by Docutils 3 | % rubber: set program xelatex 4 | \usepackage{fontspec} 5 | % \defaultfontfeatures{Scale=MatchLowercase} 6 | % straight double quotes (defined T1 but missing in TU): 7 | \ifdefined \UnicodeEncodingName 8 | \DeclareTextCommand{\textquotedbl}{\UnicodeEncodingName}{% 9 | {\addfontfeatures{RawFeature=-tlig,Mapping=}\char34}}% 10 | \fi 11 | \usepackage{ifthen} 12 | \usepackage{alltt} 13 | 14 | %%% Custom LaTeX preamble 15 | 16 | \setmainfont{Linux Libertine O} 17 | \setsansfont{Linux Biolinum O} 18 | \setmonofont[Scale=MatchLowercase]{Fira Code} 19 | 20 | %%% User specified packages and stylesheets 21 | \usepackage{alectryon} 22 | \usepackage{pygments} 23 | 24 | %%% Fallback definitions for Docutils-specific commands 25 | % hyperlinks: 26 | \ifthenelse{\isundefined{\hypersetup}}{ 27 | \usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref} 28 | \usepackage{bookmark} 29 | \urlstyle{same} % normal text font (alternatives: tt, rm, sf) 30 | }{} 31 | \hypersetup{ 32 | pdftitle={Coq directive options}, 33 | } 34 | 35 | \title{Coq directive options% 36 | \label{coq-directive-options}} 37 | \author{} 38 | \date{} 39 | 40 | %%% Body 41 | \begin{document} 42 | \maketitle 43 | 44 | \let\oldalltt\alltt 45 | \def\alltt{\oldalltt\scriptsize} 46 | 47 | This file checks that \texttt{:class:} and \texttt{:name:} attributes work on \texttt{.. coq::} directives: 48 | 49 | \begin{quote} 50 | \begin{alltt} 51 | alectryon directive-options.rst 52 | # reST → Coq; produces ‘directive-options.html’ 53 | alectryon directive-options.rst --latex-dialect xelatex -o directive-options.xe.tex 54 | # reST → LaTeX; produces ‘directive-options.xe.tex’ 55 | \end{alltt} 56 | \end{quote} 57 | 58 | \begin{alectryon} 59 | \anchor{test} 60 | \sep 61 | % Generator: Alectryon 62 | \sep 63 | \begin{sentence} 64 | \begin{input} 65 | \PY{k+kn}{Definition}~\PY{n+nf}{test}~\PY{o}{:=}~\PY{l+s+s2}{\PYZdq{}~=:~ʇsǝʇ~uoıʇıuıɟǝᗡ\PYZdq{}}\PY{o}{.} 66 | \end{input} 67 | \end{sentence} 68 | \end{alectryon} 69 | 70 | Link to \hyperref[test]{first Coq fragment}. 71 | Another \hyperref[test]{link}. 72 | 73 | \end{document} 74 | -------------------------------------------------------------------------------- /recipes/_output/tests/docinfo_flags.txt: -------------------------------------------------------------------------------- 1 | exit: 0 2 | -------------------------------------------------------------------------------- /recipes/_output/tests/doctests.out: -------------------------------------------------------------------------------- 1 | README.rst 2 | Doctest: README.rst ... ok 3 | make_ident (alectryon.coq.CoqIdents) 4 | Doctest: alectryon.coq.CoqIdents.make_ident ... ok 5 | split_fpath (alectryon.coq.CoqIdents) 6 | Doctest: alectryon.coq.CoqIdents.split_fpath ... ok 7 | annotate (alectryon.coqc_time.CoqcTime) 8 | Doctest: alectryon.coqc_time.CoqcTime.annotate ... ok 9 | split_fragment (alectryon.core.Document) 10 | Doctest: alectryon.core.Document.split_fragment ... ok 11 | split_fragments (alectryon.core.Document) 12 | Doctest: alectryon.core.Document.split_fragments ... ok 13 | strip_separators (alectryon.core.Document) 14 | Doctest: alectryon.core.Document.strip_separators ... ok 15 | translate_offset (alectryon.core.PosView) 16 | Doctest: alectryon.core.PosView.translate_offset ... ok 17 | DeduplicatingSerializer (alectryon.json) 18 | Doctest: alectryon.json.DeduplicatingSerializer ... ok 19 | FullyDeduplicatingSerializer (alectryon.json) 20 | Doctest: alectryon.json.FullyDeduplicatingSerializer ... ok 21 | PlainSerializer (alectryon.json) 22 | Doctest: alectryon.json.PlainSerializer ... ok 23 | annotate (alectryon.lean3.Lean3) 24 | Doctest: alectryon.lean3.Lean3.annotate ... ok 25 | step (alectryon.literate.CoqParser) 26 | Doctest: alectryon.literate.CoqParser.step ... ok 27 | step (alectryon.literate.LeanParser) 28 | Doctest: alectryon.literate.LeanParser.step ... ok 29 | __len__ (alectryon.literate.Line) 30 | Doctest: alectryon.literate.Line.__len__ ... ok 31 | _last_directive (alectryon.literate) 32 | Doctest: alectryon.literate._last_directive ... ok 33 | code2rst (alectryon.literate) 34 | Doctest: alectryon.literate.code2rst ... ok 35 | rst2code (alectryon.literate) 36 | Doctest: alectryon.literate.rst2code ... ok 37 | rst_partition (alectryon.literate) 38 | Doctest: alectryon.literate.rst_partition ... ok 39 | cli (alectryon.minimal) 40 | Doctest: alectryon.minimal.cli ... ok 41 | StripErrorsTokenFilter (alectryon.pygments) 42 | Doctest: alectryon.pygments.StripErrorsTokenFilter ... ok 43 | add_tokens (alectryon.pygments) 44 | Doctest: alectryon.pygments.add_tokens ... ok 45 | highlight_html (alectryon.pygments) 46 | Doctest: alectryon.pygments.highlight_html ... ok 47 | annotate (alectryon.serapi) 48 | Doctest: alectryon.serapi.annotate ... ok 49 | _all_sub_objects (alectryon.transforms) 50 | Doctest: alectryon.transforms._all_sub_objects ... ok 51 | _attach_comments_to_code (alectryon.transforms) 52 | Doctest: alectryon.transforms._attach_comments_to_code ... ok 53 | coalesce_text (alectryon.transforms) 54 | Doctest: alectryon.transforms.coalesce_text ... ok 55 | dedent (alectryon.transforms) 56 | Doctest: alectryon.transforms.dedent ... ok 57 | group_hypotheses (alectryon.transforms) 58 | Doctest: alectryon.transforms.group_hypotheses ... ok 59 | group_whitespace_with_code (alectryon.transforms) 60 | Doctest: alectryon.transforms.group_whitespace_with_code ... ok 61 | lean3_split_comments (alectryon.transforms) 62 | Doctest: alectryon.transforms.lean3_split_comments ... ok 63 | lean3_truncate_vernacs (alectryon.transforms) 64 | Doctest: alectryon.transforms.lean3_truncate_vernacs ... ok 65 | partition_fragments (alectryon.transforms) 66 | Doctest: alectryon.transforms.partition_fragments ... ok 67 | 68 | ---------------------------------------------------------------------- 69 | Ran 33 tests 70 | 71 | OK 72 | -------------------------------------------------------------------------------- /recipes/_output/tests/errors.py.out: -------------------------------------------------------------------------------- 1 | test_errors (__main__.cli) ... ok 2 | test_warnings_and_errors (__main__.coqc_time) ... ok 3 | test_errors (__main__.core) ... ok 4 | test_errors (__main__.docutils) ... ok 5 | test_errors (__main__.json) ... ok 6 | test_warnings (__main__.json) ... ok 7 | test_errors (__main__.literate) ... ok 8 | test_features (__main__.literate) ... ok 9 | test_failed_import (__main__.myst) ... ok 10 | test_errors (__main__.pygments) ... ok 11 | test_warnings (__main__.pygments) ... ok 12 | test_warnings_and_errors (__main__.serapi) ... ok 13 | test_errors (__main__.sexp) ... ok 14 | 15 | ---------------------------------------------------------------------- 16 | Ran 13 tests 17 | 18 | OK 19 | -------------------------------------------------------------------------------- /recipes/_output/tests/errors.sh.out: -------------------------------------------------------------------------------- 1 | $PYTHON -m alectryon.literate -; echo $? 2 | literate.py: error: Reading from standard input requires one of --coq2rst, --rst2coq, --lean32rst, --rst2lean3, --lean42rst, --rst2lean4. 3 | 2 4 | $PYTHON -m alectryon.literate xyz.unsupported; echo $? 5 | literate.py: error: Unexpected file extension: expected '.v', '.lean3', '.lean', '.rst', got '.unsupported'. 6 | 2 7 | $ALECTRYON xyz.unsupported; echo $? 8 | Exiting early due to an error; use --traceback to diagnose: 9 | input: Not sure what to do with 'xyz.unsupported'. 10 | Try passing --frontend? 11 | 1 12 | $ALECTRYON xyz.v -o xyz.unsupported; echo $? 13 | Exiting early due to an error; use --traceback to diagnose: 14 | output: Not sure what to do with 'xyz.unsupported'. 15 | Try passing --backend? 16 | 1 17 | $ALECTRYON xyz.v.json -o xyz.rst; echo $? 18 | Exiting early due to an error; use --traceback to diagnose: 19 | argument --backend: Frontend 'coq.json' does not support backend 'rst': expecting one of 'null', 'json', 'snippets-html', 'snippets-latex' 20 | 1 21 | $ALECTRYON a.v.json b.v.json -o c.v.json; echo $? 22 | alectryon.py: error: argument --output: Not valid with multiple inputs 23 | 2 24 | $ALECTRYON a.v.json --stdin-filename b.v.json; echo $? 25 | alectryon.py: error: argument --stdin-filename: input must be '-' 26 | 2 27 | $ALECTRYON a.v.json --mark-point not_an_int ⊙; echo $? 28 | alectryon.py: error: argument --mark-point: Expecting a number, not 'not_an_int' 29 | 2 30 | -------------------------------------------------------------------------------- /recipes/_output/tests/excepthook.v.out: -------------------------------------------------------------------------------- 1 | Traceback (most recent call last): 2 | File …, line …, in 3 | File …, line …, in main 4 | File …, line …, in process_pipelines 5 | File …, line …, in call_pipeline_step 6 | File …, line …, in read_plain 7 | FileNotFoundError: [Errno 2] No such file or directory: 'not_found.v' 8 | -------------------------------------------------------------------------------- /recipes/_output/tests/fatal.v.out: -------------------------------------------------------------------------------- 1 | tests/fatal.v:(9:6)-(9:15): (ERROR/3) Coq raised an exception: 2 | > The reference not_found was not found in the current environment. 3 | The offending chunk is delimited by >>>…<<< below: 4 | > # Plain Coq → HTML + errors; produces ‘fatal.v.out’ **) 5 | > 6 | > Goal >>>not_found<<< = 1. 7 | Results past this point may be unreliable. 8 | exit: 1 9 | -------------------------------------------------------------------------------- /recipes/_output/tests/fatal_transform.v.out: -------------------------------------------------------------------------------- 1 | Exiting early due to an error; use --traceback to diagnose: 2 | No match found for `.g#4` in `Goal True.` 3 | exit: 1 4 | -------------------------------------------------------------------------------- /recipes/_output/tests/frontend_warnings.json.out: -------------------------------------------------------------------------------- 1 | WARNING: Frontend `json` is ambiguous; use `coq.json` instead. 2 | [ 3 | [ 4 | { 5 | "_type": "sentence", 6 | "contents": "Axiom A: Type.", 7 | "messages": [], 8 | "goals": [] 9 | } 10 | ] 11 | ] -------------------------------------------------------------------------------- /recipes/_output/tests/lean3_error.out: -------------------------------------------------------------------------------- 1 | tests/lean3_error.rst:: (ERROR/3) Lean raised an exception: 2 | Driver Lean3 (lean) exited with code 1: 3 | ....lean:1:0: error: command expected 4 | 5 | 6 | exit: 13 7 | -------------------------------------------------------------------------------- /recipes/_output/tests/literate.marked-empty.rst: -------------------------------------------------------------------------------- 1 | 1 FIN 2 | -------------------------------------------------------------------------------- /recipes/_output/tests/literate.marked-end.rst: -------------------------------------------------------------------------------- 1 | 52 FIN 2 | -------------------------------------------------------------------------------- /recipes/_output/tests/literate.v: -------------------------------------------------------------------------------- 1 | (*| 2 | ============================== 3 | reST → Coq translation tests 4 | ============================== 5 | 6 | To compile:: 7 | 8 | $ alectryon literate.rst --backend coq # reST → Coq; produces ‘literate.v’ 9 | 10 | Code blocks 11 | =========== 12 | |*) 13 | 14 | Goal True /\ True. 15 | 16 | (*| 17 | The last header is needed (to avoid putting `Goal True` in the ``To compile::`` block) but the next one is superfluous: 18 | |*) 19 | 20 | split. 21 | 22 | (*| 23 | This one is needed because it includes a ``:name:``: 24 | 25 | .. coq:: 26 | :name: exact 27 | |*) 28 | 29 | exact I. 30 | 31 | (*| 32 | .. note:: This note includes two Coq fragments: 33 | 34 | .. coq:: 35 | |*) 36 | 37 | idtac "This comment is part of the note". 38 | 39 | (*| 40 | The last header is needed (to include the comment in the note) but the next one is superfluous. The one after the note is needed, but not the one after that (though a ``(\ *||*\ )`` comment is still needed, because otherwise the two blocks would get merged into one): 41 | |*) 42 | 43 | idtac "This comment is part of the note". 44 | 45 | (*| 46 | .. coq:: 47 | |*) 48 | 49 | (* This comment isn't part of the note *) 50 | 51 | (*||*) 52 | 53 | Qed. 54 | 55 | (*| 56 | Comments and strings 57 | ==================== 58 | 59 | Coq comment markers that appear within doc comments (\ *like this one*\ ) must be escaped, especially if they aren't well-parenthesized (like *this*\ ) (\ *or this*, for example). 60 | |*) 61 | 62 | (* This comment doesn't need "*)" escaping though, even if ProofGeneral mishighlights it *) 63 | 64 | (*| 65 | Strings can be tricky too: 66 | |*) 67 | 68 | Require Import String. 69 | Open Scope string_scope. 70 | 71 | Definition a := "a""b""c\n\n\n". 72 | Print a. 73 | -------------------------------------------------------------------------------- /recipes/_output/tests/literate.v.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | reST → Coq translation tests 3 | ============================== 4 | 5 | To compile:: 6 | 7 | $ alectryon literate.v --backend rst --mark-point 908 ⊙ 8 | # Coq → reST; produces ‘literate.v.rst’ 9 | 10 | $ alectryon literate.v --backend rst -o - --mark-point 42000 "F"IN | \ 11 | nl | grep "F"IN > literate.marked-end.rst 12 | # Coq → reST; produces ‘literate.marked-end.rst’ 13 | 14 | $ alectryon --frontend coq --backend rst /dev/null -o - --mark-point 42000 "F"IN | \ 15 | nl | grep "F"IN > literate.marked-empty.rst 16 | # Coq → reST; produces ‘literate.marked-empty.rst’ 17 | 18 | .. coq:: 19 | 20 | Goal True /\ True. 21 | 22 | The last header is needed (to avoid putting `Goal True` in the ``To compile::`` block) but here we don't need one: 23 | 24 | .. coq:: 25 | 26 | split. 27 | 28 | This one is needed because it includes a ``:name:``: 29 | 30 | .. coq:: 31 | :name: exact 32 | 33 | exact I. 34 | 35 | .. note:: This note includes two Coq⊙ fragments: 36 | 37 | .. coq:: 38 | 39 | idtac "This comment is part of the note". 40 | 41 | The last header is needed (to include the comment in the note) but here we don't need one. The next one (after the note) is needed to exit the note. The ``(*||*)`` comment is needed too, because otherwise the two blocks would get merged into one): 42 | 43 | .. coq:: 44 | 45 | idtac "This comment is part of the note". 46 | 47 | .. coq:: 48 | 49 | (* This comment isn't part of the note *) 50 | 51 | .. coq:: 52 | 53 | idtac. 54 | 55 | We also want to correctly handle ``.. coq::`` blocks in comments: 56 | 57 | .. coq:: 58 | 59 | idtac 60 | 61 | This can't happen from translating a reST file, but it can happen from a user adding such a block directly. 62 | 63 | .. coq:: 64 | 65 | Qed. 66 | 67 | Comments and strings 68 | ==================== 69 | 70 | Coq comment markers that appear within doc comments (*like this one*) must be escaped, especially if they aren't well-parenthesized (like *this*) (*or this*, for example). 71 | 72 | .. coq:: 73 | 74 | (* This comment doesn't need "*)" escaping though, even if ProofGeneral mishighlights it *) 75 | 76 | Strings can be tricky too: 77 | 78 | .. coq:: 79 | 80 | Require Import String. 81 | Open Scope string_scope. 82 | 83 | Definition a := "a""b""c\n\n\n". 84 | Print a. 85 | 86 | And so can deeply nested comments: 87 | 88 | .. coq:: 89 | 90 | (* (*! (** (*|*) **) !*) *) 91 | -------------------------------------------------------------------------------- /recipes/_output/tests/misc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Misc commands that don't have other tests 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
Built with Alectryon, running Coq+SerAPI. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑ Ctrl+↓ to navigate, Ctrl+🖱️ to focus. On Mac, use instead of Ctrl.
18 |

Misc commands that don't have other tests

19 | 20 |

To compile:

21 |
22 | alectryon misc.rst # reST → HTML; produces ‘misc.html’
23 | 
24 |
25 |

Exercise: Commutativity of addition

26 |

forall x y : nat, x + y = y + x
27 |
28 |
29 | 30 | -------------------------------------------------------------------------------- /recipes/_output/tests/plain_cli.noext.html: -------------------------------------------------------------------------------- 1 | 2 | 63 3 | 4 |
nat 5 | : Set
6 |
-------------------------------------------------------------------------------- /recipes/_output/tests/plain_cli.stdin.html: -------------------------------------------------------------------------------- 1 | 2 | - 3 | 4 |
nat 5 | : Set
6 |
-------------------------------------------------------------------------------- /recipes/_output/tests/plain_cli.tmp.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpitclaudel/alectryon/5a35676636766d2c6de8f1856a4bc168a3515ecb/recipes/_output/tests/plain_cli.tmp.html -------------------------------------------------------------------------------- /recipes/_output/tests/recording.snippets.html: -------------------------------------------------------------------------------- 1 |

True
2 |
test

True
3 |
exact I. 4 | Qed. 5 |
6 | -------------------------------------------------------------------------------- /recipes/_output/tests/recording.snippets.tex: -------------------------------------------------------------------------------- 1 | \begin{alectryon} 2 | % Generator: Alectryon 3 | \sep 4 | \begin{sentence} 5 | \begin{input} 6 | \PY{k+kn}{Goal}~\PY{k+kt}{True}\PY{o}{.} 7 | \end{input} 8 | \sep 9 | \begin{output} 10 | \begin{goals} 11 | \begin{goal} 12 | \begin{hyps}\end{hyps} 13 | \sep 14 | \infrule{} 15 | \sep 16 | \begin{conclusion} 17 | \PY{k+kt}{True} 18 | \end{conclusion} 19 | \end{goal} 20 | \end{goals} 21 | \end{output} 22 | \end{sentence} 23 | \sep 24 | \begin{sentence} 25 | \begin{input} 26 | ~~\PY{k+kp}{idtac}~\PY{l+s+s2}{\PYZdq{}test\PYZdq{}}\PY{o}{.} 27 | \end{input} 28 | \sep 29 | \begin{output} 30 | \begin{messages} 31 | \begin{message} 32 | \PY{n}{test} 33 | \end{message} 34 | \end{messages} 35 | \sep 36 | \begin{goals} 37 | \begin{goal} 38 | \begin{hyps}\end{hyps} 39 | \sep 40 | \infrule{} 41 | \sep 42 | \begin{conclusion} 43 | \PY{k+kt}{True} 44 | \end{conclusion} 45 | \end{goal} 46 | \end{goals} 47 | \end{output} 48 | \end{sentence} 49 | \sep 50 | \begin{sentence} 51 | \begin{input} 52 | ~~\PY{n+nb+bp}{exact}~\PY{n}{I}\PY{o}{.}\nl 53 | \end{input} 54 | \end{sentence} 55 | \sep 56 | \begin{sentence} 57 | \begin{input} 58 | \PY{k+kn}{Qed}\PY{o}{.}\nl 59 | \end{input} 60 | \end{sentence} 61 | \end{alectryon} 62 | %% alectryon-block-end 63 | -------------------------------------------------------------------------------- /recipes/_output/tests/recording.v.html: -------------------------------------------------------------------------------- 1 | 2 | recording.v.io.json 3 | 4 |
Built with Alectryon, running Coq+SerAPI. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑ Ctrl+↓ to navigate, Ctrl+🖱️ to focus. On Mac, use instead of Ctrl.

True
5 |
test

True
6 |
exact I. 7 | Qed. 8 |
-------------------------------------------------------------------------------- /recipes/_output/tests/recording.v.io.json: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | { 4 | "_type": "text", 5 | "contents": "(* To compile:\n alectryon recording.v --frontend coq --backend json\n # Coq \u2192 JSON; produces \u2018recording.v.io.json\u2019 *)\n" 6 | }, 7 | { 8 | "_type": "sentence", 9 | "contents": "Goal True.", 10 | "messages": [], 11 | "goals": [ 12 | { 13 | "_type": "goal", 14 | "name": null, 15 | "conclusion": "True", 16 | "hypotheses": [] 17 | } 18 | ] 19 | }, 20 | { 21 | "_type": "text", 22 | "contents": "\n " 23 | }, 24 | { 25 | "_type": "sentence", 26 | "contents": "idtac \"test\".", 27 | "messages": [ 28 | { 29 | "_type": "message", 30 | "contents": "test" 31 | } 32 | ], 33 | "goals": [ 34 | { 35 | "_type": "goal", 36 | "name": null, 37 | "conclusion": "True", 38 | "hypotheses": [] 39 | } 40 | ] 41 | }, 42 | { 43 | "_type": "text", 44 | "contents": "\n " 45 | }, 46 | { 47 | "_type": "sentence", 48 | "contents": "exact I.", 49 | "messages": [], 50 | "goals": [] 51 | }, 52 | { 53 | "_type": "text", 54 | "contents": "\n" 55 | }, 56 | { 57 | "_type": "sentence", 58 | "contents": "Qed.", 59 | "messages": [], 60 | "goals": [] 61 | }, 62 | { 63 | "_type": "text", 64 | "contents": "\n" 65 | } 66 | ] 67 | ] -------------------------------------------------------------------------------- /recipes/_output/tests/stylesheets.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Stylesheets and Pygments stylesheets 8 | 10 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
Built with Alectryon, running Coq+SerAPI. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑ Ctrl+↓ to navigate, Ctrl+🖱️ to focus. On Mac, use instead of Ctrl.
22 |

Stylesheets and Pygments stylesheets

23 | 24 |

To compile:

25 |
26 | $ DOCUTILSCONFIG=tests/stylesheets.docutils.conf \
27 |     alectryon stylesheets.v --pygments-style emacs -o - \
28 |     | sed -r '/^ *<style type="text.css">/,/^ *<.style>/ { /^ *<style |<.style>|Alectryon/b; d}' \
29 |     > stylesheets.html
30 |   # reST → HTML; produces ‘stylesheets.html’
31 | 
32 | $ DOCUTILSCONFIG=tests/stylesheets.docutils.conf \
33 |     alectryon stylesheets.v --pygments-style emacs --backend latex -o - \
34 |     | sed -r '/^% embedded stylesheet/,/^\\makeatother/ { /^\\makeat|Alectryon/b; d}' \
35 |     > stylesheets.part.tex
36 |   # reST → LaTeX; produces ‘stylesheets.part.tex’
37 | 
38 |
Nat.add = 39 | fix add (n m : nat) {struct n} : nat := 40 | match n with 41 | | 0 => m 42 | | S p => S (add p m) 43 | end 44 | : nat -> nat -> nat 45 | 46 | Arguments Nat.add (n m)%nat_scope
47 |
48 |
49 | 50 | -------------------------------------------------------------------------------- /recipes/_output/tests/stylesheets.part.tex: -------------------------------------------------------------------------------- 1 | \documentclass[a4paper]{article} 2 | % generated by Docutils 3 | \usepackage{cmap} % fix search and cut-and-paste in Acrobat 4 | \usepackage{ifthen} 5 | \usepackage[T1]{fontenc} 6 | \usepackage[utf8]{inputenc} 7 | \usepackage{alltt} 8 | \usepackage{textcomp} % text symbol macros 9 | 10 | %%% Custom LaTeX preamble 11 | % PDF Standard Fonts 12 | \usepackage{mathptmx} % Times 13 | \usepackage[scaled=.90]{helvet} 14 | \usepackage{courier} 15 | 16 | %%% User specified packages and stylesheets 17 | \makeatletter 18 | \makeatletter 19 | \makeatother 20 | 21 | \makeatother 22 | \makeatletter 23 | % Pygments stylesheet generated by Alectryon (style=emacs) 24 | \makeatletter 25 | \makeatother 26 | 27 | \makeatother 28 | 29 | 30 | %%% Fallback definitions for Docutils-specific commands 31 | 32 | % hyperlinks: 33 | \ifthenelse{\isundefined{\hypersetup}}{ 34 | \usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref} 35 | \usepackage{bookmark} 36 | \urlstyle{same} % normal text font (alternatives: tt, rm, sf) 37 | }{} 38 | \hypersetup{ 39 | pdftitle={Stylesheets and Pygments stylesheets}, 40 | } 41 | 42 | %%% Body 43 | \begin{document} 44 | \title{Stylesheets and Pygments stylesheets% 45 | \label{stylesheets-and-pygments-stylesheets}} 46 | \author{} 47 | \date{} 48 | \maketitle 49 | 50 | To compile: 51 | 52 | \begin{quote} 53 | \begin{alltt} 54 | $ DOCUTILSCONFIG=tests/stylesheets.docutils.conf \textbackslash{} 55 | alectryon stylesheets.v -{}-pygments-style emacs -o - \textbackslash{} 56 | | sed -r '/^ * 13 | 14 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /recipes/backstop/screenshot.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const puppeteer = require('puppeteer'); 4 | const path = require('path'); 5 | 6 | async function screenshot(src, dst, options={}, pdfOptions={}, launchOptions={}) { 7 | const browser = await puppeteer.launch({ 8 | // executablePath: "google-chrome", 9 | ...launchOptions 10 | }); 11 | 12 | const page = await browser.newPage(); 13 | page.emulateMediaType('screen'); 14 | 15 | await page.evaluateOnNewDocument(function() { 16 | window.runDelayed = f => 17 | (window.delayed = window.delayed || []).push(f); 18 | }); 19 | 20 | // https://github.com/puppeteer/puppeteer/issues/422, but this is not enough 21 | // when a font isn't used until later (e.g. the bold font of hyp names) 22 | await page.goto('file://' + src, { waitUntil: 'networkidle2' }); 23 | 24 | await page.evaluate(function() { 25 | const html = document.querySelector("html"); 26 | const link = document.createElement("style"); 27 | // link.rel = "stylesheet"; 28 | link.type = "text/css"; 29 | link.innerText = ` 30 | body { font-size: 26px; /* background: #eeeeec88; */ } 31 | pre.alectryon-io { font-family: Iosevka; margin-top: 0.5em; } 32 | .alectryon-banner { display: none; } 33 | .big p { font-size: 87.5% } 34 | .big .alectryon-input { font-size: 125%; } 35 | p { /* https://stackoverflow.com/questions/13975198/ */ 36 | -webkit-print-color-adjust: exact; 37 | -webkit-filter: blur(0); 38 | } 39 | `; 40 | document.head.appendChild(link); 41 | 42 | document.querySelector('.alectryon-root').classList.add('alectryon-windowed'); 43 | document.querySelectorAll('.alectryon-extra-goal-toggle').forEach(c => c.checked = true); 44 | document.querySelectorAll(".alectryon-sentence").forEach(s => { 45 | if (s.innerText.match(/destruct y/)) 46 | s.classList.add('alectryon-target'); 47 | }) 48 | }); 49 | 50 | options.script && await page.evaluate(options.script); 51 | 52 | // Make sure that any fonts needed by text revealed by `options.script` are 53 | // actually loaded. 54 | await page.evaluateHandle('document.fonts.ready'); 55 | // Make sure that MathJax has rendered 56 | await page.evaluateHandle(async () => { 57 | window.MathJax && await MathJax.startup.promise; 58 | }); 59 | 60 | await page.pdf({ path: dst, printBackground: true, 61 | width: `${838 * 1.85}px`, 62 | height: `855px`, 63 | ...pdfOptions }); 64 | 65 | await browser.close(); 66 | } 67 | 68 | async function cli(...args) { 69 | const fsrc = path.resolve(process.argv[2]); 70 | const fdst = path.resolve(process.argv[3]); 71 | await screenshot(fsrc, fdst, ...args); 72 | } 73 | 74 | cli(); 75 | -------------------------------------------------------------------------------- /recipes/caching.v: -------------------------------------------------------------------------------- 1 | (*| 2 | ====================================== 3 | Caching results for faster execution 4 | ====================================== 5 | 6 | Alectryon can generate cache files to memoize Coq's output, yielding faster compilation when Coq fragments embedded in a document have not changed:: 7 | 8 | alectryon --cache-directory _output/ --cache-compression=xz caching.v 9 | # Coq+reST → HTML, cached to _output/caching.v.cache; produces ‘caching.html’ 10 | 11 | (The ``--cache-compression`` flag is option; the default is to not compress caches.) 12 | |*) 13 | 14 | Print nat. 15 | -------------------------------------------------------------------------------- /recipes/coq_drivers.v: -------------------------------------------------------------------------------- 1 | (*| 2 | =============================================== 3 | Using ``coqc`` to compile Alectryon documents 4 | =============================================== 5 | 6 | The normal way to compile Coq documents with Alectryon is to use ``serapi``. 7 | When this is not possible, however, you can use ``coqc`` with (much) reduced 8 | functionality: Alectryon will be able to parse individual sentences and refer to 9 | them, but not to compute goals and messages. To compile with ``coqc``, pass 10 | ``--coq-driver={sertop|sertop_noexec|coqc_time}`` to Alectryon:: 11 | 12 | alectryon --coq-driver=sertop coq_drivers.v -o coq_drivers.html 13 | # Coq+reST → HTML; produces ‘coq_drivers.html’ 14 | 15 | alectryon --coq-driver=coqc_time coq_drivers.v -o coq_drivers.coqc.html 16 | # Coq+reST → HTML; produces ‘coq_drivers.coqc.html’ 17 | 18 | alectryon --coq-driver=sertop_noexec coq_drivers.v -o coq_drivers.noexec.html 19 | # Coq+reST → HTML; produces ‘coq_drivers.noexec.html’ 20 | 21 | .. coq:: none 22 | |*) 23 | 24 | From Coq Require Import List. 25 | Open Scope list_scope. 26 | 27 | (*||*) 28 | 29 | Lemma fold_left_app : forall {A B} (f: A -> B -> A) (l l': list B) a, 30 | fold_left f (l ++ l') a = fold_left f l' (fold_left f l a). 31 | Proof. 32 | induction l; simpl; auto. 33 | Qed. 34 | 35 | Goal forall {A B} (f: A -> B -> B) (l: list A) b, 36 | fold_right f b l = fold_left (fun acc b => f b acc) (rev l) b. 37 | Proof. 38 | intros. 39 | induction l; simpl; intros. (* .unfold *) 40 | - reflexivity. 41 | - rewrite IHl, fold_left_app; simpl; auto. 42 | Qed. 43 | 44 | Check nat. 45 | 46 | (*| 47 | Limited reference functionality is still available: :mref:`.s(Proof)`, :mref:`.s(Goal).in`, :mquote:`.s(Goal).in`. 48 | |*) 49 | -------------------------------------------------------------------------------- /recipes/coqdoc.v: -------------------------------------------------------------------------------- 1 | (** * An example using Coqdoc 2 | 3 | This is a hybrid rendering mode: coqdoc is used to render literate comments, 4 | and Alectryon is used to render code, responses, and goals. It's a bit as 5 | if coqdoc was just a markup language, _without anything specific to 6 | Coq_. Cons: 7 | 8 | - There's a dependency on [coqdoc] 9 | - It doesn't use a standard markup language like reST, Markdown, etc. 10 | - You can't switch back and forth between a code view and a prose view: all 11 | prose editing happens in comments. 12 | - Annotations can't be applied to whole blocks, so if you want to unfold all 13 | sentences in a block you have to say (* .unfold *) on every sentence, for 14 | example. 15 | 16 | Use the following command to compile this file: 17 | 18 | << 19 | alectryon coqdoc.v --frontend coqdoc # Coqdoc → HTML; produces ‘coqdoc.html’ 20 | >> 21 | **) 22 | 23 | (** ** Some code **) 24 | 25 | (** 26 | Here's an *inductive specification* of evenness: 27 | **) 28 | 29 | Inductive Even : nat -> Prop := 30 | | EvenO : Even O 31 | | EvenS : forall n, Even n -> Even (S (S n)). 32 | 33 | (** 34 | … and a corresponding decision procedure: 35 | **) 36 | 37 | Fixpoint even (n: nat): bool := 38 | match n with 39 | | 0 => true 40 | | 1 => false 41 | | S (S n) => even n 42 | end. 43 | 44 | (* Ensure that we never unfold [even (S n)] *) 45 | Arguments even : simpl nomatch. 46 | 47 | (** 48 | Can we prove it correct? 49 | **) 50 | 51 | Lemma even_Even : 52 | forall n, even n = true <-> Even n. (* .fold *) 53 | Proof. 54 | induction n. (* .unfold *) 55 | all: cbn. (* .unfold *) 56 | - (* .unfold *) split. (* .unfold *) 57 | all: constructor. (* .unfold *) 58 | - (* .unfold *) Fail apply IHn. (* .fails .no-goals .unfold *) 59 | 60 | (** 61 | The induction hypothesis doesn't apply — maybe we need to destruct ``n``? 62 | **) 63 | 64 | destruct n. (* .unfold *) 65 | + (* .unfold *) split; inversion 1. (* .unfold *) 66 | + (* .unfold *) 67 | 68 | (** 69 | Stuck again! 70 | **) 71 | 72 | Abort. 73 | 74 | (** ** Strengthening the spec **) 75 | 76 | (** The usual approach is to strengthen the spec: **) 77 | 78 | Lemma even_Even : 79 | forall n, (even n = true <-> Even n) /\ 80 | (even (S n) = true <-> Even (S n)). (* .fold *) 81 | Proof. 82 | induction n; cbn. (* .unfold *) 83 | - (* .unfold *) repeat split; cbn. (* .unfold *) 84 | all: try constructor. (* .unfold *) 85 | all: inversion 1. (* .unfold *) 86 | - (* .unfold *) destruct IHn as ((Hne & HnE) & (HSne & HSnE)). (* .unfold *) 87 | repeat split; cbn. (* .unfold *) 88 | all: eauto using EvenS. (* .unfold *) 89 | inversion 1; eauto. 90 | Qed. 91 | 92 | (** ** Writing a fixpoint 93 | 94 | But writing a fixpoint is much nicer: **) 95 | 96 | Fixpoint even_Even_fp (n: nat): 97 | even n = true <-> Even n. (* .fold *) 98 | Proof. 99 | destruct n as [ | [ | n ] ]; cbn. (* .unfold *) 100 | - (* .unfold *) repeat constructor. (* .unfold *) 101 | - (* .unfold *) split; inversion 1. (* .unfold *) 102 | - (* .unfold *) split. (* .unfold *) 103 | + (* .unfold *) constructor; apply even_Even; assumption. 104 | + (* .unfold *) inversion 1; apply even_Even; assumption. 105 | Qed. 106 | -------------------------------------------------------------------------------- /recipes/custom_stylesheet.css: -------------------------------------------------------------------------------- 1 | html { 2 | background: #babcbd; 3 | } 4 | 5 | .alectryon-root { 6 | background: #eeeeec; 7 | padding: 1em; 8 | } 9 | -------------------------------------------------------------------------------- /recipes/custom_stylesheet.docutils.conf: -------------------------------------------------------------------------------- 1 | [html writers] 2 | stylesheet=../custom_stylesheet.css 3 | -------------------------------------------------------------------------------- /recipes/custom_stylesheet.rst: -------------------------------------------------------------------------------- 1 | ============================================== 2 | Using a custom CSS stylesheet with Alectryon 3 | ============================================== 4 | 5 | There are multiple ways to add custom stylesheets to an Alectryon document: 6 | 7 | 1. Use a ``.. raw:: html`` block:: 8 | 9 | .. raw:: html 10 | 11 | 12 | 13 | 2. Use a custom Alectryon driver and modify ``alectyron.html.ASSETS.DOCUTILS_CSS``:: 14 | 15 | alectryon.html.ASSETS.DOCUTILS_CSS += ("custom.css",) 16 | 17 | 3. If using Sphinx, add your stylesheet in ``conf.py``:: 18 | 19 | html_css_files = ['css/custom.css'] 20 | 21 | 4. If using plain Docutils, use the ``stylesheet`` option:: 22 | 23 | [html writers] 24 | stylesheet=custom.css 25 | 26 | As of this writing, the ``stylesheet_path`` option does not work with Alectryon. 27 | 28 | To compile this file using the last option: 29 | 30 | $ DOCUTILSCONFIG=custom_stylesheet.docutils.conf alectryon \ 31 | custom_stylesheet.rst 32 | # Coq+reST → HTML; produces ‘custom_stylesheet.html’ 33 | -------------------------------------------------------------------------------- /recipes/docutils.conf: -------------------------------------------------------------------------------- 1 | # Compilation of files processed through a Docutils pipeline can be customized 2 | # by setting Docutils options in ``docutils.conf``; here, we use that to 3 | # configure fonts for LaTeX documents. 4 | 5 | [general] 6 | myst-enable-extensions: dollarmath 7 | 8 | [xetex writer] 9 | latex-preamble: 10 | \setmainfont{Linux Libertine O} 11 | \setsansfont{Linux Biolinum O} 12 | \setmonofont[Scale=MatchLowercase]{Fira Code} 13 | -------------------------------------------------------------------------------- /recipes/fragments.v.json: -------------------------------------------------------------------------------- 1 | ["(* Alectyron can process individual chunks of Coq Code fed to in in a JSON file. *)", 2 | "(* The output is a new JSON file in which each sentence has been annotated with Coq's output. *)", 3 | "(* To compile: *)", 4 | "(* $ alectryon fragments.v.json # JSON → JSON; produces ‘fragments.v.io.json’ *)", 5 | "(* $ alectryon fragments.v.json --backend snippets-html # JSON → HTML; produces ‘fragments.snippets.html’ *)", 6 | "(* $ alectryon fragments.v.json --backend snippets-latex # JSON → LaTeX; produces ‘fragments.snippets.tex’ *)", 7 | "Example xyz (H: False): True. (* ... *) exact I. Qed.", 8 | "Print xyz."] 9 | -------------------------------------------------------------------------------- /recipes/literate.docutils.conf: -------------------------------------------------------------------------------- 1 | [latex2e writer] 2 | latex-preamble: 3 | \usepackage[margin=1in]{geometry} -------------------------------------------------------------------------------- /recipes/literate_MyST.md: -------------------------------------------------------------------------------- 1 | Writing Alectryon documents in Markdown with MyST 2 | ================================================= 3 | 4 | To compile this file, use the following command: 5 | 6 | $ alectryon literate_MyST.md # MyST → HTML, produces ‘literate_MyST.html’ 7 | 8 | Alectryon supports input files written in MyST (a Markdown variant) in addition to Coq and reStructuredText. 9 | 10 | ```{raw} html 11 | 12 | ``` 13 | 14 | In MyST Coq fragments are spelled as ```` ```{coq} ````, with arguments on the same line and options below: 15 | 16 | ```{coq} unfold 17 | :class: border 18 | 19 | Goal exists x, x * x = 49 /\ x < 10. 20 | let rec t n := (exists n; split; [reflexivity |]) || t (S n) in t 0. 21 | unfold lt. 22 | repeat constructor. 23 | ``` 24 | 25 | Math is written either in Docutils math roles ({math}`e^{i\pi} = -1`) or in `$` signs with option ``dollarmath`` (see ``docutils.conf`` in this directory: $\cos(\pi) = -1$). And unlike in reST, *built-in inline markup **nests**, including `code` and other roles like `coqid` {coqid}`references ` or [links](https://myst-parser.readthedocs.io/en/latest/syntax/reference.html#extended-block-tokens)*. 26 | -------------------------------------------------------------------------------- /recipes/literate_lean3.lean: -------------------------------------------------------------------------------- 1 | /-! 2 | ================================================== 3 | Literate programming with Alectryon (Lean3 input) 4 | ================================================== 5 | 6 | Alectryon supports literate programs and documents (combinations of code and prose) written in Lean3 and reStructuredText. Here is an example written in Lean3. It can be converted to reST, HTML, or LaTeX using the following commands:: 7 | 8 | alectryon --frontend lean3+rst literate_lean3.lean 9 | # Coq+reST → HTML; produces ‘literate_lean3.html’ 10 | alectryon --frontend lean3+rst literate_lean3.lean --backend latex \ 11 | --latex-dialect xelatex \ 12 | -o literate_lean3.xe.tex 13 | # Coq+reST → LaTeX; produces ‘literate_lean3.xe.tex’ 14 | alectryon --frontend lean3+rst literate_lean3.lean --backend rst 15 | # Coq+reST → reST; produces ‘literate_lean3.lean3.rst’ 16 | 17 | ----- 18 | 19 | .. default-role:: lean3 20 | 21 | Running queries 22 | =============== 23 | 24 | Alectryon captures the results of `#check`, `#eval`, and the like: 25 | -/ 26 | 27 | #reduce let x := 5 in x + 3 28 | 29 | /-! 30 | By default, these results are folded and are displayed upon hovering or clicking. We can unfold them by default using annotations or directives: 31 | -/ 32 | 33 | #check nat /- .unfold -/ 34 | 35 | /-! 36 | .. lean3:: unfold 37 | -/ 38 | 39 | #check bool 40 | #eval 1 + 1 41 | 42 | /-! Other flags can be used to control display, like ``.no-in``: -/ 43 | 44 | #print iff /- .unfold .no-in -/ 45 | 46 | /-! 47 | Documenting proofs 48 | ================== 49 | 50 | Alectryon also captures goals and hypotheses as proofs progress: 51 | -/ 52 | 53 | example (p q r : Prop) : p ∧ q ↔ q ∧ p := 54 | begin /- .none -/ 55 | apply iff.intro, { 56 | intro H, 57 | apply and.intro, /- .unfold -/ 58 | apply (and.elim_right H), 59 | apply (and.elim_left H), 60 | }, { 61 | intro H, 62 | apply and.intro, 63 | apply (and.elim_right H), 64 | apply (and.elim_left H), 65 | } 66 | end 67 | 68 | /-! 69 | Most features available for Coq are also available for Lean3; in particular, references (:mref:`.s(intro H)`, :mref:`.s(and.intro).h#H`), quotes (:mquote:`.s(and.intro).h#H.type`) and assertions should work. 70 | 71 | .. massert:: .s(apply iff.intro).g#2 72 | .. mquote:: .s(apply iff.intro).g#2.ccl 73 | 74 | For now, please refer to the main README and to the Coq examples for more information. 75 | -/ 76 | -------------------------------------------------------------------------------- /recipes/literate_lean4.lean: -------------------------------------------------------------------------------- 1 | /-! 2 | ================================================== 3 | Literate programming with Alectryon (Lean4 input) 4 | ================================================== 5 | 6 | Alectryon supports literate programs and documents (combinations of code and prose) written in Lean4 and reStructuredText. Here is an example written in Lean4. It can be converted to reST, HTML, or LaTeX using the following commands:: 7 | 8 | alectryon literate_lean4.lean 9 | # Lean4+reST → HTML; produces ‘literate_lean4.html’ 10 | alectryon literate_lean4.lean --backend latex \ 11 | --latex-dialect xelatex \ 12 | -o literate_lean4.xe.tex 13 | # Lean4+reST → LaTeX; produces ‘literate_lean4.xe.tex’ 14 | alectryon literate_lean4.lean --backend rst 15 | # Lean4+reST → reST; produces ‘literate_lean4.lean.rst’ 16 | 17 | ----- 18 | 19 | .. default-role:: lean4 20 | 21 | Running queries 22 | =============== 23 | 24 | Alectryon captures the results of `#check`, `#eval`, and the like: 25 | -/ 26 | 27 | def x : Nat := 5 28 | #reduce 5 + x 29 | 30 | /-! 31 | By default, these results are folded and are displayed upon hovering or clicking. We can unfold them by default using annotations or directives: 32 | -/ 33 | 34 | #check Nat /- .unfold -/ 35 | 36 | /-! 37 | .. lean4:: unfold 38 | -/ 39 | 40 | #check Bool 41 | #eval 1 + 1 42 | 43 | /-! Other flags can be used to control display, like ``.no-in``: -/ 44 | 45 | #print Iff /- .unfold .no-in -/ 46 | 47 | /-! 48 | Documenting proofs 49 | ================== 50 | 51 | Alectryon also captures goals and hypotheses as proofs progress: 52 | -/ 53 | 54 | theorem test (p q : Prop) (hp : p) (hq : q): p ∧ q ↔ q ∧ p := by 55 | apply Iff.intro 56 | . intro h 57 | apply And.intro 58 | . exact hq 59 | . exact hp 60 | . intro h 61 | apply And.intro 62 | . exact hp 63 | . exact hq 64 | -------------------------------------------------------------------------------- /recipes/literate_reST.rst: -------------------------------------------------------------------------------- 1 | ================================================== 2 | Literate programming with Alectryon (reST input) 3 | ================================================== 4 | 5 | .. raw:: latex 6 | 7 | \makeatletter\def\alectryon@nobreakspace{\alectryon@breakspace}\makeatother 8 | 9 | Alectryon supports literate programs and documents (combinations of code and prose) written in Coq and reStructuredText. Here is an example, written in reST. It can be converted to Coq, HTML, or LaTeX using the following commands:: 10 | 11 | alectryon literate_reST.rst 12 | # reST+Coq → HTML; produces ‘literate_reST.html’ 13 | $ DOCUTILSCONFIG=literate.docutils.conf alectryon \ 14 | literate_reST.rst --backend latex 15 | # reST+Coq → LaTeX; produces ‘literate_reST.tex’ 16 | alectryon literate_reST.rst --backend coq 17 | # reST+Coq → Coq; produces ‘literate_reST.v’ 18 | 19 | $ cd ..; python -m alectryon.literate \ 20 | recipes/literate_reST.rst > recipes/literate_reST.min.v 21 | # Minimal reST → Coq; produces ‘literate_reST.min.v’ 22 | $ cd ..; python -m alectryon.literate --rst2coq - \ 23 | < recipes/literate_reST.rst > recipes/literate_reST.min.stdin.v 24 | # Minimal reST → Coq; produces ‘literate_reST.min.stdin.v’ 25 | 26 | ---- 27 | 28 | Coq fragments are introduced with ``.. coq::``: 29 | 30 | .. coq:: 31 | 32 | Lemma le_l : forall y x, S x <= y -> x <= y. 33 | induction y; inversion 1; subst. 34 | all: info_eauto. 35 | Qed. 36 | 37 | Goal forall x y z, x <= y <= z -> x <= z. 38 | 39 | They can be nested nested into other reST directives, such as tables: 40 | 41 | .. list-table:: Coq commands 42 | :header-rows: 1 43 | :width: 90% 44 | 45 | - * Coq command 46 | * Description 47 | 48 | - * 49 | .. coq:: unfold 50 | 51 | intros x y. 52 | 53 | * Move the variables `x` and `y` into the context. 54 | 55 | - * 56 | .. coq:: unfold 57 | 58 | revert x; induction y. 59 | 60 | * Perform induction on `y`, generalizing `x`. 61 | 62 | - * 63 | .. coq:: unfold 64 | 65 | all: intros x z (Hl & Hr). 66 | 67 | * Move conjunction into the context, splitting it into `Hl` and `Hr`. 68 | 69 | - * 70 | .. coq:: unfold 71 | 72 | - inversion Hl; assumption. 73 | 74 | * Solve base case; `inversion` changes `x <= 0` into `x = 0`. 75 | 76 | - * 77 | .. coq:: unfold 78 | 79 | - inversion Hl; subst; try congruence. 80 | apply IHy; split; info_eauto using le_l. 81 | 82 | * Solve inductive case using the `le_l` lemma. 83 | 84 | -------------------------------------------------------------------------------- /recipes/minification.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | Generating minified HTML 3 | ========================== 4 | 5 | Alectryon normally produces plain HTML files. For very large proofs, these files can get quite large (sometimes hundreds of megabytes), but they also tend to be highly redundant; hence, Alectryon also has the ability to generate “minified” HTML files that contain special pointers (“backreferences”) to previous parts of the document. These backreferences are resolved dynamically when the page is displayed in a web browser. 6 | 7 | Use the following command to compile this file with minification (in Sphinx' ``conf.py``, you can use ``alectryon.docutils.HTML_MINIFICATION = True`` instead):: 8 | 9 | alectryon --html-minification minification.rst # reST → HTML; produces ‘minification.html’ 10 | 11 | ----- 12 | 13 | Here is an example proof, written in a way that generates lots of redundant objects (for example, section variables appear in the proof context at every step of the proof): 14 | 15 | .. coq:: 16 | 17 | Require Import List. 18 | Import ListNotations. 19 | 20 | Section Folds. 21 | Context {A} (op: A -> A -> A) (init: A). 22 | Context (init_comm: forall a, op init a = op a init). 23 | Context (op_assoc: forall x y z, op (op x y) z = op x (op y z)). 24 | 25 | Step 1: prove that init can be moved around: 26 | 27 | .. coq:: 28 | 29 | Print fold_right. (* .unfold .messages *) 30 | 31 | Lemma init_comm' l: 32 | forall a, fold_left op l (op init a) = op a (fold_left op l init). 33 | Proof. 34 | induction l. all: simpl. all: intros. 35 | - eauto using init_comm. 36 | - rewrite op_assoc. 37 | rewrite IHl. 38 | rewrite op_assoc. 39 | rewrite <- IHl. 40 | reflexivity. 41 | Qed. 42 | 43 | Step 2: prove that fold_left and fold_right are equivalent. 44 | 45 | .. coq:: 46 | 47 | Goal forall l, fold_left op l init = fold_right op init l. 48 | Proof. 49 | intros. pose (l' := l). 50 | induction l. all: simpl. all: intros. 51 | - reflexivity. 52 | - rewrite <- IHl. rewrite init_comm'. 53 | reflexivity. 54 | Qed. 55 | End Folds. 56 | -------------------------------------------------------------------------------- /recipes/minimal.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | Compiling without Alectryon 3 | ============================= 4 | 5 | Coq files can always be compiled without Alectryon. reST files are tricker, since they have ``..coq ::`` directives. The repository contains a standalone compiler that treats these directives as code blocks and includes no-op definitions for Alectryon-specific roles:: 6 | 7 | alectryon minimal.rst # reST → HTML; produces ‘minimal.html’ 8 | 9 | $ cd ..; python -m alectryon.minimal recipes/minimal.rst recipes/minimal.no-alectryon.html 10 | # Minimal reST → HTML; produces ‘minimal.no-alectryon.html’ 11 | 12 | Directives 13 | ========== 14 | 15 | Coq code: 16 | .. coq:: 17 | 18 | Print nat. (* .unfold *) 19 | 20 | .. alectryon-toggle:: 21 | 22 | Quotes: 23 | .. mquote:: .s(Print).in 24 | 25 | Assertions: 26 | .. massert:: 27 | 28 | .s(Print).msg{*nat*} 29 | 30 | .. exercise:: Title 31 | :difficulty: 1 32 | 33 | Body 34 | 35 | Roles 36 | ===== 37 | 38 | - :alectryon-bubble:`-` 39 | - :coq:`fun x => x + 1` 40 | - :coqid:`Coq.Even.even` 41 | - :mref:`.s(Print).msg{*nat*}` 42 | - :mquote:`.s(Print).msg{*nat*}` 43 | -------------------------------------------------------------------------------- /recipes/plain-lean3.lean: -------------------------------------------------------------------------------- 1 | /- To compile: 2 | alectryon --frontend lean3 plain-lean3.lean # Lean → HTML; produces ‘plain-lean3.lean.html’ -/ 3 | 4 | -- Queries: 5 | #check nat #check bool 6 | 7 | -- Proofs: 8 | example (p q r : Prop) : p ∧ q ↔ q ∧ p := 9 | begin 10 | apply iff.intro, 11 | intro H, 12 | apply and.intro, 13 | apply (and.elim_right H), 14 | apply (and.elim_left H), 15 | intro H, 16 | apply and.intro, 17 | apply (and.elim_right H), 18 | apply (and.elim_left H), 19 | end 20 | -------------------------------------------------------------------------------- /recipes/plain-lean4.lean: -------------------------------------------------------------------------------- 1 | /- To compile: 2 | alectryon --frontend lean4 plain-lean4.lean # Lean → HTML; produces ‘plain-lean4.lean.html’ -/ 3 | 4 | -- Queries: 5 | #check Nat #check Bool 6 | 7 | -- Proofs: 8 | theorem test (p q : Prop) (hp : p) (hq : q): p ∧ q ↔ q ∧ p := by 9 | apply Iff.intro 10 | . intro h 11 | apply And.intro 12 | . exact hq 13 | . exact hp 14 | . intro h 15 | apply And.intro 16 | . exact hp 17 | . exact hq 18 | -------------------------------------------------------------------------------- /recipes/plain.v: -------------------------------------------------------------------------------- 1 | (* Converting unannotated Coq files *) 2 | 3 | (* Alectryon's default is to treat Coq files as mixed Coq + reStructuredText 4 | files ‘--frontend coq+rst’, but you can use ‘--frontend coq’ all comment 5 | processing: 6 | 7 | alectryon --frontend coq plain.v # Coq → HTML; produces ‘plain.v.html’ 8 | 9 | Conversion from plain Coq to LaTeX isn't supported yet (use coq+rst): 10 | 11 | # alectryon --frontend coq --backend latex plain.v # Coq → LaTeX; produces ‘plain.v.tex’ *) 12 | 13 | Require Import List. 14 | 15 | Lemma skipn_app {A}: 16 | forall (l1 l2: list A) n, 17 | n <= List.length l1 -> 18 | skipn n (List.app l1 l2) = 19 | List.app (skipn n l1) l2. 20 | Proof. 21 | induction l1. 22 | - destruct n. 23 | all: cbn. 24 | + reflexivity. 25 | + inversion 1. 26 | - destruct n. cbn. 27 | + reflexivity. 28 | + intros. apply IHl1. 29 | Check le_S_n. 30 | apply le_S_n. 31 | match goal with 32 | | [ H: _ <= _ |- _ ] => simpl in H 33 | end. 34 | assumption. 35 | Qed. 36 | -------------------------------------------------------------------------------- /recipes/polyglot.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | Multi-prover documents 3 | ======================== 4 | 5 | Alectryon documents can use multiple provers. Inputs for each prover are processed independently. 6 | 7 | To compile:: 8 | 9 | alectryon polyglot.rst 10 | # reST+… → HTML; produces ‘polyglot.html’ 11 | 12 | .. coq:: 13 | 14 | Require Import PeanoNat. 15 | 16 | Lemma mul_comm : 17 | forall a b: nat, a * b = b * a. 18 | Proof. 19 | induction a; simpl. 20 | - induction b; simpl; congruence. 21 | - induction b; simpl. 22 | + rewrite IHa; reflexivity. 23 | + rewrite <- IHb, IHa. 24 | simpl. rewrite IHa. 25 | rewrite !Nat.add_assoc. 26 | rewrite (Nat.add_comm b a). 27 | reflexivity. 28 | Qed. 29 | 30 | .. lean4:: 31 | 32 | open Nat 33 | 34 | def customSum : Nat -> Nat 35 | | 0 => 0 36 | | succ n => succ n + customSum n 37 | 38 | #eval customSum 10 39 | 40 | namespace Nat 41 | theorem mul_two : ∀ n : Nat, 2 * n = n + n := by 42 | intros n 43 | induction n with 44 | | zero => simp 45 | | succ n n_ih => 46 | rw [← Nat.add_one, Nat.left_distrib, n_ih] 47 | simp [Nat.add_assoc, Nat.add_comm 1] 48 | 49 | theorem gauss : ∀ n : Nat, 2 * customSum n = n * (n + 1) := by 50 | intros n 51 | induction n with 52 | | zero => simp [customSum] 53 | | succ n n_ih => 54 | simp [customSum] 55 | simp [Nat.left_distrib] 56 | rw [n_ih, ← Nat.add_one] 57 | simp [Nat.left_distrib, Nat.right_distrib, 58 | Nat.mul_one, Nat.one_mul, Nat.mul_two] 59 | rw [Nat.add_comm] 60 | simp [Nat.add_assoc] 61 | rw [Nat.add_one, Nat.add_comm 1] 62 | end Nat 63 | -------------------------------------------------------------------------------- /recipes/references.docutils.conf: -------------------------------------------------------------------------------- 1 | [xetex writer] 2 | latex-preamble: 3 | \setmainfont{Linux Libertine O} 4 | \setsansfont{Linux Biolinum O} 5 | \setmonofont[Scale=MatchLowercase]{Fira Code} 6 | 7 | \usepackage{xeCJK} 8 | \setCJKmainfont{Noto Sans CJK JP} 9 | \setCJKsansfont{Noto Sans CJK JP} 10 | \setCJKmonofont[Scale=0.9777]{Noto Sans CJK JP} 11 | -------------------------------------------------------------------------------- /recipes/sphinx/.gitignore: -------------------------------------------------------------------------------- 1 | _build/latex/ -------------------------------------------------------------------------------- /recipes/sphinx/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | $(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | latexpdf: Makefile 18 | +$(MAKE) latex 19 | texfot --tee=/dev/null --no-stderr $(MAKE) -C $(BUILDDIR)/latex 20 | 21 | %: Makefile 22 | $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 23 | -------------------------------------------------------------------------------- /recipes/sphinx/MyST.md: -------------------------------------------------------------------------------- 1 | Integration with MyST 2 | ===================== 3 | 4 | To combine Alectryon and MyST (a Markdown parser with support for docutils/Sphinx directives), just load both plugins in your Sphinx configuration: 5 | 6 | extensions = ["alectryon.sphinx", "myst_parser"] 7 | 8 | That's enough to run Coq fragments and link to identifiers: 9 | 10 | ```{coq} unfold 11 | Print nat. 12 | ``` 13 | 14 | For roles use `` {role}`argument` `` syntax: _{coqid}`like this `_. For math use either the `{math}` role ({math}`e^{i\pi} = -1`) or `$` signs (with the ``dollarmath`` extension, see ``conf.py``): ($\cos(\pi) = -1$). 15 | 16 | Note that MyST disables MathJax's heuristics for finding text to process (by marking the root of the document with `mathjax_ignore`), so any math outside of `{math}` or `$` delimiters is not processed: \\(this is not math\\); use the `mathjax_process` HTML class to revert that. 17 | -------------------------------------------------------------------------------- /recipes/sphinx/_build/alectryon/MyST.md.cache: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "cache_version": "2" 4 | }, 5 | "&coq": { 6 | "driver": [ 7 | "Coq+SerAPI", 8 | "8.18.0+0.18.2" 9 | ], 10 | "metadata": { 11 | "sertop_args": [] 12 | }, 13 | "chunks": [ 14 | "Print nat." 15 | ], 16 | "annotated": [ 17 | [ 18 | { 19 | "_type": "sentence", 20 | "contents": "Print nat.", 21 | "messages": [ 22 | { 23 | "_type": "message", 24 | "contents": "Inductive nat : Set := O : nat | S : nat -> nat.\n\nArguments S _%nat_scope" 25 | } 26 | ], 27 | "goals": [] 28 | } 29 | ] 30 | ] 31 | } 32 | } -------------------------------------------------------------------------------- /recipes/sphinx/_build/alectryon/coqchapter.v.cache: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "cache_version": "2" 4 | }, 5 | "&coq": { 6 | "driver": [ 7 | "Coq+SerAPI", 8 | "8.18.0+0.18.2" 9 | ], 10 | "metadata": { 11 | "sertop_args": [] 12 | }, 13 | "chunks": [ 14 | "Compute ((fun (n: nat) (opt: option nat) (eq: opt = Some n) => n)\n _ (Some 3) eq_refl)." 15 | ], 16 | "annotated": [ 17 | [ 18 | { 19 | "_type": "sentence", 20 | "contents": "Compute ((fun (n: nat) (opt: option nat) (eq: opt = Some n) => n)\n _ (Some 3) eq_refl).", 21 | "messages": [ 22 | { 23 | "_type": "message", 24 | "contents": " = 3\n : nat" 25 | } 26 | ], 27 | "goals": [] 28 | } 29 | ] 30 | ] 31 | } 32 | } -------------------------------------------------------------------------------- /recipes/sphinx/_build/alectryon/math.rst.cache: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "cache_version": "2" 4 | }, 5 | "&coq": { 6 | "driver": [ 7 | "Coq+SerAPI", 8 | "8.18.0+0.18.2" 9 | ], 10 | "metadata": { 11 | "sertop_args": [] 12 | }, 13 | "chunks": [ 14 | "Notation \"\\mathbb{N}\" := nat.\nPrint nat. (* .unfold *)", 15 | "Notation \"\\mathbb{B}\" := bool.\nPrint bool. (* .unfold *)" 16 | ], 17 | "annotated": [ 18 | [ 19 | { 20 | "_type": "sentence", 21 | "contents": "Notation \"\\mathbb{N}\" := nat.", 22 | "messages": [], 23 | "goals": [] 24 | }, 25 | { 26 | "_type": "text", 27 | "contents": "\n" 28 | }, 29 | { 30 | "_type": "sentence", 31 | "contents": "Print nat.", 32 | "messages": [ 33 | { 34 | "_type": "message", 35 | "contents": "Inductive nat : Set :=\n O : \\mathbb{N} | S : \\mathbb{N} -> \\mathbb{N}.\n\nArguments S _%nat_scope" 36 | } 37 | ], 38 | "goals": [] 39 | }, 40 | { 41 | "_type": "text", 42 | "contents": " (* .unfold *)" 43 | } 44 | ], 45 | [ 46 | { 47 | "_type": "sentence", 48 | "contents": "Notation \"\\mathbb{B}\" := bool.", 49 | "messages": [], 50 | "goals": [] 51 | }, 52 | { 53 | "_type": "text", 54 | "contents": "\n" 55 | }, 56 | { 57 | "_type": "sentence", 58 | "contents": "Print bool.", 59 | "messages": [ 60 | { 61 | "_type": "message", 62 | "contents": "Inductive bool : Set :=\n true : \\mathbb{B} | false : \\mathbb{B}." 63 | } 64 | ], 65 | "goals": [] 66 | }, 67 | { 68 | "_type": "text", 69 | "contents": " (* .unfold *)" 70 | } 71 | ] 72 | ] 73 | } 74 | } -------------------------------------------------------------------------------- /recipes/sphinx/_static/mathjax_config.js: -------------------------------------------------------------------------------- 1 | MathJax = { 2 | options: { 3 | // Added alectryon-io 4 | processHtmlClass: 'tex2jax_process|mathjax_process|math|output_area|alectryon-io' 5 | }, 6 | startup: { 7 | pageReady: function () { 8 | document.querySelectorAll( // Find blocks to replace math in 9 | ".coq-math .alectryon-input, " + 10 | ".coq-math .alectryon-message, " + 11 | ".coq-math .goal-conclusion, " + 12 | ".coq-math .hyp-body span, " + 13 | ".coq-math .hyp-type span" 14 | ).forEach(function (e) { // Wrap each math node in math delimiters 15 | e.innerHTML = e.innerHTML.replace(/([\\]mathbb{N})/g, '\\($1\\)'); 16 | }); 17 | // Then run MathJax 18 | return MathJax.startup.defaultPageReady(); 19 | } 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /recipes/sphinx/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | 3 | # -- Path setup -------------------------------------------------------------- 4 | 5 | import os 6 | import sys 7 | sys.path.insert(0, os.path.abspath('../../')) 8 | 9 | # -- Project information ----------------------------------------------------- 10 | 11 | project = 'alectryon-demo' 12 | copyright = '2019-2021, Clément Pit-Claudel' 13 | author = 'Clément Pit-Claudel' 14 | today = " " 15 | 16 | # -- General configuration --------------------------------------------------- 17 | 18 | extensions = ["alectryon.sphinx", "sphinx.ext.mathjax"] 19 | 20 | try: 21 | import myst_parser 22 | extensions.append("myst_parser") 23 | except ImportError: 24 | print("/!\\ `myst_parser` not found, skipping MyST tests /!\\", file=sys.stderr) 25 | myst_enable_extensions = ["dollarmath"] 26 | 27 | pygments_style = "emacs" 28 | 29 | # -- Options for HTML output ------------------------------------------------- 30 | 31 | html_theme = 'alabaster' 32 | html_static_path = ['_static'] 33 | 34 | # -- Alectryon configuration ------------------------------------------------- 35 | 36 | import alectryon.docutils 37 | alectryon.docutils.CACHE_DIRECTORY = "_build/alectryon/" 38 | 39 | # -- MathJax configuration --------------------------------------------------- 40 | 41 | import sphinx 42 | from sphinx.ext import mathjax 43 | mathjax.MATHJAX_URL = alectryon.docutils.HtmlTranslator.MATHJAX_URL # MathJax 3 44 | 45 | # This configuration is explained in recipes/mathjax.rst 46 | # Use either this (Sphinx ≥ 4.0 only): 47 | 48 | html_js_files = ['mathjax_config.js'] 49 | mathjax_options = { "priority": 1000 } 50 | 51 | # or this (but inline the configuration instead of Path(…).read_text()): 52 | 53 | from pathlib import Path 54 | html_js_files = [ 55 | (None, { 56 | "body": Path("_static/mathjax_config.js").read_text(), 57 | # The required priority depends on the version of Sphinx 58 | "priority": 0 if sphinx.version_info < (4,) else 1000 59 | }) 60 | ] 61 | 62 | # or this: 63 | 64 | priority = 0 if sphinx.version_info < (4,) else 1000 65 | html_js_files = [('mathjax_config.js', 66 | # The required priority depends on the version of Sphinx 67 | { "priority": 0 if sphinx.version_info < (4,) else 1000 })] 68 | -------------------------------------------------------------------------------- /recipes/sphinx/coqchapter.v: -------------------------------------------------------------------------------- 1 | (*| 2 | ================================ 3 | A chapter stored as a Coq file 4 | ================================ 5 | |*) 6 | 7 | Compute ((fun (n: nat) (opt: option nat) (eq: opt = Some n) => n) 8 | _ (Some 3) eq_refl). 9 | -------------------------------------------------------------------------------- /recipes/sphinx/docutils.conf: -------------------------------------------------------------------------------- 1 | [restructuredtext parser] 2 | syntax_highlight = short 3 | -------------------------------------------------------------------------------- /recipes/sphinx/index.rst: -------------------------------------------------------------------------------- 1 | .. alectryon-demo documentation master file, created by 2 | sphinx-quickstart on Sat Jul 11 15:24:03 2020. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to alectryon-demo's documentation! 7 | ========================================== 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | :caption: Contents: 12 | 13 | coqchapter 14 | math 15 | MyST 16 | 17 | The default role produces Coq code: `let a := 1 in a + a`. 18 | 19 | .. coq:: 20 | 21 | Definition example_from_sphinx: nat. (* .unfold *) 22 | Proof. (* .no-goals *) 23 | simple apply Nat.add. 24 | exact 1. 25 | assert (n: nat). 26 | 2: clear n. 27 | exact 3. 28 | exact 2. 29 | Defined. 30 | 31 | Print example_from_sphinx. (* .unfold *) 32 | 33 | Indices and tables 34 | ================== 35 | 36 | * :ref:`genindex` 37 | * :ref:`modindex` 38 | * :ref:`search` 39 | -------------------------------------------------------------------------------- /recipes/sphinx/math.rst: -------------------------------------------------------------------------------- 1 | =================== 2 | MathJax in Sphinx 3 | =================== 4 | 5 | Using any math on a page causes Sphinx to automatically load MathJax: `e^{i\pi} = -1`:math:. 6 | 7 | If you want to highlight pieces of code with MathJax, too, then you can either: 8 | 9 | - Use a custom mathjax config script (see the discussion in ``recipes/mathjax.rst``, the configuration in ``conf.py``, and the implementation in ``recipes/sphinx/_static/mathjax_config.js``). Math in the following snippet is highlighted using this technique; look for the link to ``mathjax_config.js`` in the ```` of this webpage: 10 | 11 | .. coq:: 12 | :class: coq-math 13 | 14 | Notation "\mathbb{N}" := nat. 15 | Print nat. (* .unfold *) 16 | 17 | - Use a custom script to add class ``mathjax_process`` to each ``.alectryon-io`` block, on a single page. This works best if you need MathJax on just one page and you do not need to customize MathJax further. Math in the following Coq snippet is highlighted that way (view the source of this page to see the custom script): 18 | 19 | .. raw:: html 20 | 21 | 36 | 37 | .. coq:: 38 | :class: coq-math-2 39 | 40 | Notation "\mathbb{B}" := bool. 41 | Print bool. (* .unfold *) 42 | -------------------------------------------------------------------------------- /recipes/src/imported.v: -------------------------------------------------------------------------------- 1 | Definition xyz := 1. 2 | -------------------------------------------------------------------------------- /recipes/tests/alternative_clis.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates how to call Alectryon's through Docutils CLI. 3 | 4 | To run:: 5 | 6 | $ python alternative_clis.py > alternative_clis.out 7 | # CLIs; produces ‘alternative_clis.out’ 8 | """ 9 | import sys 10 | from io import BytesIO, TextIOWrapper 11 | 12 | from alectryon import cli, literate 13 | 14 | def run(cmd, args, stdin): 15 | sys.argv = ["({})".format(cmd.__name__), "-", "--traceback", *args] 16 | sys.stdin = TextIOWrapper(BytesIO(stdin.encode("utf-8"))) 17 | print("== {} ==".format(cmd.__name__)) 18 | try: 19 | cmd() 20 | sys.exit(0) 21 | except SystemExit as e: 22 | print("-- exit: {} --\n".format(e.code)) 23 | 24 | def main(): 25 | COQ_INPUT = "Check nat." 26 | REST_INPUT = literate.coq2rst(COQ_INPUT) 27 | run(cli.rstcoq2html, ["--no-header", "--pygments-style=emacs"], COQ_INPUT) 28 | run(cli.coqrst2html, ["--no-header"], REST_INPUT) 29 | run(cli.rstcoq2latex, [], COQ_INPUT) 30 | run(cli.coqrst2latex, [], REST_INPUT) 31 | 32 | if __name__ == '__main__': 33 | main() 34 | -------------------------------------------------------------------------------- /recipes/tests/auto_toggle.rst: -------------------------------------------------------------------------------- 1 | ==================================================================== 2 | Test that a toggle is automatically inserted after a docinfo block 3 | ==================================================================== 4 | 5 | :date: Test 6 | 7 | To compile:: 8 | 9 | alectryon auto_toggle.rst # reST → HTML; produces ‘auto_toggle.html’ 10 | 11 | .. coq:: 12 | 13 | Check 1. 14 | -------------------------------------------------------------------------------- /recipes/tests/cache_v1.v: -------------------------------------------------------------------------------- 1 | (*| 2 | ============== 3 | Cached reads 4 | ============== 5 | 6 | This file checks that reading from a cache works. To compile:: 7 | 8 | alectryon cache_v1.v --cache-directory tests/ 9 | # Coq → HTML (cached); produces ‘cache_v1.html’ 10 | 11 | .. coq:: 12 | |*) 13 | 14 | Check 1. 15 | -------------------------------------------------------------------------------- /recipes/tests/cache_v1.v.cache: -------------------------------------------------------------------------------- 1 | { 2 | "generator": [ 3 | "Coq+SerAPI", 4 | "8.12.0+0.12.0" 5 | ], 6 | "metadata": { 7 | "sertop_args": [], 8 | "cache_version": "1" 9 | }, 10 | "chunks": [ 11 | "Check 1." 12 | ], 13 | "annotated": [ 14 | [ 15 | { 16 | "_type": "sentence", 17 | "contents": "Check 1.", 18 | "messages": [ 19 | { 20 | "_type": "message", 21 | "contents": "1\n : nat" 22 | } 23 | ], 24 | "goals": [] 25 | } 26 | ] 27 | ] 28 | } -------------------------------------------------------------------------------- /recipes/tests/cache_v2.v: -------------------------------------------------------------------------------- 1 | (*| 2 | ============== 3 | Cached reads 4 | ============== 5 | 6 | This file checks that reading from a cache works. To compile:: 7 | 8 | alectryon cache_v2.v --cache-directory tests/ 9 | # Coq → HTML (cached); produces ‘cache_v2.html’ 10 | 11 | .. coq:: 12 | |*) 13 | 14 | Check 1. 15 | -------------------------------------------------------------------------------- /recipes/tests/cache_v2.v.cache: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "cache_version": "2" 4 | }, 5 | "&coq": { 6 | "generator": [ 7 | "Coq+SerAPI", 8 | "8.13.0+0.13.0" 9 | ], 10 | "metadata": { 11 | "sertop_args": [] 12 | }, 13 | "chunks": [ 14 | "Check 1." 15 | ], 16 | "annotated": [ 17 | [ 18 | { 19 | "_type": "sentence", 20 | "contents": "Check 1.", 21 | "messages": [ 22 | { 23 | "_type": "message", 24 | "contents": "1\n : nat" 25 | } 26 | ], 27 | "goals": [] 28 | } 29 | ] 30 | ] 31 | } 32 | } -------------------------------------------------------------------------------- /recipes/tests/cli_flags.rst: -------------------------------------------------------------------------------- 1 | ================================== 2 | SerAPI flags on the command line 3 | ================================== 4 | 5 | To compile:: 6 | 7 | alectryon cli_flags.rst -o - >/dev/null \ 8 | --debug --traceback --expect-unexpected --long-line-threshold=-1 \ 9 | -I . -R ../recipes/ custom_flag_recipes \ 10 | -Q ../alectryon/ custom_flag_alectryon_tests; \ 11 | echo "exit: $?" > cli_flags.txt 12 | # reST + assertions; produces ‘cli_flags.txt’ 13 | 14 | .. massert:: .io#lp 15 | 16 | .s(LoadPath).msg{*custom_flag_recipes*alectryon/recipes*} 17 | .s(LoadPath).msg{*alectryon*custom_flag_alectryon_tests*} 18 | 19 | .. coq:: 20 | :name: lp 21 | 22 | Print LoadPath. (* .in *) 23 | -------------------------------------------------------------------------------- /recipes/tests/coqc_time_error.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | Errors from coqc -time 3 | ======================== 4 | 5 | An incomplete file is an error with ``coqc -time``. 6 | To compile:: 7 | 8 | alectryon --coq-driver=coqc_time --backend webpage -o /dev/null \ 9 | coqc_time_error.rst > coqc_time_error.out 2>&1; \ 10 | echo "exit: $?" >> coqc_time_error.out; \ 11 | sed -i 's/in file [^:]*//' coqc_time_error.out 12 | # ReST → HTML; produces ‘coqc_time_error.out’ 13 | 14 | .. coq:: 15 | 16 | Goal True. 17 | -------------------------------------------------------------------------------- /recipes/tests/corner_cases.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | Corner cases 3 | ============== 4 | 5 | To compile:: 6 | 7 | alectryon --stdin-filename corner_cases.rst --frontend rst \ 8 | -o corner_cases.html - < corner_cases.rst 9 | # Coq → HTML; produces ‘corner_cases.html’ 10 | alectryon corner_cases.rst -o corner_cases.xe.tex \ 11 | --latex-dialect xelatex 12 | # Coq → LaTeX; produces ‘corner_cases.xe.tex’ 13 | 14 | Goal names 15 | ========== 16 | 17 | .. coq:: 18 | 19 | Goal True -> True /\ True. 20 | intros; refine (conj ?[G1] ?[G2]). (* .unfold *) 21 | [G1]: exact I. 22 | [G2]: exact I. 23 | Qed. 24 | 25 | Self-reference 26 | ============== 27 | 28 | .. coq:: 29 | 30 | Definition a := 1. 31 | Check corner_cases.a. 32 | 33 | Blanks at beginning of snippet 34 | ============================== 35 | 36 | .. coq:: 37 | 38 | Goal True. 39 | 40 | .. coq:: 41 | 42 | - (* .out .unfold *) exact I. Qed. 43 | 44 | Blanks around sentences 45 | ======================= 46 | 47 | Bubble: :alectryon-bubble:`-` 48 | 49 | .. alectryon-toggle:: 50 | 51 | .. coq:: 52 | 53 | (* xyz *) Goal True /\ True. 54 | - idtac. 55 | pose (a := 1). 56 | (* xyz *) split. (* xyz *) 57 | + (* xyz *) idtac. 58 | idtac. (* x 59 | yz *) 60 | split. 61 | + (* xyz *) { (* xyz *) split. } 62 | Qed. (* xyz *) 63 | 64 | References 65 | ========== 66 | 67 | :mref:`.s(pose).h#a.body`, :mref:`.s(pose).h#a.type`, :mref:`.s(pose).h#a`. 68 | 69 | .. role:: mq(mref) 70 | :kind: quote 71 | -------------------------------------------------------------------------------- /recipes/tests/dialects.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | LaTeX and HTML dialects 3 | ========================= 4 | 5 | .. raw:: latex 6 | 7 | \let\oldalltt\alltt 8 | \def\alltt{\oldalltt\scriptsize} 9 | 10 | This simple file demos LaTeX and HTML dialect configuration:: 11 | 12 | alectryon --html-dialect=html4 -o dialects.4.html dialects.rst 13 | # HTML4; produces ‘dialects.4.html’ 14 | alectryon --html-dialect=html5 -o dialects.5.html dialects.rst 15 | # HTML5; produces ‘dialects.5.html’ 16 | 17 | alectryon --latex-dialect=pdflatex -o dialects.tex dialects.rst 18 | # LaTeX; produces ‘dialects.tex’ 19 | alectryon --latex-dialect=xelatex -o dialects.xe.tex dialects.rst 20 | # XeLaTeX; produces ‘dialects.xe.tex’ 21 | alectryon --latex-dialect=lualatex -o dialects.lua.tex dialects.rst 22 | # LuaLaTeX; produces ‘dialects.lua.tex’ 23 | 24 | .. coq:: unfold 25 | 26 | Goal True. 27 | exact I. 28 | Show Proof. 29 | -------------------------------------------------------------------------------- /recipes/tests/directive-options.rst: -------------------------------------------------------------------------------- 1 | ======================= 2 | Coq directive options 3 | ======================= 4 | 5 | .. raw:: latex 6 | 7 | \let\oldalltt\alltt 8 | \def\alltt{\oldalltt\scriptsize} 9 | 10 | This file checks that ``:class:`` and ``:name:`` attributes work on ``.. coq::`` directives:: 11 | 12 | alectryon directive-options.rst 13 | # reST → Coq; produces ‘directive-options.html’ 14 | alectryon directive-options.rst --latex-dialect xelatex -o directive-options.xe.tex 15 | # reST → LaTeX; produces ‘directive-options.xe.tex’ 16 | 17 | .. coq:: none 18 | 19 | From Coq Require Import String. 20 | Open Scope string_scope. 21 | 22 | .. raw:: html 23 | 24 | 25 | 26 | .. coq:: 27 | :class: upside-down 28 | :name: test 29 | 30 | Definition test := " =: ʇsǝʇ uoıʇıuıɟǝᗡ". 31 | 32 | Link to `first Coq fragment `_. 33 | Another `link `__. 34 | -------------------------------------------------------------------------------- /recipes/tests/display-flags.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | Output flags 3 | ============== 4 | 5 | This file tests various combinations of display flags. To compile:: 6 | 7 | alectryon display-flags.rst # reST → HTML; produces ‘display-flags.html’ 8 | 9 | .. coq:: none 10 | 11 | Require Import Coq.Unicode.Utf8. (* .none *) 12 | Require Import PeanoNat ArithRing. 13 | 14 | Fixpoint nsum max f := 15 | match max with 16 | | O => f 0 17 | | S max' => f max + nsum max' f 18 | end. 19 | 20 | .. coq:: no-hyps unfold 21 | 22 | Lemma Gauss: ∀ n, 23 | 2 * (nsum n (fun x => x)) = 24 | n * (n + 1). 25 | Proof. (* .fold *) 26 | induction n; cbn [nsum]. (* .hyps *) 27 | - (* n ← 0 *) (* .hyps *) 28 | Show Proof. (* .in .messages *) 29 | reflexivity. 30 | - (* n ← S _ *) (* .hyps *) 31 | rewrite Nat.mul_add_distr_l. (* .fold *) 32 | rewrite IHn. 33 | Show Proof. (* .all .no-goals *) 34 | ring. 35 | Qed. 36 | 37 | .. coq:: unfold 38 | 39 | Check nat. 40 | About bool. (* .fold *) 41 | 42 | .. coq:: fails 43 | 44 | Fail Check 1 + true. 45 | Fail Definition a := asd. 46 | Definition xyz := 123. (* .succeeds *) 47 | 48 | .. coq:: none 49 | 50 | Set Printing Implicit. 51 | Check id. (* .all *) 52 | Unset Printing Implicit. 53 | 54 | .. coq:: 55 | 56 | (* Visible *) 57 | 58 | .. coq:: none 59 | 60 | (* Hidden *) 61 | 62 | .. coq:: 63 | 64 | Fail Definition a := (* .fails .unfold *) 65 | (* `.fails` adds red highlight and removes "indeed failed". *) 66 | 1 + true. 67 | 68 | Require PeanoNat. (* ← Executed but hidden *) (* .none *) 69 | Goal True. (* ← Goal unfolded *) (* .unfold *) 70 | Fail exact 1. (* ← Goal omitted *) (* .in .messages *) 71 | Fail fail. (* ← Error message shown, input hidden *) (* .unfold .messages *) 72 | exact I. (* ← Executed but hidden *) (* -.s{*} *) 73 | Qed. 74 | 75 | .. coq:: -.h#l* -.h#[aA] -.s(Check let).msg(Check) -.s{Proof.}.in -.s{Proof.}.g#* -.s{Proof.}.msg(*) 76 | :name: pr 77 | 78 | Require Import Coq.Sorting.Permutation. (* .none *) 79 | Check let t := nat in forall {n: t}, n >= 0. (* .unfold *) 80 | Theorem Permutation_In {A} (l l' : list A) (a: A) : 81 | Permutation l l' -> List.In a l -> List.In a l'. (* .unfold *) 82 | Proof. 83 | induction 1; intros * Hin; [ | refine ?[gg] | .. ]. (* 84 | .unfold -.g#* .g#2 .g#4 .g#4.h{list A} *) 85 | all: simpl in *. (* -.g#*.ccl *) 86 | all: tauto. 87 | Qed. 88 | -------------------------------------------------------------------------------- /recipes/tests/docinfo_flags.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | SerAPI flags in docinfo 3 | ========================= 4 | 5 | To compile:: 6 | 7 | alectryon docinfo_flags.rst -o - >/dev/null; echo "exit: $?" > docinfo_flags.txt 8 | # reST + assertions; produces ‘docinfo_flags.txt’ 9 | 10 | :date: August 30, 2021 11 | :alectryon/serapi/args: -I . -R ../recipes/ custom_flag_recipes 12 | :alectryon/serapi/args: -Q ../alectryon/ custom_flag_alectryon_tests 13 | 14 | .. topic:: Checking for SerAPI flags 15 | 16 | Calling :mquote:`.io#lp.s(Print LoadPath).in` (:mref:`.io#lp.s(Print LoadPath)`) prints all know paths, and the assert below checks that the expected paths are found. 17 | 18 | .. massert:: .io#lp 19 | 20 | .s(LoadPath).msg{*custom_flag_recipes*alectryon/recipes*} 21 | .s(LoadPath).msg{*alectryon*custom_flag_alectryon_tests*} 22 | 23 | .. coq:: 24 | :name: lp 25 | 26 | Print LoadPath. (* .in *) 27 | 28 | .. coq:: 29 | 30 | Check nat. 31 | -------------------------------------------------------------------------------- /recipes/tests/doctests.py: -------------------------------------------------------------------------------- 1 | r""" 2 | Run Alectryon's doctests. 3 | 4 | $ python doctests.py | sed 's/\(tests\) in [0-9.]\+s$/\1/g' > doctests.out 5 | # Run doctests; produces ‘doctests.out’ 6 | 7 | (but make sure that the ROOT of this repo is in PYTHONPATH) 8 | """ 9 | 10 | import doctest 11 | import re 12 | import sys 13 | import unittest 14 | 15 | from fnmatch import fnmatchcase 16 | from pathlib import Path 17 | 18 | DIR = Path(__file__).parent 19 | ROOT = (DIR / "../../").resolve() 20 | 21 | EXCLUDED = "__main__.py" 22 | FLAGS = doctest.NORMALIZE_WHITESPACE 23 | 24 | class Checker(doctest.OutputChecker): 25 | COMMENTS = re.compile(r"#.*(?:\n\s*)?") 26 | 27 | def check_output(self, want, got, optionflags): 28 | """Like ``OutputChecker.check_output``, but strip comments.""" 29 | want = self.COMMENTS.sub("", want) 30 | return super().check_output(want, got, optionflags) 31 | 32 | def overwrite_docfilecase(): 33 | """Replace ``doctest.DocFileCase`` with a custom class that prints relative paths.""" 34 | class DocFileCase(doctest.DocFileCase): 35 | def __repr__(self): 36 | return Path(self._dt_test.filename).name 37 | doctest.DocFileCase = DocFileCase 38 | overwrite_docfilecase() 39 | 40 | def suite(loader): 41 | s = unittest.TestSuite() 42 | 43 | files = [(ROOT / "README.rst"), 44 | *sorted((ROOT / "alectryon").glob("*.py"))] 45 | 46 | for f in files: 47 | if f.name in EXCLUDED: 48 | continue 49 | if loader.testNamePatterns is not None: 50 | if not any(fnmatchcase(f.name, pat) for pat in loader.testNamePatterns): 51 | continue 52 | if f.name.endswith(".rst"): 53 | s.addTests(doctest.DocFileSuite( 54 | str(f.resolve()), module_relative=False, 55 | checker=Checker(), optionflags=FLAGS)) 56 | else: 57 | s.addTests(doctest.DocTestSuite(f"alectryon.{f.stem}", optionflags=FLAGS)) 58 | 59 | return s 60 | 61 | def load_tests(loader, _standard_tests, _pattern): # pylint: disable=unused-argument 62 | # s.addTests(tests) # There are no tests in this file 63 | return suite(loader) 64 | 65 | if __name__ == '__main__': 66 | sys.stderr = sys.stdout 67 | unittest.main(verbosity=2) 68 | -------------------------------------------------------------------------------- /recipes/tests/errors.rst: -------------------------------------------------------------------------------- 1 | ===================== 2 | Errors and warnings 3 | ===================== 4 | 5 | :alectryon/pygments/tacn: test 6 | :alectryon/pygments/coq/xyz: test 7 | 8 | The ``lint`` backend in Alectryon runs the compiler and reports errors on ``stderr``:: 9 | 10 | alectryon errors.rst --backend lint; echo "exit: $?" >> errors.lint.json 11 | # reST → JSON errors; produces ‘errors.lint.json’ 12 | alectryon errors.rst --copy-assets none --backend webpage -o /dev/null 2> errors.txt; echo "exit: $?" >> errors.txt 13 | # reST → HTML + errors; produces ‘errors.txt’ 14 | 15 | Markup 16 | ====== 17 | 18 | A reST error: *here. 19 | 20 | Roles 21 | ===== 22 | 23 | ``coqid`` 24 | --------- 25 | 26 | Not fully qualified: 27 | - :coqid:`A` 28 | 29 | Not registered: 30 | - :coqid:`Lib.A` 31 | 32 | ``mref``, ``mquote`` 33 | -------------------- 34 | 35 | Bad syntax: 36 | - :mref:`...` 37 | - :mref:`.io` 38 | - :mref:`.abc` 39 | - :mref:`.s()` 40 | - :mref:`.s.g` 41 | - :mref:`.io{*}.g` 42 | - :mref:`.s[a-z]` 43 | - :mref:`.s(abc)[a-z]` 44 | - .. massert:: <.s(Goal)> 45 | 46 | Bad targets (static): 47 | - :mref:`.s(Goal).g#0.name` 48 | 49 | Missing sentence: 50 | - :mref:`.g#0.in` 51 | 52 | Incompatible selectors: 53 | - :mref:`.s(Goal).in.ccl` 54 | 55 | Unquotable: 56 | - :mquote:`.s(Goal)` 57 | - :mquote:`.s(Goal).g#1` 58 | - .. mquote:: .s(Goal) 59 | - .. mquote:: .s(Goal).g#1 60 | 61 | Quote and title 62 | - :mquote:`test <.s(Goal)>` 63 | - .. mquote:: test <.s(Goal)> 64 | - .. massert:: 65 | 66 | test <.s(Goal)> 67 | 68 | Bad prefix: 69 | .. role:: mq2(mquote) 70 | :prefix: 71 | .. role:: mq2(mquote) 72 | :prefix: .s.i 73 | 74 | No block to reference (dynamic): 75 | - :mref:`.s(Goal True)` 76 | 77 | .. coq:: 78 | 79 | Goal True. 80 | pose proof 1 as n. 81 | exact I. (* A very long line *) (* whose contents are split *) (* across *) (* multiple *) (* comments *) 82 | Qed. 83 | 84 | Bad targets (dynamic): 85 | - :mref:`.io#nope.s(123)` 86 | - :mref:`.s(Goal).g#25` 87 | - :mref:`.s(pose proof).h#n.body` 88 | 89 | Bad assertions (dynamic): 90 | .. massert:: .s(Goal True) 91 | 92 | .g{*not found*} 93 | 94 | .msg 95 | 96 | Directives 97 | ========== 98 | 99 | .. exercise:: Title 100 | 101 | (Missing :difficulty: flag.) 102 | 103 | Flags 104 | ----- 105 | 106 | Unknown directive flags 107 | .. coq:: unknown 108 | 109 | Check nat. 110 | 111 | Leftover flags 112 | .. coq:: unfold out .xyz 113 | 114 | Check nat. 115 | 116 | Bad syntax in directive flags 117 | .. coq:: .s.g 118 | 119 | Check nat. 120 | 121 | Bad syntax in inline flags (dynamic) 122 | .. coq:: 123 | 124 | Check nat. (* .io.s *) 125 | Check nat. (* .unfold .xyz *) 126 | 127 | Inapplicable targets (dynamic) 128 | .. coq:: 129 | 130 | Check nat. (* .io#abc *) 131 | Check nat. (* .g#1 *) 132 | Goal True. (* .g#1.ccl .in .g#1.name *) 133 | idtac. (* .g#1.h{*}.body .g#1.h{*}.type .g#1.h{*}.name *) 134 | Abort. (* .msg{*} *) 135 | 136 | Inconsistent flags 137 | .. coq:: unfold out 138 | 139 | Check nat. (* .fold *) 140 | 141 | Broken code 142 | =========== 143 | 144 | .. coq:: 145 | 146 | Notation "'🆄🄽🅘ⓒ⒪𝖉∈' α ⊕ ⤋ β" := α ++ β. (* Bytes vs. str *) 147 | 148 | .. coq:: 149 | 150 | Definition a 151 | : true := 1. (* Next line *) 152 | -------------------------------------------------------------------------------- /recipes/tests/errors.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This file tests various fatal errors raised from the command line. 4 | # To run: 5 | # $ PYTHON="python " ALECTRYON="alectryon " bash errors.sh 2>&1 | sed '/^usage\|^ \{10,\}/d' > errors.sh.out 6 | # Errors and warnings; produces ‘errors.sh.out’ 7 | 8 | set -v 9 | $PYTHON -m alectryon.literate -; echo $? 10 | $PYTHON -m alectryon.literate xyz.unsupported; echo $? 11 | $ALECTRYON xyz.unsupported; echo $? 12 | $ALECTRYON xyz.v -o xyz.unsupported; echo $? 13 | $ALECTRYON xyz.v.json -o xyz.rst; echo $? 14 | $ALECTRYON a.v.json b.v.json -o c.v.json; echo $? 15 | $ALECTRYON a.v.json --stdin-filename b.v.json; echo $? 16 | $ALECTRYON a.v.json --mark-point not_an_int ⊙; echo $? 17 | -------------------------------------------------------------------------------- /recipes/tests/excepthook.v: -------------------------------------------------------------------------------- 1 | (** This file tests the hook installed in sys.excepthook. 2 | 3 | To run: 4 | 5 | alectryon not_found.v --frontend coq \ 6 | --traceback -o - 2>&1 | \ 7 | sed 's/File ".\+\?", line [0-9]\+/File …, line …/g' | \ 8 | sed '/^ /d' | sed '/^ *$/d' | uniq | \ 9 | cat > excepthook.v.out; ! test $? -eq 0 10 | # Plain Coq → HTML + errors; produces ‘excepthook.v.out’ **) 11 | -------------------------------------------------------------------------------- /recipes/tests/fatal.v: -------------------------------------------------------------------------------- 1 | (** This file tests for immediate exit: when not going through the 2 | Docutils pipeline, all errors are immediately fatal. 3 | 4 | To run: 5 | alectryon fatal.v --frontend coq -o - > /dev/null \ 6 | 2> fatal.v.out; echo "exit: $?" >> fatal.v.out 7 | # Plain Coq → HTML + errors; produces ‘fatal.v.out’ **) 8 | 9 | Goal not_found = 1. 10 | -------------------------------------------------------------------------------- /recipes/tests/fatal_transform.v: -------------------------------------------------------------------------------- 1 | (** This file tests for immediate exit: when not going through the 2 | Docutils pipeline, all errors are immediately fatal. 3 | 4 | To run: 5 | alectryon fatal_transform.v --frontend coq -o - > /dev/null \ 6 | 2> fatal_transform.v.out; echo "exit: $?" >> fatal_transform.v.out 7 | # Plain Coq → HTML + errors; produces ‘fatal_transform.v.out’ **) 8 | 9 | Goal True. (* -.g#4 *) 10 | -------------------------------------------------------------------------------- /recipes/tests/frontend_warnings.json: -------------------------------------------------------------------------------- 1 | // This file ensures that --frontend json is supported but with a warning:: 2 | // 3 | // $ alectryon frontend_warnings.json -o - > frontend_warnings.json.out 2>&1 # Frontend warnings; produces ‘frontend_warnings.json.out’ 4 | ["Axiom A: Type."] 5 | -------------------------------------------------------------------------------- /recipes/tests/lean3_error.rst: -------------------------------------------------------------------------------- 1 | =================== 2 | Errors from Lean3 3 | =================== 4 | 5 | Lean 3 doesn't support file with errors:: 6 | 7 | alectryon lean3_error.rst --backend webpage -o /dev/null 2>&1 | \ 8 | sed 's/^\( *\).*\?\([.]lean:\)/\1...\2/g' > lean3_error.out; \ 9 | echo "exit: $?" >> lean3_error.out 10 | # ReST → HTML; produces ‘lean3_error.out’ 11 | 12 | .. lean3:: 13 | 14 | #asd 1 15 | -------------------------------------------------------------------------------- /recipes/tests/lists.lean3: -------------------------------------------------------------------------------- 1 | /- To compile: 2 | alectryon lists.lean3 -o lists.lean3.html 3 | # Lean → HTML; produces ‘lists.lean3.html’ -/ 4 | 5 | variables {α β : Type*} 6 | 7 | open list 8 | 9 | theorem mem_map_of_mem (f : α → β) 10 | {a : α} {l : list α} (h : a ∈ l) : f a ∈ map f l := 11 | begin 12 | induction l with b l' ih, 13 | {cases h}, 14 | {cases h with h, 15 | {apply or.inl, rw h}, 16 | {exact or.inr (ih h)}} 17 | end 18 | 19 | theorem exists_of_mem_map {f : α → β} 20 | {b : β} {l : list α} (h : b ∈ map f l) : 21 | ∃ a, a ∈ l ∧ f a = b := 22 | begin 23 | induction l with c l' ih, 24 | { cases h }, 25 | { cases (eq_or_mem_of_mem_cons h) with h h, 26 | { exact ⟨c, mem_cons_self _ _, h.symm⟩ }, 27 | { cases ih h with a ha₁, 28 | exact ⟨a, mem_cons_of_mem _ ha₁.1, ha₁.2⟩ }} 29 | end 30 | 31 | @[simp] theorem mem_map {f : α → β} {b : β} {l : list α} 32 | : b ∈ map f l ↔ ∃ a, a ∈ l ∧ f a = b := 33 | ⟨exists_of_mem_map, 34 | λ ⟨a, la, h⟩, by rw [← h]; exact mem_map_of_mem f la⟩ 35 | 36 | lemma forall_mem_map_iff {f : α → β} {l : list α} {P : β → Prop} : 37 | (∀ i ∈ l.map f, P i) ↔ ∀ j ∈ l, P (f j) := 38 | begin 39 | split, 40 | { assume H j hj, 41 | exact H (f j) (mem_map_of_mem f hj) }, 42 | { assume H i hi, 43 | cases mem_map.1 hi with j hj, 44 | rw ← hj.2, 45 | exact H j hj.1 } 46 | end 47 | -------------------------------------------------------------------------------- /recipes/tests/literate.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | reST → Coq translation tests 3 | ============================== 4 | 5 | To compile:: 6 | 7 | $ alectryon literate.rst --backend coq # reST → Coq; produces ‘literate.v’ 8 | 9 | Code blocks 10 | =========== 11 | 12 | .. coq:: 13 | 14 | Goal True /\ True. 15 | 16 | The last header is needed (to avoid putting `Goal True` in the ``To compile::`` block) but the next one is superfluous: 17 | 18 | .. coq:: 19 | 20 | split. 21 | 22 | This one is needed because it includes a ``:name:``: 23 | 24 | .. coq:: 25 | :name: exact 26 | 27 | exact I. 28 | 29 | .. note:: This note includes two Coq fragments: 30 | 31 | .. coq:: 32 | 33 | idtac "This comment is part of the note". 34 | 35 | The last header is needed (to include the comment in the note) but the next one is superfluous. The one after the note is needed, but not the one after that (though a ``(*||*)`` comment is still needed, because otherwise the two blocks would get merged into one): 36 | 37 | .. coq:: 38 | 39 | idtac "This comment is part of the note". 40 | 41 | .. coq:: 42 | 43 | (* This comment isn't part of the note *) 44 | 45 | .. coq:: 46 | 47 | Qed. 48 | 49 | Comments and strings 50 | ==================== 51 | 52 | Coq comment markers that appear within doc comments (*like this one*) must be escaped, especially if they aren't well-parenthesized (like *this*) (*or this*, for example). 53 | 54 | .. coq:: 55 | 56 | (* This comment doesn't need "*)" escaping though, even if ProofGeneral mishighlights it *) 57 | 58 | Strings can be tricky too: 59 | 60 | .. coq:: 61 | 62 | Require Import String. 63 | Open Scope string_scope. 64 | 65 | Definition a := "a""b""c\n\n\n". 66 | Print a. 67 | -------------------------------------------------------------------------------- /recipes/tests/literate.v: -------------------------------------------------------------------------------- 1 | (*| 2 | ============================== 3 | reST → Coq translation tests 4 | ============================== 5 | 6 | To compile:: 7 | 8 | $ alectryon literate.v --backend rst --mark-point 908 ⊙ 9 | # Coq → reST; produces ‘literate.v.rst’ 10 | 11 | $ alectryon literate.v --backend rst -o - --mark-point 42000 "F"IN | \ 12 | nl | grep "F"IN > literate.marked-end.rst 13 | # Coq → reST; produces ‘literate.marked-end.rst’ 14 | 15 | $ alectryon --frontend coq --backend rst /dev/null -o - --mark-point 42000 "F"IN | \ 16 | nl | grep "F"IN > literate.marked-empty.rst 17 | # Coq → reST; produces ‘literate.marked-empty.rst’ 18 | 19 | .. coq:: 20 | |*) 21 | 22 | Goal True /\ True. 23 | 24 | (*| 25 | The last header is needed (to avoid putting `Goal True` in the ``To compile::`` block) but here we don't need one: 26 | |*) 27 | 28 | split. 29 | 30 | (*| 31 | This one is needed because it includes a ``:name:``: 32 | 33 | .. coq:: 34 | :name: exact 35 | |*) 36 | 37 | exact I. 38 | 39 | (*| 40 | .. note:: This note includes two Coq fragments: 41 | 42 | .. coq:: 43 | |*) 44 | 45 | idtac "This comment is part of the note". 46 | 47 | (*| 48 | The last header is needed (to include the comment in the note) but here we don't need one. The next one (after the note) is needed to exit the note. The ``(\ *||*\ )`` comment is needed too, because otherwise the two blocks would get merged into one): 49 | |*) 50 | 51 | idtac "This comment is part of the note". 52 | 53 | (*| 54 | .. coq:: 55 | |*) 56 | 57 | (* This comment isn't part of the note *) 58 | 59 | (*||*) 60 | 61 | idtac. 62 | 63 | (*| 64 | We also want to correctly handle ``.. coq::`` blocks in comments: 65 | 66 | .. coq:: 67 | 68 | idtac 69 | 70 | This can't happen from translating a reST file, but it can happen from a user adding such a block directly. 71 | |*) 72 | 73 | Qed. 74 | 75 | (*| 76 | Comments and strings 77 | ==================== 78 | 79 | Coq comment markers that appear within doc comments (\ *like this one*\ ) must be escaped, especially if they aren't well-parenthesized (like *this*\ ) (\ *or this*, for example). 80 | |*) 81 | 82 | (* This comment doesn't need "*)" escaping though, even if ProofGeneral mishighlights it *) 83 | 84 | (*| 85 | Strings can be tricky too: 86 | |*) 87 | 88 | Require Import String. 89 | Open Scope string_scope. 90 | 91 | Definition a := "a""b""c\n\n\n". 92 | Print a. 93 | 94 | (*| 95 | And so can deeply nested comments: 96 | |*) 97 | 98 | (* (*! (** (*|*) **) !*) *) 99 | -------------------------------------------------------------------------------- /recipes/tests/minification.v: -------------------------------------------------------------------------------- 1 | (** * Minification 2 | 3 | This file ensures that minification works with plain Coq files (the 4 | ``minification.rst`` recipe uses the Docutils pipeline. 5 | 6 | To run it:: 7 | 8 | alectryon --frontend coq --html-minification minification.v 9 | # Plain Coq → HTML (minified); produces ‘minification.v.html’ **) 10 | 11 | Require Import PeanoNat. 12 | 13 | Section Redundant. 14 | Context (non_negative := Nat.le_0_l). 15 | 16 | Fixpoint tree (n: nat) := 17 | match n with 18 | | 0 => forall m: nat, m >= 0 19 | | S n => tree n /\ tree n 20 | end. 21 | 22 | Goal tree 4. 23 | repeat split. 24 | all: simpl. 25 | all: intros. 26 | all: idtac. 27 | 1: { apply non_negative. } 28 | 1: { apply non_negative. } 29 | 1: { apply non_negative. } 30 | 1: { apply non_negative. } 31 | 1: { apply non_negative. } 32 | 1: { apply non_negative. } 33 | 1: { apply non_negative. } 34 | 1: { apply non_negative. } 35 | 1: { apply non_negative. } 36 | 1: { apply non_negative. } 37 | 1: { apply non_negative. } 38 | 1: { apply non_negative. } 39 | 1: { apply non_negative. } 40 | 1: { apply non_negative. } 41 | 1: { apply non_negative. } 42 | 1: { apply non_negative. } 43 | Qed. 44 | End Redundant. 45 | -------------------------------------------------------------------------------- /recipes/tests/misc.rst: -------------------------------------------------------------------------------- 1 | =========================================== 2 | Misc commands that don't have other tests 3 | =========================================== 4 | 5 | To compile:: 6 | 7 | alectryon misc.rst # reST → HTML; produces ‘misc.html’ 8 | 9 | .. exercise:: Commutativity of addition 10 | :difficulty: 1 11 | 12 | .. coq:: 13 | 14 | Goal forall x y: nat, x + y = y + x. 15 | -------------------------------------------------------------------------------- /recipes/tests/plain_cli.rst: -------------------------------------------------------------------------------- 1 | =========== 2 | Plain CLI 3 | =========== 4 | 5 | This file calls Alectryon without the flags that other tests use. To compile:: 6 | 7 | $ python -m "alectryon" --no-header --copy-assets none --frontend coq --backend webpage <(echo "Check nat.") -o - > plain_cli.noext.html 8 | # Coq → HTML; produces ‘plain_cli.noext.html’ 9 | 10 | $ TMP=$(mktemp); \ 11 | python -m "alectryon" --no-header --copy-assets none --frontend coq --backend webpage $TMP && \ 12 | cp $TMP plain_cli.tmp.html 13 | # Coq → HTML; produces ‘plain_cli.tmp.html’ 14 | 15 | $ echo "Check nat." | \ 16 | python -m "alectryon" --no-header --copy-assets none --frontend coq --backend webpage - > plain_cli.stdin.html 17 | # Coq → HTML; produces ‘plain_cli.stdin.html’ 18 | -------------------------------------------------------------------------------- /recipes/tests/recording.v: -------------------------------------------------------------------------------- 1 | (* To compile: 2 | alectryon recording.v --frontend coq --backend json 3 | # Coq → JSON; produces ‘recording.v.io.json’ *) 4 | Goal True. 5 | idtac "test". 6 | exact I. 7 | Qed. 8 | -------------------------------------------------------------------------------- /recipes/tests/recording.v.io.json: -------------------------------------------------------------------------------- 1 | // To compile: 2 | // $ alectryon recording.v.io.json # IO → HTML, produces ‘recording.v.html’ 3 | // $ alectryon recording.v.io.json --backend snippets-html # IO → HTML, produces ‘recording.snippets.html’ 4 | // $ alectryon recording.v.io.json --backend snippets-latex # IO → LaTeX, produces ‘recording.snippets.tex’ 5 | [ 6 | [ 7 | { 8 | "_type": "sentence", 9 | "contents": "Goal True.", 10 | "messages": [], 11 | "goals": [ 12 | { 13 | "_type": "goal", 14 | "name": null, 15 | "conclusion": "True", 16 | "hypotheses": [] 17 | } 18 | ] 19 | }, 20 | { 21 | "_type": "text", 22 | "contents": "\n " 23 | }, 24 | { 25 | "_type": "sentence", 26 | "contents": "idtac \"test\".", 27 | "messages": [ 28 | { 29 | "_type": "message", 30 | "contents": "test" 31 | } 32 | ], 33 | "goals": [ 34 | { 35 | "_type": "goal", 36 | "name": null, 37 | "conclusion": "True", 38 | "hypotheses": [] 39 | } 40 | ] 41 | }, 42 | { 43 | "_type": "text", 44 | "contents": "\n " 45 | }, 46 | { 47 | "_type": "sentence", 48 | "contents": "exact I.", 49 | "messages": [], 50 | "goals": [] 51 | }, 52 | { 53 | "_type": "text", 54 | "contents": "\n" 55 | }, 56 | { 57 | "_type": "sentence", 58 | "contents": "Qed.", 59 | "messages": [], 60 | "goals": [] 61 | }, 62 | { 63 | "_type": "text", 64 | "contents": "\n" 65 | } 66 | ] 67 | ] 68 | -------------------------------------------------------------------------------- /recipes/tests/screenshot.v: -------------------------------------------------------------------------------- 1 | (*| 2 | .. coq:: none 3 | |*) 4 | 5 | From Coq Require Import Utf8 ZArith List. 6 | Open Scope Z_scope. 7 | Import ListNotations. 8 | 9 | (*| 10 | .. 11 | To compile: alectryon screenshot.v 12 | # Coq → HTML; produces ‘screenshot.html’ 13 | 14 | .. raw:: html 15 | 16 | 62 | 63 | .. 64 | |*) 65 | 66 | Goal forall x y, [x + y] = [x - y] -> y = 0. (* .unfold *) 67 | 68 | (*| 69 | ‘`[=…]`’ is an `injection pattern `__ 70 | |*) 71 | 72 | intros x y [=Heq]. (* .unfold *) 73 | Search (?u+_ = ?u+_ -> _). (* .unfold .no-goals *) 74 | apply Z.add_reg_l in Heq. (* .unfold *) 75 | 76 | (*| 77 | .. container:: big 78 | 79 | 3 cases: `y=0`, `y<0`, `y>0` 80 | |*) 81 | 82 | destruct y; simpl in *. 83 | 84 | (*| 85 | `y=0`: trivial 86 | |*) 87 | 1: reflexivity. 88 | (*| 89 | `y!=0`: contradiction in `Heq` 90 | |*) 91 | all: discriminate. 92 | Qed. 93 | -------------------------------------------------------------------------------- /recipes/tests/stylesheets.docutils.conf: -------------------------------------------------------------------------------- 1 | [html writers] 2 | embed_stylesheet = True 3 | 4 | [latex writers] 5 | embed_stylesheet = True 6 | -------------------------------------------------------------------------------- /recipes/tests/stylesheets.v: -------------------------------------------------------------------------------- 1 | (*| 2 | ====================================== 3 | Stylesheets and Pygments stylesheets 4 | ====================================== 5 | 6 | To compile:: 7 | 8 | $ DOCUTILSCONFIG=tests/stylesheets.docutils.conf \ 9 | alectryon stylesheets.v --pygments-style emacs -o - \ 10 | | sed -r '/^ *