v1.0.0
Publicado em 2/12/2015 – Versão de texto

TOML v0.4.0

Tom's Obvious, Minimal Language.

Por Tom Preston-Werner.

Fique ciente de que esta especificação ainda está mudando muito. Até que seja marcado como 1.0, você deve presumir que é instável e agir de acordo.

Objetivos

TOML visa ser um formato de arquivo de configuração mínimo que seja fácil de ler devido à semântica óbvia. O TOML foi projetado para mapear inequivocamente para uma tabela de hash. O TOML deve ser fácil de analisar em estruturas de dados em uma ampla variedade de idiomas.

Especificação

  • TOML é case-sensitive, ou seja, diferencia maiúsculo de minúsculo.
  • Um arquivo TOML deve ser um documento Unicode válido codificado em UTF-8.
  • Espaço em branco significa tabulação (0x09) ou espaço (0x20).
  • Nova linha significa LF (0x0A) ou CRLF (0x0D0A).

Comentário

Fale o que pensa com o símbolo de cerquilha. Eles vão do símbolo até o final da linha.

# Sou um comentário. Ouça-me rugir. Grrrrr.
chave = "valor" # É, você pode fazer isso.

String

Há quatro maneiras de expressar strings: básica, básica multilinha, literal e literal multilinha. Todas as strings devem conter apenas caracteres UTF-8 válidos.

Strings básicas são delimitadas por aspas. Qualquer caractere Unicode pode ser usado, exceto aqueles que devem ter escape: aspas, barra invertida e os caracteres de controle (U+0000 a U+001F).

"Sou uma string. \"Você me colocar entre aspas\". Nome\tJos\u00E9\nLocal\tSF."

Por conveniência, alguns caracteres populares têm uma sequência de escape compacta.

\b         - backspace         (U+0008)
\t         - tabulações        (U+0009)
\n         - linefeed          (U+000A)
\f         - form feed         (U+000C)
\r         - carriage return   (U+000D)
\"         - aspas             (U+0022)
\\         - barra invertida   (U+005C)
\uXXXX     - unicode           (U+XXXX)
\UXXXXXXXX - unicode           (U+XXXXXXXX)

Qualquer caractere Unicode pode ter escape com as formas \uXXXX ou \UXXXXXXXX. Os códigos de escape devem ser valores escalares em Unicode válido.

Todas as outras sequências de escape não listadas acima são reservadas e, se forem usadas, TOML deve produzir um erro.

Às vezes, você precisa expressar passagens de texto (por exemplo, arquivos de tradução) ou deseja dividir uma string muito longa em várias linhas. O TOML torna isso fácil. Strings básicas multilinha são delimitadas por três aspas em cada lado e permitem novas linhas. Uma nova linha imediatamente após o delimitador de abertura será cortada. Todos os outros espaços em branco e caracteres de nova linha permanecem intactos.

chave1 = """
Rosas são vermelhas
Violetas são azuis"""

Os analisadores TOML devem se sentir à vontade para normalizar a nova linha para o que fizer sentido para sua plataforma.

# Em um sistema Unix, a string multilinha acima provavelmente será a mesma que:
chave2 = "Rosas são vermelhas\nVioletas são azuis"

# Em um sistema Windows, provavelmente será equivalente a:
chave3 = "Rosas são vermelhas\r\nVioletas são azuis"

Para escrever strings longas sem introduzir espaços em branco estranhos, termine uma linha com \. O \ será cortado junto com todos os espaços em branco (incluindo novas linhas) até o próximo caractere diferente de espaço em branco ou delimitador de fechamento. Se os primeiros caracteres após o delimitador de abertura forem uma barra invertida e uma nova linha, ambos serão cortados junto com todos os espaços em branco e novas linhas até o próximo caractere sem espaço em branco ou delimitador de fechamento. Todas as sequências de escape que são válidas para strings básicas também são válidas para strings básicas multilinha.

# As strings a seguir são equivalentes byte a byte:
chave1 = "A rápida raposa marrom pula sobre o cachorro preguiçoso."

chave2 = """
A rápida raposa \


  marrom pula sobre \
    o cachorro preguiçoso."""

