| Title: | Read and Write 'TOML' Documents |
| Version: | 0.1.0 |
| Description: | Parses 'TOML' (Tom's Obvious Minimal Language) documents, versions 1.0.0 and 1.1.0 https://toml.io/en/v1.1.0, into ordinary R values, and writes R values as 'TOML'. Every 'TOML' type maps to one R type, including the four date and time types, and strings are never reinterpreted. Invalid documents are refused with the line and column at fault, under configurable size, depth, item and string limits, and output is deterministic. Both directions pass the 'toml-test' conformance suite https://github.com/toml-lang/toml-test. The parser and writer are written in C, with no system library required. |
| License: | MIT + file LICENSE |
| URL: | https://github.com/pedrobtz/zutoml, https://pedrobtz.github.io/zutoml/ |
| BugReports: | https://github.com/pedrobtz/zutoml/issues |
| Depends: | R (≥ 4.1) |
| LinkingTo: | zufast (≥ 0.1.0) |
| Suggests: | knitr, rmarkdown, testthat (≥ 3.0.0), withr, zujson |
| VignetteBuilder: | knitr |
| Config/testthat/edition: | 3 |
| Encoding: | UTF-8 |
| Language: | en-GB |
| Config/roxygen2/version: | 8.1.0 |
| NeedsCompilation: | yes |
| Packaged: | 2026-10-23 14:18:23 UTC; pbtz |
| Author: | Pedro Baltazar [aut, cre, cph] |
| Maintainer: | Pedro Baltazar <pedrobtz@gmail.com> |
| Repository: | CRAN |
| Date/Publication: | 2026-11-07 15:40:02 UTC |
zutoml: Read and Write 'TOML' Documents
Description
Parses 'TOML' (Tom's Obvious Minimal Language) documents, versions 1.0.0 and 1.1.0 https://toml.io/en/v1.1.0, into ordinary R values, and writes R values as 'TOML'. Every 'TOML' type maps to one R type, including the four date and time types, and strings are never reinterpreted. Invalid documents are refused with the line and column at fault, under configurable size, depth, item and string limits, and output is deterministic. Both directions pass the 'toml-test' conformance suite https://github.com/toml-lang/toml-test. The parser and writer are written in C, with no system library required.
Author(s)
Maintainer: Pedro Baltazar pedrobtz@gmail.com [copyright holder]
Authors:
Pedro Baltazar pedrobtz@gmail.com [copyright holder]
See Also
toml_parse() to read, toml_emit() to write, and
vignette("zutoml") for a tour.
Say how one value is written as TOML
Description
Markers for toml_emit(), which otherwise chooses each value's form by
fixed rules. They change how a value is written, never what it is:
toml_parse() reads the text back as the unmarked value.
Usage
toml_inline(x)
toml_literal(x)
toml_multiline(x)
Arguments
x |
For |
Details
-
toml_inline()writes a named list as an inline table (point = { x = 1, y = 2 }) rather than under its own[header], and an unnamed list of named lists as an array of inline tables rather than an array of tables. ACargo.tomldependency such asserde = { version = "1.0", features = ["derive"] }is written this way. -
toml_literal()writes strings as literal strings, which need no escapes:'C:\Users\tom'. A string with a single quote or a newline is a multi-line literal string. One that no literal string can hold (a control character other than tab and newline, or three single quotes in a row) raiseszutoml_invalid_argument. -
toml_multiline()writes strings as multi-line basic strings, even without a newline.
Like I(), which writes a vector of one as an array, the markers are
classes on the value; any other class it has is kept.
Value
x, with the marker class added.
Examples
cat(toml_emit(list(
dependencies = list(
serde = toml_inline(list(version = "1.0", features = c("derive", "std"))),
rand = "0.8"
),
path = toml_literal("C:\\Users\\tom"),
motd = toml_multiline("Welcome!")
)))
Integers beyond what a double holds exactly
Description
TOML integers are 64-bit. Those beyond 2^53 cannot be held exactly by a
double, so toml_parse() returns them, by default, as a toml_bigint
vector: their exact decimal text, with a class.
Usage
toml_bigint(x)
Arguments
x |
Decimal strings ( |
Value
A character vector of class toml_bigint, holding canonical
decimal text: no leading zeros or plus sign.
Examples
x <- toml_bigint(c("9007199254740993", "-42"))
x
as.numeric(x) # loses the precision a bigint keeps
toml_parse("big = 9223372036854775807")$big
Write R values as TOML
Description
toml_emit() turns a named list into the text of a TOML document;
toml_write() writes that text to a file or connection. The output is
deterministic (the same value gives the same bytes on every platform) and
is always text toml_parse() reads back.
Usage
toml_emit(
x,
indent = 2L,
inline = 0L,
width = 80L,
na = c("error", "omit"),
strings = c("basic", "literal"),
max_depth = 128L
)
toml_write(x, file, ...)
Arguments
x |
A named list: the document's top-level table. |
indent |
Spaces before each element of an array written one element per line. |
inline |
A nested named list with at most this many keys, none of
them a list, is written as an inline table ( |
width |
An array whose line would pass this column is written one
element per line. |
na |
|
strings |
|
max_depth |
The deepest nesting of tables and arrays written,
counted as |
file |
For |
... |
For |
Value
toml_emit(): a single string, UTF-8, with \n line endings and a
final newline. toml_write(): file, invisibly.
R to TOML
| R | TOML |
character | basic string, or literal with strings = "literal" |
integer | integer |
double, whole, within 64-bit range, not -0 | integer |
any other double | float, in the shortest digits that read back exactly |
logical | boolean |
POSIXct with a time zone | offset date-time, in UTC (Z) |
POSIXct with no time zone (tzone = "") | local date-time |
Date | local date; a fractional one as its day |
difftime in seconds, from 0 to a day | local time |
toml_bigint() | integer |
factor | its labels, as strings |
| a vector of length one | the value; I() makes it an array of one |
| a longer vector | array |
named list | table, or an inline table inside an array |
unnamed list of named lists | array of tables ([[key]]) |
other unnamed list | array |
data.frame | array of tables, one per row; NA cells are left out
|
A table's own keys are written first, in the list's order, then its sub-tables: TOML has no other way to say which keys belong to which table. A sub-table that holds only sub-tables gets no header of its own. A string with a newline is written as a multi-line string.
Refused with zutoml_unsupported_type: complex and raw vectors,
functions, environments, POSIXlt, matrices and arrays with dim, and
atomic vectors of other classes. NA and NULL, which TOML has no form
for, raise zutoml_na_error unless na = "omit". A value that breaks
its class's invariants raises zutoml_invalid_argument: a data frame
without a name for every column, or with a column that is not a vector of
one cell per row, and an infinite date or time. Each condition carries
path, the key path of the value at fault, as a.b[3].c.
Examples
cfg <- list(
title = "Example",
port = 8080,
tags = c("a", "b"),
owner = list(name = "Tom", since = as.Date("1979-05-27")),
servers = list(
list(name = "alpha", ip = "10.0.0.1"),
list(name = "beta", ip = "10.0.0.2")
)
)
cat(toml_emit(cfg))
identical(toml_parse(toml_emit(cfg))$port, 8080L)
path <- tempfile(fileext = ".toml")
toml_write(cfg, path)
toml_parse(readLines(path) |> paste(collapse = "\n"))$owner$name
Parse a TOML document
Description
Reads a TOML document into ordinary R values: each table becomes a named
list with its keys in document order, each array a vector or a list, and
each value the R type below. Types come from the document's syntax:
"2024-01-01" stays a string and 2024-01-01 becomes a Date. The whole
document is checked, as by toml_validate(), before any R value is
built.
Usage
toml_parse(
x,
select = NULL,
version = c("1.1.0", "1.0.0"),
simplify = c("preserve", "none"),
data_frame = FALSE,
big_integers = c("bigint", "double", "error"),
datetimes = c("convert", "keep"),
local_time = c("character", "difftime"),
positions = FALSE,
max_size = 64 * 1024^2,
max_depth = 128L,
max_items = 1e+06,
max_string = 2^31 - 1
)
Arguments
x |
A TOML document: a single string, or a raw vector of UTF-8 bytes. |
select |
|
version |
The TOML version to read: |
simplify |
|
data_frame |
If |
big_integers |
For integers beyond 2^53, which a double cannot hold
exactly: |
datetimes |
|
local_time |
|
positions |
If |
max_size |
The largest document accepted, in bytes, or |
max_depth |
The deepest nesting of tables and arrays, counting both; at most 1023. |
max_items |
The most keys plus array elements, or |
max_string |
The longest string or key, in decoded bytes, or |
Value
A named list, one element per top-level key; with select, the
value at that path.
TOML to R
| TOML | R |
| string | character (UTF-8) |
| integer | integer; double beyond R's integer range (and for -2^31); beyond 2^53, see big_integers |
| float | double; inf and nan as Inf and NaN |
| boolean | logical |
| offset date-time | POSIXct in UTC |
| local date-time | POSIXct in the session's time zone (tzone = "") |
| local date | Date |
| local time | character ("07:32:00"), or difftime in seconds |
| table, inline table | named list |
| array of tables | unnamed list of named lists, or a data.frame |
| array | a vector when its elements share a type, else a list
|
An array becomes a vector when all its elements are strings, all
booleans, all numbers, all dates, or all date-times of one kind. Numbers
widen: integers with floats give a double vector. Booleans never join
the numbers. An empty array is logical(0). A one-element array that
becomes a vector is marked with I(), so that writing it back gives an
array again. Anything else is a list. simplify = "none" makes every
array a list.
With datetimes = "keep", date-times are returned as RFC 3339 text
instead ("1979-05-27T07:32:00Z", a zero offset written Z, the fraction
in 0, 3, 6 or 9 digits). Fractions past nanoseconds are truncated, as
TOML requires; a POSIXct holds microseconds.
Valid TOML that R cannot hold raises zutoml_unrepresentable: a string or
key containing U+0000, a float beyond the range of a double, or an
integer beyond 2^53 with big_integers = "error".
See Also
toml_validate() to check a document without building it;
zutoml-conditions for the errors.
Examples
doc <- '
title = "TOML Example"
ports = [8000, 8001, 8002]
[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00
[[products]]
name = "Hammer"
sku = 738594937
[[products]]
name = "Nail"
sku = 284758393
color = "gray"
'
x <- toml_parse(doc)
x$owner$dob
x$ports
toml_parse(doc, data_frame = TRUE)$products
# Only part of it:
toml_parse(doc, select = "owner")
toml_parse(doc, select = "products[2].color")
# Where each value is, for messages about the document:
pos <- attr(toml_parse(doc, positions = TRUE), "toml_positions")
pos[pos$path == "owner.dob", ]
Read a TOML document from a file or connection
Description
Reads the input and parses it as toml_parse() would. At most
max_size + 1 bytes are ever read, so an oversized file or an endless
connection fails with zutoml_size_limit rather than exhausting memory.
Usage
toml_read(file, ..., max_size = 64 * 1024^2)
Arguments
file |
A file path, a URL, or a connection. |
... |
Arguments passed on to |
max_size |
The largest document accepted, in bytes, or |
Details
A string with an http, https, ftp, ftps or file scheme is read
with url(), any other string as a file path. A connection that is not
open is opened in "rb" mode and closed afterwards; an open one must be
binary, is read from its current position, and is left open.
A URL or file that cannot be opened, such as one that answers HTTP 404,
fails with zutoml_io_error.
Value
As toml_parse().
Examples
path <- tempfile(fileext = ".toml")
writeLines(c("[package]", 'name = "zutoml"', "version = 1"), path)
toml_read(path)
unlink(path)
Check that a document is valid TOML
Description
Runs the whole check phase of toml_parse() (the lexer, the grammar, the
rules for defining tables and keys, every value, and the limits) without
building any R value. It is exactly the check toml_parse() makes, with
the same limits: the two disagree only where a document is valid TOML
that R cannot hold, such as a string containing U+0000.
Usage
toml_validate(
x,
version = c("1.1.0", "1.0.0"),
max_size = 64 * 1024^2,
max_depth = 128L,
max_items = 1e+06,
max_string = 2^31 - 1,
error = FALSE
)
Arguments
x |
A TOML document: a single string, or a raw vector of UTF-8 bytes. |
version |
The TOML version to read: |
max_size |
The largest document accepted, in bytes, or |
max_depth |
The deepest nesting of tables and arrays, counting both; at most 1023. |
max_items |
The most keys plus array elements, or |
max_string |
The longest string or key, in decoded bytes, or |
error |
If |
Value
TRUE when x is valid TOML within the limits. Otherwise FALSE,
or with error = TRUE a condition of class zutoml_parse_error or
zutoml_limit_error (see zutoml-conditions) carrying the line,
column, byte offset and kind of the fault.
Examples
toml_validate('title = "TOML"\n[owner]\nname = "Tom"\n')
toml_validate("a = 1\na = 2\n")
# Why not, and where:
tryCatch(
toml_validate("a = 1\na = 2\n", error = TRUE),
zutoml_parse_error = function(e) c(e$kind, e$line, e$column)
)
Conditions raised by zutoml
Description
Every error zutoml raises carries a condition class, so it can be caught
by kind rather than by matching the message, which may change. Every
class below inherits from zutoml_error.
Details
zutoml_invalid_argumentAn argument was unusable, or a value cannot be written as TOML as given, such as a list with partial names or a string that is not valid UTF-8. Carries
arg, the argument at fault.zutoml_parse_errorThe document is not TOML. Carries
lineandcolumn(1-based, in characters),offset(0-based, in bytes) andkind, the name of the rule broken, such as"bad_escape"or"table_redefined".zutoml_unrepresentableThe document is valid TOML but holds a value R cannot: a string containing U+0000, a string longer than an R string, or an integer beyond 2^53 with
big_integers = "error".zutoml_unsupported_typeAn R value has no TOML form, such as a function, a raw vector or a matrix. Carries
path, the key path of the value, asa.b[3].c.zutoml_na_errorAn
NAorNULLvalue withna = "error". Carriespath.zutoml_limit_errorA limit was reached. The subclasses
zutoml_size_limit,zutoml_depth_limit,zutoml_item_limitandzutoml_string_limitname which one. Carrieslimit, the argument's name, such as"max_depth", andlimit_value.zutoml_io_errorA file, URL or connection could not be opened, read or written.
zutoml_missing_keytoml_parse()'sselectnames a path the document does not have. Carriespath, the path asked for, andfound, the part of it that exists.
Examples
tryCatch(
stop(structure(
class = c("zutoml_parse_error", "zutoml_error", "error", "condition"),
list(message = "TOML parse error", call = NULL)
)),
zutoml_error = function(e) class(e)[1]
)
Information about the zutoml build
Description
Information about the zutoml build
Usage
zutoml_info()
Value
A list: version, the package version; zufast, the version of
the 'zufast' headers it was compiled against; toml_test, the version of
the toml-test conformance suite it passes; toml_versions, the TOML
versions toml_parse() reads; max_depth_cap, the largest max_depth
accepted; and ndebug, whether the C code was compiled without
assertions.
Examples
zutoml_info()$toml_test