Repository navigation
3.0.0
roc-parser 3.0.0
3.0.0 is the first final release since 2.0.0. It adds JsonValue, a lossless
JSON reader and writer; compile-time parsing for every format; RocSyntax for
Roc source; AsciiDoc parsing and rendering with include selection that
matches Asciidoctor exactly; and source-preserving YAML tokens. The 2.1.0
release candidates previewed part of this work. 2.1.0 itself was never
published.
Use this release
Add the package to your app's header:
parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/3.0.0/6xsRdRqenJrAb4wG7HaST4Xh349vD5sHAJDsKTK6E3dr.tar.zst",The release also carries the examples, pinned to this bundle, the manual as a
PDF and as a static site with the API reference, and an SBOM.
Upgrade impact
- Requires a current Roc nightly:
nightly-2026-10-21-5349569or later.
roc-parser now tracks the latest Roc nightly. The "Compilers" section below names the
nightly this release was built and tested with. - From 2.0.0: existing runtime parsing functions keep their signatures and
acceptance rules. The new checked conversions reject enabled lint checks by
default, and each check can be disabled through defaulted props. The
unicodedependency is now
4.3.0. - Why a major version:
roc bumpcannot compare this release with 2.0.0,
which no longer compiles on current Roc nightlies, or with the 2.1.0
candidates (roc#12506), so the
version cannot be checked mechanically. AsciiDoc rendering also changed
since the release candidates, and the manual counts any change in a public
function's result as a major change. - JSON module name: the module is
JsonValue. It is not calledJsonbecause Roc
auto-imports a builtinJson, and its error tag isInvalidJsonValueso
that it does not collide with the builtin'sInvalidJson(Str). - From the 2.1.0 release candidates:
AsciiDoc.empty_itemand
AsciiDoc.without_internal_cachesare no longer public, and
include::expansion now selects exactly the lines Asciidoctor 2.0.23
selects, so rendered output changes for these inputs:indent=removes leading tabs as well as spaces, so tab-indented code
included withindent=0loses its outer tab. Each tab counts as one
character. A line of only spaces in a file that isn't AsciiDoc counts toward
the shared indent, andindent=Nleaves empty lines empty.- A tag re-opened before its end nests, so it needs a second end.
- The innermost open tag decides, so
tags=x;!yselectsxinsidey. - With
tags=*, an end that crosses another tag is ignored. - A tag marker must start a word, and its
[]must end the line or come
before a space. So"tag::x[]"in a string literal is no longer a marker. - An empty
lines=,tag=orindent=counts as present. An empty
lines=ortag=selects the whole file and ignores the other
selectors, and an emptyindent=dedents. tag=a;bnames one tag,a;b. Usetags=a;bfor two tags.- An empty file includes no lines, so it no longer splits a paragraph.
- Lines from an included file that isn't AsciiDoc (any name not ending in
.adoc,.asciidoc,.asc,.ador.txt) keep their trailing
whitespace, as in Asciidoctor. Listings of program output keep spaces at line
ends. In a paragraph, list item or admonition the spaces stay in the text, a
line of only spaces no longer ends a paragraph, and a delimiter or block
attribute line with trailing whitespace is ordinary text. Directives such as
ifdef::andinclude::in such a file are no longer processed. - The document's own lines lose trailing whitespace before directives and
delimiters are recognized, so a----delimiter or anifdef::x[]
directive followed by spaces now works. tabsizeis now honoured. In listing, source, literal and verse blocks and
paragraphs, tabs expand to the next multiple of the block'stabsizeor,
without one, the document's, counting characters. Zero, negative or
non-numeric values expand nothing. In a document that setstabsize, include
indent=expands tabs first; atabsize=on the directive is ignored, as
in Asciidoctor.- A verbatim block's
indentfollows Asciidoctor: only the block's own
attribute counts,indent=abcdedents and a negative width keeps
indentation, empty lines stay empty, and passthrough, stem and table content is
no longer re-indented. A[source],[listing],[literal]or[verse]
paragraph whose first line is indented keeps its style. Indent widths and tab
sizes above 1024 count as 1024. - A backtick followed by an apostrophe renders as a right single quote, as in
Asciidoctor, so a possessive written after inline code renders the same way
Asciidoctor renders it. A backslash before the pair keeps both characters.
New features
JsonValue
JsonValue reads JSON whose shape a record type cannot describe, such as
software bills of materials, JSON Schema files and configuration with
comments, and writes it back out. Use the builtin Json to decode JSON into
records.
- Lossless values. A
JsonValueisNull,Bool,Number,String,
ArrayorObject. Numbers keep their exact text and objects keep their
member order.get,at,get_path,keysandas_str,as_bool,
as_listandas_objectwalk a value;as_i64,as_u64andas_dec
convert a number. - Strict by default.
parse_straccepts only RFC 8259 JSON, and also
rejects repeated keys and nesting deeper than 128 levels. Errors are
InvalidJsonValue({ line, column, message }). - Options.
parse_withtakes aJsonValue.Optionsrecord with defaulted
fields:- limits:
max_depth,max_items,max_string_bytesand
max_number_digits; - relaxations, each off by default:
commentsandtrailing_commas
(JSONC),bom,non_finite(NaNandInfinity),
control_characters, andlone_surrogates: Replace; - checks:
duplicates(Reject,CollapseIdentical,KeepFirstor
KeepLast),top_level, andinteroperable_numbers(I-JSON).
- limits:
- Writer.
to_strwrites compact JSON andto_str_withtakesindent
andsort_keys. The layout matches Python's
json.dumps(ensure_ascii=False)byte for byte, with number text kept as
written. Values JSON cannot express are refused withInvalidNumber,
DuplicateKeyorTooDeep. - Compile time. A string literal annotated
JsonValueis checked during
compilation, andJsonValue.validatechecks an imported file with the same
options asparse_with. - Composition.
parserandparser_withareParser(Utf8.Bytes, JsonValue)
values for use with the combinators. - Evidence.
scripts/review_json.pycompares the parser and writer with
Python'sjsonmodule on 397 hand-written cases, the 318 JSONTestSuite
cases and five pinned SPDX, CycloneDX, Syft and Trivy documents: all 720
pass, with no known failures. Three fuzz targets (json-raw,
json-optionsandjson-roundtrip) run in CI with the other targets, and
review_json.py campaignadds a differential robustness campaign.
Benchmarks compare it with serde_json, Go'sencoding/jsonand Python's
json.
AsciiDoc include selection
AsciiDoc.select_include(source, attributes) selects lines from an include
source exactly as include:: expansion does, so tools that expand includes
themselves, such as roc-docs, no longer reimplement the rules. It reads
lines=, tag=, tags= (with *, ** and ! negation) and
indent=, and returns an AsciiDoc.IncludeSelection with the selected
text, the one-based source lines and a list of AsciiDoc.IncludeProblem
values. Selection is Asciidoctor's, as lenient as it is. MissingTag,
UnclosedTag and UnmatchedEnd are exactly Asciidoctor's warnings, while
InvalidLines and EmptySelection flag selections that Asciidoctor makes
without a warning, so a strict caller can reject them. The source is read as
a file that isn't AsciiDoc, as code listings are.
AsciiDoc.select_include_with(source, attributes, { tabsize }), with
AsciiDoc.IncludeContext, selects lines for a document that sets tabsize.
scripts/asciidoc/include-selection-fixtures.json compares the selections and
problems with Asciidoctor 2.0.23. See "Reusing include selection" in
Reading AsciiDoc.
Compile-time parsing
- Compile-time quotations for YAML, XML, CSV, JSON, Markdown, AsciiDoc, HTTP,
and Roc source, plus typed YAML and CSV decoding. - Direct nominal document types for
CSV,AsciiDoc, andRocSyntax, with
from_quotemethods;Markdown.Documentand HTTP request/response quotation
targets cover their distinct document shapes. - Configurable
validateandvalidate_asconversions, simple located errors,
andLiteral.requirefor demanded top-level constants from imported text. Utf8.parse_quotefor custom combinator-based quotation types.- An enduring compile-time parsing guide, runnable examples with adjacent
assets, and compiler rejection and cache-invalidation tests in CI.
See Parse and validate during compilation
and Reading JSON.
RocSyntax
RocSyntax parses Roc source into source-backed tokens, a tolerant indexed syntax tree and located diagnostics. It is meant for highlighters and source-analysis tools.
- The tree represents expressions, patterns, type declarations, constraints and every header form.
- Parsing has explicit nesting and token limits. It does not do binding or type analysis.
- Malformed input gets partial structure, and its tokens can still be highlighted.
tokenize_str,tokenize_bytesandhighlight_categoryserve lexical highlighting without building a tree.
Legacy Rust compiler syntax is outside its scope. See the manual's "Parsing Roc source" chapter.
On the development machine, every input in the reference performance comparison parses within 1.5 times the time of the compiler's own Zig parser. The syntax parsed matches compiler commit 0d4b0ab0. That grammar is still changing, so expect follow-up releases as the language evolves.
AsciiDoc
AsciiDoc adds block parsing, preprocessing on virtual sources, and embedded HTML rendering. It is tested against a pinned Asciidoctor on authored fixtures, independently generated models and reproducible mutations. Compatibility with Asciidoctor is not complete; see the manual's AsciiDoc chapter for what is covered.
YAML source highlighting
Yaml.tokenize_str and Yaml.tokenize_bytes cover the original source with tokens, keeping its exact bytes. They include comments, whitespace and malformed snippets. They also report mapping-key roles, YAML 1.2 scalar categories and located diagnostics. Yaml.highlight_category maps tokens to theme categories that HTML and PDF renderers can share.
Semantic YAML decoding is unchanged.
Documentation and tooling
- New manual chapters for Roc syntax, AsciiDoc and YAML source highlighting, each with tested examples.
scripts/check_roc_syntax.pyruns the Roc syntax feedback loop. It includes a persistent compiler reference worker, independent generated syntax, raw and structured fuzzing, an upstream snapshot corpus, and runtime, allocation and scaling checks.- AsciiDoc and YAML source fuzz targets run in the property quality workflow.
- A new "Read and write JSON" chapter, and JSON conformance, fuzzing and
benchmark coverage. - CI follows the roc-automation model: one
checkjob runs every pull request
check, releases are published through roc-automation'spublish-release,
and a daily job tests the latest release's examples against the latest Roc
nightly.
Release candidates
2.1.0-rc.1, rc.2 and rc.3 previewed RocSyntax, AsciiDoc and YAML source
highlighting. Since rc.3 the package moved to the latest Roc nightly and
unicode 4.3.0, gained JsonValue, compile-time parsing and include selection,
and finished the Roc syntax performance work.
Compilers
Roc compiler version nightly-2026-10-21-5349569