; From: https://raw.githubusercontent.com/ldthomas/apg-js2-examples/master/ini-file/ini-file.bnf
;
; Ref: https://en.wikipedia.org: INI File
;
; comments begin with the semicolon, ";" and continue to the end of the line
; comments may appear on valid section and value lines as well as blank lines
; line ends may be CRLF, LF or CR
; tabs, 0x09, may NOT occur in quoted strings
;
; keys may have multiple values
;   - multiple values may be given as a comma delimited list on a single line
;   - multiple values may be listed separately on separate lines with the same key name
;
; section names are optional
;   - keys need not appear in a named section
;
; sections are "disjoint",
;   - that is the keys in multiple occurrences of a section name are
;   - simply joined together as if they appeared contiguously in a single section
;
; sections end at the beginning of a new section or the end of file
;
; section and key names are alphanumeric + underscore (must begin with alpha or underscore)
; values that are not alphanumeric must be single or double quoted
;
; The grammar is designed to accept any string of ASCII characters without failure.
; The "error productions", BadSectionLine, BadValueLine, BadBlankLine are meant to accept all lines
; that are not otherwise correct blank, section or value lines. This is so that
; parser callback functions can recognize input errors and report or react to them
; in an application-dependent manner.
;
;
IniFile         := *(BlankLine/ValueLine) *Section
Section         = SectionLine *(BlankLine/ValueLine)
SectionLine     = GoodSectionLine/BadSectionLine
GoodSectionLine = "[" wsp SectionName wsp "]" wsp [comment] LineEnd
BadSectionLine  = "[" *any LineEnd;
ValueLine       := GoodValueLine/BadValueLine
GoodValueLine   = KeyName wsp "=" wsp ValueArray wsp [comment] LineEnd
BadValueLine    = (%d33-90/%d92-126) *any LineEnd
ValueArray      = Value *(wsp "," wsp Value)
SectionName     = (alpha/%d95) *(alpha/digit/%d95)
KeyName         ::= (alpha/%d95) *(alpha/digit/%d95)
Value           ::= DQuotedString/SQuotedString/AlphaDigit
DQuotedString   = %d34 1*(%d32-33/%d35-126) %d34
SQuotedString   = %d39 1*(%d32-38/%d40-126) %d39
AlphaDigit      = 1*(alpha/digit)
BlankLine       = GoodBlankLine/BadBlankLine
GoodBlankLine   = wsp [comment] LineEnd
BadBlankLine    = (%d32/%d9) wsp (%d33-58/%d60-126) *any LineEnd
LineEnd         = %d13.10/%d10/%d13
comment         = (%d59/%d35) *any
wsp             = *(%d32/%d9)
alpha           = %d65-90/%d97-122
digit           = %d48-57
any             = %d32-126/%d9
