language-smtlib: Parsing, printing and incremental I/O for the SMT-LIB 2 format

[ bsd3, language, library, program, smt ] [ Propose Tags ] [ Report a vulnerability ]

A Text-based library for parsing, printing, and incrementally streaming the SMT-LIB 2.7 format.

It provides a parametric, fully-annotated AST in which every node carries an optional source-span annotation; a megaparsec-based parser with both whole-text and incremental (S-expression framing) entry points, suitable for solver pipes and REPLs; and a prettyprinter-based printer that guarantees parse . render == id for well-formed trees.

See the README at https://github.com/msakai/language-smtlib#readme for details and examples.


[Skip to Readme]

Flags

Manual Flags

NameDescriptionDefault
conformance

Build language-smtlib-conformance, a driver that round-trips large external SMT-LIB benchmark suites (e.g. the SMT-LIB / SMT-COMP collections on Zenodo). Off by default: it is not part of the normal build, test suite or CI.

Disabled
profiling

Add -fprof-auto (automatic cost centres) to this package's own components when building with profiling. Off by default so that downstream packages profiling their own code are not cluttered with cost centres from language-smtlib.

Disabled

Use -f <flag> to enable a flag, or -f -<flag> to disable that flag. More info

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.1.0.0
Change log CHANGELOG.md
Dependencies base (>=4.7 && <5), containers (>=0.6 && <0.8), directory (>=1.3 && <1.4), filepath (>=1.4 && <1.6), language-smtlib, megaparsec (>=9.0 && <10), parser-combinators (>=1.0 && <1.4), prettyprinter (>=1.7 && <1.8), scientific (>=0.3 && <0.4), text (>=1.2 && <2.2) [details]
Tested with ghc ==9.6.7, ghc ==9.8.4, ghc ==9.10.3, ghc ==9.12.4
License BSD-3-Clause
Copyright 2026 Masahiro Sakai
Author Masahiro Sakai
Maintainer masahiro.sakai@gmail.com
Uploaded by MasahiroSakai at 2026-06-08T00:48:04Z
Category Language, SMT
Home page https://github.com/msakai/language-smtlib#readme
Bug tracker https://github.com/msakai/language-smtlib/issues
Source repo head: git clone https://github.com/msakai/language-smtlib
Distributions
Executables language-smtlib-exe, language-smtlib-conformance
Downloads 0 total (0 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2026-06-08 [all 1 reports]

Readme for language-smtlib-0.1.0.0

[back to package description]

language-smtlib

build

A robust, Text-based Haskell library for reading, writing and incrementally streaming the SMT-LIB 2 format.

Features

  • Full SMT-LIB 2.7 grammar — commands, terms, sorts, datatypes (declare-datatype(s), match, par), the 2.7 additions (lambda, declare-sort-parameter, define-const, the _ wildcard pattern), and solver command responses.
  • Text-based throughout, with rich parse errors from megaparsec.
  • Optional source spans. Every AST node carries a final annotation type parameter a. Use () for a plain tree or SrcSpan for one decorated with source offsets; noAnn (= void) erases annotations uniformly.
  • Incremental S-expression framer with attoparsec-Partial-style semantics: it distinguishes complete / needs-more-input / error and reads only as much as needed to frame one S-expression — so a REPL can prompt for continuation lines and a pipe driver never blocks reading past one command.
  • Round-trip guarantee. parse . render == id for well-formed trees; the printer is the single source of truth for symbol/string quoting.

Quick start

{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text.IO as T
import Language.SMTLIB

main :: IO ()
main = do
  src <- T.readFile "problem.smt2"
  case parseScript "problem.smt2" src of
    Left err     -> putStr (errorBundlePretty err)
    Right script -> T.putStr (renderScript script)   -- canonical re-print

Parse into location-free trees with parseScript' / parseCommand' / parseTerm', or keep spans with parseScript / parseCommand / parseTerm.

Incremental input (REPL)

import Language.SMTLIB

-- frameCommand decides the boundary before parsing:
--   Done (Right cmd) rest  -- a command, plus the unconsumed remainder
--   Done (Left err)  rest  -- a complete frame that failed to parse
--   Partial k              -- input ends mid-command: prompt for more, then `feed`
--   Failed fe rest         -- a framing error (EndOfInput = clean end of stream)
step = frameCommand "(assert (> x"   -- => Partial ...

Streaming from a handle or solver pipe

import Language.SMTLIB.Reader.Handle

driver h = do
  r <- newHandleReader h
  readCommand r   -- reads only until one command is complete; never over-reads

Modules

Module Purpose
Language.SMTLIB umbrella: AST + parser + printer
Language.SMTLIB.Syntax the AST (Term, Command, Sort, …) and annotation machinery
Language.SMTLIB.Parser whole-text + incremental parsing
Language.SMTLIB.Parser.SExpr the low-level incremental framer
Language.SMTLIB.Parser.Response solver-response parsers
Language.SMTLIB.Printer rendering to Text
Language.SMTLIB.Reader / .Reader.Handle pure / Handle-based incremental readers

Conformance notes

  • Targets SMT-LIB 2.7 as the baseline. The string-escape rules and reserved-word set are isolated in Language.SMTLIB.Syntax.Constant and Language.SMTLIB.Internal.Lexical for easy verification against the 2.7 reference. The -> map sort parses as an ordinary sort application; the higher-order apply operator _ is parsed where it coincides with the indexed-identifier syntax ((_ f x)), matching the 2.7 concrete grammar (Appendix B), which adds no dedicated application production.
  • As a benign superset, Unicode letters are accepted in simple symbols (so identifiers like あいうえお need no quoting), and (push) / (pop) without a numeral are read as (push 1) / (pop 1).
  • Numeric literals (decimal/hex/binary) keep their raw lexeme, so printing round-trips byte-for-byte; use the interpreters in Language.SMTLIB.Syntax.Constant for their values.

Building

stack build
stack test     # round-trip properties, framer units, and sample files

Testing against large external benchmarks

To stress-test the parser and printer against the full SMT-LIB / SMT-COMP benchmark suites on Zenodo, there is an optional language-smtlib-conformance driver, built only behind the conformance flag (so it is never part of the normal build, test suite, or CI) and run on benchmark data that is downloaded separately and never committed. See conformance/README.md.

Round-trip checking a corpus of .smt2 files

For a quick, dependency-free check against an arbitrary collection of .smt2 files (for example the example/regression suites shipped with cvc5, OpenSMT, Yices2, or Z3), use scripts/roundtrip-check.sh. It drives the language-smtlib-exe front end (parse → render) over every file and verifies that the canonical rendering is idempotent:

scripts/roundtrip-check.sh [--build] [--out DIR] [PATH...]

For each file it runs the parser/printer twice and compares the results:

  • stage 1parse(src) → out1; counted as parse_fail if the source does not parse (this just means the input is not standard SMT-LIB 2.7, e.g. a solver-specific extension, a negative-test file, or non-smt2 data);
  • stage 2parse(out1) → out2; counted as reprint_fail if our own output fails to re-parse;
  • compareout1 == out2; counted as diff if the rendering is not idempotent.

Because the library contract is parse . render == id, a stable canonical rendering (out1 == out2) is a necessary consequence, so any reprint_fail or diff flags a genuine parser/printer bug — and the script exits non-zero only in that case, making it usable as a CI gate. Options:

  • --build runs stack build first;
  • --out DIR writes the failing-file lists to DIR (parse-fail.tsv includes the first parse-error message for each file);
  • PATH... are the files and/or directories to scan (directories are searched recursively for *.smt2; default: the current directory).
# example: build, then check the bundled solver corpora, saving failure lists
scripts/roundtrip-check.sh --build --out /tmp/rt misc