changeset 1372:2da623ec519b

fix: integration of toml parser
author sam <sam@basx.dev>
date Wed, 04 Dec 2024 22:17:41 +0700
parents 7427925a4246
children 19469f21f34e cd2455307a6c
files semicongine/loaders.nim semicongine/thirdparty/parsetoml.nim
diffstat 2 files changed, 600 insertions(+), 489 deletions(-) [+]
line wrap: on
line diff
--- a/semicongine/loaders.nim	Wed Dec 04 16:49:38 2024 +0700
+++ b/semicongine/loaders.nim	Wed Dec 04 22:17:41 2024 +0700
@@ -1,5 +1,4 @@
 import std/json
-import std/parsecfg
 import std/strutils
 import std/sequtils
 import std/os
@@ -11,6 +10,8 @@
 import ./gltf
 import ./image
 import ./resources
+import ./thirdparty/parsetoml
+export parsetoml
 
 proc loadBytes*(path, package: string): seq[byte] {.gcsafe.} =
   cast[seq[byte]](toSeq(path.loadResource_intern(package = package).readAll()))
@@ -18,8 +19,8 @@
 proc loadJson*(path: string, package = DEFAULT_PACKAGE): JsonNode {.gcsafe.} =
   path.loadResource_intern(package = package).readAll().parseJson()
 
