├── .gitattributes
├── .pre-commit-config.yaml
├── .prettierrc.toml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── docs
└── README.md
├── logos
├── toml-100.png
├── toml-200.png
├── toml-400.png
├── toml.ai
└── toml.svg
├── scripts
└── release.py
├── toml.abnf
└── toml.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | toml.abnf text eol=crlf
2 | /scripts/* linguist-detectable=false
3 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/pre-commit/mirrors-prettier
3 | rev: v4.0.0-alpha.8
4 | hooks:
5 | - id: prettier
6 |
7 | - repo: https://github.com/pre-commit/pre-commit-hooks
8 | rev: v5.0.0
9 | hooks:
10 | - id: check-added-large-files
11 | - id: check-case-conflict
12 | - id: end-of-file-fixer
13 | - id: forbid-new-submodules
14 | - id: trailing-whitespace
15 | exclude: .*/toml\.ai
16 | args: [--markdown-linebreak-ext=md]
17 |
--------------------------------------------------------------------------------
/.prettierrc.toml:
--------------------------------------------------------------------------------
1 | proseWrap = "always"
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # TOML Changelog
2 |
3 | ## unreleased
4 |
5 | - Allow newlines and trailing commas in inline tables ([#904]).
6 |
7 | Previously an inline table had to be on a single line and couldn't end with a
8 | trailing comma. This is now relaxed so that the following is valid:
9 |
10 | tbl = {
11 | key = "a string",
12 | moar-tbl = {
13 | key = 1,
14 | },
15 | }
16 |
17 | - Add `\xHH` notation to basic strings for codepoints <255 ([#796]):
18 |
19 | null = "null byte: \x00; letter a: \x61"
20 |
21 | - Add `\e` escape for the escape character ([#790]):
22 |
23 | csi = "\e["
24 |
25 | - Seconds in datetime and time values are now optional ([#894]). The following
26 | are now valid:
27 |
28 | dt = 2010-02-03 14:15
29 | t = 14:15
30 |
31 | - Clarify that comments never affect the tables produced by parsers ([#950]).
32 |
33 | - Clarify Unicode and UTF-8 references ([#929]).
34 |
35 | - Clarify where and how dotted keys define tables ([#859]).
36 |
37 | - Clarify newline normalization in multi-line literal strings ([#842]).
38 |
39 | - Clarify sub-millisecond precision is allowed ([#805]).
40 |
41 | [#790]: https://github.com/toml-lang/toml/pull/790
42 | [#796]: https://github.com/toml-lang/toml/pull/796
43 | [#805]: https://github.com/toml-lang/toml/pull/805
44 | [#842]: https://github.com/toml-lang/toml/pull/842
45 | [#859]: https://github.com/toml-lang/toml/pull/859
46 | [#894]: https://github.com/toml-lang/toml/pull/894
47 | [#904]: https://github.com/toml-lang/toml/pull/904
48 | [#929]: https://github.com/toml-lang/toml/pull/929
49 | [#950]: https://github.com/toml-lang/toml/pull/950
50 |
51 | ## 1.0.0 / 2021-01-11
52 |
53 | - Clarify how tables are created and defined.
54 | - Clarify and describe the top-level table.
55 | - Clarify that indentation before keys is ignored.
56 | - Clarify that indentation before table headers is ignored.
57 | - Clarify that indentation between array values is ignored.
58 |
59 | ## 1.0.0-rc.3 / 2020-10-07
60 |
61 | - Clarify that comments and newlines are allowed before commas in arrays.
62 | - Mark the ABNF as canonical, and reference it from the text specification.
63 |
64 | ## 1.0.0-rc.2 / 2020-08-09
65 |
66 | - Create https://toml.io as the new primary location to read the TOML spec.
67 | - Clarify meaning of "quotation marks".
68 | - Clarify meaning of "expected" value ranges.
69 | - Clarify that EOF is allowed after key/value pair.
70 | - Clarify that the various styles for writing keys are equivalent.
71 | - Clarify that line-ending backslashes must be unescaped in multi-line strings.
72 | - Add examples for invalid float values.
73 |
74 | ## 1.0.0-rc.1 / 2020-04-01
75 |
76 | - Clarify in ABNF how quotes in multi-line basic and multi-line literal strings
77 | are allowed to be used.
78 | - Leading zeroes in exponent parts of floats are permitted.
79 | - Clarify that control characters are not permitted in comments.
80 | - Clarify behavior of tables defined implicitly by dotted keys.
81 | - Clarify that inline tables are immutable.
82 | - Clarify that trailing commas are not allowed in inline tables.
83 | - Clarify in ABNF that UTF-16 surrogate code points (U+D800 - U+DFFF) are not
84 | allowed in strings or comments.
85 | - Allow raw tab characters in basic strings and multi-line basic strings.
86 | - Allow heterogenous values in arrays.
87 |
88 | ## 0.5.0 / 2018-07-11
89 |
90 | - Add dotted keys.
91 | - Add hex, octal, and binary integer formats.
92 | - Add special float values (inf, nan).
93 | - Rename Datetime to Offset Date-Time.
94 | - Add Local Date-Time.
95 | - Add Local Date.
96 | - Add Local Time.
97 | - Add ABNF specification.
98 | - Allow space (instead of T) to separate date and time in Date-Time.
99 | - Allow accidental whitespace between backslash and newline in the line
100 | continuation operator in multi-line basic strings.
101 | - Specify that the standard file extension is `.toml`.
102 | - Specify that MIME type is `application/toml`
103 | - Clarify that U+007F is an escape character.
104 | - Clarify that keys are always strings.
105 | - Clarify that you cannot use array-of-table to append to a static array.
106 | - Clarify that a TOML file must be a valid UTF-8 document.
107 | - Clarify valid Array values.
108 | - Clarify that literal strings can be table keys.
109 | - Clarify that at least millisecond precision expected for Date-Time and Time.
110 | - Clarify that comments are OK in multiline arrays.
111 | - Clarify that +0, -0, +0.0, and -0.0 are valid and what they mean.
112 | - TOML has a logo!
113 |
114 | ## 0.4.0 / 2015-02-12
115 |
116 | - Add Inline Table syntax.
117 | - Allow underscores in numbers.
118 | - Remove forward slash as an escapable character.
119 | - Unicode escapes must be scalar values.
120 | - Newline is now defined as LF or CRLF.
121 |
122 | ## 0.3.1 / 2014-11-11
123 |
124 | - Fix incorrect datetime examples.
125 |
126 | ## 0.3.0 / 2014-11-10
127 |
128 | - Add scientific notation for floats.
129 | - Allow optional + prefix on integers.
130 | - Switch to RFC 3339 for datetimes (allowing offsets and fractional seconds).
131 | - Add multiline and literal strings.
132 | - Clarify what characters valid keys can contain.
133 |
134 | ## 0.2.0 / 2013-09-24
135 |
136 | - Use "table" instead of "key group" terminology.
137 | - Add the ability to define nestable arrays of tables.
138 |
139 | ## 0.1.0 / 2013-03-17
140 |
141 | - From Twitter rage to reality; TOML is now a thing.
142 | - First proper release.
143 | - TOML adheres to the SemVer standard for version numbers.
144 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) Tom Preston-Werner
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # TOML
4 |
5 | Tom's Obvious, Minimal Language.
6 |
7 | By Tom Preston-Werner, Pradyun Gedam, et al.
8 |
9 | > This repository contains the in-development version of the TOML specification.
10 | > You can find the released versions at https://toml.io.
11 |
12 | ## Objectives
13 |
14 | TOML aims to be a minimal configuration file format that's easy to read due to
15 | obvious semantics. TOML is designed to map unambiguously to a hash table. TOML
16 | should be easy to parse into data structures in a wide variety of languages.
17 |
18 | ## Example
19 |
20 | ```toml
21 | # This is a TOML document.
22 |
23 | title = "TOML Example"
24 |
25 | [owner]
26 | name = "Tom Preston-Werner"
27 | dob = 1979-05-27T07:32:00-08:00 # First class dates
28 |
29 | [database]
30 | server = "192.168.1.1"
31 | ports = [ 8000, 8001, 8002 ]
32 | connection_max = 5000
33 | enabled = true
34 |
35 | [servers]
36 |
37 | # Indentation (tabs and/or spaces) is allowed but not required
38 | [servers.alpha]
39 | ip = "10.0.0.1"
40 | dc = "eqdc10"
41 |
42 | [servers.beta]
43 | ip = "10.0.0.2"
44 | dc = "eqdc10"
45 |
46 | [clients]
47 | data = [ ["gamma", "delta"], [1, 2] ]
48 |
49 | # Line breaks are OK when inside arrays
50 | hosts = [
51 | "alpha",
52 | "omega"
53 | ]
54 | ```
55 |
56 | ## Comparison with Other Formats
57 |
58 | TOML shares traits with other file formats used for application configuration
59 | and data serialization, such as YAML and JSON. TOML and JSON both are simple and
60 | use ubiquitous data types, making them easy to code for or parse with machines.
61 | TOML and YAML both emphasize human readability features, like comments that make
62 | it easier to understand the purpose of a given line. TOML differs in combining
63 | these, allowing comments (unlike JSON) but preserving simplicity (unlike YAML).
64 |
65 | Because TOML is explicitly intended as a configuration file format, parsing it
66 | is easy, but it is not intended for serializing arbitrary data structures. TOML
67 | always has a hash table at the top level of the file, which can easily have data
68 | nested inside its keys, but it doesn't permit top-level arrays or floats, so it
69 | cannot directly serialize some data. There is also no standard identifying the
70 | start or end of a TOML file, which can complicate sending it through a stream.
71 | These details must be negotiated on the application layer.
72 |
73 | INI files are frequently compared to TOML for their similarities in syntax and
74 | use as configuration files. However, there is no standardized format for INI and
75 | they do not gracefully handle more than one or two levels of nesting.
76 |
77 | Further reading:
78 |
79 | - YAML spec: https://yaml.org/spec/1.2/spec.html
80 | - JSON spec: https://tools.ietf.org/html/rfc8259
81 | - Wikipedia on INI files: https://en.wikipedia.org/wiki/INI_file
82 |
83 | ## Get Involved
84 |
85 | Documentation, bug reports, pull requests, and all other contributions are
86 | welcome!
87 |
88 | ## Wiki
89 |
90 | We have an [Official TOML Wiki](https://github.com/toml-lang/toml/wiki) that
91 | catalogs the following:
92 |
93 | - Projects using TOML
94 | - Implementations
95 | - Validators
96 | - Language-agnostic test suite for TOML decoders and encoders
97 | - Editor support
98 | - Encoders
99 | - Converters
100 |
101 | Please take a look if you'd like to view or add to that list. Thanks for being a
102 | part of the TOML community!
103 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Maintainer documentation
2 |
3 | This section of the repository contains documentation for TOML's maintainers.
4 |
5 | ## Release Process
6 |
7 | - Set up local repos of [toml-lang/toml] and [toml-lang/toml.io], as described
8 | in [scripts/release.py](../scripts/release.py).
9 | - In the root of the toml-lang/toml clone, run
10 | `python3.8 scripts/release.py `.
11 | - Done! ✨
12 |
13 | [toml-lang/toml]: https://github.com/toml-lang/toml
14 | [toml-lang/toml.io]: https://github.com/toml-lang/toml.io
15 |
--------------------------------------------------------------------------------
/logos/toml-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toml-lang/toml/d0c77ee8a76f506c8fddbec6d5eef282f96efa90/logos/toml-100.png
--------------------------------------------------------------------------------
/logos/toml-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toml-lang/toml/d0c77ee8a76f506c8fddbec6d5eef282f96efa90/logos/toml-200.png
--------------------------------------------------------------------------------
/logos/toml-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toml-lang/toml/d0c77ee8a76f506c8fddbec6d5eef282f96efa90/logos/toml-400.png
--------------------------------------------------------------------------------
/logos/toml.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toml-lang/toml/d0c77ee8a76f506c8fddbec6d5eef282f96efa90/logos/toml.ai
--------------------------------------------------------------------------------
/logos/toml.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
22 |
--------------------------------------------------------------------------------
/scripts/release.py:
--------------------------------------------------------------------------------
1 | """Release Automation!
2 |
3 | Baked assumptions:
4 |
5 | - CWD is the root of the toml repository (this one)
6 | - The toml.io repository is available at "../toml.io"
7 | - Changelog file:
8 | - is `CHANGELOG.md`
9 | - has a "## unreleased" heading line.
10 | - Markdown file:
11 | - is `toml.md`
12 | - goes to `specs/en/v{version}` in the website repo
13 | - lines "TOML" and "====" are the main heading.
14 | - ABNF file:
15 | - is `toml.abnf`
16 |
17 | Checked assumptions:
18 |
19 | - Both this and the toml.io repository have an "upstream" remote
20 | - "upstream" remotes point to "github.com/toml-lang/{repo}"
21 | - Current branch is the default branch
22 | - Current branch is up to date with remote
23 | - Working directory is clean
24 |
25 | """
26 |
27 | import fileinput
28 | import os
29 | import re
30 | import shutil
31 | import subprocess
32 | import sys
33 | import tempfile
34 | import textwrap
35 | from contextlib import contextmanager
36 | from datetime import datetime
37 | from pathlib import Path
38 | from typing import Callable, List, Tuple
39 |
40 | # Copied from semver.org and broken up for readability + line length.
41 | SEMVER_REGEX = re.compile(
42 | r"""
43 | ^
44 | (?P0|[1-9]\d*)
45 | \.
46 | (?P0|[1-9]\d*)
47 | \.
48 | (?P0|[1-9]\d*)
49 | (?:
50 | -
51 | (?P
52 | (?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)
53 | (?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*
54 | )
55 | )?
56 | (?:
57 | \+
58 | (?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*)
59 | )?
60 | $
61 | """,
62 | re.VERBOSE,
63 | )
64 |
65 |
66 | #
67 | # Helpers
68 | #
69 | @contextmanager
70 | def task(message: str):
71 | """A little thing to allow for nicer code organization."""
72 | global _INDENT
73 |
74 | log(f"{message}...")
75 | _INDENT += 1
76 | try:
77 | yield
78 | except AssertionError as e:
79 | log(f"ERROR: {e}", error=True)
80 | sys.exit(1)
81 | finally:
82 | _INDENT -= 1
83 |
84 |
85 | def log(message: str, *, error=False) -> None:
86 | output = textwrap.indent(message, " " * _INDENT)
87 |
88 | if error:
89 | file = sys.stderr
90 | # A dash of red
91 | if sys.stdout.isatty():
92 | output = f"\033[31m{output}\033[0m"
93 | else:
94 | file = sys.stdout
95 |
96 | print(output, file=file)
97 |
98 |
99 | _INDENT = 0
100 |
101 |
102 | def run(*args, cwd: Path):
103 | """Runs a command, while also pretty-printing it."""
104 | result = subprocess.run(args, cwd=cwd, capture_output=True)
105 | if result.returncode == 0:
106 | return result.stdout.decode().rstrip("\n")
107 |
108 | # Print information about the failed command.
109 | log(" ".join(["$", *args]))
110 | log(" stdout ".center(80, "-"))
111 | log(result.stdout.decode() or "")
112 | log(" stderr ".center(80, "-"))
113 | log(result.stderr.decode() or "")
114 |
115 | assert False, f"Exited with non-zero exit code: {result.returncode}"
116 |
117 |
118 | @contextmanager
119 | def replacement_file(path: Path):
120 | fh, tmp_path = tempfile.mkstemp()
121 | with os.fdopen(fh, "w") as dest:
122 | with path.open() as source:
123 | yield source, dest
124 |
125 | # Replace current file with rewritten file
126 | shutil.copymode(path, tmp_path)
127 | path.unlink()
128 | shutil.move(tmp_path, path)
129 |
130 |
131 | def matches(line):
132 | return (line + "\n").__eq__
133 |
134 |
135 | def contains(fragment):
136 | def marker(line):
137 | return fragment in line
138 |
139 | return marker
140 |
141 |
142 | def change_line(path: Path, *, marker: Callable[[str], bool], to: List[str]) -> None:
143 | with replacement_file(path) as (source, dest):
144 | for line in source:
145 | # not-to-be-replaced lines
146 | if not marker(line):
147 | dest.write(line)
148 | continue
149 | # replacement lines
150 | for replacement in to:
151 | dest.write(replacement + "\n")
152 |
153 |
154 | def git_commit(message: str, *, files: List[str], repo: Path):
155 | run("git", "add", *files, cwd=repo)
156 | run("git", "commit", "-m", message, cwd=repo)
157 |
158 |
159 | #
160 | # Actual automation
161 | #
162 | def get_version() -> str:
163 | assert len(sys.argv) == 2, "Got wrong number of arguments, expected 1."
164 |
165 | version = sys.argv[1]
166 |
167 | match = SEMVER_REGEX.match(version)
168 | assert match is not None, "Given version is not a valid semver."
169 | assert not match.group("buildmetadata"), "Shouldn't have build metadata in version."
170 |
171 | return version
172 |
173 |
174 | def check_repo_state(repo: Path, *, name: str):
175 | # Check upstream remote is configured correctly
176 | upstream = run("git", "config", "--get", "remote.upstream.url", cwd=repo)
177 | assert (
178 | upstream == f"git@github.com:toml-lang/{name}.git"
179 | ), f"Got incorrect upstream repo: {upstream}"
180 |
181 | # Check current branch is correct
182 | current_branch = run("git", "branch", "--show-current", cwd=repo)
183 | assert current_branch in ("main", "master"), current_branch
184 |
185 | # Check working directory is clean
186 | working_directory_state = run("git", "status", "--porcelain", cwd=repo)
187 | assert (
188 | working_directory_state == ""
189 | ), f"Dirty working directory\n{working_directory_state}"
190 |
191 | # Check up-to-date with remote
192 | with task("Checking against remote"):
193 | run("git", "remote", "update", "upstream", cwd=repo)
194 |
195 | deviation = run(
196 | "git",
197 | "rev-list",
198 | f"{current_branch}..upstream/{current_branch}",
199 | "--left-right",
200 | cwd=repo,
201 | )
202 | assert not deviation, f"Local branch deviates from upstream\n{deviation}"
203 |
204 |
205 | def get_repositories() -> Tuple[Path, Path]:
206 | spec_repo = Path(".").resolve()
207 | website_repo = spec_repo.parent / "toml.io"
208 |
209 | with task("Checking repositories"):
210 | with task("toml"):
211 | check_repo_state(spec_repo, name="toml")
212 | with task("toml.io"):
213 | check_repo_state(website_repo, name="toml.io")
214 |
215 | return website_repo, spec_repo
216 |
217 |
218 | def prepare_release(version: str, spec_repo: Path, website_repo: Path) -> None:
219 | # Make "backup" tags
220 | backup_tag = "backup/{now}".format(now=str(int(datetime.now().timestamp())))
221 | run("git", "tag", "-m", "backup", backup_tag, cwd=spec_repo)
222 | run("git", "tag", "-m", "backup", backup_tag, cwd=website_repo)
223 |
224 | date = datetime.today().strftime("%Y-%m-%d")
225 | release_heading = f"## {version} / {date}"
226 | release_message = f"Release v{version}"
227 |
228 | with task("Updating changelog for release"):
229 | unreleased_heading = "## unreleased"
230 | changelog = spec_repo / "CHANGELOG.md"
231 |
232 | change_line(changelog, marker=matches(unreleased_heading), to=[release_heading])
233 | git_commit(release_message, files=[str(changelog)], repo=spec_repo)
234 |
235 | with task("Creating release tag"):
236 | run("git", "tag", "-m", release_message, version, cwd=spec_repo)
237 |
238 | with task("Updating changelog for development"):
239 | change_line(
240 | changelog,
241 | marker=matches(release_heading),
242 | to=[unreleased_heading, "", "Nothing.", "", release_heading],
243 | )
244 | git_commit("Bump for development", files=[str(changelog)], repo=spec_repo)
245 |
246 | with task("Copy to website"):
247 | source_md = spec_repo / "toml.md"
248 | destination_md = website_repo / "specs" / "en" / f"v{version}.md"
249 |
250 | shutil.copyfile(source_md, destination_md)
251 |
252 | new_heading = f"TOML v{version}"
253 | new_abnf_link = f"https://github.com/toml-lang/toml/blob/{version}/toml.abnf"
254 |
255 | with task("Updating spec for release"):
256 | change_line(destination_md, marker=matches("TOML"), to=[new_heading])
257 | change_line(destination_md, marker=matches("===="), to=["=" * len(new_heading)])
258 | change_line(
259 | destination_md,
260 | marker=matches("[abnf]: ./toml.abnf"),
261 | to=[f"[abnf]: {new_abnf_link}"],
262 | )
263 |
264 | netlify_toml = website_repo / "netlify.toml"
265 |
266 | with task("Update latest redirect"):
267 | marker = "RELEASE AUTOMATION MARKER: version"
268 | new_line = f' to = "/en/v{version}" # {marker}'
269 |
270 | change_line(netlify_toml, marker=contains(marker), to=[new_line])
271 |
272 | with task("Commit new version"):
273 | git_commit(
274 | release_message,
275 | files=[str(destination_md), str(netlify_toml)],
276 | repo=website_repo,
277 | )
278 |
279 |
280 | def push_release(version: str, spec_repo: Path, website_repo: Path) -> None:
281 | print("Publishing changes...")
282 | with task("specs repository"):
283 | run("git", "push", "origin", "HEAD", version, cwd=spec_repo)
284 | run("git", "push", "upstream", "HEAD", version, cwd=spec_repo)
285 |
286 | with task("website repository"):
287 | run("git", "push", "origin", "HEAD", cwd=website_repo)
288 | run("git", "push", "upstream", "HEAD", cwd=website_repo)
289 |
290 |
291 | def main() -> None:
292 | version = get_version()
293 | website_repo, spec_repo = get_repositories()
294 |
295 | with task("Preparing release"):
296 | prepare_release(version, spec_repo, website_repo)
297 |
298 | input("Press enter when ready.") # a chance to stop/pause before publishing
299 |
300 | with task("Publishing release"):
301 | push_release(version, spec_repo, website_repo)
302 |
303 |
304 | if __name__ == "__main__":
305 | main()
306 |
--------------------------------------------------------------------------------
/toml.abnf:
--------------------------------------------------------------------------------
1 | ;; This document describes TOML's syntax, using the ABNF format (defined in
2 | ;; RFC 5234 -- https://www.ietf.org/rfc/rfc5234.txt).
3 | ;;
4 | ;; Although a TOML document must be valid UTF-8, this grammar refers to the
5 | ;; Unicode Code Points you get after you decode the UTF-8 input.
6 | ;;
7 | ;; All valid TOML documents will match this description, however certain
8 | ;; invalid documents would need to be rejected as per the semantics described
9 | ;; in the supporting text description.
10 |
11 | ;; It is possible to try this grammar interactively, using instaparse.
12 | ;; http://instaparse.mojombo.com/
13 | ;;
14 | ;; To do so, in the lower right, click on Options and change `:input-format` to
15 | ;; ':abnf'. Then paste this entire ABNF document into the grammar entry box
16 | ;; (above the options). Then you can type or paste a sample TOML document into
17 | ;; the beige box on the left. Tada!
18 |
19 | ;; Overall Structure
20 |
21 | toml = expression *( newline expression )
22 |
23 | expression = ws [ comment ]
24 | expression =/ ws keyval ws [ comment ]
25 | expression =/ ws table ws [ comment ]
26 |
27 | ;; Whitespace
28 |
29 | ws = *wschar
30 | wschar = %x20 ; Space
31 | wschar =/ %x09 ; Horizontal tab
32 |
33 | ;; Newline
34 |
35 | newline = %x0A ; LF
36 | newline =/ %x0D.0A ; CRLF
37 |
38 | ;; Comment
39 |
40 | comment-start-symbol = %x23 ; #
41 | non-ascii = %x80-D7FF / %xE000-10FFFF
42 | non-eol = %x09 / %x20-7E / non-ascii
43 |
44 | comment = comment-start-symbol *non-eol
45 |
46 | ;; Key-Value pairs
47 |
48 | keyval = key keyval-sep val
49 | key = simple-key / dotted-key
50 | val = string / boolean / array / inline-table / date-time / float / integer
51 |
52 | simple-key = quoted-key / unquoted-key
53 | unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _
54 |
55 | ;; Quoted and dotted key
56 |
57 | quoted-key = basic-string / literal-string
58 | dotted-key = simple-key 1*( dot-sep simple-key )
59 |
60 | dot-sep = ws %x2E ws ; . Period
61 | keyval-sep = ws %x3D ws ; =
62 |
63 | ;; String
64 |
65 | string = ml-basic-string / basic-string / ml-literal-string / literal-string
66 |
67 | ;; Basic String
68 |
69 | basic-string = quotation-mark *basic-char quotation-mark
70 |
71 | quotation-mark = %x22 ; "
72 |
73 | basic-char = basic-unescaped / escaped
74 | basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii
75 | escaped = escape escape-seq-char
76 |
77 | escape = %x5C ; \
78 | escape-seq-char = %x22 ; " quotation mark U+0022
79 | escape-seq-char =/ %x5C ; \ reverse solidus U+005C
80 | escape-seq-char =/ %x62 ; b backspace U+0008
81 | escape-seq-char =/ %x65 ; e escape U+001B
82 | escape-seq-char =/ %x66 ; f form feed U+000C
83 | escape-seq-char =/ %x6E ; n line feed U+000A
84 | escape-seq-char =/ %x72 ; r carriage return U+000D
85 | escape-seq-char =/ %x74 ; t tab U+0009
86 | escape-seq-char =/ %x78 2HEXDIG ; xHH U+00HH
87 | escape-seq-char =/ %x75 4HEXDIG ; uHHHH U+HHHH
88 | escape-seq-char =/ %x55 8HEXDIG ; UHHHHHHHH U+HHHHHHHH
89 |
90 | ;; Multiline Basic String
91 |
92 | ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body
93 | ml-basic-string-delim
94 | ml-basic-string-delim = 3quotation-mark
95 | ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ]
96 |
97 | mlb-content = basic-char / newline / mlb-escaped-nl
98 | mlb-quotes = 1*2quotation-mark
99 | mlb-escaped-nl = escape ws newline *( wschar / newline )
100 |
101 | ;; Literal String
102 |
103 | literal-string = apostrophe *literal-char apostrophe
104 |
105 | apostrophe = %x27 ; ' apostrophe
106 |
107 | literal-char = %x09 / %x20-26 / %x28-7E / non-ascii
108 |
109 | ;; Multiline Literal String
110 |
111 | ml-literal-string = ml-literal-string-delim [ newline ] ml-literal-body
112 | ml-literal-string-delim
113 | ml-literal-string-delim = 3apostrophe
114 | ml-literal-body = *mll-content *( mll-quotes 1*mll-content ) [ mll-quotes ]
115 |
116 | mll-content = literal-char / newline
117 | mll-quotes = 1*2apostrophe
118 |
119 | ;; Integer
120 |
121 | integer = dec-int / hex-int / oct-int / bin-int
122 |
123 | minus = %x2D ; -
124 | plus = %x2B ; +
125 | underscore = %x5F ; _
126 | digit1-9 = %x31-39 ; 1-9
127 | digit0-7 = %x30-37 ; 0-7
128 | digit0-1 = %x30-31 ; 0-1
129 |
130 | hex-prefix = %x30.78 ; 0x
131 | oct-prefix = %x30.6F ; 0o
132 | bin-prefix = %x30.62 ; 0b
133 |
134 | dec-int = [ minus / plus ] unsigned-dec-int
135 | unsigned-dec-int = DIGIT / digit1-9 1*( DIGIT / underscore DIGIT )
136 |
137 | hex-int = hex-prefix HEXDIG *( HEXDIG / underscore HEXDIG )
138 | oct-int = oct-prefix digit0-7 *( digit0-7 / underscore digit0-7 )
139 | bin-int = bin-prefix digit0-1 *( digit0-1 / underscore digit0-1 )
140 |
141 | ;; Float
142 |
143 | float = float-int-part ( exp / frac [ exp ] )
144 | float =/ special-float
145 |
146 | float-int-part = dec-int
147 | frac = decimal-point zero-prefixable-int
148 | decimal-point = %x2E ; .
149 | zero-prefixable-int = DIGIT *( DIGIT / underscore DIGIT )
150 |
151 | exp = "e" float-exp-part
152 | float-exp-part = [ minus / plus ] zero-prefixable-int
153 |
154 | special-float = [ minus / plus ] ( inf / nan )
155 | inf = %x69.6E.66 ; inf
156 | nan = %x6E.61.6E ; nan
157 |
158 | ;; Boolean
159 |
160 | boolean = true / false
161 |
162 | true = %x74.72.75.65 ; true
163 | false = %x66.61.6C.73.65 ; false
164 |
165 | ;; Date and Time (as defined in RFC 3339)
166 |
167 | date-time = offset-date-time / local-date-time / local-date / local-time
168 |
169 | date-fullyear = 4DIGIT
170 | date-month = 2DIGIT ; 01-12
171 | date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year
172 | time-delim = "T" / %x20 ; T, t, or space
173 | time-hour = 2DIGIT ; 00-23
174 | time-minute = 2DIGIT ; 00-59
175 | time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second rules
176 | time-secfrac = "." 1*DIGIT
177 | time-numoffset = ( "+" / "-" ) time-hour ":" time-minute
178 | time-offset = "Z" / time-numoffset
179 |
180 | partial-time = time-hour ":" time-minute [ ":" time-second [ time-secfrac ] ]
181 | full-date = date-fullyear "-" date-month "-" date-mday
182 | full-time = partial-time time-offset
183 |
184 | ;; Offset Date-Time
185 |
186 | offset-date-time = full-date time-delim full-time
187 |
188 | ;; Local Date-Time
189 |
190 | local-date-time = full-date time-delim partial-time
191 |
192 | ;; Local Date
193 |
194 | local-date = full-date
195 |
196 | ;; Local Time
197 |
198 | local-time = partial-time
199 |
200 | ;; Array
201 |
202 | array = array-open [ array-values ] ws-comment-newline array-close
203 |
204 | array-open = %x5B ; [
205 | array-close = %x5D ; ]
206 |
207 | array-values = ws-comment-newline val ws-comment-newline array-sep array-values
208 | array-values =/ ws-comment-newline val ws-comment-newline [ array-sep ]
209 |
210 | array-sep = %x2C ; , Comma
211 |
212 | ws-comment-newline = *( wschar / [ comment ] newline )
213 |
214 | ;; Table
215 |
216 | table = std-table / array-table
217 |
218 | ;; Standard Table
219 |
220 | std-table = std-table-open key std-table-close
221 |
222 | std-table-open = %x5B ws ; [ Left square bracket
223 | std-table-close = ws %x5D ; ] Right square bracket
224 |
225 | ;; Inline Table
226 |
227 | inline-table = inline-table-open [ inline-table-keyvals ] ws-comment-newline inline-table-close
228 |
229 | inline-table-open = %x7B ; {
230 | inline-table-close = %x7D ; }
231 | inline-table-sep = %x2C ; , Comma
232 |
233 | inline-table-keyvals = ws-comment-newline keyval ws-comment-newline inline-table-sep inline-table-keyvals
234 | inline-table-keyvals =/ ws-comment-newline keyval ws-comment-newline [ inline-table-sep ]
235 |
236 | ;; Array Table
237 |
238 | array-table = array-table-open key array-table-close
239 |
240 | array-table-open = %x5B.5B ws ; [[ Double left square bracket
241 | array-table-close = ws %x5D.5D ; ]] Double right square bracket
242 |
243 | ;; Built-in ABNF terms, reproduced here for clarity
244 |
245 | ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
246 | DIGIT = %x30-39 ; 0-9
247 | HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
248 |
--------------------------------------------------------------------------------
/toml.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # TOML
4 |
5 | Tom's Obvious, Minimal Language.
6 |
7 | By Tom Preston-Werner, Pradyun Gedam, et al.
8 |
9 | ## Objectives
10 |
11 | TOML aims to be a minimal configuration file format that's easy to read due to
12 | obvious semantics. TOML is designed to map unambiguously to a hash table. TOML
13 | should be easy to parse into data structures in a wide variety of languages.
14 |
15 | ## Table of contents
16 |
17 | - [Preliminaries](#user-content-preliminaries)
18 | - [Comment](#user-content-comment)
19 | - [Key/Value Pair](#user-content-keyvalue-pair)
20 | - [Keys](#user-content-keys)
21 | - [String](#user-content-string)
22 | - [Integer](#user-content-integer)
23 | - [Float](#user-content-float)
24 | - [Boolean](#user-content-boolean)
25 | - [Offset Date-Time](#user-content-offset-date-time)
26 | - [Local Date-Time](#user-content-local-date-time)
27 | - [Local Date](#user-content-local-date)
28 | - [Local Time](#user-content-local-time)
29 | - [Array](#user-content-array)
30 | - [Table](#user-content-table)
31 | - [Inline Table](#user-content-inline-table)
32 | - [Array of Tables](#user-content-array-of-tables)
33 | - [Filename Extension](#user-content-filename-extension)
34 | - [MIME Type](#user-content-mime-type)
35 | - [ABNF Grammar](#user-content-abnf-grammar)
36 |
37 | ## Preliminaries
38 |
39 | - TOML is case-sensitive.
40 | - Whitespace means tab (U+0009) or space (U+0020).
41 | - Newline means LF (U+000A) or CRLF (U+000D U+000A).
42 | - A TOML file must be a valid UTF-8 encoded Unicode document.
43 |
44 | Specifically this means that a file _as a whole_ must form a
45 | [well-formed code-unit sequence](https://unicode.org/glossary/#well_formed_code_unit_sequence).
46 | Otherwise, it must be rejected (preferably) or have ill-formed byte sequences
47 | replaced with U+FFFD, as per the Unicode specification.
48 |
49 | ## Comment
50 |
51 | A hash symbol marks the rest of the line as a comment, except when inside a
52 | string.
53 |
54 | ```toml
55 | # This is a full-line comment
56 | key = "value" # This is a comment at the end of a line
57 | another = "# This is not a comment"
58 | ```
59 |
60 | Control characters other than tab (U+0000 to U+0008, U+000A to U+001F, U+007F)
61 | are not permitted in comments.
62 |
63 | Comments should be used to communicate between the human readers of a file.
64 | Parsers must not modify keys or values, based on the presence (or contents) of a
65 | comment.
66 |
67 | ## Key/Value Pair
68 |
69 | The primary building block of a TOML document is the key/value pair.
70 |
71 | Keys are on the left of the equals sign and values are on the right. Whitespace
72 | is ignored around key names and values. The key, equals sign, and value must be
73 | on the same line (though some values can be broken over multiple lines).
74 |
75 | ```toml
76 | key = "value"
77 | ```
78 |
79 | Values must have one of the following types.
80 |
81 | - [String](#user-content-string)
82 | - [Integer](#user-content-integer)
83 | - [Float](#user-content-float)
84 | - [Boolean](#user-content-boolean)
85 | - [Offset Date-Time](#user-content-offset-date-time)
86 | - [Local Date-Time](#user-content-local-date-time)
87 | - [Local Date](#user-content-local-date)
88 | - [Local Time](#user-content-local-time)
89 | - [Array](#user-content-array)
90 | - [Inline Table](#user-content-inline-table)
91 |
92 | Unspecified values are invalid.
93 |
94 | ```toml
95 | key = # INVALID
96 | ```
97 |
98 | There must be a newline (or EOF) after a key/value pair. (See
99 | [Inline Table](#user-content-inline-table) for exceptions.)
100 |
101 | ```
102 | first = "Tom" last = "Preston-Werner" # INVALID
103 | ```
104 |
105 | ## Keys
106 |
107 | A key may be either bare, quoted, or dotted.
108 |
109 | **Bare keys** may only contain ASCII letters, ASCII digits, underscores, and
110 | dashes (`A-Za-z0-9_-`). Note that bare keys are allowed to be composed of only
111 | ASCII digits, e.g. `1234`, but are always interpreted as strings.
112 |
113 | ℹ️ The exact ranges of allowed code points can be found in the
114 | [ABNF grammar file][abnf].
115 |
116 | ```toml
117 | key = "value"
118 | bare_key = "value"
119 | bare-key = "value"
120 | 1234 = "value"
121 | ```
122 |
123 | **Quoted keys** follow the exact same rules as either basic strings or literal
124 | strings and allow you to use a much broader set of key names. Best practice is
125 | to use bare keys except when absolutely necessary.
126 |
127 | ```toml
128 | "127.0.0.1" = "value"
129 | "character encoding" = "value"
130 | "ʎǝʞ" = "value"
131 | 'key2' = "value"
132 | 'quoted "value"' = "value"
133 | ```
134 |
135 | A bare key must be non-empty, but an empty quoted key is allowed (though
136 | discouraged). You cannot use multi-line strings to define quoted keys.
137 |
138 | ```toml
139 | = "no key name" # INVALID
140 | """key""" = "not allowed" # INVALID
141 | "" = "blank" # VALID but discouraged
142 | '' = 'blank' # VALID but discouraged
143 | ```
144 |
145 | **Dotted keys** are a sequence of bare or quoted keys joined with a dot. This
146 | allows for grouping similar properties together:
147 |
148 | ```toml
149 | name = "Orange"
150 | physical.color = "orange"
151 | physical.shape = "round"
152 | site."google.com" = true
153 | ```
154 |
155 | In JSON land, that would give you the following structure:
156 |
157 | ```json
158 | {
159 | "name": "Orange",
160 | "physical": {
161 | "color": "orange",
162 | "shape": "round"
163 | },
164 | "site": {
165 | "google.com": true
166 | }
167 | }
168 | ```
169 |
170 | For details regarding the tables that dotted keys define, refer to the
171 | [Table](#user-content-table) section below.
172 |
173 | Whitespace around dot-separated parts is ignored. However, best practice is to
174 | not use any extraneous whitespace.
175 |
176 | ```toml
177 | fruit.name = "banana" # this is best practice
178 | fruit. color = "yellow" # same as fruit.color
179 | fruit . flavor = "banana" # same as fruit.flavor
180 | ```
181 |
182 | Indentation is treated as whitespace and ignored.
183 |
184 | Defining a key multiple times is invalid.
185 |
186 | ```
187 | # DO NOT DO THIS
188 | name = "Tom"
189 | name = "Pradyun"
190 | ```
191 |
192 | Note that bare keys and quoted keys are equivalent:
193 |
194 | ```
195 | # THIS WILL NOT WORK
196 | spelling = "favorite"
197 | "spelling" = "favourite"
198 | ```
199 |
200 | As long as a key hasn't been directly defined, you may still write to it and to
201 | names within it.
202 |
203 | ```toml
204 | # This makes the key "fruit" into a table.
205 | fruit.apple.smooth = true
206 |
207 | # So then you can add to the table "fruit" like so:
208 | fruit.orange = 2
209 | ```
210 |
211 | ```
212 | # THE FOLLOWING IS INVALID
213 |
214 | # This defines the value of fruit.apple to be an integer.
215 | fruit.apple = 1
216 |
217 | # But then this treats fruit.apple like it's a table.
218 | # You can't turn an integer into a table.
219 | fruit.apple.smooth = true
220 | ```
221 |
222 | Defining dotted keys out-of-order is discouraged.
223 |
224 | ```toml
225 | # VALID BUT DISCOURAGED
226 |
227 | apple.type = "fruit"
228 | orange.type = "fruit"
229 |
230 | apple.skin = "thin"
231 | orange.skin = "thick"
232 |
233 | apple.color = "red"
234 | orange.color = "orange"
235 | ```
236 |
237 | ```toml
238 | # RECOMMENDED
239 |
240 | apple.type = "fruit"
241 | apple.skin = "thin"
242 | apple.color = "red"
243 |
244 | orange.type = "fruit"
245 | orange.skin = "thick"
246 | orange.color = "orange"
247 | ```
248 |
249 | Since bare keys can be composed of only ASCII integers, it is possible to write
250 | dotted keys that look like floats but are 2-part dotted keys. Don't do this
251 | unless you have a good reason to (you probably don't).
252 |
253 | ```toml
254 | 3.14159 = "pi"
255 | ```
256 |
257 | The above TOML maps to the following JSON.
258 |
259 | ```json
260 | { "3": { "14159": "pi" } }
261 | ```
262 |
263 | ## String
264 |
265 | There are four ways to express strings: basic, multi-line basic, literal, and
266 | multi-line literal. All strings must contain only Unicode characters.
267 |
268 | **Basic strings** are surrounded by quotation marks (`"`). Any Unicode character
269 | may be used except those that must be escaped: quotation mark, backslash, and
270 | the control characters other than tab (U+0000 to U+0008, U+000A to U+001F,
271 | U+007F).
272 |
273 | ```toml
274 | str = "I'm a string. \"You can quote me\". Name\tJos\xE9\nLocation\tSF."
275 | ```
276 |
277 | For convenience, some popular characters have a compact escape sequence.
278 |
279 | ```
280 | \b - backspace (U+0008)
281 | \t - tab (U+0009)
282 | \n - linefeed (U+000A)
283 | \f - form feed (U+000C)
284 | \r - carriage return (U+000D)
285 | \e - escape (U+001B)
286 | \" - quote (U+0022)
287 | \\ - backslash (U+005C)
288 | \xHH - unicode (U+00HH)
289 | \uHHHH - unicode (U+HHHH)
290 | \UHHHHHHHH - unicode (U+HHHHHHHH)
291 | ```
292 |
293 | Any Unicode character may be escaped with the `\xHH`, `\uHHHH`, or `\UHHHHHHHH`
294 | forms. The escape codes must be Unicode
295 | [scalar values](https://unicode.org/glossary/#unicode_scalar_value).
296 |
297 | Keep in mind that all TOML strings are sequences of Unicode characters, _not_
298 | byte sequences. For binary data, avoid using these escape codes. Instead,
299 | external binary-to-text encoding strategies, like hexadecimal sequences or
300 | [Base64](https://www.base64decode.org/), are recommended for converting between
301 | bytes and strings.
302 |
303 | All other escape sequences not listed above are reserved; if they are used, TOML
304 | should produce an error.
305 |
306 | Sometimes you need to express passages of text (e.g. translation files) or would
307 | like to break up a very long string into multiple lines. TOML makes this easy.
308 |
309 | **Multi-line basic strings** are surrounded by three quotation marks on each
310 | side and allow newlines. A newline immediately following the opening delimiter
311 | will be trimmed. All other whitespace and newline characters remain intact.
312 |
313 | ```toml
314 | str1 = """
315 | Roses are red
316 | Violets are blue"""
317 | ```
318 |
319 | TOML parsers should feel free to normalize newline to whatever makes sense for
320 | their platform.
321 |
322 | ```toml
323 | # On a Unix system, the above multi-line string will most likely be the same as:
324 | str2 = "Roses are red\nViolets are blue"
325 |
326 | # On a Windows system, it will most likely be equivalent to:
327 | str3 = "Roses are red\r\nViolets are blue"
328 | ```
329 |
330 | For writing long strings without introducing extraneous whitespace, use a "line
331 | ending backslash". When the last non-whitespace character on a line is an
332 | unescaped `\`, it will be trimmed along with all whitespace (including newlines)
333 | up to the next non-whitespace character or closing delimiter. All of the escape
334 | sequences that are valid for basic strings are also valid for multi-line basic
335 | strings.
336 |
337 | ```toml
338 | # The following strings are byte-for-byte equivalent:
339 | str1 = "The quick brown fox jumps over the lazy dog."
340 |
341 | str2 = """
342 | The quick brown \
343 |
344 |
345 | fox jumps over \
346 | the lazy dog."""
347 |
348 | str3 = """\
349 | The quick brown \
350 | fox jumps over \
351 | the lazy dog.\
352 | """
353 | ```
354 |
355 | Any Unicode character may be used except those that must be escaped: backslash
356 | and the control characters other than tab, line feed, and carriage return
357 | (U+0000 to U+0008, U+000B, U+000C, U+000E to U+001F, U+007F). Carriage returns
358 | (U+000D) are only allowed as part of a newline sequence.
359 |
360 | You can write a quotation mark, or two adjacent quotation marks, anywhere inside
361 | a multi-line basic string. They can also be written just inside the delimiters.
362 |
363 | ```toml
364 | str4 = """Here are two quotation marks: "". Simple enough."""
365 | # str5 = """Here are three quotation marks: """.""" # INVALID
366 | str5 = """Here are three quotation marks: ""\"."""
367 | str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
368 |
369 | # "This," she said, "is just a pointless statement."
370 | str7 = """"This," she said, "is just a pointless statement.""""
371 | ```
372 |
373 | If you're a frequent specifier of Windows paths or regular expressions, then
374 | having to escape backslashes quickly becomes tedious and error-prone. To help,
375 | TOML supports literal strings which do not allow escaping at all.
376 |
377 | **Literal strings** are surrounded by single quotes. Like basic strings, they
378 | must appear on a single line:
379 |
380 | ```toml
381 | # What you see is what you get.
382 | winpath = 'C:\Users\nodejs\templates'
383 | winpath2 = '\\ServerX\admin$\system32\'
384 | quoted = 'Tom "Dubs" Preston-Werner'
385 | regex = '<\i\c*\s*>'
386 | ```
387 |
388 | Since there is no escaping, there is no way to write a single quote inside a
389 | literal string enclosed by single quotes. Luckily, TOML supports a multi-line
390 | version of literal strings that solves this problem.
391 |
392 | **Multi-line literal strings** are surrounded by three single quotes on each
393 | side and allow newlines. Like literal strings, there is no escaping whatsoever.
394 | A newline immediately following the opening delimiter will be trimmed. TOML
395 | parsers must normalize newlines in the same manner as multi-line basic strings.
396 |
397 | All other content between the delimiters is interpreted as-is without
398 | modification.
399 |
400 | ```toml
401 | regex2 = '''I [dw]on't need \d{2} apples'''
402 | lines = '''
403 | The first newline is
404 | trimmed in literal strings.
405 | All other whitespace
406 | is preserved.
407 | '''
408 | ```
409 |
410 | You can write 1 or 2 single quotes anywhere within a multi-line literal string,
411 | but sequences of three or more single quotes are not permitted.
412 |
413 | ```toml
414 | quot15 = '''Here are fifteen quotation marks: """""""""""""""'''
415 |
416 | # apos15 = '''Here are fifteen apostrophes: '''''''''''''''''' # INVALID
417 | apos15 = "Here are fifteen apostrophes: '''''''''''''''"
418 |
419 | # 'That,' she said, 'is still pointless.'
420 | str = ''''That,' she said, 'is still pointless.''''
421 | ```
422 |
423 | Control characters other than tab are not permitted in a literal string.
424 |
425 | ## Integer
426 |
427 | Integers are whole numbers. Positive numbers may be prefixed with a plus sign.
428 | Negative numbers are prefixed with a minus sign.
429 |
430 | ```toml
431 | int1 = +99
432 | int2 = 42
433 | int3 = 0
434 | int4 = -17
435 | ```
436 |
437 | For large numbers, you may use underscores between digits to enhance
438 | readability. Each underscore must be surrounded by at least one digit on each
439 | side.
440 |
441 | ```toml
442 | int5 = 1_000
443 | int6 = 5_349_221
444 | int7 = 53_49_221 # Indian number system grouping
445 | int8 = 1_2_3_4_5 # VALID but discouraged
446 | ```
447 |
448 | Leading zeros are not allowed. Integer values `-0` and `+0` are valid and
449 | identical to an unprefixed zero.
450 |
451 | Non-negative integer values may also be expressed in hexadecimal, octal, or
452 | binary. In these formats, leading `+` is not allowed and leading zeros are
453 | allowed (after the prefix). Hex values are case-insensitive. Underscores are
454 | allowed between digits (but not between the prefix and the value).
455 |
456 | ```toml
457 | # hexadecimal with prefix `0x`
458 | hex1 = 0xDEADBEEF
459 | hex2 = 0xdeadbeef
460 | hex3 = 0xdead_beef
461 |
462 | # octal with prefix `0o`
463 | oct1 = 0o01234567
464 | oct2 = 0o755 # useful for Unix file permissions
465 |
466 | # binary with prefix `0b`
467 | bin1 = 0b11010110
468 | ```
469 |
470 | Arbitrary 64-bit signed integers (from −2^63 to 2^63−1) should be accepted and
471 | handled losslessly. If an integer cannot be represented losslessly, an error
472 | must be thrown.
473 |
474 | ## Float
475 |
476 | Floats should be implemented as IEEE 754 binary64 values.
477 |
478 | A float consists of an integer part (which follows the same rules as decimal
479 | integer values) followed by a fractional part and/or an exponent part. If both a
480 | fractional part and exponent part are present, the fractional part must precede
481 | the exponent part.
482 |
483 | ```toml
484 | # fractional
485 | flt1 = +1.0
486 | flt2 = 3.1415
487 | flt3 = -0.01
488 |
489 | # exponent
490 | flt4 = 5e+22
491 | flt5 = 1e06
492 | flt6 = -2E-2
493 |
494 | # both
495 | flt7 = 6.626e-34
496 | ```
497 |
498 | A fractional part is a decimal point followed by one or more digits.
499 |
500 | An exponent part is an E (upper or lower case) followed by an integer part
501 | (which follows the same rules as decimal integer values but may include leading
502 | zeros).
503 |
504 | The decimal point, if used, must be surrounded by at least one digit on each
505 | side.
506 |
507 | ```
508 | # INVALID FLOATS
509 | invalid_float_1 = .7
510 | invalid_float_2 = 7.
511 | invalid_float_3 = 3.e+20
512 | ```
513 |
514 | Similar to integers, you may use underscores to enhance readability. Each
515 | underscore must be surrounded by at least one digit.
516 |
517 | ```toml
518 | flt8 = 224_617.445_991_228
519 | ```
520 |
521 | Float values `-0.0` and `+0.0` are valid and should map according to IEEE 754.
522 |
523 | Special float values can also be expressed. They are always lowercase.
524 |
525 | ```toml
526 | # infinity
527 | sf1 = inf # positive infinity
528 | sf2 = +inf # positive infinity
529 | sf3 = -inf # negative infinity
530 |
531 | # not a number
532 | sf4 = nan # actual sNaN/qNaN encoding is implementation-specific
533 | sf5 = +nan # same as `nan`
534 | sf6 = -nan # valid, actual encoding is implementation-specific
535 | ```
536 |
537 | ## Boolean
538 |
539 | Booleans are just the tokens you're used to. Always lowercase.
540 |
541 | ```toml
542 | bool1 = true
543 | bool2 = false
544 | ```
545 |
546 | ## Offset Date-Time
547 |
548 | To unambiguously represent a specific instant in time, you may use an
549 | [RFC 3339](https://tools.ietf.org/html/rfc3339) formatted date-time with offset.
550 |
551 | ```toml
552 | odt1 = 1979-05-27T07:32:00Z
553 | odt2 = 1979-05-27T00:32:00-07:00
554 | odt3 = 1979-05-27T00:32:00.5-07:00
555 | odt4 = 1979-05-27T00:32:00.999999-07:00
556 | ```
557 |
558 | For the sake of readability, you may replace the T delimiter between date and
559 | time with a space character (as permitted by RFC 3339 section 5.6).
560 |
561 | ```toml
562 | odt4 = 1979-05-27 07:32:00Z
563 | ```
564 |
565 | One exception to RFC 3339 is permitted: seconds may be omitted, in which case
566 | `:00` will be assumed. The offset immediately follows the minutes.
567 |
568 | ```toml
569 | odt5 = 1979-05-27 07:32Z
570 | odt6 = 1979-05-27 07:32-07:00
571 | ```
572 |
573 | Implementations are required to support at least millisecond precision.
574 | Additional digits of precision may be specified, but if they exceed the
575 | supported precision then the extra digits must be truncated, not rounded.
576 |
577 | ## Local Date-Time
578 |
579 | If you omit the offset from an [RFC 3339](https://tools.ietf.org/html/rfc3339)
580 | formatted date-time, it will represent the given date-time without any relation
581 | to an offset or timezone. It cannot be converted to an instant in time without
582 | additional information. Conversion to an instant, if required, is
583 | implementation-specific.
584 |
585 | ```toml
586 | ldt1 = 1979-05-27T07:32:00
587 | ldt2 = 1979-05-27T07:32:00.5
588 | ldt3 = 1979-05-27T00:32:00.999999
589 | ```
590 |
591 | Seconds may be omitted, in which case `:00` will be assumed.
592 |
593 | ```toml
594 | ldt3 = 1979-05-27T07:32
595 | ```
596 |
597 | Millisecond precision is required. Further precision of fractional seconds is
598 | implementation-specific. If the value contains greater precision than the
599 | implementation can support, the additional precision must be truncated, not
600 | rounded.
601 |
602 | Implementations are required to support at least millisecond precision.
603 | Additional digits of precision may be specified, but if they exceed the
604 | supported precision then the extra digits must be truncated, not rounded.
605 |
606 | ## Local Date
607 |
608 | If you include only the date portion of an
609 | [RFC 3339](https://tools.ietf.org/html/rfc3339) formatted date-time, it will
610 | represent that entire day without any relation to an offset or timezone.
611 |
612 | ```toml
613 | ld1 = 1979-05-27
614 | ```
615 |
616 | ## Local Time
617 |
618 | If you include only the time portion of an
619 | [RFC 3339](https://tools.ietf.org/html/rfc3339) formatted date-time, it will
620 | represent that time of day without any relation to a specific day or any offset
621 | or timezone.
622 |
623 | ```toml
624 | lt1 = 07:32:00
625 | lt2 = 00:32:00.5
626 | lt3 = 00:32:00.999999
627 | ```
628 |
629 | Seconds may be omitted, in which case `:00` will be assumed.
630 |
631 | ```toml
632 | lt3 = 07:32
633 | ```
634 |
635 | Millisecond precision is required. Further precision of fractional seconds is
636 | implementation-specific. If the value contains greater precision than the
637 | implementation can support, the additional precision must be truncated, not
638 | rounded.
639 |
640 | Implementations are required to support at least millisecond precision.
641 | Additional digits of precision may be specified, but if they exceed the
642 | supported precision then the extra digits must be truncated, not rounded.
643 |
644 | ## Array
645 |
646 | Arrays are ordered values surrounded by square brackets. Whitespace is ignored.
647 | Elements are separated by commas. Arrays can contain values of the same data
648 | types as allowed in key/value pairs. Values of different types may be mixed.
649 |
650 | ```toml
651 | integers = [ 1, 2, 3 ]
652 | colors = [ "red", "yellow", "green" ]
653 | nested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ]
654 | nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ]
655 | string_array = [ "all", 'strings', """are the same""", '''type''' ]
656 |
657 | # Mixed-type arrays are allowed
658 | numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ]
659 | contributors = [
660 | "Foo Bar ",
661 | { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" }
662 | ]
663 | ```
664 |
665 | Arrays can span multiple lines. A terminating comma (also called a trailing
666 | comma) is permitted after the last value of the array. Any number of newlines
667 | and comments may precede values, commas, and the closing bracket. Indentation
668 | between array values and commas is treated as whitespace and ignored.
669 |
670 | ```toml
671 | integers2 = [
672 | 1, 2, 3
673 | ]
674 |
675 | integers3 = [
676 | 1,
677 | 2, # this is ok
678 | ]
679 | ```
680 |
681 | ## Table
682 |
683 | Tables (also known as hash tables or dictionaries) are collections of key/value
684 | pairs. They are defined by headers, with square brackets on a line by
685 | themselves. You can tell headers apart from arrays because arrays are only ever
686 | values.
687 |
688 | ```toml
689 | [table]
690 | ```
691 |
692 | Under that, and until the next header or EOF, are the key/values of that table.
693 | Key/value pairs within tables are not guaranteed to be in any specific order.
694 |
695 | ```toml
696 | [table-1]
697 | key1 = "some string"
698 | key2 = 123
699 |
700 | [table-2]
701 | key1 = "another string"
702 | key2 = 456
703 | ```
704 |
705 | Naming rules for tables are the same as for keys (see definition of
706 | [Keys](#user-content-keys) above).
707 |
708 | ```toml
709 | [dog."tater.man"]
710 | type.name = "pug"
711 | ```
712 |
713 | In JSON land, that would give you the following structure:
714 |
715 | ```json
716 | { "dog": { "tater.man": { "type": { "name": "pug" } } } }
717 | ```
718 |
719 | Whitespace around the key is ignored. However, best practice is to not use any
720 | extraneous whitespace.
721 |
722 | ```toml
723 | [a.b.c] # this is best practice
724 | [ d.e.f ] # same as [d.e.f]
725 | [ g . h . i ] # same as [g.h.i]
726 | [ j . "ʞ" . 'l' ] # same as [j."ʞ".'l']
727 | ```
728 |
729 | Indentation is treated as whitespace and ignored.
730 |
731 | You don't need to specify all the super-tables if you don't want to. TOML knows
732 | how to do it for you.
733 |
734 | ```toml
735 | # [x] you
736 | # [x.y] don't
737 | # [x.y.z] need these
738 | [x.y.z.w] # for this to work
739 |
740 | [x] # defining a super-table afterward is ok
741 | ```
742 |
743 | Empty tables are allowed and simply have no key/value pairs within them.
744 |
745 | Like keys, you cannot define a table more than once. Doing so is invalid.
746 |
747 | ```
748 | # DO NOT DO THIS
749 |
750 | [fruit]
751 | apple = "red"
752 |
753 | [fruit]
754 | orange = "orange"
755 | ```
756 |
757 | ```
758 | # DO NOT DO THIS EITHER
759 |
760 | [fruit]
761 | apple = "red"
762 |
763 | [fruit.apple]
764 | texture = "smooth"
765 | ```
766 |
767 | Defining tables out-of-order is discouraged.
768 |
769 | ```toml
770 | # VALID BUT DISCOURAGED
771 | [fruit.apple]
772 | [animal]
773 | [fruit.orange]
774 | ```
775 |
776 | ```toml
777 | # RECOMMENDED
778 | [fruit.apple]
779 | [fruit.orange]
780 | [animal]
781 | ```
782 |
783 | The top-level table, also called the root table, starts at the beginning of the
784 | document and ends just before the first table header (or EOF). Unlike other
785 | tables, it is nameless and cannot be relocated.
786 |
787 | ```toml
788 | # Top-level table begins.
789 | name = "Fido"
790 | breed = "pug"
791 |
792 | # Top-level table ends.
793 | [owner]
794 | name = "Regina Dogman"
795 | member_since = 1999-08-04
796 | ```
797 |
798 | Dotted keys create and define a table for each key part before the last one. Any
799 | such table must have all its key/value pairs defined under the current `[table]`
800 | header, or in the root table if defined before all headers, or in one inline
801 | table.
802 |
803 | ```toml
804 | fruit.apple.color = "red"
805 | # Defines a table named fruit
806 | # Defines a table named fruit.apple
807 |
808 | fruit.apple.taste.sweet = true
809 | # Defines a table named fruit.apple.taste
810 | # fruit and fruit.apple were already created
811 | ```
812 |
813 | Since tables cannot be defined more than once, redefining such tables using a
814 | `[table]` header is not allowed. Likewise, using dotted keys to redefine tables
815 | already defined in `[table]` form is not allowed. The `[table]` form can,
816 | however, be used to define sub-tables within tables defined via dotted keys.
817 |
818 | ```toml
819 | [fruit]
820 | apple.color = "red"
821 | apple.taste.sweet = true
822 |
823 | # [fruit.apple] # INVALID
824 | # [fruit.apple.taste] # INVALID
825 |
826 | [fruit.apple.texture] # you can add sub-tables
827 | smooth = true
828 | ```
829 |
830 | ## Inline Table
831 |
832 | Inline tables provide a more compact syntax for expressing tables. They are
833 | especially useful for grouped nested data that can otherwise quickly become
834 | verbose.
835 |
836 | Inline tables are fully defined within curly braces: `{` and `}`. Within the
837 | braces, zero or more comma-separated key/value pairs may appear. Key/value pairs
838 | take the same form as key/value pairs in standard tables. All value types are
839 | allowed, including inline tables.
840 |
841 | Inline tables can have multiple key/value pairs on the same line, or they can be
842 | put on different lines. A terminating comma (also called trailing comma) is
843 | permitted after the last key/value pair.
844 |
845 | ```toml
846 | name = { first = "Tom", last = "Preston-Werner" }
847 | point = {x=1, y=2}
848 | animal = { type.name = "pug" }
849 | contact = {
850 | personal = {
851 | name = "Donald Duck",
852 | email = "donald@duckburg.com",
853 | },
854 | work = {
855 | name = "Coin cleaner",
856 | email = "donald@ScroogeCorp.com",
857 | },
858 | }
859 | ```
860 |
861 | The inline tables above are identical to the following standard table
862 | definitions:
863 |
864 | ```toml
865 | [name]
866 | first = "Tom"
867 | last = "Preston-Werner"
868 |
869 | [point]
870 | x = 1
871 | y = 2
872 |
873 | [animal]
874 | type.name = "pug"
875 |
876 | [contact.personal]
877 | name = "Donald Duck"
878 | email = "donald@duckburg.com"
879 |
880 | [contact.work]
881 | name = "Coin cleaner"
882 | email = "donald@ScroogeCorp.com"
883 | ```
884 |
885 | Inline tables are fully self-contained and define all keys and sub-tables within
886 | them. Keys and sub-tables cannot be added outside the braces.
887 |
888 | ```toml
889 | [product]
890 | type = { name = "Nail" }
891 | # type.edible = false # INVALID
892 | ```
893 |
894 | Similarly, inline tables cannot be used to add keys or sub-tables to an
895 | already-defined table.
896 |
897 | ```toml
898 | [product]
899 | type.name = "Nail"
900 | # type = { edible = false } # INVALID
901 | ```
902 |
903 | ## Array of Tables
904 |
905 | The last syntax that has not yet been described allows writing arrays of tables.
906 | These can be expressed by using a header with a name in double brackets. The
907 | first instance of that header defines the array and its first table element, and
908 | each subsequent instance creates and defines a new table element in that array.
909 | The tables are inserted into the array in the order encountered.
910 |
911 | ```toml
912 | [[product]]
913 | name = "Hammer"
914 | sku = 738594937
915 |
916 | [[product]] # empty table within the array
917 |
918 | [[product]]
919 | name = "Nail"
920 | sku = 284758393
921 |
922 | color = "gray"
923 | ```
924 |
925 | In JSON land, that would give you the following structure.
926 |
927 | ```json
928 | {
929 | "product": [
930 | { "name": "Hammer", "sku": 738594937 },
931 | {},
932 | { "name": "Nail", "sku": 284758393, "color": "gray" }
933 | ]
934 | }
935 | ```
936 |
937 | Any reference to an array of tables points to the most recently defined table
938 | element of the array. This allows you to define sub-tables, and even sub-arrays
939 | of tables, inside the most recent table.
940 |
941 | ```toml
942 | [[fruits]]
943 | name = "apple"
944 |
945 | [fruits.physical] # subtable
946 | color = "red"
947 | shape = "round"
948 |
949 | [[fruits.varieties]] # nested array of tables
950 | name = "red delicious"
951 |
952 | [[fruits.varieties]]
953 | name = "granny smith"
954 |
955 |
956 | [[fruits]]
957 | name = "banana"
958 |
959 | [[fruits.varieties]]
960 | name = "plantain"
961 | ```
962 |
963 | The above TOML maps to the following JSON.
964 |
965 | ```json
966 | {
967 | "fruits": [
968 | {
969 | "name": "apple",
970 | "physical": {
971 | "color": "red",
972 | "shape": "round"
973 | },
974 | "varieties": [{ "name": "red delicious" }, { "name": "granny smith" }]
975 | },
976 | {
977 | "name": "banana",
978 | "varieties": [{ "name": "plantain" }]
979 | }
980 | ]
981 | }
982 | ```
983 |
984 | If the parent of a table or array of tables is an array element, that element
985 | must already have been defined before the child can be defined. Attempts to
986 | reverse that ordering must produce an error at parse time.
987 |
988 | ```
989 | # INVALID TOML DOC
990 | [fruit.physical] # subtable, but to which parent element should it belong?
991 | color = "red"
992 | shape = "round"
993 |
994 | [[fruit]] # parser must throw an error upon discovering that "fruit" is
995 | # an array rather than a table
996 | name = "apple"
997 | ```
998 |
999 | Attempting to append to a statically defined array, even if that array is empty,
1000 | must produce an error at parse time.
1001 |
1002 | ```
1003 | # INVALID TOML DOC
1004 | fruits = []
1005 |
1006 | [[fruits]] # Not allowed
1007 | ```
1008 |
1009 | Attempting to define a normal table with the same name as an already established
1010 | array must produce an error at parse time. Attempting to redefine a normal table
1011 | as an array must likewise produce a parse-time error.
1012 |
1013 | ```
1014 | # INVALID TOML DOC
1015 | [[fruits]]
1016 | name = "apple"
1017 |
1018 | [[fruits.varieties]]
1019 | name = "red delicious"
1020 |
1021 | # INVALID: This table conflicts with the previous array of tables
1022 | [fruits.varieties]
1023 | name = "granny smith"
1024 |
1025 | [fruits.physical]
1026 | color = "red"
1027 | shape = "round"
1028 |
1029 | # INVALID: This array of tables conflicts with the previous table
1030 | [[fruits.physical]]
1031 | color = "green"
1032 | ```
1033 |
1034 | You may also use inline tables where appropriate:
1035 |
1036 | ```toml
1037 | points = [ { x = 1, y = 2, z = 3 },
1038 | { x = 7, y = 8, z = 9 },
1039 | { x = 2, y = 4, z = 8 } ]
1040 | ```
1041 |
1042 | ## Filename Extension
1043 |
1044 | TOML files should use the extension `.toml`.
1045 |
1046 | ## MIME Type
1047 |
1048 | When transferring TOML files over the internet, the appropriate MIME type is
1049 | `application/toml`.
1050 |
1051 | ## ABNF Grammar
1052 |
1053 | A formal description of TOML's syntax is available, as a separate [ABNF
1054 | file][abnf].
1055 |
1056 | [abnf]: ./toml.abnf
1057 |
--------------------------------------------------------------------------------