Skip to content

TOML

Functions for encoding and decoding TOML data. Sema itself uses TOML for project configuration (sema.toml), making these functions useful for both general config parsing and meta-tooling.

toml/decode

(toml/decode toml-string) → Sema value

Parse a TOML string into Sema data structures. Tables become maps with keyword keys, arrays become lists, and scalar types map to their native Sema equivalents.

sema
(toml/decode "[package]\nname = \"my-app\"\nversion = \"1.0.0\"")
; => {:package {:name "my-app" :version "1.0.0"}}

Nested Tables

TOML dotted keys and sub-tables are decoded into nested maps:

sema
(toml/decode "
[server]
host = \"localhost\"
port = 8080

[server.tls]
enabled = true
cert = \"/path/to/cert.pem\"
")
; => {:server {:host "localhost" :port 8080 :tls {:enabled true :cert "/path/to/cert.pem"}}}

Arrays and Arrays of Tables

Plain arrays become lists. [[double-bracket]] arrays of tables become lists of maps:

sema
(toml/decode "
colors = [\"red\", \"green\", \"blue\"]

[[fruits]]
name = \"apple\"
color = \"red\"

[[fruits]]
name = \"banana\"
color = \"yellow\"
")
; => {:colors ("red" "green" "blue")
;     :fruits ({:color "red" :name "apple"}
;              {:color "yellow" :name "banana"})}

Inline Tables

Inline tables are decoded identically to standard tables:

sema
(toml/decode "point = { x = 1, y = 2 }")
; => {:point {:x 1 :y 2}}

Datetime Handling

TOML datetime values are converted to strings. This includes offset datetimes, local datetimes, local dates, and local times:

sema
(toml/decode "created = 2024-01-15T10:30:00Z")
; => {:created "2024-01-15T10:30:00Z"}

Error Handling

Invalid TOML throws a SemaError:

sema
(toml/decode "invalid = ")
; => Error: toml/decode: ...

toml/encode

(toml/encode map) → TOML string

Serialize a Sema map to a TOML string. The top-level value must be a map — passing any other type is an error.

sema
(toml/encode {:package {:name "my-app" :version "1.0.0"}})
; => "[package]\nname = \"my-app\"\nversion = \"1.0.0\"\n"

Nested Maps

Nested maps become TOML tables:

sema
(toml/encode {:database {:host "localhost"
                         :port 5432
                         :credentials {:user "admin"
                                       :password "secret"}}})

Error Handling

The top-level value must be a map:

sema
(toml/encode "hello")
; => Error: toml/encode: top-level value must be a map

nil values cannot be encoded (TOML has no null):

sema
(toml/encode {:key nil})
; => Error: toml/encode: cannot encode nil

Non-encodable types like functions and records throw errors:

sema
(toml/encode {:callback println})
; => Error: toml/encode: cannot encode native-fn

Type Mapping

TOML → Sema (decoding)

TOML TypeSema TypeExample
Tablemap (keyword keys){:key "val"}
Arraylist("a" "b" "c")
Stringstring"hello"
Integerint42
Floatfloat3.14
Booleanbool#t / #f
Datetimestring"2024-01-15T10:30:00Z"

Sema → TOML (encoding)

Sema TypeTOML TypeNotes
map / hashmapTableKeys converted via key_to_string
list / vectorArray
stringString
intInteger
floatFloat
boolBoolean
keywordString:foo"foo"
symbolString'foo"foo"
nil❌ ErrorTOML has no null type
function / record❌ ErrorNot representable in TOML

Practical Examples

Reading a Config File

sema
(define config (-> "config.toml" file/read toml/decode))

(println "Server:" (map/get-in config [:server :host])
         ":" (map/get-in config [:server :port]))

Updating Config Values

sema
(define config (-> "config.toml" file/read toml/decode))

;; Update the port and add a new setting
(define updated (-> config
                 (map/assoc-in [:server :port] 9090)
                 (map/assoc-in [:server :debug] true)))

(file/write "config.toml" (toml/encode updated))

Round-Trip

sema
(define config-str "
[server]
host = \"0.0.0.0\"
port = 3000

[server.cors]
origins = [\"https://example.com\"]
")

(define config (toml/decode config-str))
(define new-config (map/assoc-in config [:server :port] 8080))
(toml/encode new-config)

TOML vs JSON

TOMLJSON
Use caseConfiguration filesData interchange
Comments✅ Yes❌ No
Null type❌ Nonull
Date/time✅ Native❌ Strings only
Top-levelMust be a tableAny value
Sema decodetoml/decodejson/decode
Sema encodetoml/encodejson/encode

sema.toml

Sema uses TOML for its own project configuration file (sema.toml). You can read and manipulate it programmatically:

sema
(define project (-> "sema.toml" file/read toml/decode))
(println "Project:" (map/get-in project [:package :name]))