chave3 = """\
       A rápida raposa \
       marrom pula sobre \
       o cachorro preguiçoso.\
       """

Qualquer caractere Unicode pode ser usado, exceto aqueles que devem ter escape: barra invertida e os caracteres de controle (U+0000 a U+001F). As aspas não precisam ter escape, a menos que sua presença crie um delimitador de fechamento prematuro.

Se você com frequência especifica caminhos do Windows ou expressões regulares, ter que fazer escape de barras invertidas rapidamente se torda tedioso e sujeito a erros. Para ajudar, o TOML oferece suporte a strings literais que não permitem escape. Strings literais são delimitadas por aspas simples. Como strings básicas, mas devem aparecer um uma única linha:

# O que você vê é o que você obtém.
winpath  = 'C:\Users\nodejs\templates'
winpath2 = '\\ServerX\admin$\system32\'
com_aspas = 'Tom "Dubs" Preston-Werner'
regex    = '<\i\c*\s*>'

Como não há escape, não há como escrever uma aspa simples dentro de uma string literal entre aspas simples. Felizmente, o TOML oferece suporte a uma versão multilinha de strings literais que resolve esse problema. Strings literais multilinha são delimitadas por três aspas simples de cada lado e permitem novas linhas. Como strings literais, não há como fazer escape. Uma nova linha imediatamente após o delimitador de abertura será apagada. Todos os outros conteúdos entre os delimitadores são interpretados como estão, sem modificação.

regex2 = '''Eu( não)? preciso de \d{2} maçãs'''
linhas = '''
A primeira nova linha é
cortada em strings brutas.
   Todos os outros espaços
   em branco são preservados.
'''

Para dados binários, é recomendável usar Base64 ou outra codificação ASCII ou UTF-8 adequada. A manipulação dessa codificação será específica do aplicativo.

Inteiro

Inteiros são números inteiros. Números positivos podem ser prefixados com um sinal de mais. Números negativos são prefixados com um sinal de menos.

+99
42
0
-17

Para números grandes, você pode usar underscores entre os dígitos para melhorar a legibilidade. Cada underscore deve ser envolto por pelo menos um dígito.

1_000
5_349_221
1_2_3_4_5     # válido, mas desaconselhável

Zeros à esquerda não são permitidos. Formas hexadecimais, octais e binárias não são permitidas. Valores como "infinito" e "não é um número" que não podem ser expressos como uma série de dígitos não são permitidos.

Intervalos de 64 bits (longo com sinal) são esperados (−9,223,372,036,854,775,808 to 9,223,372,036,854,775,807).

Ponto flutuante

Um ponto flutuante consiste em uma parte inteira (que segue as mesmas regras dos valores inteiros decimais) seguida por uma parte fracionária e/ou uma parte expoente. Se uma parte fracionária e uma parte expoente estiverem presentes, a parte fracionária deve preceder a parte expoente.

# fracionário
+1.0
3.1415
-0.01

# expoente
5e+22
1e6
-2E-2

# ambos
6.626e-34

Uma parte fracionária é um ponto decimal seguido por um ou mais dígitos.

Uma parte expoente é um E (maiúsculo ou minúsculo) seguido por uma parte inteira (que pode ser prefixada com um sinal de mais ou menos).

Como inteiros, você pode usar underscores para melhorar a legibilidade. Cada underscore deve ser envolto por pelo menos um dígito.

9_224_617.445_991_228_313
1e1_000

Precisão (dupla) de 64 bits esperada.

Booleano

Booleanos são apenas os tokens com os quais você está acostumado. Sempre minúsculos.

true
false

Data e hora

Datas/horas têm o formato da RFC 3339.

1979-05-27T07:32:00Z
1979-05-27T00:32:00-07:00
1979-05-27T00:32:00.999999-07:00

Array

Arrays são colchetes com outras primitivas dentro. O espaço em branco é ignorado. Os elementos são separados por vírgulas. Os tipos de dados não podem ser misturados (apesar de que todos os tipos de string devam ser considerado do mesmo tipo).

