Skip to content

LebJe/toml.lua

Repository files navigation

toml.lua

TOML v1.0.0 parser and serializer for Lua. Powered by toml++.

Build and Test on MacOS Build and Test on Linux Build and Test on Windows LuaRocks

toml.lua is a Lua wrapper around toml++, allowing you to parse and serialize TOML in Lua.

Table of Contents

Created by gh-md-toc

Installation

Requirements

  • A C++ 17 compiler (Clang, GCC, MinGW)
  • CMake
  • Lua C headers (lua.h, lualib.h, and lauxlib.h)
  • Lua library (e.g. liblua51.<so|dylib|dll>)
  • Lua >= 5.1 or LuaJIT

LuaRocks

MacOS and Linux

luarocks install toml

Windows

LLVM

If you have installed Clang (https://llvm.org), and CMake is configured to use it, you can run:

luarocks install toml
MinGW

If you have installed MinGW, and CMake is configured to use it, you can run:

luarocks config variables.LINK_FLAGS "path\to\LuaJIT\bin\lua51.dll"
luarocks install toml
luarocks config variables.LINK_FLAGS --unset

Manual Compilation

MacOS and Linux

  1. Run cmake -S . -B build -G <generator-name> to generate the required files.

If you have a non standard Lua install location, add the environment variable LUA_DIR and have it point to the directory containing the include and lib folders for your Lua installation. For example: LUA_DIR=/usr/local/openresty/luajit cmake -S . -B build -G <generator-name>

  1. Run cmake --build build --config Release to build the project.
  2. You will find the toml.so dynamic library in the build folder.

Tip: use cmake --help to see a list of available generator names.

The above is based off of xpol/lua-rapidjson's README.

Windows

If LuaJIT is not installed, or your installation does not have the Lua headers, go to install LuaJIT.

Build with MinGW

Install MinGW (choco install mingw), then:

cmake.exe -S . -B build -G "MinGW Makefiles" -DLUA_INCLUDE_DIR="path\to\LuaJIT\include" -DLINK_FLAGS="path\to\LuaJIT\bin\lua51.dll"

cmake.exe --build build --config Release

You'll find the toml.dll file in the build directory.

Build with LLVM

Install LLVM and Ninja (choco install llvm ninja), then:

cmake.exe -S . -B build -G "Ninja Multi-Config" -DLUA_INCLUDE_DIR="path\to\LuaJIT\include"

cmake.exe --build build --config Release

You'll find the toml.dll file in the build directory.

Install LuaJIT

If you don't have LuaJIT, or your installation does not have the Lua headers, you can:

  1. Install MinGW (choco install mingw)

  2. Run scripts\buildLuaJIT.ps1:

powershell scripts\buildLuaJIT.ps1 -installDir "LuaJIT"

to build and install LuaJIT.

Usage

Decoding

local tomlStr = [[
a = 1275892
b = 'Hello, World!'
c = true
d = 124.2548

[e]
f = [ 1, 2, 3, '4', 5.142 ]
g = 1979-05-27
h = 07:32:00
i = 1979-05-27T07:32:00-07:00
]]

local toml = require("toml")
local inspect = require("inspect")

-- Decode from string
local succeeded, table = pcall(toml.decode, tomlStr)

-- Decode from file
succeeded, table = pcall(toml.decodeFromFile, "configuration.toml")

if succeeded then
-- Use `table`.
	print(inspect(table))
else
-- Error details are in `table`.
end

--[[
{
	a = 1275892,
	b = "Hello, World!",
	c = true,
	d = 124.2548,
	e = {
		f = { 1, 2, 3, "4", 5.142 },
		g = <userdata 1> -- 1979-05-27,
		h = <userdata 2> -- 07:32:00,
		i = <userdata 3> -- 1979-05-27T07:32:00-07:00
	}
}
--]]

Decoding Options

temporalTypesAsUserData
  • temporalTypesAsUserData = true: The userdata types toml.Date, toml.Time, and toml.DateTime are used to represent TOML date and time types.

  • temporalTypesAsUserData = false: Lua tables are used to represent TOML date and time types.

The default value is true

formattedIntsAsUserData
  • formattedIntsAsUserData = true: The userdata type toml.Int is used to represent integers in octal, binary, or hexadecimal format.
  • formattedIntsAsUserData = false: Integers in octal, binary, or hexadecimal format will be represented in decimal.

The default value is false

local tomlStr = [[
date = 1979-05-27
time = 07:32:00
datetime = 1979-05-27T07:32:00-07:00

hexadecimal = 0x16C3
binary = 0b110110011011
octal = 0x169F
]]

local table1 = toml.decode(tomlStr, { temporalTypesAsUserData = true, formattedIntsAsUserData = true })
local table2 = toml.decode(tomlStr, { temporalTypesAsUserData = false, formattedIntsAsUserData = false })

print(inspect(table1))
--[[
{
	date = <userdata 1> -- 1979-05-27, <-- toml.Date
	time = <userdata 2> -- 07:32:00 <-- toml.Time
	datetime = <userdata 3> -- 1979-05-27T07:32:00-07:00, <-- toml.DateTime
	binary = <userdata 4> -- 0b10011011, <-- toml.Int (with `toml.formatting.int.binary` flag)
	hexadecimal = <userdata 5> -- 0x16c3, <-- toml.Int (with `toml.formatting.int.octal` flag)
	octal = <userdata 6> -- 0x169f, <-- toml.Int (with `toml.formatting.int.hexadecimal` flag)
}
--]]

print(inspect(table2))
--[[
{
	date = {
		day = 27,
		month = 5,
		year = 1979
	},
	time = {
		hour = 7,
		minute = 32,
		nanoSecond = 0,
		second = 0
	},
	datetime = {
		date = {
			day = 27,
			month = 5,
			year = 1979
		},
		time = {
			hour = 7,
			minute = 32,
			nanoSecond = 0,
			second = 0
		},
		timeOffset = {
			minutes = -420
		}
	},
	binary = 3483,
	hexadecimal = 5827,
	octal = 5791,
}
--]]

Encoding

local toml = require("toml")

-- Inline tables: https://toml.io/en/v1.0.0#inline-table
local inlineTable = {
	a = 1275892,
	b = "Hello, World!",
	c = true,
	d = 124.2548,
}

-- Make the table inline.
setmetatable(inlineTable, { inline = true })

local table = {

	e = {
		f = { 1, 2, 3, "4", 5.142 },
		g = toml.Date.new(1979,   05,     27),
		--                year   month   day

		h = toml.Time.new( 7,     32,      0,        0),
		--                hour   minute  second   nanoSecond

		i = toml.DateTime.new(
			toml.Date.new(1979, 05, 27),
			toml.Time.new(7, 32, 0, 0),

			toml.TimeOffset.new(  -7,     0)
			--                   hour   minute
		)
	},
	inlineTable = inlineTable
}

-- Encode to string
local succeeded, documentOrErrorMessage = pcall(toml.encode, table)

-- Encode to file, this will **append** to the file.
succeeded, documentOrErrorMessage = pcall(toml.encodeToFile, table, "configuration.toml")

-- Encode to file, this will **overwrite** the file.
succeeded, documentOrErrorMessage = pcall(toml.encodeToFile, table, { file = "configuration.toml", overwrite = true })

if succeeded then
	-- Successfully encoded to string / wrote to file
	print(tomlDocumentOrErrorMessage)
else
-- Error occurred
	print(tomlDocumentOrErrorMessage)
end

--[[
inlineTable = { a = 1275892, b = "Hello, World!", c = true, d = 124.2548 }

[e]
f = [ 1, 2, 3, "4", 5.1420000000000003 ]
g = 1979-05-27
h = 07:32:00
i = 1979-05-27T07:32:00-07:00
--]]

Error Handling

local tomlStr = [[
a = 1275892
b = 'Hello, World!'
c = true
d = 124. # <-- ERROR: "Expected decimal digit"

[e]
f = [ 1, 2, 3, '4', 5.142 ]
g = 1979-05-27
h = 07:32:00
i = 1979-05-27T07:32:00-07:00
]]

local toml = require("toml")
local inspect = require("inspect")

local succeeded, table = pcall(toml.decode, tomlStr)

if succeeded then
	-- Use decoded table.
	print(inspect(table))
else
	-- Error details are in `table`.
	print(inspect(table))

	--[[
	{
		begin = {
			column = 9,
			line = 4
		},
		end = {
			column = 9,
			line = 4
		},
		reason = "Error while parsing floating-point: expected decimal digit, saw '\\n'"
	}
--]]
end

Inline Tables

Use setmetatable(myTable, { inline = true }) to create an inline table.

TOML Conversion

local toml = require("toml")

local tomlStr = [[
a = 1275892
b = 'Hello, World!'
c = true
d = 124.2548

[e]
f = [ 1, 2, 3, '4', 5.142 ]
g = 1979-05-27
h = 07:32:00
i = 1979-05-27T07:32:00-07:00
]]

JSON

-- Convert from a string
local json = toml.toJSON(tomlStr)

-- or from a table
json = toml.toJSON(toml.decode(tomlStr))

print(json)

YAML

local yaml = toml.toYAML(tomlStr)
yaml = toml.toYAML(toml.decode(tomlStr))
print(yaml)

Output Formatting

Formatting Integers

local toml = require("toml")

local normalIntegers = {
	int1 = 2582
	int2 = 3483
	int3 = 5971
}
print(toml.encode(normalIntegers))
--[[
int1 = 2582
int2 = 3483
int3 = 5791
--]]

local formattedIntegers = {
	int1 = toml.Int.new(2582, toml.formatting.int.octal),
	int2 = toml.Int.new(3483, toml.formatting.int.binary),
	int3 = toml.Int.new(5791, toml.formatting.int.hexadecimal)
}

print(toml.encode(formattedIntegers))
--[[
int1 = 0o5026
int2 = 0b110110011011
int3 = 0x169F
--]]

-- Use `int` and `flags` properties to assign and retrieve flags and integers.
local int = formattedIntegers.int1.int
local flags = formattedIntegers.int1.flags

formattedIntegers.int1.int = 5827
formattedIntegers.int1.flags = toml.formatting.int.hexadecimal

print(toml.encode(formattedIntegers))
--[[
int1 = 0x16C3
int2 = 0b110110011011
int3 = 0x169F
--]]

Formatting TOML, JSON, or YAML

toml.encode, toml.encodeToFile, toml.toJSON, and toml.toYAML all take an optional second (third in the case of toml.encodeToFile) parameter: a table containing keys that disable or enable different formatting options. Passing an empty table removes all options, while not providing a table will use the default options.

{
	--- Dates and times will be emitted as quoted strings.
	quoteDatesAndTimes = false,

	--- Infinities and NaNs will be emitted as quoted strings.
	quoteInfinitesAndNaNs = false,

	--- Strings will be emitted as single-quoted literal strings where possible.
	allowLiteralStrings = false,

	--- Strings containing newlines will be emitted as triple-quoted 'multi-line' strings where possible.
	allowMultiLineStrings = false,

	--- Allow real tab characters in string literals (as opposed to the escaped form `\t`).
	allowRealTabsInStrings = false,

	--- Allow non-ASCII characters in strings (as opposed to their escaped form, e.g. `\u00DA`).
	allow_unicode_strings = true,

	--- Allow integers with `toml.formatting.int.binary` to be emitted as binary.
	allowBinaryIntegers = true,

	--- Allow integers with `toml.formatting.int.octal` to be emitted as octal.
	allowOctalIntegers = true,

	--- Allow integers with `toml.formatting.int.hexadecimal` to be emitted as hexadecimal.
	allowHexadecimalIntegers = true,

	--- Apply indentation to tables nested within other tables/arrays.
	indentSubTables = false,

	--- Apply indentation to array elements when the array is forced to wrap over multiple lines.
	indentArrayElements = false,

	--- Combination of `indentSubTables` and `indentArrayElements`.
	indentation = true,

	--- Emit floating-point values with relaxed (human-friendly) precision.
	---
	--- Warning: Setting this flag may cause serialized documents to no longer round-
	--- trip correctly since floats might have a less precise value upon being written out
	--- than they did when being read in. Use this flag at your own risk.
	relaxedFloatPrecision = false,

	--- Avoids the use of whitespace around key-value pairs.
	terseKeyValuePairs = false
}

Date and Time

(Creating Date, Time, and DateTime is shown in the encoding section)

	record Date
		year: number
		month: number
		day: number

		new: function(year: number, month: number, day: number): Date
	end

	record Time
		hour: number
		minute: number
		second: number
		nanoSecond: number

		new: function (
			hour: number,
			minute: number,
			second: number,
			nanoSecond: number
		): Time
	end

	record TimeOffset
		minutes: number

		new: function (hours: number, minutes: number): TimeOffset
	end

	record DateTime
		date: Date
		time: Time
		TimeOffset: nil | TimeOffset

		new: function(date: Date, time: Time): DateTime
		new: function(date: Date, time: Time, timeOffset: TimeOffset): DateTime
	end

The comments for the options are from the tomlplusplus documentation

Dependencies

Licenses

The toml++ license is available at https://github.com/marzer/tomlplusplus/blob/master/LICENSE.

The sol2 license is available at https://github.com/ThePhD/sol2/blob/develop/LICENSE.txt.

The magic_enum license is available at https://github.com/Neargye/magic_enum/blob/master/LICENSE.

Contributing

Before committing, please install pre-commit, clang-format, StyLua, and Prettier, then install the pre-commit hooks. On MacOS, it would look like:

$ brew bundle # install the packages specified in Brewfile
$ pre-commit install

# Commit your changes.

To install pre-commit on other platforms, refer to the documentation.