Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 62 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
# tinytoml
[![Run Tests and Code Coverage](https://github.com/FourierTransformer/tinytoml/actions/workflows/test-and-coverage.yml/badge.svg)](https://github.com/FourierTransformer/tinytoml/actions/workflows/test-and-coverage.yml) [![Coverage Status](https://coveralls.io/repos/github/FourierTransformer/tinytoml/badge.svg?branch=refs/pull/1/merge)](https://coveralls.io/github/FourierTransformer/tinytoml?branch=main)

tinytoml is a pure Lua [TOML](https://toml.io) parsing library. It's written in [Teal](https://github.com/teal-language/tl) and works with Lua 5.1-5.5 and LuaJIT 2.0/2.1. tinytoml parses a TOML document into a standard Lua table using default Lua types. Since TOML supports various datetime types, those are by default represented by strings, but can be configured to use a custom type if desired.
tinytoml is a pure Lua [TOML](https://toml.io) parsing library. It's written in [Teal](https://github.com/teal-language/tl) and works with Lua 5.1-5.5 and LuaJIT 2.0/2.1. tinytoml parses a TOML document into a standard Lua table using default Lua types. Since TOML supports various datetime types, those are by default represented by strings, but can be configured as a table or passed in to a method so it is represented by a custom or 3rd-party library.

tinytoml passes all the [toml-test](https://github.com/toml-lang/toml-test) use cases that Lua can realistically pass (even the UTF-8 ones!). The few that fail are mostly representational:
- Lua doesn't differentiate between an array or a dictionary, so tests involving _empty_ arrays fail.
- Some Lua versions have differences in how numbers are represented. Lua 5.3 introduced integers, so tests involving integer representation pass on Lua 5.3+

Current Supported TOML Version: 1.1.0

> [!TIP]
> | [Installing](#installing) | [Parsing](#parsing-toml) | [Encoding](#encoding-toml) | [Comparison](#comparison) |
> | ---------- | ------- | -------- | ---------- |

## Installing
You can grab the `tinytoml.lua` file from this repo (or the `tinytoml.tl` file if using Teal) or install it via LuaRocks

Expand Down Expand Up @@ -68,9 +72,43 @@ There are a few parsing options available that are passed in the the `options` p
tinytoml.parse("a=2024-10-31T12:49:00Z", {load_from_string=true, type_conversion=type_conversion})
```

- `assign_value_function`
- `encode_datetime_as` (default `string`)

Allows encoding datetime either as a `string` or a `table`. The `table` will take all the individual fields and place them in a table.
This can be used in conjunction with `type_conversion` - either the string or table representation would be passed into whatever function is
specified in `type_conversion`.

Example:

```toml
offset_datetime = 1979-05-27T07:32:00Z
local_datetime = 1979-05-27T07:32:00
local_time = 07:32:00
local_date = 1979-05-27
```

```lua
-- with the option: { encode_datetime_as = "string" }
{
offset_datetime = "1979-05-27T07:32:00Z",
local_datetime = "1979-05-27T07:32:00",
local_time = "07:32:00",
local_date = "1979-05-27"
}
-- with the option: { encode_datetime_as = "table" }
{
offset_datetime = {year = 1979, month = 05, day = 27, hour = 7, min = 32, sec = 0, msec = 0, time_offset = "00:00"},
local_datetime = {year = 1979, month = 05, day = 27, hour = 7, min = 32, sec = 0, msec = 0},
local_time = {hour = 7, min = 32, sec = 0, msec = 0},
local_date = {year = 1979, month = 05, day = 27}
}

```

- `max_nesting_depth` (default `1000`) and `max_filesize` (default `100000000` - 100 MB)

The maximum nesting depth and maxmimum filesize in bytes. tinytoml will throw an error if either of these are exceeded.

this method is called when assigning _every_ value to a table. It's mostly used to help perform the unit testing using [toml-test](https://github.com/toml-lang/toml-test), since they want to see the type and parsed value for comparison purposes. This option is the only one that has potential to change, so we advice against using it. If you need specific functionality that you're implementing through this (or find this function useful in general) - please let us know.

## Encoding TOML

Expand Down Expand Up @@ -116,4 +154,24 @@ local_time = 07:32:00
local_date = 1979-05-27
```

This effectively means you'll have to pre-process dates and times to strings in your codebase, before passing them to tinytoml's encoder.
This effectively means you'll have to pre-process dates and times to strings in your codebase, before passing them to tinytoml's encoder.

## Comparison
Here's a helpful comparison table that can be useful in deciding which Lua TOML parser to use. The data was collected with the most recent versions as of 1/2026.

| Feature / Library | tinytoml | toml-edit | toml.lua | toml2lua | tomlua |
|:------------------|:------------------------------|:------------------------------|:------------------------------|:-------------------------------|:------------------------------|
| Language | Lua | Rust binding | C++ binding | Lua | C |
| TOML Version | 1.1.0 | 1.0.0 | 1.0.0 | 1.0.0 | Not Specified |
| UTF-8 Support | ✅ | ✅ | ✅ | ✅ | ✅ |
| Passes toml-test | ✅ | ✅ | ✅ | ❌ | ❌ |
| Date/Time Support | String/Table/Register Method | | Custom Userdata/Lua Table | Lua Table | Custom Userdata |
| Encoder | Basic | Comment Preserving | Basic, many options | Basic | Very Configurable |
| 16 KB TOML decode | Lua: 3.9ms <br> LuaJIT: 2.7ms | Lua: 2.8ms <br> LuaJIT: 1.0ms | Lua: dnf <br> LuaJIT: 2.4ms | Lua: 32.5ms <br> LuaJIT: 7.0ms | Lua: 1.6ms <br> LuaJIT: .29ms |
| 8 MB TOML decode | Lua: 1.49s <br> LuaJIT: 415ms | Lua: 929ms <br> LuaJIT: 462ms | Lua: error <br> LuaJIT: error | Lua: 12.01s <br> LuaJIT: 3.13s | Lua: 318ms <br> LuaJIT: 119.7ms |

**NOTES:**
- tinytoml, toml2lua, and tomlua's toml-test support were verified by running through toml-test. toml-edit and toml.lua were based on the bindings, which both passed toml-test.
- I was using hyperfine to run the tests, and toml.lua's time estimate rapidly started rising in the middle of the 16KB run and segfaulted with the higher runs.
- Tests were run in a docker container running on an arm64 Mac, as tomlua did not compile on macOS at the time the benchmarks were taken.
- Standard benchmark disclaimer: These are all relative to each other and your mileage will [likely] vary.
42 changes: 37 additions & 5 deletions spec/decoder.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,44 @@ local function float_to_string(x)
end


local assign_value_function = function(value, value_type)
if value_type == "float" then
return { ["value"] = float_to_string(value), ["type"] = value_type }
local function add_toml_test_tag(table_to_clear)
if type(table_to_clear) ~= "table" then

if type(table_to_clear) == "number" then
if math.type(table_to_clear) == "integer" then
return {type="integer", value=tostring(table_to_clear)}
else
return {type="float", value=float_to_string(table_to_clear)}
end

elseif type(table_to_clear) == "string" then
return {type="string", value=table_to_clear}

elseif type(table_to_clear) == "boolean" then
return {type="bool", value=tostring(table_to_clear)}

else
return table_to_clear["value"]
end

else
return { ["value"] = tostring(value), ["type"] = value_type }
if not (table_to_clear.type and table_to_clear.value) then
for k, v in pairs(table_to_clear) do
table_to_clear[k] = add_toml_test_tag(v)
end
end
end

return table_to_clear
end

print(cjson.encode(tinytoml.parse(io.read("*a"), { load_from_string = true, assign_value_function = assign_value_function })))
local type_conversion = {
["datetime"] = function(raw_string) return {type="datetime", value=raw_string} end,
["datetime-local"] = function(raw_string) return {type="datetime-local", value=raw_string} end,
["date-local"] = function(raw_string) return {type="date-local", value=raw_string} end,
["time-local"] = function(raw_string) return {type="time-local", value=raw_string} end,
}

local output = tinytoml.parse(io.read("*a"), { load_from_string = true, encode_datetime_as = "string", type_conversion = type_conversion })
add_toml_test_tag(output)
print(cjson.encode(output))
40 changes: 0 additions & 40 deletions spec/decoder.tl

This file was deleted.

5 changes: 2 additions & 3 deletions tinytoml-0.1.0-1.rockspec → tinytoml-1.0.0-1.rockspec
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package = "tinytoml"
version = "0.1.0-1"
version = "1.0.0-1"

source = {
url = "git://github.com/FourierTransformer/tinytoml.git",
tag = "0.1.0"
tag = "1.0.0"
}

description = {
Expand All @@ -13,7 +13,6 @@ description = {
It supports all TOML 1.1.0 features including parsing strings, numbers, datetimes, arrays, inline-tables and even validating UTF-8 with good error messages if anything fails!
]],
homepage = "https://github.com/FourierTransformer/tinytoml",
maintainer = "Fourier Transformer <ftransformer@protonmail.com>",
license = "MIT"
}

Expand Down
Loading
Loading