-proc loadConfig*(path: string, package = DEFAULT_PACKAGE): Config {.gcsafe.} =
-  path.loadResource_intern(package = package).loadConfig(filename = path)
+proc loadConfig*(path: string, package = DEFAULT_PACKAGE): TomlValueRef {.gcsafe.} =
+  path.loadResource_intern(package = package).parseStream(filename = path)
 
 proc loadImage*[T: PixelType](
     path: string, package = DEFAULT_PACKAGE
@@ -113,7 +114,7 @@
 # background loaders
 
 type ResourceType =
-  seq[byte] | JsonNode | Config | Image[Gray] | Image[BGRA] | SoundData
+  seq[byte] | JsonNode | TomlValueRef | Image[Gray] | Image[BGRA] | SoundData
 
 var rawLoader = initBackgroundLoader(loadBytes)
 var jsonLoader = initBackgroundLoader(loadJson)
@@ -127,7 +128,7 @@
     requestLoading(rawLoader[], path, package)
   elif T is JsonNode:
     requestLoading(jsonLoader[], path, package)
-  elif T is Config:
+  elif T is TomlValueRef:
     requestLoading(configLoader[], path, package)
   elif T is Image[Gray]:
     requestLoading(grayImageLoader[], path, package)
@@ -143,7 +144,7 @@
     isLoaded(rawLoader[], path, package)
   elif T is JsonNode:
     isLoaded(jsonLoader[], path, package)
-  elif T is Config:
+  elif T is TomlValueRef:
     isLoaded(configLoader[], path, package)
   elif T is Image[Gray]:
     isLoaded(grayImageLoader[], path, package)
@@ -159,7 +160,7 @@
     getLoadedData(rawLoader[], path, package)
   elif T is JsonNode:
     getLoadedData(jsonLoader[], path, package)
-  elif T is Config:
+  elif T is TomlValueRef:
     getLoadedData(configLoader[], path, package)
   elif T is Image[Gray]:
     getLoadedData(grayImageLoader[], path, package)
--- a/semicongine/thirdparty/parsetoml.nim	Wed Dec 04 16:49:38 2024 +0700
+++ b/semicongine/thirdparty/parsetoml.nim	Wed Dec 04 22:17:41 2024 +0700
@@ -46,18 +46,21 @@
     OverflowDefect* = OverflowError
 
 type
-  Sign* = enum None, Pos, Neg
+  Sign* = enum
+    None
+    Pos
+    Neg
 
   TomlValueKind* {.pure.} = enum
     None
-    Int,
-    Float,
-    Bool,
-    Datetime,
-    Date,
-    Time,
-    String,
-    Array,
+    Int
+    Float
+    Bool
+    Datetime
+    Date
+    Time
+    String
+    Array
     Table
 
   TomlDate* = object
@@ -112,20 +115,26 @@
     location*: ParserState
 
   NumberBase = enum
-    base10, base16, base8, base2
+    base10
+    base16
+    base8
+    base2
 
   StringType {.pure.} = enum
-    Basic,  # Enclosed within double quotation marks
+    Basic # Enclosed within double quotation marks
     Literal # Enclosed within single quotation marks
 
 const
   defaultStringCapacity = 256
-  ctrlChars = {'\x00' .. '\x08', '\x0A' .. '\x1F', '\x7F'} # '\x09' - TAB is not counted as control char
+  ctrlChars = {'\x00' .. '\x08', '\x0A' .. '\x1F', '\x7F'}
+    # '\x09' - TAB is not counted as control char
   ctrlCharsExclCrLf = ctrlChars - {'\x0A', '\x0D'}
 
 proc newTomlError(location: ParserState, msg: string): ref TomlError =
-  result = newException(TomlError, location.fileName & "(" & $location.line &
-                        ":" & $location.column & ")" & " " & msg)
+  result = newException(
+    TomlError,
+    location.fileName & "(" & $location.line & ":" & $location.column & ")" & " " & msg,
+  )
   result.location = location
 
 proc getNextChar(state: var ParserState): char =
@@ -153,20 +162,21 @@
 proc pushBackChar(state: var ParserState, c: char) {.inline.} =
   state.pushback = c
 
-type
-  LfSkipMode = enum
-    skipLf, skipNoLf
+type LfSkipMode = enum
+  skipLf
+  skipNoLf
 
-proc getNextNonWhitespace(state: var ParserState,
-                          skip: LfSkipMode): char =
+proc getNextNonWhitespace(state: var ParserState, skip: LfSkipMode): char =
   # Note: this procedure does *not* consider a newline as a
   # "whitespace". Since newlines are often mandatory in TOML files
   # (e.g. after a key/value specification), we do not want to miss
   # them...
 
-  let whitespaces = (case skip
-                     of skipLf: {' ', '\t', '\r', '\l'}
-                     of skipNoLf: {' ', '\t', '\r'})
+  let whitespaces = (
+    case skip
+    of skipLf: {' ', '\t', '\r', '\l'}
+    of skipNoLf: {' ', '\t', '\r'}
+  )
 
   var nextChar: char
   while true:
@@ -179,28 +189,33 @@
         # Control characters other than tab (U+0009) are not permitted in comments.
         # Invalid control characters: U+0000 to U+0008, U+000A to U+001F, U+007F
         if nextChar in ctrlCharsExclCrLf:
-          raise newTomlError(state, "invalid control char 0x$# found in a comment" % [nextChar.ord.toHex(2)])
+          raise newTomlError(
+            state,
+            "invalid control char 0x$# found in a comment" % [nextChar.ord.toHex(2)],
+          )
 
-    if nextChar notin whitespaces: break
+    if nextChar notin whitespaces:
+      break
 
   result = nextChar
 
 proc charToInt(c: char, base: NumberBase): int {.inline, noSideEffect.} =
   case base
-  of base10, base8, base2: result = int(c) - int('0')
+  of base10, base8, base2:
+    result = int(c) - int('0')
   of base16:
     if c in strutils.Digits:
       result = charToInt(c, base10)
     else:
       result = 10 + int(toUpperAscii(c)) - int('A')
 
-type
-  LeadingChar {.pure.} = enum
-    AllowZero, DenyZero
+type LeadingChar {.pure.} = enum
+  AllowZero
+  DenyZero
 
-proc parseInt(state: var ParserState,
-              base: NumberBase,
-              leadingChar: LeadingChar): int64 =
+proc parseInt(
+    state: var ParserState, base: NumberBase, leadingChar: LeadingChar
+): int64 =
   var
     nextChar: char
     firstPos = true
@@ -208,16 +223,20 @@
     wasUnderscore = false
 
   let
-    baseNum = (case base
-               of base2: 2
-               of base8: 8
-               of base10: 10
-               of base16: 16)
-    digits = (case base
-              of base2: {'0', '1'}
-              of base8: {'0', '1', '2', '3', '4', '5', '6', '7'}
-              of base10: strutils.Digits
-              of base16: strutils.HexDigits)
+    baseNum = (
+      case base
+      of base2: 2
+      of base8: 8
+      of base10: 10
+      of base16: 16
+    )
+    digits = (
+      case base
+      of base2: {'0', '1'}
+      of base8: {'0', '1', '2', '3', '4', '5', '6', '7'}
+      of base10: strutils.Digits
+      of base16: strutils.HexDigits
+    )
 
   result = 0
   while true:
@@ -225,36 +244,33 @@
     nextChar = state.getNextChar()
     if nextChar == '_':
       if firstPos or wasUnderscore:
-        raise(newTomlError(state,
-                           "underscore must be surrounded by digit"))
+        raise (newTomlError(state, "underscore must be surrounded by digit"))
       continue
 
     if nextChar in {'+', '-'} and firstPos:
       firstPos = false
-      if nextChar == '-': negative = true
+      if nextChar == '-':
+        negative = true
       continue
 
     if nextChar == '0' and firstPos and leadingChar == LeadingChar.DenyZero:
       # TOML specifications forbid this
       var upcomingChar = state.getNextChar()
       if upcomingChar in Digits:
-        raise(newTomlError(state,
-                           "leading zeroes are not allowed in integers"))
+        raise (newTomlError(state, "leading zeroes are not allowed in integers"))
       else:
         state.pushBackChar(upcomingChar)
 
     if nextChar notin digits:
       if wasUnderscore:
-        raise(newTomlError(state,
-                           "underscore must be surrounded by digit"))
+        raise (newTomlError(state, "underscore must be surrounded by digit"))
       state.pushBackChar(nextChar)
       break
 
     try:
       result = result * baseNum - charToInt(nextChar, base)
     except OverflowDefect:
-      raise(newTomlError(state,
-                         "integer numbers wider than 64 bits not allowed"))
+      raise (newTomlError(state, "integer numbers wider than 64 bits not allowed"))
 
     firstPos = false
 
@@ -262,19 +278,25 @@
     try:
       result = -result
     except OverflowDefect:
-      raise(newTomlError(state,
-                         "integer numbers wider than 64 bits not allowed"))
+      raise (newTomlError(state, "integer numbers wider than 64 bits not allowed"))
 
 proc parseEncoding(state: var ParserState): TomlValueRef =
   let nextChar = state.getNextChar()
-  case nextChar:
-    of 'b':
-      return TomlValueRef(kind: TomlValueKind.Int, intVal: parseInt(state, base2, LeadingChar.AllowZero))
-    of 'o':
-      return TomlValueRef(kind: TomlValueKind.Int, intVal: parseInt(state, base8, LeadingChar.AllowZero))
-    of 'x':
-      return TomlValueRef(kind: TomlValueKind.Int, intVal: parseInt(state, base16, LeadingChar.AllowZero))
-    else: raise newTomlError(state, "illegal character")
+  case nextChar
+  of 'b':
+    return TomlValueRef(
+      kind: TomlValueKind.Int, intVal: parseInt(state, base2, LeadingChar.AllowZero)
+    )
+  of 'o':
+    return TomlValueRef(
+      kind: TomlValueKind.Int, intVal: parseInt(state, base8, LeadingChar.AllowZero)
+    )
+  of 'x':
+    return TomlValueRef(
+      kind: TomlValueKind.Int, intVal: parseInt(state, base16, LeadingChar.AllowZero)
+    )
+  else:
+    raise newTomlError(state, "illegal character")
 
 proc parseDecimalPart(state: var ParserState): float64 =
   var
@@ -288,13 +310,11 @@
     nextChar = state.getNextChar()
     if nextChar == '_':
       if firstPos or wasUnderscore:
-        raise(newTomlError(state,
-                           "underscore must be surrounded by digit"))
+        raise (newTomlError(state, "underscore must be surrounded by digit"))
       continue
     if nextChar notin strutils.Digits:
       if wasUnderscore:
-        raise(newTomlError(state,
-                           "underscore must be surrounded by digit"))
+        raise (newTomlError(state, "underscore must be surrounded by digit"))
       state.pushBackChar(nextChar)
       if firstPos:
         raise newTomlError(state, "decimal part empty")
@@ -307,9 +327,11 @@
   discard parseutils.parseFloat(decimalPartStr, result)
 
 proc stringDelimiter(kind: StringType): char {.inline, noSideEffect.} =
-  result = (case kind
-            of StringType.Basic: '\"'
-            of StringType.Literal: '\'')
+  result = (
+    case kind
+    of StringType.Basic: '\"'
+    of StringType.Literal: '\''
+  )
 
 proc parseUnicode(state: var ParserState): string =
   let
@@ -319,34 +341,45 @@
   if state.line != oldState.line:
     raise newTomlError(state, "invalid Unicode codepoint, can't span lines")
   if escapeKindChar == 'u' and state.column - 5 != oldState.column:
-    raise newTomlError(state, "invalid Unicode codepoint, 'u' must have " &
-                       "four character value")
+    raise newTomlError(
+      state, "invalid Unicode codepoint, 'u' must have " & "four character value"
+    )
   if escapeKindChar == 'U' and state.column - 9 != oldState.column:
-    raise newTomlError(state, "invalid Unicode codepoint, 'U' must have " &
-                       "eight character value")
-  if code notin 0'i64..0xD7FF and code notin 0xE000'i64..0x10FFFF:
-    raise(newTomlError(state, "invalid Unicode codepoint, " &
-                       "must be a Unicode scalar value"))
+    raise newTomlError(
+      state, "invalid Unicode codepoint, 'U' must have " & "eight character value"
+    )
+  if code notin 0'i64 .. 0xD7FF and code notin 0xE000'i64 .. 0x10FFFF:
+    raise (
+      newTomlError(
+        state, "invalid Unicode codepoint, " & "must be a Unicode scalar value"
+      )
+    )
 
   return unicode.toUTF8(Rune(code))
 
 proc parseEscapeChar(state: var ParserState, escape: char): string =
   case escape
-  of 'b': result = "\b"
-  of 't': result = "\t"
-  of 'n': result = "\l"
-  of 'f': result = "\f"
-  of 'r': result = "\r"
-  of '\'': result = "\'"
-  of '\"': result = "\""
-  of '\\': result = "\\"
+  of 'b':
+    result = "\b"
+  of 't':
+    result = "\t"
+  of 'n':
+    result = "\l"
+  of 'f':
+    result = "\f"
+  of 'r':
+    result = "\r"
+  of '\'':
+    result = "\'"
+  of '\"':
+    result = "\""
+  of '\\':
+    result = "\\"
   of 'u', 'U':
     state.pushBackChar(escape)
     result = parseUnicode(state)
   else:
-    raise(newTomlError(state,
-                       "unknown escape " &
-                       "sequence \"\\" & escape & "\""))
+    raise (newTomlError(state, "unknown escape " & "sequence \"\\" & escape & "\""))
 
 proc parseSingleLineString(state: var ParserState, kind: StringType): string =
   # This procedure parses strings enclosed within single/double
@@ -365,14 +398,16 @@
       break
 
     if nextChar == '\0':
-      raise(newTomlError(state, "unterminated string"))
+      raise (newTomlError(state, "unterminated string"))
 
     # https://toml.io/en/v1.0.0#string
     # Any Unicode character may be used except those that must be escaped:
     # quotation mark, backslash, and the control characters other than tab
     # (U+0000 to U+0008, U+000A to U+001F, U+007F).
     if nextChar in ctrlChars:
-      raise(newTomlError(state, "invalid character in string: 0x$#" % nextChar.ord.toHex(2)))
+      raise (
+        newTomlError(state, "invalid character in string: 0x$#" % nextChar.ord.toHex(2))
+      )
 
     if nextChar == '\\' and kind == StringType.Basic:
       nextChar = state.getNextChar()
@@ -438,7 +473,7 @@
         continue
 
     if nextChar == '\0':
-      raise(newTomlError(state, "unterminated string"))
+      raise (newTomlError(state, "unterminated string"))
 
     # https://toml.io/en/v1.0.0#string
     # Any Unicode character may be used except those that must be
@@ -446,7 +481,9 @@
     # line feed, and carriage return (U+0000 to U+0008, U+000B,
     # U+000C, U+000E to U+001F, U+007F).
     if nextChar in ctrlCharsExclCrLf:
-      raise(newTomlError(state, "invalid character in string: 0x$#" % nextChar.ord.toHex(2)))
+      raise (
+        newTomlError(state, "invalid character in string: 0x$#" % nextChar.ord.toHex(2))
+      )
 
     result.add(nextChar)
     isFirstChar = false
@@ -473,8 +510,8 @@
     return parseSingleLineString(state, kind)
 
 # Forward declaration
-proc parseValue(state: var ParserState): TomlValueRef
-proc parseInlineTable(state: var ParserState): TomlValueRef
+proc parseValue(state: var ParserState): TomlValueRef {.gcsafe.}
+proc parseInlineTable(state: var ParserState): TomlValueRef {.gcsafe.}
 
 proc parseArray(state: var ParserState): seq[TomlValueRef] =
   # This procedure assumes that "state" has already consumed the '['
@@ -490,7 +527,7 @@
     of ',':
       if len(result) == 0:
         # This happens with "[, 1, 2]", for instance
-        raise(newTomlError(state, "first array element missing"))
+        raise (newTomlError(state, "first array element missing"))
 
       # Check that this is not a terminating comma (like in
       #  "[b,]")
@@ -512,16 +549,13 @@
         # Check that the type of newValue is compatible with the
         # previous ones
         if newValue.kind != result[low(result)].kind:
-          raise(newTomlError(oldState,
-                             "array members with incompatible types"))
+          raise (newTomlError(oldState, "array members with incompatible types"))
 
       result.add(newValue)
 
-proc parseStrictNum(state: var ParserState,
-                    minVal: int,
-                    maxVal: int,
-                    count: Slice[int],
-                    msg: string): int =
+proc parseStrictNum(
+    state: var ParserState, minVal: int, maxVal: int, count: Slice[int], msg: string
+): int =
   var
     nextChar: char
     parsedChars = 0
@@ -538,23 +572,24 @@
       result = result * 10 + charToInt(nextChar, base10)
       parsedChars += 1
     except OverflowDefect:
-      raise(newTomlError(state,
-                         "integer numbers wider than 64 bits not allowed"))
+      raise (newTomlError(state, "integer numbers wider than 64 bits not allowed"))
 
   if parsedChars notin count:
-    raise(newTomlError(state,
-                       "too few or too many characters in digit, expected " &
-                       $count & " got " & $parsedChars))
+    raise (
+      newTomlError(
+        state,
+        "too few or too many characters in digit, expected " & $count & " got " &
+          $parsedChars,
+      )
+    )
 
   if result < minVal or result > maxVal:
-    raise(newTomlError(state, msg & " (" & $result & ")"))
+    raise (newTomlError(state, msg & " (" & $result & ")"))
 
-template parseStrictNum(state: var ParserState,
-                    minVal: int,
-                    maxVal: int,
-                    count: int,
-                    msg: string): int =
-  parseStrictNum(state, minVal, maxVal, (count..count), msg)
+template parseStrictNum(
+    state: var ParserState, minVal: int, maxVal: int, count: int, msg: string
+): int =
+  parseStrictNum(state, minVal, maxVal, (count .. count), msg)
 
 proc parseTimePart(state: var ParserState, val: var TomlTime) =
   var
@@ -562,19 +597,20 @@
     curLine = state.line
 
   # Parse the minutes
-  val.minute = parseStrictNum(state, minVal = 0, maxVal = 59, count = 2,
-                                   "number out of range for minutes")
+  val.minute = parseStrictNum(
+    state, minVal = 0, maxVal = 59, count = 2, "number out of range for minutes"
+  )
   if curLine != state.line:
-    raise(newTomlError(state, "invalid date field, stopped in or after minutes field"))
+    raise (newTomlError(state, "invalid date field, stopped in or after minutes field"))
 
   nextChar = state.getNextChar()
   if nextChar != ':':
-    raise(newTomlError(state,
-                       "\":\" expected after the number of seconds"))
+    raise (newTomlError(state, "\":\" expected after the number of seconds"))
 
   # Parse the second. Note that seconds=60 *can* happen (leap second)
-  val.second = parseStrictNum(state, minVal = 0, maxVal = 60, count = 2,
-                                   "number out of range for seconds")
+  val.second = parseStrictNum(
+    state, minVal = 0, maxVal = 60, count = 2, "number out of range for seconds"
+  )
 
   nextChar = state.getNextChar()
   if nextChar == '.':
@@ -582,9 +618,7 @@
   else:
     state.pushBackChar(nextChar)
 
-proc parseDateTimePart(state: var ParserState,
-                       dateTime: var TomlDateTime): bool =
-
+proc parseDateTimePart(state: var ParserState, dateTime: var TomlDateTime): bool =
   # This function is called whenever a datetime object is found. They follow
   # an ISO convention and can use one of the following format:
   #
@@ -610,49 +644,55 @@
     curLine = state.line
 
   # Parse the month
-  dateTime.date.month = parseStrictNum(state, minVal = 1, maxVal = 12, count = 2,
-                                  "number out of range for the month")
+  dateTime.date.month = parseStrictNum(
+    state, minVal = 1, maxVal = 12, count = 2, "number out of range for the month"
+  )
   if curLine != state.line:
-    raise(newTomlError(state, "invalid date field, stopped in or after month field"))
+    raise (newTomlError(state, "invalid date field, stopped in or after month field"))
 
   nextChar = state.getNextChar()
   if nextChar != '-':
-    raise(newTomlError(state, "\"-\" expected after the month number"))
+    raise (newTomlError(state, "\"-\" expected after the month number"))
 
   # Parse the day
-  dateTime.date.day = parseStrictNum(state, minVal = 1, maxVal = 31, count = 2,
-                                "number out of range for the day")
+  dateTime.date.day = parseStrictNum(
+    state, minVal = 1, maxVal = 31, count = 2, "number out of range for the day"
+  )
   if curLine != state.line:
     return false
   else:
     nextChar = state.getNextChar()
     if nextChar notin {'t', 'T', ' '}:
-      raise(newTomlError(state, "\"T\", \"t\", or space expected after the day number"))
+      raise
+        (newTomlError(state, "\"T\", \"t\", or space expected after the day number"))
 
     # Parse the hour
-    dateTime.time.hour = parseStrictNum(state, minVal = 0, maxVal = 23, count = 2,
-                                   "number out of range for the hours")
+    dateTime.time.hour = parseStrictNum(
+      state, minVal = 0, maxVal = 23, count = 2, "number out of range for the hours"
+    )
     if curLine != state.line:
-      raise(newTomlError(state, "invalid date field, stopped in or after hours field"))
+      raise (newTomlError(state, "invalid date field, stopped in or after hours field"))
 
     nextChar = state.getNextChar()
     if nextChar != ':':
-      raise(newTomlError(state, "\":\" expected after the number of hours"))
+      raise (newTomlError(state, "\":\" expected after the number of hours"))
 
     # Parse the minutes
-    dateTime.time.minute = parseStrictNum(state, minVal = 0, maxVal = 59, count = 2,
-                                     "number out of range for minutes")
+    dateTime.time.minute = parseStrictNum(
+      state, minVal = 0, maxVal = 59, count = 2, "number out of range for minutes"
+    )
     if curLine != state.line:
-      raise(newTomlError(state, "invalid date field, stopped in or after minutes field"))
+      raise
+        (newTomlError(state, "invalid date field, stopped in or after minutes field"))
 
     nextChar = state.getNextChar()
     if nextChar != ':':
-      raise(newTomlError(state,
-                         "\":\" expected after the number of seconds"))
+      raise (newTomlError(state, "\":\" expected after the number of seconds"))
 
     # Parse the second. Note that seconds=60 *can* happen (leap second)
-    dateTime.time.second = parseStrictNum(state, minVal = 0, maxVal = 60, count = 2,
-                                     "number out of range for seconds")
+    dateTime.time.second = parseStrictNum(
+      state, minVal = 0, maxVal = 60, count = 2, "number out of range for seconds"
+    )
 
     nextChar = state.getNextChar()
     if nextChar == '.':
@@ -669,80 +709,92 @@
         shift: true,
         isShiftPositive: true,
         zoneHourShift: 0,
-        zoneMinuteShift: 0
+        zoneMinuteShift: 0,
       )
     of '+', '-':
       dateTime = TomlDateTime(
         time: dateTime.time,
         date: dateTime.date,
         shift: true,
-        isShiftPositive: (nextChar == '+')
+        isShiftPositive: (nextChar == '+'),
+      )
+      dateTime.zoneHourShift = parseStrictNum(
+        state, minVal = 0, maxVal = 23, count = 2, "number out of range for shift hours"
       )
