tomelr
is a library that will typically be auto-installed via
another package requiring it.
If you are developing a package and want to use this library, you can
install it locally using Emacs package.el
as follows as it’s
available via GNU ELPA:
M-x package-install
RET tomelr
RET
- Add this library in the Package-Requires header. Here’s an
example from ~ox-hugo~:
;; Package-Requires: ((emacs "24.4") (org "9.0") tomelr))
- Require it.
(require 'tomelr)
- Use the
tomelr-encode
function.- Input
- Lisp data expression in Alist or Plist format
- Output
- TOML string
Here’s an example of input alist that can be processed by
tomelr-encode
.
'((title . "Some Title") ;String
(author . ("fn ln")) ;List
(description . "some long description\nthat spans multiple\nlines") ;Multi-line string
(date . 2022-03-14T01:49:00-04:00) ;RFC 3339 date format
(tags . ("tag1" "tag2"))
(draft . "false") ;Boolean
(versions . ((emacs . "28.1.50") (org . "release_9.5-532-gf6813d"))) ;Map or TOML Table
(org_logbook . (((timestamp . 2022-04-08T14:53:00-04:00) ;Array of maps or TOML Tables
(note . "This note addition prompt shows up on typing the `C-c C-z` binding.\nSee [org#Drawers](https://www.gnu.org/software/emacs/manual/html_mono/org.html#Drawers)."))
((timestamp . 2018-09-06T11:45:00-04:00)
(note . "Another note **bold** _italics_."))
((timestamp . 2018-09-06T11:37:00-04:00)
(note . "A note `mono`.")))))
Here’s an example of input plist that can be processed by
tomelr-encode
.
'(:title "Some Title" ;String
:author ("fn ln") ;List
:description "some long description\nthat spans multiple\nlines" ;Multi-line string
:date 2022-03-14T01:49:00-04:00 ;RFC 3339 date format
:tags ("tag1" "tag2")
:draft "false" ;Boolean
:versions (:emacs "28.1.50" :org "release_9.5-532-gf6813d") ;Map or TOML Table
:org_logbook ((:timestamp 2022-04-08T14:53:00-04:00 ;Array of maps or TOML Tables
:note "This note addition prompt shows up on typing the `C-c C-z` binding.\nSee [org#Drawers](https://www.gnu.org/software/emacs/manual/html_mono/org.html#Drawers).")
(:timestamp 2018-09-06T11:45:00-04:00
:note "Another note **bold** _italics_.")
(:timestamp 2018-09-06T11:37:00-04:00
:note "A note `mono`.")))
You will get the below TOML output for either of the above input data:
title = "Some Title"
author = ["fn ln"]
description = """
some long description
that spans multiple
lines"""
date = 2022-03-14T01:49:00-04:00
tags = ["tag1", "tag2"]
draft = false
[versions]
emacs = "28.1.50"
org = "release_9.5-532-gf6813d"
[[org_logbook]]
timestamp = 2022-04-08T14:53:00-04:00
note = """
This note addition prompt shows up on typing the `C-c C-z` binding.
See [org#Drawers](https://www.gnu.org/software/emacs/manual/html_mono/org.html#Drawers)."""
[[org_logbook]]
timestamp = 2018-09-06T11:45:00-04:00
note = "Another note **bold** _italics_."
[[org_logbook]]
timestamp = 2018-09-06T11:37:00-04:00
note = "A note `mono`."
Right now, the scalars and tables/array of tables does not get ordered in the right order automatically. So the user needs to ensure that the S-exp has all the scalars in the very beginning and then followed by TOML tables and arrays of tables.
:white_check_mark: Put the scalars first and then maps or tables.
'((title . "Hello") ;First the scalar
(img . ((file . "foo.png") ;Then the map or table
(credit . "Bar Zoo"))))
title = "Hello"
[img]
file = "foo.png"
credit = "Bar Zoo"
:x: Don’t do this!: Map or table first and then scalar.
'((img . ((file . "foo.png")
(credit . "Bar Zoo")))
(title . "Hello"))
Incorrect order! Now the title
became part of the [img]
table!
[img]
file = "foo.png"
credit = "Bar Zoo"
title = "Hello"
Following examples shown how S-expressions get translated to various TOML object types.
https://toml.io/en/v1.0.0#boolean'((bool1 . t)
(bool2 . :false))
bool1 = true
bool2 = false
{ "bool1": true, "bool2": false }https://toml.io/en/v1.0.0#integer
'((int1 . +99)
(int2 . 42)
(int3 . 0)
(int4 . -17))
int1 = 99
int2 = 42
int3 = 0
int4 = -17
{ "int1": 99, "int2": 42, "int3": 0, "int4": -17 }https://toml.io/en/v1.0.0#float
'((flt1 . +1.0)
(flt2 . 3.1415)
(flt3 . -0.01)
(flt4 . 5e+22)
(flt5 . 1e06)
(flt6 . -2E-2)
(flt7 . 6.626e-34))
flt1 = 1.0
flt2 = 3.1415
flt3 = -0.01
flt4 = 5e+22
flt5 = 1000000.0
flt6 = -0.02
flt7 = 6.626e-34
{ "flt1": 1.0, "flt2": 3.1415, "flt3": -0.01, "flt4": 5e+22, "flt5": 1000000.0, "flt6": -0.02, "flt7": 6.626e-34 }https://toml.io/en/v1.0.0#string
'((str1 . "Roses are red")
(str2 . "Roses are red\nViolets are blue"))
str1 = "Roses are red"
str2 = """
Roses are red
Violets are blue"""
{ "str1": "Roses are red", "str2": "Roses are red\nViolets are blue" }https://toml.io/en/v1.0.0#local-date
'((ld1 . "1979-05-27"))
ld1 = 1979-05-27
{ "ld1": "1979-05-27" }https://toml.io/en/v1.0.0#offset-date-time
'((odt1 . "1979-05-27T07:32:00Z")
(odt2 . "1979-05-27T00:32:00-07:00")
(odt3 . "1979-05-27T00:32:00.999999-07:00"))
odt1 = 1979-05-27T07:32:00Z
odt2 = 1979-05-27T00:32:00-07:00
odt3 = 1979-05-27T00:32:00.999999-07:00
{ "odt1": "1979-05-27T07:32:00Z", "odt2": "1979-05-27T00:32:00-07:00", "odt3": "1979-05-27T00:32:00.999999-07:00" }
'((key1 . 123)
(key2 . nil)
(key3 . "abc")
(key4 . :false)
(key5 . t))
key1 = 123
key3 = "abc"
key4 = false
key5 = true
{ "key1": 123, "key2": null, "key3": "abc", "key4": false, "key5": true }
https://toml.io/en/v1.0.0#array
'((integers . (1 2 3))
(integers2 . [1 2 3]) ;Same as above
(colors . ("red" "yellow" "green"))
;; Mixed-type arrays are allowed
(numbers . (0.1 0.2 0.5 1 2 5)))
integers = [1, 2, 3]
integers2 = [1, 2, 3]
colors = ["red", "yellow", "green"]
numbers = [0.1, 0.2, 0.5, 1, 2, 5]
{ "integers": [ 1, 2, 3 ], "integers2": [ 1, 2, 3 ], "colors": [ "red", "yellow", "green" ], "numbers": [ 0.1, 0.2, 0.5, 1, 2, 5 ] }
'((nested_arrays_of_ints . [(1 2) (3 4 5)])
(nested_mixed_array . [(1 2) ("a" "b" "c")]))
nested_arrays_of_ints = [[1, 2], [3, 4, 5]]
nested_mixed_array = [[1, 2], ["a", "b", "c"]]
{ "nested_arrays_of_ints": [ [ 1, 2 ], [ 3, 4, 5 ] ], "nested_mixed_array": [ [ 1, 2 ], [ "a", "b", "c" ] ] }
https://toml.io/en/v1.0.0#table
'((table-1 . ((key1 . "some string")
(key2 . 123)))
(table-2 . ((key1 . "another string")
(key2 . 456))))
[table-1]
key1 = "some string"
key2 = 123
[table-2]
key1 = "another string"
key2 = 456
{ "table-1": { "key1": "some string", "key2": 123 }, "table-2": { "key1": "another string", "key2": 456 } }
'((table-1 . ((table-1a . ((key1 . "some string")
(key2 . 123)))
(table-1b . ((key1 . "foo")
(key2 . 98765)))))
(menu . (("auto weight" . ((weight . 4033)
(identifier . "foo"))))))
[table-1]
[table-1.table-1a]
key1 = "some string"
key2 = 123
[table-1.table-1b]
key1 = "foo"
key2 = 98765
[menu]
[menu."auto weight"]
weight = 4033
identifier = "foo"
{ "table-1": { "table-1a": { "key1": "some string", "key2": 123 }, "table-1b": { "key1": "foo", "key2": 98765 } }, "menu": { "auto weight": { "weight": 4033, "identifier": "foo" } } }
https://toml.io/en/v1.0.0#array-of-tables
'((products . (((name . "Hammer")
(sku . 738594937))
()
((name . "Nail")
(sku . 284758393)
(color . "gray"))))
(org_logbook . (((timestamp . 2022-04-08T14:53:00-04:00)
(note . "This note addition prompt shows up on typing the `C-c C-z` binding.\nSee [org#Drawers](https://www.gnu.org/software/emacs/manual/html_mono/org.html#Drawers)."))
((timestamp . 2018-09-06T11:45:00-04:00)
(note . "Another note **bold** _italics_."))
((timestamp . 2018-09-06T11:37:00-04:00)
(note . "A note `mono`.")))))
[[products]]
name = "Hammer"
sku = 738594937
[[products]]
[[products]]
name = "Nail"
sku = 284758393
color = "gray"
[[org_logbook]]
timestamp = 2022-04-08T14:53:00-04:00
note = """
This note addition prompt shows up on typing the `C-c C-z` binding.
See [org#Drawers](https://www.gnu.org/software/emacs/manual/html_mono/org.html#Drawers)."""
[[org_logbook]]
timestamp = 2018-09-06T11:45:00-04:00
note = "Another note **bold** _italics_."
[[org_logbook]]
timestamp = 2018-09-06T11:37:00-04:00
note = "A note `mono`."
{ "products": [ { "name": "Hammer", "sku": 738594937 }, null, { "name": "Nail", "sku": 284758393, "color": "gray" } ], "org_logbook": [ { "timestamp": "2022-04-08T14:53:00-04:00", "note": "This note addition prompt shows up on typing the `C-c C-z` binding.\nSee [org#Drawers](https://www.gnu.org/software/emacs/manual/html_mono/org.html#Drawers)." }, { "timestamp": "2018-09-06T11:45:00-04:00", "note": "Another note **bold** _italics_." }, { "timestamp": "2018-09-06T11:37:00-04:00", "note": "A note `mono`." } ] }
'((fruits . (((name . "apple")
(physical . ((color . "red")
(shape . "round")))
(varieties . (((name . "red delicious"))
((name . "granny smith")))))
((name . "banana")
(varieties . (((name . "plantain"))))))))
[[fruits]]
name = "apple"
[fruits.physical]
color = "red"
shape = "round"
[[fruits.varieties]]
name = "red delicious"
[[fruits.varieties]]
name = "granny smith"
[[fruits]]
name = "banana"
[[fruits.varieties]]
name = "plantain"
{ "fruits": [ { "name": "apple", "physical": { "color": "red", "shape": "round" }, "varieties": [ { "name": "red delicious" }, { "name": "granny smith" } ] }, { "name": "banana", "varieties": [ { "name": "plantain" } ] } ] }
'((title . "Keyword Collection")
(author . ("firstname1 lastname1" "firstname2 lastname2" "firstname3 lastname3"))
(aliases . ("/posts/keyword-concatenation" "/posts/keyword-merging"))
(images . ("image 1" "image 2"))
(keywords . ("keyword1" "keyword2" "three word keywords3"))
(outputs . ("html" "json"))
(series . ("series 1" "series 2"))
(tags . ("mega front-matter" "keys" "collection" "concatenation" "merging"))
(categories . ("cat1" "cat2"))
(videos . ("video 1" "video 2"))
(draft . :false)
(categories_weight . 999)
(tags_weight . 88)
(weight . 7)
(myfoo . "bar")
(mybaz . "zoo")
(alpha . 1)
(beta . "two words")
(gamma . 10)
(animals . ("dog" "cat" "penguin" "mountain gorilla"))
(strings-symbols . ("abc" "def" "two words"))
(integers . (123 -5 17 1234))
(floats . (12.3 -5.0 -1.7e-05))
(booleans . (t :false))
(dog . ((legs . 4)
(eyes . 2)
(friends . ("poo" "boo"))))
(header . ((image . "projects/Readingabook.jpg")
(caption . "stay hungry stay foolish")))
(collection . ((nothing . :false)
(nonnil . t)
(animals . ("dog" "cat" "penguin" "mountain gorilla"))
(strings-symbols . ("abc" "def" "two words"))
(integers . (123 -5 17 1234))
(floats . (12.3 -5.0 -1.7e-05))
(booleans . (t :false))))
(menu . ((foo . ((identifier . "keyword-collection")
(weight . 10)))))
(resources . (((src . "*.png")
(name . "my-cool-image-:counter")
(title . "The Image #:counter")
(params . ((foo . "bar")
(floats . (12.3 -5.0 -1.7e-05))
(strings-symbols . ("abc" "def" "two words"))
(animals . ("dog" "cat" "penguin" "mountain gorilla"))
(integers . (123 -5 17 1234))
(booleans . (t :false))
(byline . "bep"))))
((src . "image-4.png")
(title . "The Fourth Image"))
((src . "*.jpg")
(title . "JPEG Image #:counter")))))
title = "Keyword Collection"
author = ["firstname1 lastname1", "firstname2 lastname2", "firstname3 lastname3"]
aliases = ["/posts/keyword-concatenation", "/posts/keyword-merging"]
images = ["image 1", "image 2"]
keywords = ["keyword1", "keyword2", "three word keywords3"]
outputs = ["html", "json"]
series = ["series 1", "series 2"]
tags = ["mega front-matter", "keys", "collection", "concatenation", "merging"]
categories = ["cat1", "cat2"]
videos = ["video 1", "video 2"]
draft = false
categories_weight = 999
tags_weight = 88
weight = 7
myfoo = "bar"
mybaz = "zoo"
alpha = 1
beta = "two words"
gamma = 10
animals = ["dog", "cat", "penguin", "mountain gorilla"]
strings-symbols = ["abc", "def", "two words"]
integers = [123, -5, 17, 1234]
floats = [12.3, -5.0, -1.7e-05]
booleans = [true, false]
[dog]
legs = 4
eyes = 2
friends = ["poo", "boo"]
[header]
image = "projects/Readingabook.jpg"
caption = "stay hungry stay foolish"
[collection]
nothing = false
nonnil = true
animals = ["dog", "cat", "penguin", "mountain gorilla"]
strings-symbols = ["abc", "def", "two words"]
integers = [123, -5, 17, 1234]
floats = [12.3, -5.0, -1.7e-05]
booleans = [true, false]
[menu]
[menu.foo]
identifier = "keyword-collection"
weight = 10
[[resources]]
src = "*.png"
name = "my-cool-image-:counter"
title = "The Image #:counter"
[resources.params]
foo = "bar"
floats = [12.3, -5.0, -1.7e-05]
strings-symbols = ["abc", "def", "two words"]
animals = ["dog", "cat", "penguin", "mountain gorilla"]
integers = [123, -5, 17, 1234]
booleans = [true, false]
byline = "bep"
[[resources]]
src = "image-4.png"
title = "The Fourth Image"
[[resources]]
src = "*.jpg"
title = "JPEG Image #:counter"
{ "title": "Keyword Collection", "author": [ "firstname1 lastname1", "firstname2 lastname2", "firstname3 lastname3" ], "aliases": [ "/posts/keyword-concatenation", "/posts/keyword-merging" ], "images": [ "image 1", "image 2" ], "keywords": [ "keyword1", "keyword2", "three word keywords3" ], "outputs": [ "html", "json" ], "series": [ "series 1", "series 2" ], "tags": [ "mega front-matter", "keys", "collection", "concatenation", "merging" ], "categories": [ "cat1", "cat2" ], "videos": [ "video 1", "video 2" ], "draft": false, "categories_weight": 999, "tags_weight": 88, "weight": 7, "myfoo": "bar", "mybaz": "zoo", "alpha": 1, "beta": "two words", "gamma": 10, "animals": [ "dog", "cat", "penguin", "mountain gorilla" ], "strings-symbols": [ "abc", "def", "two words" ], "integers": [ 123, -5, 17, 1234 ], "floats": [ 12.3, -5.0, -1.7e-05 ], "booleans": [ true, false ], "dog": { "legs": 4, "eyes": 2, "friends": [ "poo", "boo" ] }, "header": { "image": "projects/Readingabook.jpg", "caption": "stay hungry stay foolish" }, "collection": { "nothing": false, "nonnil": true, "animals": [ "dog", "cat", "penguin", "mountain gorilla" ], "strings-symbols": [ "abc", "def", "two words" ], "integers": [ 123, -5, 17, 1234 ], "floats": [ 12.3, -5.0, -1.7e-05 ], "booleans": [ true, false ] }, "menu": { "foo": { "identifier": "keyword-collection", "weight": 10 } }, "resources": [ { "src": "*.png", "name": "my-cool-image-:counter", "title": "The Image #:counter", "params": { "foo": "bar", "floats": [ 12.3, -5.0, -1.7e-05 ], "strings-symbols": [ "abc", "def", "two words" ], "animals": [ "dog", "cat", "penguin", "mountain gorilla" ], "integers": [ 123, -5, 17, 1234 ], "booleans": [ true, false ], "byline": "bep" } }, { "src": "image-4.png", "title": "The Fourth Image" }, { "src": "*.jpg", "title": "JPEG Image #:counter" } ] }
'(:int 123
:remove_this_key nil
:str "abc"
:bool_false :false
:bool_true t
:int_list (1 2 3)
:str_list ("a" "b" "c")
:bool_list (t :false t :false)
:list_of_lists [(1 2) (3 4 5)]
:map (:key1 123
:key2 "xyz")
:list_of_maps [(:key1 123
:key2 "xyz")
(:key1 567
:key2 "klm")])
int = 123
str = "abc"
bool_false = false
bool_true = true
int_list = [1, 2, 3]
str_list = ["a", "b", "c"]
bool_list = [true, false, true, false]
list_of_lists = [[1, 2], [3, 4, 5]]
[map]
key1 = 123
key2 = "xyz"
[[list_of_maps]]
key1 = 123
key2 = "xyz"
[[list_of_maps]]
key1 = 567
key2 = "klm"
{ "int": 123, "remove_this_key": null, "str": "abc", "bool_false": false, "bool_true": true, "int_list": [ 1, 2, 3 ], "str_list": [ "a", "b", "c" ], "bool_list": [ true, false, true, false ], "list_of_lists": [ [ 1, 2 ], [ 3, 4, 5 ] ], "map": { "key1": 123, "key2": "xyz" }, "list_of_maps": [ { "key1": 123, "key2": "xyz" }, { "key1": 567, "key2": "klm" } ] }
make test
Run make test MATCH=<string>
. For example, to run all tests where
the name matches “scalar” completely or partially, run:
make test MATCH=scalar
This library started off by extracting the JSON Encoding pieces from the Emacs core library *json.el*.
It was then refactored to meet the specification defined below.