[ 1, 2, 3 ]
[ "vermelho", "amarelo", "verde" ]
[ [ 1, 2 ], [3, 4, 5] ]
[ "todas", 'strings', """são do mesmo""", '''tipo'''] # tudo bem
[ [ 1, 2 ], ["a", "b", "c"] ] # tudo bem
[ 1, 2.0 ] # nota: isso NÃO está correto

Arrays também podem ser multilinhas. Portanto, além de ignorar os espaços em branco, os arrays também ignoram as novas linhas entre os colchetes. As vírgulas de terminação são aceitas antes do colchete de fechamento.

chave = [
  1, 2, 3
]

chave = [
  1,
  2, # tudo bem
]

Tabela

Tabelas (também conhecidas como tabelas hash ou dicionários) são coleções de pares chave/valor. Elas aparecem entre colchetes em uma linha própria. Você pode diferenciá-las de arrays porque os arrays são apenas valores.

[tabela]

Abaixo disso, e até a próxima tabela ou EOF, estão as chaves/valores dessa tabela. As chaves estão à esquerda do sinal de igual e os valores à direita. O espaço em branco é ignorado em torno de nomes e valores de chave. A chave, o sinal de igual e o valor devem estar na mesma linha (embora alguns valores possam ser divididos em várias linhas).

Chaves podem ser simples ou entre aspas. Chaves simples podem conter apenas letras, números, underscores e hífenes (A-Za-z0-9_-). Chaves entre aspas seguem exatamente as mesmas regras de strings básicas e permitem que você use um conjunto muito mais amplo de nomes de chaves. A melhor prática é usar chaves simples, exceto quando absolutamente necessário.

Não há garantia de que os pares chave/valor nas tabelas estejam em uma ordem específica.

[tabela]
chave = "valor"
chave_simples = "valor"
chave-simples = "valor"

"127.0.0.1" = "valor"
"codificação de caracteres" = "valor"
"ǝʌɐɥɔ" = "valor"

Pontos são proibidos em chaves simples porque os pontos são usados para significar tabelas aninhadas! As regras de nomenclatura para cada parte separada por ponto são as mesmas das chaves (veja acima).

[cachorro."homem.batatinha"]
tipo.nome = "pug"

Na terra do JSON, isso resultaria na seguinte estrutura:

{ "cachorro": { "homem.batatinha": { "tipo": { "nome": "pug" } } } }

Os espaços em branco ao redor das partes separadas por pontos são ignorados. No entanto, a melhor prática é não usar nenhum espaço em branco estranho.

[a.b.c]          # esta é a melhor prática
[ d.e.f ]        # mesmo que [d.e.f]
[ g .  h  . i ]  # mesmo que [g.h.i]
[ j . "ʞ" . l ]  # mesmo que [j."ʞ".l]

Você não precisa especificar todas as supertabelas se não quiser. TOML sabe como fazer isso para você.

# [x] você
# [x.y] não
# [x.y.z] precisa disso
[x.y.z.w] # para que isso funcione

Tabelas vazias são permitidas e simplesmente não possuem pares chave/valor dentro delas.

Desde que uma supertabela não tenha sido definida diretamente e não tenha definido uma chave específica, você ainda pode escrever nela.

[a.b]
c = 1

[a]
d = 2

Você não pode definir nenhuma chave ou tabela mais de uma vez. Fazer isso é inválido.

# NÃO FAÇA ISSO

[a]
b = 1

[a]
c = 2
# TAMBÉM NÃO FAÇA ISSO

[a]
b = 1

[a.b]
c = 2

Todos os nomes e chaves de tabelas devem estar vazios.

# NÃO É UM TOML VÁLIDO
[]
[a.]
[a..b]
[.b]
[.]
 = "sem nome de chave" # não permitido

Tabela inline

As tabelas inline fornecem uma sintaxe mais compacta para expressar tabelas. Elas são especialmente úteis para dados agrupados que, de outra forma, podem rapidamente se tornar detalhadas. As tabelas inline são definidas entre chaves: { e }. Dentro das chaves, zero ou mais pares chave/valor separados por vírgulas podem aparecer. Os pares chave/valor assumem o mesmo formato que os pares chave/valor nas tabelas padrão. Todos os tipos de valor são permitidos, incluindo tabelas inline.