-      dateTime.zoneHourShift =
-        parseStrictNum(state, minVal = 0, maxVal = 23, count = 2,
-                               "number out of range for shift hours")
       if curLine != state.line:
-        raise(newTomlError(state, "invalid date field, stopped in or after shift hours field"))
+        raise (
+          newTomlError(
+            state, "invalid date field, stopped in or after shift hours field"
+          )
+        )
 
       nextChar = state.getNextChar()
       if nextChar != ':':
-        raise(newTomlError(state,
-                           "\":\" expected after the number of shift hours"))
+        raise (newTomlError(state, "\":\" expected after the number of shift hours"))
 
-      dateTime.zoneMinuteShift =
-        parseStrictNum(state, minVal = 0, maxVal = 59, count = 2,
-                               "number out of range for shift minutes")
+      dateTime.zoneMinuteShift = parseStrictNum(
+        state,
+        minVal = 0,
+        maxVal = 59,
+        count = 2,
+        "number out of range for shift minutes",
+      )
     else:
       if curLine == state.line:
-        raise(newTomlError(state, "unexpected character " & escape($nextChar) &
-                           " instead of the time zone"))
+        raise (
+          newTomlError(
+            state,
+            "unexpected character " & escape($nextChar) & " instead of the time zone",
+          )
+        )
       else: # shift is automatically initialized to false
         state.pushBackChar(nextChar)
 
     return true
 
-proc parseDateOrTime(state: var ParserState, digits: int, yearOrHour: int): TomlValueRef =
+proc parseDateOrTime(
+    state: var ParserState, digits: int, yearOrHour: int
+): TomlValueRef =
   var
     nextChar: char
     yoh = yearOrHour
     d = digits
   while true:
     nextChar = state.getNextChar()
-    case nextChar:
-      of ':':
-        if d != 2:
-          raise newTomlError(state, "wrong number of characters for hour")
-        var val: TomlTime
-        val.hour = yoh
+    case nextChar
+    of ':':
+      if d != 2:
+        raise newTomlError(state, "wrong number of characters for hour")
+      var val: TomlTime
+      val.hour = yoh
 
