; Source:  https://github.com/toml-lang/toml
; License: MIT

;; This is an attempt to define TOML in ABNF according to the grammar defined
;; in RFC 4234 (http://www.ietf.org/rfc/rfc4234.txt).

;; TOML

toml = expression *( newline expression )
expression = (
  ws /
  ws comment /
  ws keyval ws [ comment ] /
  ws table ws [ comment ]
)

;; Newline

newline = (
  %x0A /              ; LF
  %x0D.0A             ; CRLF
)

newlines = 1*newline

;; Whitespace

ws = *(
  %x20 /              ; Space
  %x09                ; Horizontal tab
)

;; Comment

comment-start-symbol = %x23 ; #
non-eol = %x09 / %x20-10FFFF
comment = comment-start-symbol *non-eol

;; Key-Value pairs

keyval-sep = ws %x3D ws ; =
keyval = key keyval-sep val

key = unquoted-key / quoted-key
unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _
quoted-key = quotation-mark 1*basic-char quotation-mark ; See Basic Strings

val = integer / float / string / boolean / date-time / array / inline-table

;; Table

table = std-table / array-table

;; Standard Table

std-table-open  = %x5B ws     ; [ Left square bracket
std-table-close = ws %x5D     ; ] Right square bracket
table-key-sep   = ws %x2E ws  ; . Period

std-table = std-table-open key *( table-key-sep key) std-table-close

;; Array Table

array-table-open  = %x5B.5B ws  ; [[ Double left square bracket
array-table-close = ws %x5D.5D  ; ]] Double right square bracket

array-table = array-table-open key *( table-key-sep key) array-table-close

;; Integer

integer = [ minus / plus ] int
minus = %x2D                       ; -
plus = %x2B                        ; +
digit1-9 = %x31-39                 ; 1-9
underscore = %x5F                  ; _
int = DIGIT / digit1-9 1*( DIGIT / underscore DIGIT )

;; Float

float = integer ( frac / frac exp / exp )
zero-prefixable-int = DIGIT *( DIGIT / underscore DIGIT )
frac = decimal-point zero-prefixable-int
decimal-point = %x2E               ; .
exp = e integer
e = %x65 / %x45                    ; e E

;; String

string = basic-string / ml-basic-string / literal-string / ml-literal-string

;; Basic String

basic-string = quotation-mark *basic-char quotation-mark

quotation-mark = %x22            ; "

basic-char = basic-unescaped / escaped
escaped = escape ( %x22 /          ; "    quotation mark  U+0022
                   %x5C /          ; \    reverse solidus U+005C
                   %x2F /          ; /    solidus         U+002F
                   %x62 /          ; b    backspace       U+0008
                   %x66 /          ; f    form feed       U+000C
                   %x6E /          ; n    line feed       U+000A
                   %x72 /          ; r    carriage return U+000D
                   %x74 /          ; t    tab             U+0009
                   %x75 4HEXDIG /  ; uXXXX                U+XXXX
                   %x55 8HEXDIG )  ; UXXXXXXXX            U+XXXXXXXX

basic-unescaped = %x20-21 / %x23-5B / %x5D-10FFFF

escape = %x5C                    ; \

;; Multiline Basic String

ml-basic-string-delim = quotation-mark quotation-mark quotation-mark
ml-basic-string = ml-basic-string-delim ml-basic-body ml-basic-string-delim
ml-basic-body = *( ml-basic-char / newline / ( escape newline ))

ml-basic-char = ml-basic-unescaped / escaped
ml-basic-unescaped = %x20-5B / %x5D-10FFFF

;; Literal String

literal-string = apostraphe *literal-char apostraphe

apostraphe = %x27 ; ' Apostrophe

literal-char = %x09 / %x20-26 / %x28-10FFFF

;; Multiline Literal String

ml-literal-string-delim = apostraphe apostraphe apostraphe
ml-literal-string = ml-literal-string-delim ml-literal-body ml-literal-string-delim

ml-literal-body = *( ml-literal-char / newline )
ml-literal-char = %x09 / %x20-10FFFF

;; Boolean

boolean = true / false
true    = %x74.72.75.65     ; true
false   = %x66.61.6C.73.65  ; false

;; Datetime (as defined in RFC 3339)

date-fullyear  = 4DIGIT
date-month     = 2DIGIT  ; 01-12
date-mday      = 2DIGIT  ; 01-28, 01-29, 01-30, 01-31 based on month/year
time-hour      = 2DIGIT  ; 00-23
time-minute    = 2DIGIT  ; 00-59
time-second    = 2DIGIT  ; 00-58, 00-59, 00-60 based on leap second rules
time-secfrac   = "." 1*DIGIT
time-numoffset = ( "+" / "-" ) time-hour ":" time-minute
time-offset    = "Z" / time-numoffset

partial-time   = time-hour ":" time-minute ":" time-second [time-secfrac]
full-date      = date-fullyear "-" date-month "-" date-mday
full-time      = partial-time time-offset

date-time      = full-date "T" full-time

;; Array

array-open  = %x5B ws  ; [
array-close = ws %x5D  ; ]

array = array-open array-values array-close

array-values = [ val [ array-sep ] [ ( comment newlines) / newlines ] /
                 val array-sep [ ( comment newlines) / newlines ] array-values ]

array-sep = ws %x2C ws  ; , Comma

;; Inline Table

inline-table-open  = %x7B ws     ; {
inline-table-close = ws %x7D     ; }
inline-table-sep   = ws %x2C ws  ; , Comma

inline-table = inline-table-open inline-table-keyvals inline-table-close

inline-table-keyvals = [ inline-table-keyvals-non-empty ]
inline-table-keyvals-non-empty = key keyval-sep val /
                                 key keyval-sep val inline-table-sep inline-table-keyvals-non-empty

;; Built-in ABNF terms, reproduced here for clarity

; ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
; DIGIT = %x30-39 ; 0-9
; HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