As tabelas inline devem aparecer em uma única linha. Nenhuma nova linha é permitida entre as chaves, a menos que sejam válidas dentro de um valor. Mesmo assim, é altamente desencorajado quebrar uma tabela inline em várias linhas. Se você se sentir dominado por esse desejo, isso significa que você deve usar tabelas padrão.

nome = { nome = "Tom", sobrenome = "Preston-Werner" }
ponto = { x = 1, y = 2 }

As tabelas inline acima são idênticas às seguintes definições de tabela padrão:

[nome]
nome = "Tom"
sobrenome = "Preston-Werner"

[ponto]
x = 1
y = 2

Array de tabelas

A última sintaxe que ainda não foi expressa é um array de tabelas. Este pode ser expresso usando um nome de tabela entre colchetes duplos. Cada tabela com o mesmo nome entre colchetes duplos será um elemento no array. As tabelas são inseridas no array na ordem encontrada. Uma tabela com colchetes duplos sem nenhum par chave/valor será considerada uma tabela vazia.

[[produtos]]
nome = "Martelo"
sku = 738594937

[[produtos]]

[[produtos]]
nome = "Prego"
sku = 284758393
cor = "cinza"

Na terra do JSON, isso resultaria na seguinte estrutura.

{
  "produtos": [
    { "nome": "Martelo", "sku": 738594937 },
    { },
    { "nome": "Prego", "sku": 284758393, "color": "cinza" }
  ]
}

Você também pode criar arrays aninhados de tabelas. Basta usar a mesma sintaxe de colchetes duplos nas subtabelas. Cada subtabela entre colchetes pertencerá ao elemento de tabela definido mais recentemente acima dela.

[[fruta]]
  nome = "uva"

  [fruta.fisico]
    cor = "roxa"
    forma = "redonda"

  [[fruta.variedade]]
    nome = "roxa deliciosa"

  [[fruta.variedade]]
    nome = "cabernet sauvignon"

[[fruta]]
  nome = "banana"

  [[fruta.variedade]]
    nome = "banana-da-terra"

O TOML acima é mapeado para o seguinte JSON.

{
  "fruta": [
    {
      "nome": "uva",
      "fisico": {
        "cor": "roxa",
        "forma": "redonda"
      },
      "variedade": [
        { "nome": "roxa deliciosa" },
        { "nome": "cabernet sauvignon" }
      ]
    },
    {
      "nome": "banana",
      "variedade": [
        { "nome": "banana-da-terra" }
      ]
    }
  ]
}

A tentativa de definir uma tabela normal com o mesmo nome de um array já estabelecido deve produzir um erro no momento da análise.

# DOCUMENTO TOML INVÁLIDO
[[fruta]]
  nome = "uva"

  [[fruta.variedade]]
    nome = "roxa deliciosa"

  # Esta tabela conflita com a tabela anterior
  [fruta.variedade]
    nome = "cabernet sauvignon"

Você também pode usar tabelas inline quando apropriado:

pontos = [ { x = 1, y = 2, z = 3 },
           { x = 7, y = 8, z = 9 },
           { x = 2, y = 4, z = 8 } ]

Sério?

É.

Mas por que?

Porque precisamos de um formato decente legível por humanos que mapeie inequivocamente para uma tabela hash e a especificação YAML tenha cerca de 80 páginas e me dê raiva. Não, JSON não conta. Você sabe porque.

Oh Deus, você está certo

É isso aí. Quer ajudar? Envie uma pull request. Ou escreva um analisador. SEJA CORAJOSO.

Projetos usando TOML

  • Cargo - O gerenciador de pacotes da linguagem Rust.
  • InfluxDB - Banco de dados de série temporal distribuído.
  • Heka - Sistema de processamento de fluxo pela Mozilla.
  • Hugo - Gerador de site estáticos em Go.

Implementações

Se você tiver uma implementação, envie uma pull request adicionando a esta lista. Observe o commit SHA1 ou tag de versão compatível com seu analisador em seu Readme.

Validadores

Conjunto de testes agnósticos de linguagem para codificadores e decodificadores TOML

Suporte em editores

Codificador

Conversores