-        parseTimePart(state, val)
-        return TomlValueRef(kind: TomlValueKind.Time, timeVal: val)
-      of '-':
-        if d != 4:
-          raise newTomlError(state, "wrong number of characters for year")
-        var val: TomlDateTime
-        val.date.year = yoh
-        let fullDate = parseDateTimePart(state, val)
+      parseTimePart(state, val)
+      return TomlValueRef(kind: TomlValueKind.Time, timeVal: val)
+    of '-':
+      if d != 4:
+        raise newTomlError(state, "wrong number of characters for year")
+      var val: TomlDateTime
+      val.date.year = yoh
+      let fullDate = parseDateTimePart(state, val)
 
-        if fullDate:
-          return TomlValueRef(kind: TomlValueKind.DateTime,
-                                dateTimeVal: val)
-        else:
-          return TomlValueRef(kind: TomlValueKind.Date,
-                                dateVal: val.date)
-      of strutils.Digits:
-        if d == 4:
-          raise newTomlError(state, "leading zero not allowed")
-        try:
-          yoh *= 10
-          yoh += ord(nextChar) - ord('0')
-          d += 1
-        except OverflowDefect:
-          raise newTomlError(state, "number larger than 64 bits wide")
-        continue
-      of strutils.Whitespace:
+      if fullDate:
+        return TomlValueRef(kind: TomlValueKind.DateTime, dateTimeVal: val)
+      else:
+        return TomlValueRef(kind: TomlValueKind.Date, dateVal: val.date)
+    of strutils.Digits:
+      if d == 4:
         raise newTomlError(state, "leading zero not allowed")
-      else: raise newTomlError(state, "illegal character")
+      try:
+        yoh *= 10
+        yoh += ord(nextChar) - ord('0')
+        d += 1
+      except OverflowDefect:
+        raise newTomlError(state, "number larger than 64 bits wide")
+      continue
+    of strutils.Whitespace:
+      raise newTomlError(state, "leading zero not allowed")
+    else:
+      raise newTomlError(state, "illegal character")
     break
 
 proc parseFloat(state: var ParserState, intPart: int, forcedSign: Sign): TomlValueRef =
@@ -760,7 +812,15 @@
       pow(10.0, exponent.float64) * (float64(intPart) - decimalPart)
     else:
       pow(10.0, exponent.float64) * (float64(intPart) + decimalPart)
-  return TomlValueRef(kind: TomlValueKind.Float, floatVal: if forcedSign != Neg: -value else: value, forcedSign: forcedSign)
+  return TomlValueRef(
+    kind: TomlValueKind.Float,
+    floatVal:
+      if forcedSign != Neg:
+        -value
+      else:
+        value,
+    forcedSign: forcedSign,
+  )
 
 proc parseNumOrDate(state: var ParserState): TomlValueRef =
   var
@@ -769,113 +829,135 @@
 
   while true:
     nextChar = state.getNextChar()
-    case nextChar:
-      of '0':
-        nextChar = state.getNextChar()
-        if forcedSign == None:
-          if nextChar in {'b', 'x', 'o'}:
+    case nextChar
+    of '0':
+      nextChar = state.getNextChar()
+      if forcedSign == None:
+        if nextChar in {'b', 'x', 'o'}:
+          state.pushBackChar(nextChar)
+          return parseEncoding(state)
+        else:
+          # This must now be a float or a date/time, or a sole 0
+          case nextChar
+          of '.':
+            return parseFloat(state, 0, forcedSign)
+          of strutils.Whitespace:
             state.pushBackChar(nextChar)
-            return parseEncoding(state)
+            return TomlValueRef(kind: TomlValueKind.Int, intVal: 0)
+          of strutils.Digits:
+            # This must now be a date/time
+            return
+              parseDateOrTime(state, digits = 2, yearOrHour = ord(nextChar) - ord('0'))
           else:
-            # This must now be a float or a date/time, or a sole 0
-            case nextChar:
-              of '.':
-                return parseFloat(state, 0, forcedSign)
-              of strutils.Whitespace:
-                state.pushBackChar(nextChar)
-                return TomlValueRef(kind: TomlValueKind.Int, intVal: 0)
-              of strutils.Digits:
-                # This must now be a date/time
-                return parseDateOrTime(state, digits = 2, yearOrHour = ord(nextChar) - ord('0'))
+            # else is a sole 0
+            return TomlValueRef(kind: TomlValueKind.Int, intVal: 0)
+      else:
+        # This must now be a float, or a sole 0
+        case nextChar
+        of '.':
+          return parseFloat(state, 0, forcedSign)
+        of strutils.Whitespace:
+          state.pushBackChar(nextChar)
+          return TomlValueRef(kind: TomlValueKind.Int, intVal: 0)
+        else:
+          # else is a sole 0
+          return TomlValueRef(kind: TomlValueKind.Int, intVal: 0)
+    of strutils.Digits - {'0'}:
+      # This might be a date/time, or an int or a float
+      var
+        digits = 1
+        curSum = ord('0') - ord(nextChar)
+        wasUnderscore = false
+      while true:
+        nextChar = state.getNextChar()
+        if wasUnderscore and nextChar notin strutils.Digits:
+          raise newTomlError(state, "underscores must be surrounded by digits")
+        case nextChar
+        of ':':
+          if digits != 2:
+            raise newTomlError(state, "wrong number of characters for hour")
+          var val: TomlTime
+          val.hour = -curSum
+          parseTimePart(state, val)
+          return TomlValueRef(kind: TomlValueKind.Time, timeVal: val)
+        of '-':
+          if digits != 4:
+            raise newTomlError(state, "wrong number of characters for year")
+          var val: TomlDateTime
+          val.date.year = -curSum
+          let fullDate = parseDateTimePart(state, val)
+          if fullDate:
+            return TomlValueRef(kind: TomlValueKind.DateTime, dateTimeVal: val)
+          else:
+            return TomlValueRef(kind: TomlValueKind.Date, dateVal: val.date)
+        of '.':
+          return parseFloat(state, curSum, forcedSign)
+        of 'e', 'E':
+          var exponent = parseInt(state, base10, LeadingChar.AllowZero)
+          let value = pow(10.0, exponent.float64) * float64(curSum)
+          return TomlValueRef(
+            kind: TomlValueKind.Float,
+            floatVal:
+              if forcedSign != Neg:
+                -value
               else:
-                # else is a sole 0
-                return TomlValueRef(kind: TomlValueKind.Int, intVal: 0)
-        else:
-          # This must now be a float, or a sole 0
-          case nextChar:
-            of '.':
-              return parseFloat(state, 0, forcedSign)
-            of strutils.Whitespace:
-              state.pushBackChar(nextChar)
-              return TomlValueRef(kind: TomlValueKind.Int, intVal: 0)
-            else:
-              # else is a sole 0
-              return TomlValueRef(kind: TomlValueKind.Int, intVal: 0)
-      of strutils.Digits - {'0'}:
-        # This might be a date/time, or an int or a float
-        var
-          digits = 1
-          curSum = ord('0') - ord(nextChar)
+                value,
+          )
+        of strutils.Digits:
+          try:
+            curSum *= 10
+            curSum += ord('0') - ord(nextChar)
+            digits += 1
+          except OverflowDefect:
+            raise newTomlError(state, "number larger than 64 bits wide")
           wasUnderscore = false
