# NimYAML - YAML implementation in Nim # (c) Copyright 2015-2023 Felix Krause # # See the file "copying.txt", included in this # distribution, for details about the copyright. ## ================= ## Module yaml/hints ## ================= ## ## The hints API enables you to guess the type of YAML scalars. import macros import private/internal type TypeHint* = enum ## A type hint can be computed from scalar content and tells you what ## NimYAML thinks the scalar's type is. It is generated by ## `guessType <#guessType,string>`_ The first matching RegEx ## in the following table will be the type hint of a scalar string. ## ## You can use it to determine the type of YAML scalars that have a '?' ## non-specific tag, but using this feature is completely optional. ## ## See also: https://yaml.org/spec/1.2.2/#103-core-schema ## ## ================== ========================= ## Name RegEx ## ================== ========================= ## ``yTypeInteger`` ``[-+]? [0-9]+`` ## ``yTypeFloat`` ``[-+]? ( \. [0-9]+ | [0-9]+ ( \. [0-9]* )? ) ( [eE] [-+]? [0-9]+ )?`` ## ``yTypeFloatInf`` ``[-+]? ( \.inf | \.Inf | \.INF )`` ## ``yTypeFloatNaN`` ``\.nan | \.NaN | \.NAN`` ## ``yTypeBoolTrue`` ``true | True | TRUE`` ## ``yTypeBoolFalse`` ``false | False | FALSE`` ## ``yTypeNull`` ``null | Null | NULL | ~`` ## ``yTypeTimestamp`` see `here `_. ## ``yTypeUnknown`` ``*`` ## ================== ========================= yTypeInteger, yTypeFloat, yTypeFloatInf, yTypeFloatNaN, yTypeBoolTrue, yTypeBoolFalse, yTypeNull, yTypeUnknown, yTypeTimestamp YamlTypeHintState = enum ythInitial, ythF, ythFA, ythFAL, ythFALS, ythFALSE, ythN, ythNU, ythNUL, ythNULL, ythT, ythTR, ythTRU, ythTRUE, ythPoint, ythPointI, ythPointIN, ythPointINF, ythPointN, ythPointNA, ythPointNAN, ythLowerF, ythLowerFA, ythLowerFAL, ythLowerFALS, ythLowerN, ythLowerNU, ythLowerNUL, ythLowerT, ythLowerTR, ythLowerTRU, ythPointLowerI, ythPointLowerIN, ythPointLowerN, ythPointLowerNA, ythMinus, ythPlus, ythInt1, ythInt2, ythInt3, ythInt4, ythInt, ythDecimal, ythNumE, ythNumEPlusMinus, ythExponent, ythYearMinus, ythMonth1, ythMonth2, ythMonthMinus, ythMonthMinusNoYmd, ythDay1, ythDay1NoYmd, ythDay2, ythDay2NoYmd, ythAfterDayT, ythAfterDaySpace, ythHour1, ythHour2, ythHourColon, ythMinute1, ythMinute2, ythMinuteColon, ythSecond1, ythSecond2, ythFraction, ythAfterTimeSpace, ythAfterTimeZ, ythAfterTimePlusMinus, ythTzHour1, ythTzHour2, ythTzHourColon, ythTzMinute1, ythTzMinute2 macro typeHintStateMachine(c: untyped, content: varargs[untyped]) = yAssert content.kind == nnkArgList result = newNimNode(nnkCaseStmt, content).add(copyNimNode(c)) for branch in content.children: yAssert branch.kind == nnkOfBranch var charBranch = newNimNode(nnkOfBranch, branch) i = 0 stateBranches = newNimNode(nnkCaseStmt, branch).add( newIdentNode("typeHintState")) while branch[i].kind != nnkStmtList: charBranch.add(copyNimTree(branch[i])) inc(i) for rule in branch[i].children: yAssert rule.kind == nnkInfix yAssert rule[0].strVal == "=>" var stateBranch = newNimNode(nnkOfBranch, rule) case rule[1].kind of nnkBracket: for item in rule[1].children: stateBranch.add(item) of nnkIdent: stateBranch.add(rule[1]) else: internalError("Invalid rule kind: " & $rule[1].kind) if rule[2].kind == nnkNilLit: stateBranch.add(newStmtList(newNimNode(nnkDiscardStmt).add( newEmptyNode()))) else: stateBranch.add(newStmtList(newAssignment( newIdentNode("typeHintState"), copyNimTree(rule[2])))) stateBranches.add(stateBranch) stateBranches.add(newNimNode(nnkElse).add(newStmtList( newNimNode(nnkReturnStmt).add(newIdentNode("yTypeUnknown"))))) charBranch.add(newStmtList(stateBranches)) result.add(charBranch) result.add(newNimNode(nnkElse).add(newStmtList( newNimNode(nnkReturnStmt).add(newIdentNode("yTypeUnknown"))))) template advanceTypeHint(ch: char) {.dirty.} = typeHintStateMachine ch: of '~': ythInitial => ythNULL of '.': [ythInt1, ythInt2, ythInt3, ythInt4, ythInt] => ythDecimal [ythInitial, ythMinus, ythPlus] => ythPoint ythSecond2 => ythFraction of '+': ythInitial => ythPlus ythNumE => ythNumEPlusMinus [ythFraction, ythSecond2] => ythAfterTimePlusMinus of '-': ythInitial => ythMinus ythNumE => ythNumEPlusMinus ythInt4 => ythYearMinus ythMonth1 => ythMonthMinusNoYmd ythMonth2 => ythMonthMinus [ythFraction, ythSecond2] => ythAfterTimePlusMinus of '_': [ythInt1, ythInt2, ythInt3, ythInt4] => ythInt [ythInt, ythDecimal] => nil of ':': [ythHour1, ythHour2] => ythHourColon ythMinute2 => ythMinuteColon [ythTzHour1, ythTzHour2] => ythTzHourColon of '0'..'9': ythInitial => ythInt1 ythInt1 => ythInt2 ythInt2 => ythInt3 ythInt3 => ythInt4 [ythInt4, ythMinus, ythPlus] => ythInt [ythNumE, ythNumEPlusMinus] => ythExponent ythYearMinus => ythMonth1 ythMonth1 => ythMonth2 ythMonthMinus => ythDay1 ythMonthMinusNoYmd => ythDay1NoYmd ythDay1 => ythDay2 ythDay1NoYmd => ythDay2NoYmd [ythAfterDaySpace, ythAfterDayT] => ythHour1 ythHour1 => ythHour2 ythHourColon => ythMinute1 ythMinute1 => ythMinute2 ythMinuteColon => ythSecond1 ythSecond1 => ythSecond2 ythAfterTimePlusMinus => ythTzHour1 ythTzHour1 => ythTzHour2 ythTzHourColon => ythTzMinute1 ythTzMinute1 => ythTzMinute2 ythPoint => ythDecimal [ythInt, ythDecimal, ythExponent, ythFraction] => nil of 'a': [ythF, ythLowerF] => ythLowerFA ythPointN => ythPointNA ythPointLowerN => ythPointLowerNA of 'A': ythF => ythFA ythPointN => ythPointNA of 'e': [ythInt, ythDecimal, ythInt1, ythInt2, ythInt3, ythInt4] => ythNumE ythLowerFALS => ythFALSE ythLowerTRU => ythTRUE of 'E': [ythInt, ythDecimal, ythInt1, ythInt2, ythInt3, ythInt4] => ythNumE ythFALS => ythFALSE ythTRU => ythTRUE of 'f': ythInitial => ythLowerF ythPointLowerIN => ythPointINF of 'F': ythInitial => ythF ythPointIN => ythPointINF of 'i': ythPoint => ythPointLowerI of 'I': ythPoint => ythPointI of 'l': ythLowerNU => ythLowerNUL ythLowerNUL => ythNULL ythLowerFA => ythLowerFAL of 'L': ythNU => ythNUL ythNUL => ythNULL ythFA => ythFAL of 'n': ythInitial => ythLowerN ythPoint => ythPointLowerN [ythPointI, ythPointLowerI] => ythPointLowerIN ythPointLowerNA => ythPointNAN of 'N': ythInitial => ythN ythPoint => ythPointN ythPointI => ythPointIN ythPointNA => ythPointNAN of 'r': [ythT, ythLowerT] => ythLowerTR of 'R': ythT => ythTR of 's': ythLowerFAL => ythLowerFALS of 'S': ythFAL => ythFALS of 't': ythInitial => ythLowerT [ythDay1, ythDay2, ythDay1NoYmd, ythDay2NoYmd] => ythAfterDayT of 'T': ythInitial => ythT [ythDay1, ythDay2, ythDay1NoYmd, ythDay2NoYmd] => ythAfterDayT of 'u': [ythN, ythLowerN] => ythLowerNU ythLowerTR => ythLowerTRU of 'U': ythN => ythNU ythTR => ythTRU of 'Z': [ythSecond2, ythFraction, ythAfterTimeSpace] => ythAfterTimeZ of ' ', '\t': [ythSecond2, ythFraction] => ythAfterTimeSpace [ythDay1, ythDay2, ythDay1NoYmd, ythDay2NoYmd] => ythAfterDaySpace [ythAfterTimeSpace, ythAfterDaySpace] => nil proc guessType*(scalar: string): TypeHint {.raises: [].} = ## Parse scalar string according to the RegEx table documented at ## `TypeHint <#TypeHind>`_. var typeHintState: YamlTypeHintState = ythInitial for c in scalar: advanceTypeHint(c) case typeHintState of ythNULL, ythInitial: result = yTypeNull of ythTRUE: result = yTypeBoolTrue of ythFALSE: result = yTypeBoolFalse of ythInt1, ythInt2, ythInt3, ythInt4, ythInt: result = yTypeInteger of ythDecimal, ythExponent: result = yTypeFloat of ythPointINF: result = yTypeFloatInf of ythPointNAN: result = yTypeFloatNaN of ythDay2, ythSecond2, ythFraction, ythAfterTimeZ, ythTzHour1, ythTzHour2, ythTzMinute1, ythTzMinute2: result = yTypeTimestamp else: result = yTypeUnknown