All notable changes to this project are recorded here. The format follows Keep a Changelog, and the project follows semantic versioning over its Elixir API.
Note that the package version and Tptp.bnf_version/0 are two different numbers: the
latter is the TPTP release whose BNF the shipped parser was generated from, and it
moves when TPTP moves, not when this library does.
Unreleased
0.1.3 - 2026-10-11
The SZS reader accepts the status and output lines systems print in practice, and
Tptp.Span resolves a line and column back to a byte offset.
Added
Tptp.Span.offset/3, the inverse ofline_column/2: a one-based line and byte column to a byte offset, as{:ok, offset}, or:errorfor a line the index does not have, a column below one, or a column past the line's line feed. The index does not record the source's length, so a column on the last line is not bounded above.
Changed
- The SZS readers (
Tptp.Szs.status/1,statuses/1,value/1,success?/1andblocks/1) accept the framing systems print in practice:#as well as%, any text before it on the line (StarExec's elapsed-time prefix), and nofor <problem>(E). The value and dataform are still looked up inTptp.Szs.Ontologyas written. - The problem in
Tptp.Szs.status/0,Tptp.Szs.unknown/0andTptp.Szs.block/0isbinary() | nil,nilwhen the line names none. Code that matches the problem as a binary needs a clause fornil. A block opened without a problem is closed only by anendwithout one, and likewise with. Tptp.Szs.status_line/3andTptp.Szs.output_block/3takenilfor the problem and then leave outfor <problem>, so what the readers return can be written back.
Fixed
- A status line no longer takes its problem or comment from the following line:
the separators were
\s, which matches a line feed, so"% SZS status Theorem\nfor X"was read as a status forX.
0.1.2 - 2026-09-30
Re-release of 0.1.1 for the Zenodo archive; no changes to the library.
0.1.1 - 2026-09-29
The SZS ontologies are now read from the TPTP World's BNF for them rather than
transcribed, which brings the isa hierarchy with it.
Added
CITATION.cffand.zenodo.jsonfor archiving releases on Zenodo;CITATION.cffis shipped in the Hex package.
Changed
Tptp.Szs.Ontologyis now generated bymix tptp.genfrom the TPTP World'sSZSOntology.bnf(https://github.com/TPTPWorld/SZSOntologies), vendored atpriv/bnf/SZSOntology.bnfand attributed inNOTICE. The 112 values and their three ontologies are unchanged;values/0now lists them in the BNF's order.- The SZS
isahierarchy is modelled, read off the BNF's rules:parents/1,children/1,ancestors/1,descendants/1,isa?/2,ontologies/0andarguments/1([:unknown, :success]forAssumed,[]otherwise). It is a DAG, not a tree —Theoremhas three parents. Tptp.Szs.Pageholds what the BNF does not carry and the ontology page does:mnemonic/1,from_mnemonic/1,from_status_value/1anddescribe/1, transcribed by hand as before.Tptp.Szs.Ontologydelegates to it, so those calls are unchanged;Ontology.source/0now names the BNF andPage.source/0the page.priv/bnf/SyntaxBNF-v9.3.1.3is re-vendored from https://github.com/TPTPWorld/SyntaxBNF, whose file differs from the published page under the same version number:unknownis gone from<source>, and the<status_value>rule is gone while<inference_status>still references it. The generated grammar is byte-for-byte what it was, since the library already droppedunknown; that departure is now the BNF's own andTptp.Bnf.Generator.departures/0lists three.Tptp.Bnf.Vocabulary.status_value_values/0is taken from the SZS BNF's<inference_status_value>, under the name theSyntaxBNFreferences: 47 values, the success ontology less its type-check and verification branches, where the page's<status_value>listed 34.
Removed
subontology/1ofTptp.Szs.Ontology, which stood in for the hierarchy while it was unpublished.isa?(value, :semantic_success)and its siblings say the same, and more.
0.1.0 - 2026-09-11
First release. The parser is generated from TPTP BNF v9.3.1.3, and the SZS values are transcribed from the ontology published at https://szs.tptp.org. Every problem and axiom file of TPTP v9.3.1 is read through it; the four it refuses are defects in the files, and are recorded under Notes below.
Added
Tptp.from_string/2,from_file/2and the!variants, plusstream_string!/1andstream_file!/2for input too large to hold as statements.A resumable hand-written lexer with byte-accurate spans, and a statement splitter that bounds the blast radius of malformed input to one statement.
An LALR(1) parser generated from the vendored BNF by
mix tptp.gen, and a CST whose node tags are BNF nonterminals. No elaboration, no typing, no inference: explicit type arguments are recorded verbatim, in source order, with spans.Tptp.Node.value/1, the canonical atomic word behind a leaf's spelling, so that'p'andpare one symbol everywhere they are identified — the symbol table, statement names, inference parents andincludeselections all key on it.Tptp.Node.new/3for consumers that emit TPTP rather than read it, with the print-and-reparse round trip as the validity check for a constructed tree.includeresolution over a pluggable resolver —Fs,Http,Cascade,Mapand the defaultNone— with cycle detection, formula selection and aTptp.Unitthat keeps both the flattened statement list and the include tree.Lint rules over one fused walk. The undeclared-symbol rule applies to the dialects written with
thfalone — TH0, TH1, DH0, DH1 and NHF — since the TPTP language page gives TFF, TXF, TCF and NXF default typing,($i,...,$i) > $ofor a predicate and($i,...,$i) > $ifor a function, and says THF "does not admit default typing".Tptp.Query: the dialect of a file or a unit,rank/1anddialects/0for the total listing order, andwithin?/2as a partial order over the dialects. They are not a line — TCF and the non-classical languages are branches — sowithin?/2answers a containment question and is not a sort comparator.It recognises the dependently typed higher-order dialects, DH0 and DH1. What a
!>binds is the whole difference:!>[A: $tType]abstracts over a type and is polymorphism,!>[A: nat]abstracts over a term and is a dependent type, and the same distinction separateslist: $tType > $tTypefromfin: nat > $tType. Checked against the TPTP's ownSPCheader over the 26,021 problems of v9.3.1 that carry one: 85 DH0 and 46 DH1 identified exactly, with no TH0 or TH1 problem misread as either.TXF is recognised by its FOOL constructs and not only by the three that have a node kind of their own. A
$ovariable, a formula or$true/$falsein a term position, a declared$oargument type,$iteand$distinctall carry it, so all 340 TX0 problems in the library are classified correctly, with no false positive among the 2,251 TF0, 679 TF1 and 148 TX1 problems beside them.Tptp.analyze/2andTptp.Analysis: the file, its diagnostics, the symbol table and the dialect from a single traversal,Tptp.Lint.scan/2, of whichTptp.Lint.run/2,run_unit/2andtable/1are projections. An opt-in line index turns span offsets into line and column once rather than once per diagnostic, and input that does not parse still yields an analysis carrying the diagnostics that record the failure.Tptp.Analyzer, a behaviour for a named diagnostic producer over aTptp.Analysis, withrun_all/3dispatching by dialect,{:exactly, dialects}for an analyzer that means the dialects it named, and a raising analyzer contained as aTPTP0800diagnostic naming it.Tptp.Lintimplements it as:tptp_lint.Three printers: canonical, pretty (
Inspect.Algebra) and format-preserving, the last of which backsmix tptp.formatand provably changes no token.Inspectimplementations forTptp.File,Tptp.Unit,Tptp.Node,Tptp.Analysis,Tptp.Lint.Tableand both statement structs, each printing a summary in constant time rather than the source binary and every node that points into it.inspect(term, structs: false)still shows the map.Tptp.Szsfor the status lines provers print, over the 112 published SZS values.Diagnostics on every stage, tiered by code, never raised at the caller.
mix tptp.lint, a command line overTptp.analyze/2. It printspath:line:column: severity: message [CODE]and exits non-zero where anything at or above--severitywas reported;--includeresolves the graph and lints the unit,--onlyand--suppressfilter by code, and--format jsonemits one object per line.mix tptp.corpus, which reads a local TPTP library through the parser and writes CORPUS.md, andmix tptp.census, which records where that library applies a type constructor and in which dialects (CENSUS.md) — including the files applying one at arity ≥ 2 over a type variable, which is the one shape an elaborator cannot monomorphise into a fresh sort. A nightly workflow sweeps the library entire; a pull request sweeps one file in five.Both sweeps parse each file in its own process under a
max_heap_sizeceiling, with--heapas the total across workers and a serial retry for a file that exceeds its share. A file's size does not predict its parse: the corpus runs from 2.5 to 111 bytes per CST node, so bounding a sweep by bytes of source bounds the wrong quantity.FINDINGS.md: what the four unparseable
SYN000*2.pfiles were hiding from every stage after the parser. With the announced fix applied to copies, two further defects in those files surfaced, and a sweep of the library for anything else a skipped file could conceal showed three rules of this library's to be wrong. Each finding carries its citation and a command reproducing it, and the checks that found nothing are recorded with them.
Notes
- Zero runtime dependencies.
yecc,:crypto,:inetsand:sslship with OTP. - No atom is ever created from input. A custom Credo check enforces it, because the atom table is never collected and this library reads untrusted files.
- The TPTP is this library's ground truth: where the two disagree the parser is wrong, being generated from the published BNF, and correcting it by hand would make it a parser for something else. Where the TPTP's own published sources disagreed with each other, the disagreement was reported upstream rather than worked around. Six were carried during development; BNF v9.3.1.3 and the SZS ontology's move to https://szs.tptp.org resolve all of them.
- Four library files do not parse, and both reasons are defects in the files.
SYN000-2.p,SYN000+2.pandSYN000^2.pusetheory(equality)as an inference parent, which<source>has not derived since v9.3.1.2; that was fixed upstream on 10/09/26 — see https://tptp.org/TPTP/Distribution/BuggedProblems-v9.3.1.txt — and all three parse against the corrected text, so they leaveMix.Tasks.Tptp.Corpus.known_failures/0when the distributed tarball carries the edit.SYN000_2.pcarries a second and independent one, reachable only once the first was resolved: it writesintroduced(assumption,[from,the,world,[]])where both the BNF and the TPTP language page stateintroduced(<intro_type>,<useful_info>,<parents>), and the other three dialects' copies of the same file writeintroduced(assumption,[from,the,world],[]). Reported upstream on 2026-09-11. SYN000^2.pcarries a further defect that only surfaces once it parses: itslet_tuple_4appliesqll @ a @ b, and nothing declaresqll. THF admits no default typing, so that is an error, andTPTP0501reports it.ql, declared as$int > $int > $o, has exactly the type the use needs, so it is presumably a typo. Reported upstream on 2026-09-11.- The SZS
isahierarchy is deliberately not modelled: it is published only as diagrams, and guessing at it would put unverifiable relations into a library whose contract is faithfulness. (Modelled from the SZS BNF since 0.1.1.) - One condition the TPTP states is not checked: "If a symbol's type is declared more
than once, and the types are not the same, that's an error." A symbol is a name and
an arity — "Symbols may be overloaded with different arity signatures, and are
treated as different symbols" — so this compares two declarations at the same
arity, and
Tptp.Lint.Tablekeys on the name alone and cannot express it yet.