-        while true:
-          nextChar = state.getNextChar()
-          if wasUnderscore and nextChar notin strutils.Digits:
-            raise newTomlError(state, "underscores must be surrounded by digits")
-          case nextChar:
-            of ':':
-              if digits != 2:
-                raise newTomlError(state, "wrong number of characters for hour")
-              var val: TomlTime
-              val.hour = -curSum
-              parseTimePart(state, val)
-              return TomlValueRef(kind: TomlValueKind.Time, timeVal: val)
-            of '-':
-              if digits != 4:
-                raise newTomlError(state, "wrong number of characters for year")
-              var val: TomlDateTime
-              val.date.year = -curSum
-              let fullDate = parseDateTimePart(state, val)
-              if fullDate:
-                return TomlValueRef(kind: TomlValueKind.DateTime,
-                                      dateTimeVal: val)
+          continue
+        of '_':
+          wasUnderscore = true
+          continue
+        of strutils.Whitespace:
+          state.pushBackChar(nextChar)
+          return TomlValueRef(
+            kind: TomlValueKind.Int,
+            intVal:
+              if forcedSign != Neg:
+                -curSum
+              else:
+                curSum,
+          )
+        else:
+          state.pushBackChar(nextChar)
+          return TomlValueRef(
+            kind: TomlValueKind.Int,
+            intVal:
+              if forcedSign != Neg:
+                -curSum
               else:
-                return TomlValueRef(kind: TomlValueKind.Date,
-                                      dateVal: val.date)
-            of '.':
-              return parseFloat(state, curSum, forcedSign)
-            of 'e', 'E':
-              var exponent = parseInt(state, base10, LeadingChar.AllowZero)
-              let value = pow(10.0, exponent.float64) * float64(curSum)
-              return TomlValueRef(kind: TomlValueKind.Float, floatVal: if forcedSign != Neg: -value else: value)
-            of strutils.Digits:
-              try:
-                curSum *= 10
-                curSum += ord('0') - ord(nextChar)
-                digits += 1
-              except OverflowDefect:
-                raise newTomlError(state, "number larger than 64 bits wide")
-              wasUnderscore = false
-              continue
-            of '_':
-              wasUnderscore = true
-              continue
-            of strutils.Whitespace:
-              state.pushBackChar(nextChar)
-              return TomlValueRef(kind: TomlValueKind.Int, intVal: if forcedSign != Neg: -curSum else: curSum)
-            else:
-              state.pushBackChar(nextChar)
-              return TomlValueRef(kind: TomlValueKind.Int, intVal: if forcedSign != Neg: -curSum else: curSum)
-          break
-      of '+', '-':
-        forcedSign = if nextChar == '+': Pos else: Neg
-        continue
-      of 'i':
-        #  Is this "inf"?
-        let oldState = state
-        if state.getNextChar() != 'n' or
-           state.getNextChar() != 'f':
-            raise(newTomlError(oldState, "unknown identifier"))
-        return TomlValueRef(kind: TomlValueKind.Float, floatVal: if forcedSign == Neg: NegInf else: Inf, forcedSign: forcedSign)
-
-      of 'n':
-        #  Is this "nan"?
-        let oldState = state
-        if state.getNextChar() != 'a' or
-           state.getNextChar() != 'n':
-            raise(newTomlError(oldState, "unknown identifier"))
-        return TomlValueRef(kind: TomlValueKind.Float, floatVal: NaN, forcedSign: forcedSign)
-      else:
-        raise newTomlError(state, "illegal character " & escape($nextChar))
+                curSum,
+          )
+        break
+    of '+', '-':
+      forcedSign = if nextChar == '+': Pos else: Neg
+      continue
+    of 'i':
+      #  Is this "inf"?
+      let oldState = state
+      if state.getNextChar() != 'n' or state.getNextChar() != 'f':
+        raise (newTomlError(oldState, "unknown identifier"))
+      return TomlValueRef(
+        kind: TomlValueKind.Float,
+        floatVal: if forcedSign == Neg: NegInf else: Inf,
+        forcedSign: forcedSign,
+      )
+    of 'n':
+      #  Is this "nan"?
+      let oldState = state
+      if state.getNextChar() != 'a' or state.getNextChar() != 'n':
+        raise (newTomlError(oldState, "unknown identifier"))
+      return
+        TomlValueRef(kind: TomlValueKind.Float, floatVal: NaN, forcedSign: forcedSign)
+    else:
+      raise newTomlError(state, "illegal character " & escape($nextChar))
     break
 
 proc parseValue(state: var ParserState): TomlValueRef =
@@ -889,39 +971,32 @@
   of 't':
     # Is this "true"?
     let oldState = state # Only used for error messages
-    if state.getNextChar() != 'r' or
-       state.getNextChar() != 'u' or
-       state.getNextChar() != 'e':
-        raise(newTomlError(oldState, "unknown identifier"))
+    if state.getNextChar() != 'r' or state.getNextChar() != 'u' or
+        state.getNextChar() != 'e':
+      raise (newTomlError(oldState, "unknown identifier"))
     result = TomlValueRef(kind: TomlValueKind.Bool, boolVal: true)
-
   of 'f':
     # Is this "false"?
     let oldState = state # Only used for error messages
-    if state.getNextChar() != 'a' or
-       state.getNextChar() != 'l' or
-       state.getNextChar() != 's' or
-       state.getNextChar() != 'e':
-        raise(newTomlError(oldState, "unknown identifier"))
+    if state.getNextChar() != 'a' or state.getNextChar() != 'l' or
+        state.getNextChar() != 's' or state.getNextChar() != 'e':
+      raise (newTomlError(oldState, "unknown identifier"))
     result = TomlValueRef(kind: TomlValueKind.Bool, boolVal: false)
-
   of '\"':
     # A basic string (accepts \ escape codes)
-    result = TomlValueRef(kind: TomlValueKind.String,
-                          stringVal: parseString(state, StringType.Basic))
-
+    result = TomlValueRef(
+      kind: TomlValueKind.String, stringVal: parseString(state, StringType.Basic)
+    )
   of '\'':
     # A literal string (does not accept \ escape codes)
-    result = TomlValueRef(kind: TomlValueKind.String,
-                          stringVal: parseString(state, StringType.Literal))
-
+    result = TomlValueRef(
+      kind: TomlValueKind.String, stringVal: parseString(state, StringType.Literal)
+    )
   of '[':
     # An array
-    result = TomlValueRef(kind: TomlValueKind.Array,
-                          arrayVal: parseArray(state))
+    result = TomlValueRef(kind: TomlValueKind.Array, arrayVal: parseArray(state))
   else:
-    raise(newTomlError(state,
-                       "unexpected character " & escape($nextChar)))
+    raise (newTomlError(state, "unexpected character " & escape($nextChar)))
 
 proc parseName(state: var ParserState): string =
   # This parses the name of a key or a table
@@ -939,18 +1014,17 @@
       # Any of the above characters marks the end of the name
       state.pushBackChar(nextChar)
       break
-    elif (nextChar notin {'a'..'z', 'A'..'Z', '0'..'9', '_', '-'}):
-      raise(newTomlError(state,
-        "bare key has illegal character: " & escape($nextChar)))
+    elif (nextChar notin {'a' .. 'z', 'A' .. 'Z', '0' .. '9', '_', '-'}):
+      raise
+        (newTomlError(state, "bare key has illegal character: " & escape($nextChar)))
     else:
       result.add(nextChar)
 
-type
-  BracketType {.pure.} = enum
-    single, double
+type BracketType {.pure.} = enum
+  single
+  double
 
-proc parseTableName(state: var ParserState,
-                    brackets: BracketType): seq[string] =
+proc parseTableName(state: var ParserState, brackets: BracketType): seq[string] =
   # This code assumes that '[' has already been consumed
   result = newSeq[string](0)
 
@@ -972,21 +1046,18 @@
       if brackets == BracketType.double:
         nextChar = state.getNextChar()
         if nextChar != ']':
-          raise(newTomlError(state,
-                             "\"]]\" expected"))
+          raise (newTomlError(state, "\"]]\" expected"))
 
       # We must check that there is nothing else in this line
       nextChar = state.getNextNonWhitespace(skipNoLf)
       if nextChar notin {'\l', '\0'}:
-        raise(newTomlError(state,
-                           "unexpected character " & escape($nextChar)))
+        raise (newTomlError(state, "unexpected character " & escape($nextChar)))
 
       break
-
-    of '.': continue
+    of '.':
+      continue
     else:
-      raise(newTomlError(state,
-                         "unexpected character " & escape($nextChar)))
+      raise (newTomlError(state, "unexpected character " & escape($nextChar)))
 
 proc setEmptyTableVal(val: var TomlValueRef) =
   val = TomlValueRef(kind: TomlValueKind.Table)
@@ -1004,7 +1075,7 @@
       return
     of ',':
       if firstComma:
-        raise(newTomlError(state, "first inline table element missing"))
+        raise (newTomlError(state, "first inline table element missing"))
       # Check that this is not a terminating comma (like in
       #  "[b,]")
       nextChar = state.getNextNonWhitespace(skipNoLf)
@@ -1013,7 +1084,7 @@
 
       state.pushBackChar(nextChar)
     of '\n':
-      raise(newTomlError(state, "inline tables cannot contain newlines"))
+      raise (newTomlError(state, "inline tables cannot contain newlines"))
     else:
       firstComma = false
       state.pushBackChar(nextChar)
@@ -1030,8 +1101,7 @@
         nextChar = state.getNextNonWhitespace(skipNoLf)
 
       if nextChar != '=':
-        raise(newTomlError(state,
-                           "key names cannot contain spaces"))
+        raise (newTomlError(state, "key names cannot contain spaces"))
       nextChar = state.getNextNonWhitespace(skipNoLf)
       if nextChar == '{':
         curTable[key] = state.parseInlineTable()
@@ -1039,11 +1109,11 @@
         state.pushBackChar(nextChar)
         curTable[key] = state.parseValue()
 
-proc createTableDef(state: var ParserState,
-                    tableNames: seq[string],
-                    dotted = false)
+proc createTableDef(
+  state: var ParserState, tableNames: seq[string], dotted = false
+) {.gcsafe.}
 
-proc parseKeyValuePair(state: var ParserState) =
+proc parseKeyValuePair(state: var ParserState) {.gcsafe.} =
   var
     tableKeys: seq[string]
     key: string
@@ -1063,8 +1133,8 @@
       break
 
   if nextChar != '=':
-    raise(newTomlError(state,
-                       "key names cannot contain character \"" & nextChar & "\""))
+    raise
+      (newTomlError(state, "key names cannot contain character \"" & nextChar & "\""))
 
   nextChar = state.getNextNonWhitespace(skipNoLf)
   # Check that this is a regular value and not an inline table
