NimYAML/yaml/hints.nim

239 lines
9.0 KiB
Nim
Raw Normal View History

# 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.
## =================
2021-05-17 22:31:47 +00:00
## Module yaml/hints
## =================
##
## The hints API enables you to guess the type of YAML scalars.
2016-09-20 19:53:38 +00:00
import macros
2017-02-06 19:39:04 +00:00
import private/internal
2016-09-20 19:53:38 +00:00
type
2016-09-20 19:53:38 +00:00
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
##
2016-09-20 19:53:38 +00:00
## ================== =========================
## 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 | ~``
2016-11-08 20:13:01 +00:00
## ``yTypeTimestamp`` see `here <http://yaml.org/type/timestamp.html>`_.
2016-09-20 19:53:38 +00:00
## ``yTypeUnknown`` ``*``
## ================== =========================
yTypeInteger, yTypeFloat, yTypeFloatInf, yTypeFloatNaN, yTypeBoolTrue,
2016-11-08 20:13:01 +00:00
yTypeBoolFalse, yTypeNull, yTypeUnknown, yTypeTimestamp
2016-09-20 19:53:38 +00:00
2016-04-02 15:48:22 +00:00
YamlTypeHintState = enum
ythInitial,
ythF, ythFA, ythFAL, ythFALS, ythFALSE,
ythN, ythNU, ythNUL, ythNULL,
ythT, ythTR, ythTRU, ythTRUE,
2016-07-07 15:14:18 +00:00
2016-04-02 15:48:22 +00:00
ythPoint, ythPointI, ythPointIN, ythPointINF,
ythPointN, ythPointNA, ythPointNAN,
2016-07-07 15:14:18 +00:00
ythLowerF, ythLowerFA, ythLowerFAL, ythLowerFALS,
ythLowerN, ythLowerNU, ythLowerNUL,
ythLowerT, ythLowerTR, ythLowerTRU,
2016-07-07 15:14:18 +00:00
ythPointLowerI, ythPointLowerIN,
ythPointLowerN, ythPointLowerNA,
2016-07-07 15:14:18 +00:00
ythMinus, ythPlus, ythInt1, ythInt2, ythInt3, ythInt4, ythInt,
2016-11-08 20:13:01 +00:00
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]) =
2017-06-30 13:07:43 +00:00
yAssert content.kind == nnkArgList
2016-04-02 15:48:22 +00:00
result = newNimNode(nnkCaseStmt, content).add(copyNimNode(c))
for branch in content.children:
yAssert branch.kind == nnkOfBranch
2016-07-07 15:14:18 +00:00
var
2016-04-02 15:48:22 +00:00
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 == "=>"
2016-04-02 15:48:22 +00:00
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)
2016-04-02 15:48:22 +00:00
if rule[2].kind == nnkNilLit:
stateBranch.add(newStmtList(newNimNode(nnkDiscardStmt).add(
newEmptyNode())))
2016-04-02 15:48:22 +00:00
else:
stateBranch.add(newStmtList(newAssignment(
newIdentNode("typeHintState"), copyNimTree(rule[2]))))
2016-04-02 15:48:22 +00:00
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.} =
2016-04-02 15:48:22 +00:00
typeHintStateMachine ch:
of '~': ythInitial => ythNULL
of '.':
[ythInt1, ythInt2, ythInt3, ythInt4, ythInt] => ythDecimal
[ythInitial, ythMinus, ythPlus] => ythPoint
ythSecond2 => ythFraction
2016-11-08 20:13:01 +00:00
of '+':
ythInitial => ythPlus
ythNumE => ythNumEPlusMinus
2016-11-08 20:13:01 +00:00
[ythFraction, ythSecond2] => ythAfterTimePlusMinus
2016-04-02 15:48:22 +00:00
of '-':
2016-11-08 20:13:01 +00:00
ythInitial => ythMinus
ythNumE => ythNumEPlusMinus
ythInt4 => ythYearMinus
2016-11-08 20:13:01 +00:00
ythMonth1 => ythMonthMinusNoYmd
ythMonth2 => ythMonthMinus
[ythFraction, ythSecond2] => ythAfterTimePlusMinus
of '_':
[ythInt1, ythInt2, ythInt3, ythInt4] => ythInt
[ythInt, ythDecimal] => nil
2016-11-08 20:13:01 +00:00
of ':':
[ythHour1, ythHour2] => ythHourColon
ythMinute2 => ythMinuteColon
[ythTzHour1, ythTzHour2] => ythTzHourColon
of '0'..'9':
2016-11-08 20:13:01 +00:00
ythInitial => ythInt1
ythInt1 => ythInt2
ythInt2 => ythInt3
ythInt3 => ythInt4
[ythInt4, ythMinus, ythPlus] => ythInt
2016-04-02 15:48:22 +00:00
[ythNumE, ythNumEPlusMinus] => ythExponent
2016-11-08 20:13:01 +00:00
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
2016-11-08 20:13:01 +00:00
[ythInt, ythDecimal, ythExponent, ythFraction] => nil
2016-04-02 15:48:22 +00:00
of 'a':
[ythF, ythLowerF] => ythLowerFA
ythPointN => ythPointNA
ythPointLowerN => ythPointLowerNA
2016-04-02 15:48:22 +00:00
of 'A':
ythF => ythFA
ythPointN => ythPointNA
of 'e':
[ythInt, ythDecimal,
ythInt1, ythInt2, ythInt3, ythInt4] => ythNumE
2016-04-02 15:48:22 +00:00
ythLowerFALS => ythFALSE
ythLowerTRU => ythTRUE
of 'E':
[ythInt, ythDecimal,
ythInt1, ythInt2, ythInt3, ythInt4] => ythNumE
2016-04-02 15:48:22 +00:00
ythFALS => ythFALSE
ythTRU => ythTRUE
of 'f':
ythInitial => ythLowerF
2016-04-02 15:48:22 +00:00
ythPointLowerIN => ythPointINF
of 'F':
ythInitial => ythF
ythPointIN => ythPointINF
of 'i': ythPoint => ythPointLowerI
of 'I': ythPoint => ythPointI
2016-04-02 15:48:22 +00:00
of 'l':
ythLowerNU => ythLowerNUL
ythLowerNUL => ythNULL
ythLowerFA => ythLowerFAL
of 'L':
ythNU => ythNUL
ythNUL => ythNULL
ythFA => ythFAL
of 'n':
ythInitial => ythLowerN
2016-04-02 15:48:22 +00:00
ythPoint => ythPointLowerN
[ythPointI, ythPointLowerI] => ythPointLowerIN
2016-04-02 15:48:22 +00:00
ythPointLowerNA => ythPointNAN
of 'N':
ythInitial => ythN
ythPoint => ythPointN
ythPointI => ythPointIN
ythPointNA => ythPointNAN
of 'r': [ythT, ythLowerT] => ythLowerTR
2016-04-02 15:48:22 +00:00
of 'R': ythT => ythTR
of 's':
ythLowerFAL => ythLowerFALS
of 'S':
ythFAL => ythFALS
of 't':
ythInitial => ythLowerT
[ythDay1, ythDay2, ythDay1NoYmd, ythDay2NoYmd] => ythAfterDayT
of 'T':
2016-11-08 20:13:01 +00:00
ythInitial => ythT
[ythDay1, ythDay2, ythDay1NoYmd, ythDay2NoYmd] => ythAfterDayT
2016-04-02 15:48:22 +00:00
of 'u':
[ythN, ythLowerN] => ythLowerNU
ythLowerTR => ythLowerTRU
2016-04-02 15:48:22 +00:00
of 'U':
ythN => ythNU
ythTR => ythTRU
2016-11-08 20:13:01 +00:00
of 'Z': [ythSecond2, ythFraction, ythAfterTimeSpace] => ythAfterTimeZ
of ' ', '\t':
[ythSecond2, ythFraction] => ythAfterTimeSpace
[ythDay1, ythDay2, ythDay1NoYmd, ythDay2NoYmd] => ythAfterDaySpace
[ythAfterTimeSpace, ythAfterDaySpace] => nil
2016-09-20 19:53:38 +00:00
proc guessType*(scalar: string): TypeHint {.raises: [].} =
## Parse scalar string according to the RegEx table documented at
## `TypeHint <#TypeHind>`_.
2016-04-02 15:48:22 +00:00
var typeHintState: YamlTypeHintState = ythInitial
for c in scalar: advanceTypeHint(c)
case typeHintState
2017-01-12 10:19:46 +00:00
of ythNULL, ythInitial: result = yTypeNull
of ythTRUE: result = yTypeBoolTrue
of ythFALSE: result = yTypeBoolFalse
of ythInt1, ythInt2, ythInt3, ythInt4, ythInt: result = yTypeInteger
2016-04-02 15:48:22 +00:00
of ythDecimal, ythExponent: result = yTypeFloat
of ythPointINF: result = yTypeFloatInf
of ythPointNAN: result = yTypeFloatNaN
2016-11-08 20:13:01 +00:00
of ythDay2, ythSecond2, ythFraction, ythAfterTimeZ, ythTzHour1, ythTzHour2,
ythTzMinute1, ythTzMinute2: result = yTypeTimestamp
else: result = yTypeUnknown