{"id":51104241,"url":"https://github.com/msakai/language-smtlib","last_synced_at":"2026-06-24T13:01:39.467Z","repository":{"id":362801704,"uuid":"1259886653","full_name":"msakai/language-smtlib","owner":"msakai","description":"A robust, Text-based Haskell library for reading, writing and incrementally streaming the SMT-LIB 2 format","archived":false,"fork":false,"pushed_at":"2026-06-14T08:57:09.000Z","size":138,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-14T10:20:03.536Z","etag":null,"topics":["parser","smt-lib","smt-lib2","smtlib","smtlib2"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/msakai.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-05T00:48:13.000Z","updated_at":"2026-06-14T08:57:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/msakai/language-smtlib","commit_stats":null,"previous_names":["msakai/language-smtlib"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/msakai/language-smtlib","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msakai%2Flanguage-smtlib","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msakai%2Flanguage-smtlib/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msakai%2Flanguage-smtlib/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msakai%2Flanguage-smtlib/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/msakai","download_url":"https://codeload.github.com/msakai/language-smtlib/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msakai%2Flanguage-smtlib/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34733256,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-24T02:00:07.484Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["parser","smt-lib","smt-lib2","smtlib","smtlib2"],"created_at":"2026-06-24T13:01:35.997Z","updated_at":"2026-06-24T13:01:39.462Z","avatar_url":"https://github.com/msakai.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# language-smtlib\n\n[![build](https://github.com/msakai/language-smtlib/actions/workflows/build.yaml/badge.svg)](https://github.com/msakai/language-smtlib/actions/workflows/build.yaml)\n[![Hackage](https://img.shields.io/hackage/v/language-smtlib.svg)](https://hackage.haskell.org/package/language-smtlib)\n\nA robust, `Text`-based Haskell library for reading, writing and incrementally\nstreaming the [SMT-LIB 2](https://smt-lib.org/) format.\n\n## Features\n\n- **Full SMT-LIB 2.7 grammar** — commands, terms, sorts, datatypes\n  (`declare-datatype(s)`, `match`, `par`), the 2.7 additions (`lambda`,\n  `declare-sort-parameter`, `define-const`, the `_` wildcard pattern), and\n  solver command responses.\n- **`Text`-based** throughout, with rich parse errors from\n  [megaparsec](https://hackage.haskell.org/package/megaparsec).\n- **Optional source spans.** Every AST node carries a final annotation type\n  parameter `a`. Use `()` for a plain tree or `SrcSpan` for one decorated with\n  source offsets; `noAnn` (= `void`) erases annotations uniformly.\n- **Incremental S-expression framer** with attoparsec-`Partial`-style\n  semantics: it distinguishes *complete* / *needs-more-input* / *error* and\n  reads only as much as needed to frame one S-expression — so a REPL can prompt\n  for continuation lines and a pipe driver never blocks reading past one\n  command.\n- **Round-trip guarantee.** `parse . render == id` for well-formed trees; the\n  printer is the single source of truth for symbol/string quoting.\n\n## Quick start\n\n```haskell\n{-# LANGUAGE OverloadedStrings #-}\nimport qualified Data.Text.IO as T\nimport Language.SMTLIB\n\nmain :: IO ()\nmain = do\n  src \u003c- T.readFile \"problem.smt2\"\n  case parseScript \"problem.smt2\" src of\n    Left err     -\u003e putStr (errorBundlePretty err)\n    Right script -\u003e T.putStr (renderScript script)   -- canonical re-print\n```\n\nParse into location-free trees with `parseScript'` / `parseCommand'` /\n`parseTerm'`, or keep spans with `parseScript` / `parseCommand` / `parseTerm`.\n\n### Incremental input (REPL)\n\n```haskell\nimport Language.SMTLIB\n\n-- frameCommand decides the boundary before parsing:\n--   Done (Right cmd) rest  -- a command, plus the unconsumed remainder\n--   Done (Left err)  rest  -- a complete frame that failed to parse\n--   Partial k              -- input ends mid-command: prompt for more, then `feed`\n--   Failed fe rest         -- a framing error (EndOfInput = clean end of stream)\nstep = frameCommand \"(assert (\u003e x\"   -- =\u003e Partial ...\n```\n\n### Streaming from a handle or solver pipe\n\n```haskell\nimport Language.SMTLIB.Reader.Handle\n\ndriver h = do\n  r \u003c- newHandleReader h\n  readCommand r   -- reads only until one command is complete; never over-reads\n```\n\n## Modules\n\n| Module | Purpose |\n| --- | --- |\n| `Language.SMTLIB` | umbrella: AST + parser + printer |\n| `Language.SMTLIB.Syntax` | the AST (`Term`, `Command`, `Sort`, …) and annotation machinery |\n| `Language.SMTLIB.Parser` | whole-text + incremental parsing |\n| `Language.SMTLIB.Parser.SExpr` | the low-level incremental framer |\n| `Language.SMTLIB.Parser.Response` | solver-response parsers |\n| `Language.SMTLIB.Printer` | rendering to `Text` |\n| `Language.SMTLIB.Reader` / `.Reader.Handle` | pure / `Handle`-based incremental readers |\n\n## Conformance notes\n\n- Targets SMT-LIB **2.7** as the baseline. The string-escape rules and\n  reserved-word set are isolated in `Language.SMTLIB.Syntax.Constant` and\n  `Language.SMTLIB.Internal.Lexical` for easy verification against the 2.7\n  reference. The `-\u003e` map sort parses as an ordinary sort application; the\n  higher-order apply operator `_` is parsed where it coincides with the\n  indexed-identifier syntax (`(_ f x)`), matching the 2.7 concrete grammar\n  (Appendix B), which adds no dedicated application production.\n- **Scope.** The library covers SMT-LIB *scripts* (commands, terms, sorts,\n  datatypes) and the *solver-response* protocol. The theory/logic **catalog**\n  format — the `(theory …)` and `(logic …)` declarations with their attribute\n  grammars (`sort_symbol_decl`, `par_fun_symbol_decl`, `theory_attribute`, …) —\n  is intentionally **out of scope**: it describes the theory/logic definitions\n  published on smt-lib.org, not input a solver reads from a script.\n- As a benign superset, Unicode letters are accepted in simple symbols (so\n  identifiers like `あいうえお` need no quoting), and `(push)` / `(pop)` without\n  a numeral are read as `(push 1)` / `(pop 1)`.\n- A few other inputs the 2.7 grammar forbids are also accepted as benign\n  supersets — they still round-trip: leading-zero numerals (`007`), leading-zero\n  or trailing-dot decimals (`01.5`, `1.`, kept verbatim via the raw `SCDecimal`\n  lexeme), and non-printable control characters inside string and quoted-symbol\n  literals.\n- Some *well-formedness* constraints that the grammar states separately from the\n  production rules are not enforced (the parser is syntactic only): in particular\n  the equal-length linkage between the two lists of `declare-datatypes`\n  (`sort_dec`ⁿ⁺¹ / `datatype_dec`ⁿ⁺¹) and of `define-funs-rec`\n  (`function_dec`ⁿ⁺¹ / `term`ⁿ⁺¹) is accepted even when the counts differ.\n- Numeric literals (decimal/hex/binary) keep their raw lexeme, so printing\n  round-trips byte-for-byte; use the interpreters in\n  `Language.SMTLIB.Syntax.Constant` for their values.\n\n## Building\n\n```\nstack build\nstack test     # round-trip properties, framer units, and sample files\n```\n\n### Testing against large external benchmarks\n\nTo stress-test the parser and printer against the full SMT-LIB / SMT-COMP\nbenchmark suites on [Zenodo](https://zenodo.org/), there is an optional\n`language-smtlib-conformance` driver, built only behind the `tools` flag. It is\nnot part of the normal build or test suite; CI compiles it (behind the flag) so\nit cannot bit-rot, but never runs it. Running it requires benchmark data that is\ndownloaded separately and never committed. See\n[`conformance/README.md`](conformance/README.md).\n\n### Round-trip checking a corpus of `.smt2` files\n\nFor a quick, dependency-free check against an arbitrary collection of `.smt2`\nfiles (for example the example/regression suites shipped with cvc5, OpenSMT,\nYices2, or Z3), use [`scripts/roundtrip-check.sh`](scripts/roundtrip-check.sh).\nIt drives the `language-smtlib-fmt` front end (parse → render) over every file\nand verifies that the canonical rendering is idempotent (the script builds it\nwith the `tools` flag when run with `--build`):\n\n```\nscripts/roundtrip-check.sh [--build] [--out DIR] [PATH...]\n```\n\nFor each file it runs the parser/printer twice and compares the results:\n\n- **stage 1** — `parse(src) → out1`; counted as `parse_fail` if the source does\n  not parse (this just means the input is not standard SMT-LIB 2.7, e.g. a\n  solver-specific extension, a negative-test file, or non-`smt2` data);\n- **stage 2** — `parse(out1) → out2`; counted as `reprint_fail` if our own\n  output fails to re-parse;\n- **compare** — `out1 == out2`; counted as `diff` if the rendering is not\n  idempotent.\n\nBecause the library contract is `parse . render == id`, a stable canonical\nrendering (`out1 == out2`) is a necessary consequence, so any `reprint_fail` or\n`diff` flags a genuine parser/printer bug — and the script exits non-zero only\nin that case, making it usable as a CI gate. Options:\n\n- `--build` runs `stack build` first;\n- `--out DIR` writes the failing-file lists to `DIR` (`parse-fail.tsv` includes\n  the first parse-error message for each file);\n- `PATH...` are the files and/or directories to scan (directories are searched\n  recursively for `*.smt2`; default: the current directory).\n\n```\n# example: build, then check the bundled solver corpora, saving failure lists\nscripts/roundtrip-check.sh --build --out /tmp/rt misc\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmsakai%2Flanguage-smtlib","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmsakai%2Flanguage-smtlib","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmsakai%2Flanguage-smtlib/lists"}