@@ -1075,12 +1145,10 @@
     # We must check that there is nothing else in this line
     nextChar = state.getNextNonWhitespace(skipNoLf)
     if nextChar != '\l' and nextChar != '\0':
-      raise(newTomlError(state,
-                         "unexpected character " & escape($nextChar)))
+      raise (newTomlError(state, "unexpected character " & escape($nextChar)))
 
     if state.curTableRef.hasKey(key):
-      raise(newTomlError(state,
-                         "duplicate key, \"" & key & "\" already in table"))
+      raise (newTomlError(state, "duplicate key, \"" & key & "\" already in table"))
     state.curTableRef[key] = value
   else:
     #createTableDef(state, @[key])
@@ -1092,16 +1160,14 @@
 
   state.curTableRef = oldTableRef
 
-proc newParserState(s: streams.Stream,
-                    fileName: string = ""): ParserState =
+proc newParserState(s: streams.Stream, fileName: string = ""): ParserState =
   result = ParserState(fileName: fileName, line: 1, column: 1, stream: s)
 
 proc setArrayVal(val: var TomlValueRef, numOfElems: int = 0) =
   val = TomlValueRef(kind: TomlValueKind.Array)
   val.arrayVal = newSeq[TomlValueRef](numOfElems)
 
-proc advanceToNextNestLevel(state: var ParserState,
-                            tableName: string) =
+proc advanceToNextNestLevel(state: var ParserState, tableName: string) =
   let target = state.curTableRef[tableName]
   case target.kind
   of TomlValueKind.Table:
@@ -1109,12 +1175,10 @@
   of TomlValueKind.Array:
     let arr = target.arrayVal[high(target.arrayVal)]
     if arr.kind != TomlValueKind.Table:
-      raise(newTomlError(state, "\"" & tableName &
-                         "\" elements are not tables"))
+      raise (newTomlError(state, "\"" & tableName & "\" elements are not tables"))
     state.curTableRef = arr.tableVal
   else:
-    raise(newTomlError(state, "\"" & tableName &
-                       "\" is not a table"))
+    raise (newTomlError(state, "\"" & tableName & "\" is not a table"))
 
 # This function is called by the TOML parser whenever a
 # "[[table.name]]" line is encountered in the parsing process. Its
@@ -1134,13 +1198,11 @@
 # array is created, and an empty table element is added in "c". In
 # either cases, curTableRef will refer to the last element of "c".
 
-proc createOrAppendTableArrayDef(state: var ParserState,
-                                 tableNames: seq[string]) =
+proc createOrAppendTableArrayDef(state: var ParserState, tableNames: seq[string]) =
   # This is a table array entry (e.g. "[[entry]]")
   for idx, tableName in tableNames:
     if tableName.len == 0:
-      raise(newTomlError(state,
-                         "empty key not allowed"))
+      raise (newTomlError(state, "empty key not allowed"))
     let lastTableInChain = idx == high(tableNames)
 
     var newValue: TomlValueRef
@@ -1173,8 +1235,7 @@
 
       if lastTableInChain:
         if target.kind != TomlValueKind.Array:
-          raise(newTomlError(state, "\"" & tableName &
-                                    "\" is not an array"))
+          raise (newTomlError(state, "\"" & tableName & "\" is not an array"))
 
         var newValue: TomlValueRef
         new(newValue)
@@ -1191,16 +1252,13 @@
 # code will look for the "b" table that is child of "a" and it will
 # create a new table "c" which is "b"'s children.
 
-proc createTableDef(state: var ParserState,
-                    tableNames: seq[string],
-                    dotted = false) =
+proc createTableDef(state: var ParserState, tableNames: seq[string], dotted = false) =
   var newValue: TomlValueRef
 
   # This starts a new table (e.g. "[table]")
   for i, tableName in tableNames:
     if tableName.len == 0:
-      raise(newTomlError(state,
-                         "empty key not allowed"))
+      raise (newTomlError(state, "empty key not allowed"))
     if not state.curTableRef.hasKey(tableName):
       new(newValue)
       setEmptyTableVal(newValue)
@@ -1212,7 +1270,7 @@
       state.curTableRef = newValue.tableVal
     else:
       if i == tableNames.high and state.curTableRef.hasKey(tableName) and
-        state.curTableRef[tableName].kind == TomlValueKind.Table:
+          state.curTableRef[tableName].kind == TomlValueKind.Table:
         if state.curTableRef[tableName].tableVal.len == 0:
           raise newTomlError(state, "duplicate table key not allowed")
         elif not dotted:
@@ -1221,14 +1279,17 @@
               raise newTomlError(state, "duplicate table key not allowed")
       advanceToNextNestLevel(state, tableName)
 
-proc parseStream*(inputStream: streams.Stream,
-                  fileName: string = ""): TomlValueRef =
+proc parseStream*(
+    inputStream: streams.Stream, fileName: string = ""
+): TomlValueRef {.gcsafe.} =
   ## Parses a stream of TOML formatted data into a TOML table. The optional
   ## filename is used for error messages.
   if inputStream == nil:
-    raise newException(IOError,
+    raise newException(
+      IOError,
       "Unable to read from the stream created from: \"" & fileName & "\", " &
-      "possibly a missing file")
+        "possibly a missing file",
+    )
   var state = newParserState(inputStream, fileName)
   result = TomlValueRef(kind: TomlValueKind.Table)
   new(result.tableVal)
@@ -1275,12 +1336,10 @@
         createOrAppendTableArrayDef(state, tableNames)
       else:
         createTableDef(state, tableNames)
-
     of '=':
-      raise(newTomlError(state, "key name missing"))
+      raise (newTomlError(state, "key name missing"))
     of '#', '.', ']':
-      raise(newTomlError(state,
-                         "unexpected character " & escape($nextChar)))
+      raise (newTomlError(state, "unexpected character " & escape($nextChar)))
     of '\0': # EOF
       return
     else:
@@ -1318,28 +1377,38 @@
   else:
     raise newException(IOError, "cannot open: " & fileName)
 
-
 proc `$`*(val: TomlDate): string =
   ## Converts the TOML date object into the ISO format read by the parser
-  result = ($val.year).align(4, '0') & "-" & ($val.month).align(2, '0') & "-" &
+  result =
+    ($val.year).align(4, '0') & "-" & ($val.month).align(2, '0') & "-" &
     ($val.day).align(2, '0')
 
 proc `$`*(val: TomlTime): string =
   ## Converts the TOML time object into the ISO format read by the parser
-  result = ($val.hour).align(2, '0') & ":" &
-    ($val.minute).align(2, '0') & ":" & ($val.second).align(2, '0') &
-    (if val.subsecond > 0: ("." & $val.subsecond) else: "")
+  result =
+    ($val.hour).align(2, '0') & ":" & ($val.minute).align(2, '0') & ":" &
+    ($val.second).align(2, '0') & (
+      if val.subsecond > 0: ("." & $val.subsecond) else: ""
+    )
 
 proc `$`*(val: TomlDateTime): string =
   ## Converts the TOML date-time object into the ISO format read by the parser
-  result = $val.date & "T" & $val.time &
-    (if not val.shift: "" else: (
-      (if val.zoneHourShift == 0 and val.zoneMinuteShift == 0: "Z" else: (
-        ((if val.isShiftPositive: "+" else: "-") &
-        ($val.zoneHourShift).align(2, '0') & ":" &
-        ($val.zoneMinuteShift).align(2, '0'))
-      ))
-    ))
+  result =
+    $val.date & "T" & $val.time & (
+      if not val.shift: ""
+      else: (
+        (
+          if val.zoneHourShift == 0 and val.zoneMinuteShift == 0: "Z"
+          else: (
+            (
+              (if val.isShiftPositive: "+" else: "-") &
+              ($val.zoneHourShift).align(2, '0') & ":" &
+              ($val.zoneMinuteShift).align(2, '0')
+            )
+          )
+        )
+      )
+    )
 
 proc toTomlString*(value: TomlValueRef): string
 
@@ -1404,8 +1473,9 @@
     if val.kind == TomlValueKind.Table:
       echo space & key & " = table"
       dump(val.tableVal, indentLevel + 4)
-    elif (val.kind == TomlValueKind.Array and
-        val.arrayVal[0].kind == TomlValueKind.Table):
+    elif (
+      val.kind == TomlValueKind.Array and val.arrayVal[0].kind == TomlValueKind.Table
+    ):
       for idx, val in val.arrayVal:
         echo space & key & "[" & $idx & "] = table"
         dump(val.tableVal, indentLevel + 4)
@@ -1428,55 +1498,54 @@
   ## Converts a TOML value to a JSON node. This uses the format specified in
   ## the validation suite for it's output:
   ## https://github.com/BurntSushi/toml-test#example-json-encoding
-  case value.kind:
-    of TomlValueKind.Int:
-      %*{"type": "integer", "value": $value.intVal}
-    of TomlValueKind.Float:
-      if classify(value.floatVal) == fcNan:
-        if value.forcedSign != Pos:
-          %*{"type": "float", "value": $value.floatVal}
-        else:
-          %*{"type": "float", "value": "+" & $value.floatVal}
-      else:
+  case value.kind
+  of TomlValueKind.Int:
+    %*{"type": "integer", "value": $value.intVal}
+  of TomlValueKind.Float:
+    if classify(value.floatVal) == fcNan:
+      if value.forcedSign != Pos:
         %*{"type": "float", "value": $value.floatVal}
