A dependency-free, pure-Lua TOML processing library: a decoder to Lua tables, an encoder back to TOML, a formatter, and a JSON Schema (Draft 2020-12) validator with source ranges.
No C extensions, no external dependencies. Runs on Lua 5.1+ and LuaJIT. Verified against the official toml-test suite (TOML 1.1).
- Full TOML 1.1 decode, encode, and format pipeline
- JSON Schema Draft 2020-12 validator (partial subset) reporting errors with source ranges
- Structural path lookup at a
(row, col)position — useful for editor tooling built on top of the library
luarocks install --server=https://luarocks.org/dev tomltools
# or, from a checkout:
luarocks makeCopy the lua/ directory onto your package.path:
package.path="/path/to/tomltools/lua/?.lua;/path/to/tomltools/lua/?/init.lua;" ..package.pathlocaltoml=require("tomltools")
-- Parse + decode (and optionally validate against a JSON Schema)localdata, errors=toml.decode([[title = "demo"[server]host = "localhost"port = 8080]])
print(data.title) --> demoprint(data.server.port) --> 8080-- Errors are normalised to { range = { r1, c1, r2, c2 }, message = "..." }for_, einipairs(result.errors) doprint(e.message)
endlocalschema= {
type="object",
properties= {
title= { type="string" },
server= {
type="object",
properties= { port= { type="integer", minimum=1, maximum=65535 } },
required= { "port" },
},
},
required= { "title" },
}
localdata, errors=toml.decode(text, schema)
-- errors now also includes schema violations, each with a source range-- Whole document: returns a TOML stringtoml.encode({ name="hello", value=42, server= { host="localhost", port=8080 } })
-- A single snippet as lines, for inserting into an existing document:toml.encode_entry({ host="localhost", port=8080 }, { style="table", key="server" })
toml.encode_entry({ name="build" }, { style="aot", key="task" })
-- "table" style promotes sub-tables to their own [headers]; inline_subtables-- keeps them as `env = { CI = "1" }` so the snippet stays one contiguous block:toml.encode_entry({ type="shell", env= { CI="1" } },
{ style="table", key= { "tasks", "build" }, inline_subtables=true })localformatted, errors=toml.format(text) -- normalised TOML, or nil + errors| Function | Returns | Description |
|---|---|---|
decode(text, schema?) | data, errors | Decode, and optionally validate. Errors carry source ranges. |
encode(value) | string | Encode a Lua table to a complete TOML document. |
encode_entry(t, opts?) | string[] | Encode a single snippet as lines. opts.style is "inline" (default), "table", or "aot"; opts.inline_subtables keeps nested tables inline under "table". |
format(text) | string?, errors? | Reformat a TOML document (preserves comments). |
validate(data, schema) | ok, errors | Validate an already-decoded value against a JSON Schema. |
find_path(text, row, col) | PathNode[]? | Structural TOML path. |
Every pipeline stage is a standalone module:
localparser=require("tomltools.parser") -- text -> CSTlocaldecoder=require("tomltools.decoder") -- CST -> Lua table (+ DecodeTree)localencoder=require("tomltools.encoder") -- table -> TOML textlocalformatter=require("tomltools.formatter") -- CST -> formatted TOMLlocalvalidator=require("tomltools.validator") -- (schema, data) -> ok, errorsEmpty TOML tables decode to objects, not arrays. Internally this is tracked with
require("tomltools.std").empty_dict() / std.islist() — small pure-Lua
helpers that distinguish an empty object from an empty array.
Unit tests use busted; the conformance suite uses toml-test.
make unit_test # busted unit suite (spec/), pure Lua
make toml_test # official toml-test conformance suite (TOML 1.1)
make test# bothThe toml-test harness (tests/run_decoder.lua, tests/run_encoder.lua) runs
under LuaJIT by default for Lua 5.1 numeric semantics; override with
make toml_test LUA=lua5.1.
MIT