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
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
tools

Build the developer-facing command-line tools shipped with this package: language-smtlib-fmt, a round-trip front end that parses an SMT-LIB script and re-emits it in canonical form; and 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: these are development utilities, not needed by downstream packages that only depend on the library.

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, 0.2.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), hashable (>=1.3 && <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-07-06T00:15:13Z
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-fmt, language-smtlib-conformance
Downloads 5 total (5 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-07-06 [all 1 reports]

Readme for language-smtlib-0.2.0.0

[back to package description]

language-smtlib

build Hackage

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. See docs/conformance.md for the exact conformance boundaries, benign supersets, and how solver-specific extensions are handled.
  • 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

Building

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

Testing tools

Two developer tools exercise the parser and printer against real-world corpora. Both are gated behind the manual tools flag, so they stay off by default — out of a plain stack build/stack test, and never a burden on downstream consumers. CI builds them behind the flag so they cannot bit-rot, but does not run them:

stack build --flag language-smtlib:tools
  • language-smtlib-fmt parses a file/stdin and re-emits it canonically (the corpus script below drives it);
  • language-smtlib-conformance round-trips large external benchmark suites.

Testing against large external benchmarks

To stress-test the parser and printer against the full SMT-LIB / SMT-COMP benchmark suites on Zenodo, use the language-smtlib-conformance driver. Running it requires 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-fmt front end (parse → render) over every file and verifies that the canonical rendering is idempotent (the script builds it with the tools flag when run with --build):

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