-    of TomlValueKind.Bool:
-      %*{"type": "bool", "value": $value.boolVal}
-    of TomlValueKind.Datetime:
-      if value.dateTimeVal.shift == false:
-        %*{"type": "datetime-local", "value": $value.dateTimeVal}
       else:
-        %*{"type": "datetime", "value": $value.dateTimeVal}
-    of TomlValueKind.Date:
-      %*{"type": "date", "value": $value.dateVal}
-    of TomlValueKind.Time:
-      %*{"type": "time", "value": $value.timeVal}
-    of TomlValueKind.String:
-      %*{"type": "string", "value": newJString(value.stringVal)}
-    of TomlValueKind.Array:
-      if value.arrayVal.len == 0:
-        when defined(newtestsuite):
-          %[]
-        else:
-          %*{"type": "array", "value": []}
-      elif value.arrayVal[0].kind == TomlValueKind.Table:
-        %value.arrayVal.map(toJson)
+        %*{"type": "float", "value": "+" & $value.floatVal}
+    else:
+      %*{"type": "float", "value": $value.floatVal}
+  of TomlValueKind.Bool:
+    %*{"type": "bool", "value": $value.boolVal}
+  of TomlValueKind.Datetime:
+    if value.dateTimeVal.shift == false:
+      %*{"type": "datetime-local", "value": $value.dateTimeVal}
+    else:
+      %*{"type": "datetime", "value": $value.dateTimeVal}
+  of TomlValueKind.Date:
+    %*{"type": "date", "value": $value.dateVal}
+  of TomlValueKind.Time:
+    %*{"type": "time", "value": $value.timeVal}
+  of TomlValueKind.String:
+    %*{"type": "string", "value": newJString(value.stringVal)}
+  of TomlValueKind.Array:
+    if value.arrayVal.len == 0:
+      when defined(newtestsuite):
+        %[]
       else:
-        when defined(newtestsuite):
-          %*value.arrayVal.map(toJson)
-        else:
-          %*{"type": "array", "value": value.arrayVal.map(toJson)}
-    of TomlValueKind.Table:
-      value.tableVal.toJson
-    of TomlValueKind.None:
-      %*{"type": "ERROR"}
+        %*{"type": "array", "value": []}
+    elif value.arrayVal[0].kind == TomlValueKind.Table:
+      %value.arrayVal.map(toJson)
+    else:
+      when defined(newtestsuite):
+        %*value.arrayVal.map(toJson)
+      else:
+        %*{"type": "array", "value": value.arrayVal.map(toJson)}
+  of TomlValueKind.Table:
+    value.tableVal.toJson
+  of TomlValueKind.None:
+    %*{"type": "ERROR"}
 
 proc toKey(str: string): string =
   for c in str:
-    if (c notin {'a'..'z', 'A'..'Z', '0'..'9', '_', '-'}):
+    if (c notin {'a' .. 'z', 'A' .. 'Z', '0' .. '9', '_', '-'}):
       return "\"" & str & "\""
   str
 
-
 proc toTomlString*(value: TomlTableRef, parents = ""): string =
   ## Converts a TOML table to a TOML formatted string for output to a file.
   result = ""
@@ -1485,9 +1554,8 @@
     block outer:
       if value.kind == TomlValueKind.Table:
         subtables.add((key: key, value: value))
-      elif value.kind == TomlValueKind.Array and
-           value.arrayVal.len > 0 and
-           value.arrayVal[0].kind == TomlValueKind.Table:
+      elif value.kind == TomlValueKind.Array and value.arrayVal.len > 0 and
+          value.arrayVal[0].kind == TomlValueKind.Table:
         let tables = value.arrayVal.map(toTomlString)
         for table in tables:
           result = result & "[[" & key & "]]\n" & table & "\n"
@@ -1498,19 +1566,25 @@
     block outer:
       for ikey, ivalue in pairs(kv.value.tableVal):
         if ivalue.kind != TomlValueKind.Table:
-          result = result & "[" & fullKey & "]\n" &
-            kv.value.tableVal.toTomlString(fullKey) & "\n"
+          result =
+            result & "[" & fullKey & "]\n" & kv.value.tableVal.toTomlString(fullKey) &
+            "\n"
           break outer
       result = result & kv.value.tableVal.toTomlString(fullKey)
 
 proc toTomlString*(value: TomlValueRef): string =
   ## Converts a TOML value to a TOML formatted string for output to a file.
-  case value.kind:
-  of TomlValueKind.Int: $value.intVal
-  of TomlValueKind.Float: $value.floatVal
-  of TomlValueKind.Bool: $value.boolVal
-  of TomlValueKind.Datetime: $value.dateTimeVal
-  of TomlValueKind.String: "\"" & value.stringVal & "\""
+  case value.kind
+  of TomlValueKind.Int:
+    $value.intVal
+  of TomlValueKind.Float:
+    $value.floatVal
+  of TomlValueKind.Bool:
+    $value.boolVal
+  of TomlValueKind.Datetime:
+    $value.dateTimeVal
+  of TomlValueKind.String:
+    "\"" & value.stringVal & "\""
   of TomlValueKind.Array:
     if value.arrayVal.len == 0:
       "[]"
@@ -1518,7 +1592,8 @@
       value.arrayVal.map(toTomlString).join("\n")
     else:
       "[" & value.arrayVal.map(toTomlString).join(", ") & "]"
-  of TomlValueKind.Table: value.tableVal.toTomlString
+  of TomlValueKind.Table:
+    value.tableVal.toTomlString
   else:
     "UNKNOWN"
 
@@ -1556,53 +1631,69 @@
   ## Retrieves the string value of a `TomlValueKind.String TomlValueRef`.
   ##
   ## Returns ``default`` if ``n`` is not a ``TomlValueKind.String``, or if ``n`` is nil.
-  if n.isNil or n.kind != TomlValueKind.String: return default
-  else: return n.stringVal
+  if n.isNil or n.kind != TomlValueKind.String:
+    return default
+  else:
+    return n.stringVal
 
 proc getInt*(n: TomlValueRef, default: int = 0): int =
   ## Retrieves the int value of a `TomlValueKind.Int TomlValueRef`.
   ##
   ## Returns ``default`` if ``n`` is not a ``TomlValueKind.Int``, or if ``n`` is nil.
-  if n.isNil or n.kind != TomlValueKind.Int: return default
-  else: return int(n.intVal)
+  if n.isNil or n.kind != TomlValueKind.Int:
+    return default
+  else:
+    return int(n.intVal)
 
 proc getBiggestInt*(n: TomlValueRef, default: int64 = 0): int64 =
   ## Retrieves the int64 value of a `TomlValueKind.Int TomlValueRef`.
   ##
   ## Returns ``default`` if ``n`` is not a ``TomlValueKind.Int``, or if ``n`` is nil.
-  if n.isNil or n.kind != TomlValueKind.Int: return default
-  else: return n.intVal
+  if n.isNil or n.kind != TomlValueKind.Int:
+    return default
+  else:
+    return n.intVal
 
 proc getFloat*(n: TomlValueRef, default: float = 0.0): float =
   ## Retrieves the float value of a `TomlValueKind.Float TomlValueRef`.
   ##
   ## Returns ``default`` if ``n`` is not a ``TomlValueKind.Float`` or ``TomlValueKind.Int``, or if ``n`` is nil.
-  if n.isNil: return default
+  if n.isNil:
+    return default
   case n.kind
-  of TomlValueKind.Float: return n.floatVal
-  of TomlValueKind.Int: return float(n.intVal)
-  else: return default
+  of TomlValueKind.Float:
+    return n.floatVal
+  of TomlValueKind.Int:
+    return float(n.intVal)
+  else:
+    return default
 
 proc getBool*(n: TomlValueRef, default: bool = false): bool =
   ## Retrieves the bool value of a `TomlValueKind.Bool TomlValueRef`.
   ##
   ## Returns ``default`` if ``n`` is not a ``TomlValueKind.Bool``, or if ``n`` is nil.
-  if n.isNil or n.kind != TomlValueKind.Bool: return default
-  else: return n.boolVal
+  if n.isNil or n.kind != TomlValueKind.Bool:
+    return default
+  else:
+    return n.boolVal
 
 proc getTable*(n: TomlValueRef, default = new(TomlTableRef)): TomlTableRef =
   ## Retrieves the key, value pairs of a `TomlValueKind.Table TomlValueRef`.
   ##
   ## Returns ``default`` if ``n`` is not a ``TomlValueKind.Table``, or if ``n`` is nil.
-  if n.isNil or n.kind != TomlValueKind.Table: return default
-  else: return n.tableVal
+  if n.isNil or n.kind != TomlValueKind.Table:
+    return default
+  else:
+    return n.tableVal
 
 proc getElems*(n: TomlValueRef, default: seq[TomlValueRef] = @[]): seq[TomlValueRef] =
   ## Retrieves the int value of a `TomlValueKind.Array TomlValueRef`.
   ##
   ## Returns ``default`` if ``n`` is not a ``TomlValueKind.Array``, or if ``n`` is nil.
-  if n.isNil or n.kind != TomlValueKind.Array: return default
-  else: return n.arrayVal
+  if n.isNil or n.kind != TomlValueKind.Array:
+    return default
+  else:
+    return n.arrayVal
 
 proc add*(father, child: TomlValueRef) =
   ## Adds `child` to a TomlValueKind.Array node `father`.
@@ -1632,16 +1723,20 @@
 
 proc `?`*(keyVals: openArray[tuple[key: string, val: TomlValueRef]]): TomlValueRef =
   ## Generic constructor for TOML data. Creates a new `TomlValueKind.Table TomlValueRef`
-  if keyVals.len == 0: return newTArray()
+  if keyVals.len == 0:
+    return newTArray()
   result = newTTable()
-  for key, val in items(keyVals): result.tableVal[key] = val
+  for key, val in items(keyVals):
+    result.tableVal[key] = val
 
-template `?`*(j: TomlValueRef): TomlValueRef = j
+template `?`*(j: TomlValueRef): TomlValueRef =
+  j
 
 proc `?`*[T](elements: openArray[T]): TomlValueRef =
   ## Generic constructor for TOML data. Creates a new `TomlValueKind.Array TomlValueRef`
   result = newTArray()
-  for elem in elements: result.add(?elem)
+  for elem in elements:
+    result.add(?elem)
 
 when false:
   # For 'consistency' we could do this, but that only pushes people further
@@ -1659,7 +1754,8 @@
 proc `?`*(o: object): TomlValueRef =
   ## Generic constructor for TOML data. Creates a new `TomlValueKind.Table TomlValueRef`
   result = newTTable()
-  for k, v in o.fieldPairs: result[k] = ?v
+  for k, v in o.fieldPairs:
+    result[k] = ?v
 
 proc `?`*(o: ref object): TomlValueRef =
   ## Generic constructor for TOML data. Creates a new `TomlValueKind.Table TomlValueRef`
@@ -1678,13 +1774,15 @@
 proc toToml(x: NimNode): NimNode {.compileTime.} =
   case x.kind
   of nnkBracket: # array
-    if x.len == 0: return newCall(bindSym"newTArray")
+    if x.len == 0:
+      return newCall(bindSym"newTArray")
     result = newNimNode(nnkBracket)
     for i in 0 ..< x.len:
       result.add(toToml(x[i]))
     result = newCall(bindSym("?", brOpen), result)
   of nnkTableConstr: # object
-    if x.len == 0: return newCall(bindSym"newTTable")
+    if x.len == 0:
+      return newCall(bindSym"newTTable")
     result = newNimNode(nnkTableConstr)
     for i in 0 ..< x.len:
       x[i].expectKind nnkExprColonExpr
@@ -1714,7 +1812,7 @@
     curTable: NimNode = nil
   while i < x.len:
     echo x[i].kind
-    case x[i].kind:
+    case x[i].kind
     of nnkAsgn:
       if curTable.isNil:
         curTable = newNimNode(nnkTableConstr)
@@ -1726,9 +1824,12 @@
         result = curTable
       else:
         var table = newNimNode(nnkTableConstr)
-        result.add newTree(nnkExprColonExpr, newLit($x[i][0]), newCall(bindSym("?", brOpen), table))
+        result.add newTree(
+          nnkExprColonExpr, newLit($x[i][0]), newCall(bindSym("?", brOpen), table)
+        )
         curTable = table
-    else: discard
+    else:
+      discard
     i += 1
   result = newCall(bindSym("?", brOpen), result)
 
@@ -1738,10 +1839,11 @@
   result = toTomlNew(x)
   echo result.repr
 
-func `==`* (a, b: TomlValueRef): bool =
+func `==`*(a, b: TomlValueRef): bool =
   ## Check two nodes for equality
   if a.isNil:
-    if b.isNil: return true
+    if b.isNil:
+      return true
     return false
   elif b.isNil or a.kind != b.kind:
     return false
@@ -1762,11 +1864,14 @@
     of TomlValueKind.Table:
       # we cannot use OrderedTable's equality here as
       # the order does not matter for equality here.
-      if a.tableVal.len != b.tableVal.len: return false
+      if a.tableVal.len != b.tableVal.len:
+        return false
       for key, val in a.tableVal:
-        if not b.tableVal.hasKey(key): return false
+        if not b.tableVal.hasKey(key):
+          return false
         {.noSideEffect.}:
-          if b.tableVal[key] != val: return false
+          if b.tableVal[key] != val:
+            return false
       result = true
     of TomlValueKind.DateTime:
       result =
@@ -1777,21 +1882,20 @@
         a.dateTimeVal.time.minute == b.dateTimeVal.time.minute and
         a.dateTimeVal.time.second == b.dateTimeVal.time.second and
         a.dateTimeVal.time.subsecond == b.dateTimeVal.time.subsecond and
-        a.dateTimeVal.shift == b.dateTimeVal.shift and
-        (a.dateTimeVal.shift == true and
-          (a.dateTimeVal.isShiftPositive == b.dateTimeVal.isShiftPositive and
-          a.dateTimeVal.zoneHourShift == b.dateTimeVal.zoneHourShift and
-          a.dateTimeVal.zoneMinuteShift == b.dateTimeVal.zoneMinuteShift)) or
-        a.dateTimeVal.shift == false
+        a.dateTimeVal.shift == b.dateTimeVal.shift and (
+          a.dateTimeVal.shift == true and (
+            a.dateTimeVal.isShiftPositive == b.dateTimeVal.isShiftPositive and
+            a.dateTimeVal.zoneHourShift == b.dateTimeVal.zoneHourShift and
+            a.dateTimeVal.zoneMinuteShift == b.dateTimeVal.zoneMinuteShift
+          )
+        ) or a.dateTimeVal.shift == false
     of TomlValueKind.Date:
       result =
-        a.dateVal.year == b.dateVal.year and
-        a.dateVal.month == b.dateVal.month and
+        a.dateVal.year == b.dateVal.year and a.dateVal.month == b.dateVal.month and
         a.dateVal.day == b.dateVal.day
     of TomlValueKind.Time:
       result =
-        a.timeVal.hour == b.timeVal.hour and
-        a.timeVal.minute == b.timeVal.minute and
+        a.timeVal.hour == b.timeVal.hour and a.timeVal.minute == b.timeVal.minute and
         a.timeVal.second == b.timeVal.second and
         a.timeVal.subsecond == b.timeVal.subsecond
 
@@ -1833,9 +1937,12 @@
   ## If `n` is a `TomlValueKind.Table`, it returns the number of pairs.
   ## Else it returns 0.
   case n.kind
-  of TomlValueKind.Array: result = n.arrayVal.len
-  of TomlValueKind.Table: result = n.tableVal.len
-  else: discard
+  of TomlValueKind.Array:
+    result = n.arrayVal.len
+  of TomlValueKind.Table:
+    result = n.tableVal.len
+  else:
+    discard
 
 proc `[]`*(node: TomlValueRef, name: string): TomlValueRef {.inline.} =
   ## Gets a field from a `TomlValueKind.Table`, which must not be nil.
@@ -1867,8 +1974,9 @@
   assert(node.kind == TomlValueKind.Array)
   find(node.arrayVal, val) >= 0
 
-proc existsKey*(node: TomlValueRef, key: string): bool {.deprecated.} = node.hasKey(key)
+proc existsKey*(node: TomlValueRef, key: string): bool {.deprecated.} =
   ## Deprecated for `hasKey`
+  node.hasKey(key)
 
 proc `[]=`*(obj: TomlValueRef, key: string, val: TomlValueRef) {.inline.} =
   ## Sets a field from a `TomlValueKind.Table`.
@@ -1891,17 +1999,19 @@
   if not isNil(node) and node.kind == TomlValueKind.Table:
     result = node.tableVal.getOrDefault(key)
 
-template simpleGetOrDefault*{`{}`(node, [key])}(node: TomlValueRef, key: string): TomlValueRef = node.getOrDefault(key)
+template simpleGetOrDefault*{
+  `{}`(node, [key])}(node: TomlValueRef, key: string): TomlValueRef =
+  node.getOrDefault(key)
 
 proc `{}=`*(node: TomlValueRef, keys: varargs[string], value: TomlValueRef) =
   ## Traverses the node and tries to set the value at the given location
   ## to ``value``. If any of the keys are missing, they are added.
   var node = node
-  for i in 0..(keys.len-2):
+  for i in 0 .. (keys.len - 2):
     if not node.hasKey(keys[i]):
       node[keys[i]] = newTTable()
     node = node[keys[i]]
-  node[keys[keys.len-1]] = value
+  node[keys[keys.len - 1]] = value
 
 proc delete*(obj: TomlValueRef, key: string) =
   ## Deletes ``obj[key]``.