Implemented !!timestamp

This commit is contained in:
Felix Krause 2016-11-08 21:13:01 +01:00
parent 05c4aa733c
commit 64f68ae1af
3 changed files with 167 additions and 14 deletions

View File

@ -5,7 +5,7 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
import "../yaml" import "../yaml"
import unittest, strutils, streams, tables import unittest, strutils, streams, tables, times
type type
MyTuple = tuple MyTuple = tuple
@ -208,6 +208,14 @@ suite "Serialization":
var output = dump(input, tsNone, asTidy, blockOnly) var output = dump(input, tsNone, asTidy, blockOnly)
assertStringEqual yamlDirs & "\n!n!nil:string \"\"", output assertStringEqual yamlDirs & "\n!n!nil:string \"\"", output
test "Load timestamps":
let input = "[2001-12-15T02:59:43.1Z, 2001-12-14t21:59:43.10-05:00, 2001-12-14 21:59:43.10-5]"
var result: seq[Time]
load(input, result)
assert result.len() == 3
# currently, there is no good way of checking the result content, because
# the parsed Time may have any timezone offset.
test "Load string sequence": test "Load string sequence":
let input = newStringStream(" - a\n - b") let input = newStringStream(" - a\n - b")
var result: seq[string] var result: seq[string]

View File

@ -33,10 +33,11 @@ type
## ``yTypeBoolTrue`` ``y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON`` ## ``yTypeBoolTrue`` ``y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON``
## ``yTypeBoolFalse`` ``n|N|no|No|NO|false|False|FALSE|off|Off|OFF`` ## ``yTypeBoolFalse`` ``n|N|no|No|NO|false|False|FALSE|off|Off|OFF``
## ``yTypeNull`` ``~ | null | Null | NULL`` ## ``yTypeNull`` ``~ | null | Null | NULL``
## ``yTypeTimestamp`` see `here <http://yaml.org/type/timestamp.html>`_.
## ``yTypeUnknown`` ``*`` ## ``yTypeUnknown`` ``*``
## ================== ========================= ## ================== =========================
yTypeInteger, yTypeFloat, yTypeFloatInf, yTypeFloatNaN, yTypeBoolTrue, yTypeInteger, yTypeFloat, yTypeFloatInf, yTypeFloatNaN, yTypeBoolTrue,
yTypeBoolFalse, yTypeNull, yTypeUnknown yTypeBoolFalse, yTypeNull, yTypeUnknown, yTypeTimestamp
YamlTypeHintState = enum YamlTypeHintState = enum
ythInitial, ythInitial,
@ -59,7 +60,16 @@ type
ythPointLowerIN, ythPointLowerN, ythPointLowerNA, ythPointLowerIN, ythPointLowerN, ythPointLowerNA,
ythMinus, yth0, ythInt, ythDecimal, ythNumE, ythNumEPlusMinus, ythExponent ythMinus, yth0, ythInt1, ythInt1Zero, ythInt2, ythInt2Zero, ythInt3,
ythInt3Zero, ythInt4, ythInt4Zero, 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: untyped): typed = macro typeHintStateMachine(c: untyped, content: untyped): typed =
yAssert content.kind == nnkStmtList yAssert content.kind == nnkStmtList
@ -101,20 +111,78 @@ template advanceTypeHint(ch: char) {.dirty.} =
typeHintStateMachine ch: typeHintStateMachine ch:
of '~': ythInitial => ythNULL of '~': ythInitial => ythNULL
of '.': of '.':
[yth0, ythInt] => ythDecimal [yth0, ythInt1, ythInt2, ythInt3, ythInt4, ythInt] => ythDecimal
[ythInitial, ythMinus] => ythPoint [ythInitial, ythMinus] => ythPoint
of '+': ythNumE => ythNumEPlusMinus ythSecond2 => ythFraction
of '+':
ythNumE => ythNumEPlusMinus
[ythFraction, ythSecond2] => ythAfterTimePlusMinus
of '-': of '-':
ythInitial => ythMinus ythInitial => ythMinus
ythNumE => ythNumEPlusMinus ythNumE => ythNumEPlusMinus
[ythInt4, ythInt4Zero] => ythYearMinus
ythMonth1 => ythMonthMinusNoYmd
ythMonth2 => ythMonthMinus
[ythFraction, ythSecond2] => ythAfterTimePlusMinus
of ':':
[ythHour1, ythHour2] => ythHourColon
ythMinute2 => ythMinuteColon
[ythTzHour1, ythTzHour2] => ythTzHourColon
of '0': of '0':
[ythInitial, ythMinus] => yth0 ythInitial => ythInt1Zero
ythMinus => yth0
[ythNumE, ythNumEPlusMinus] => ythExponent [ythNumE, ythNumEPlusMinus] => ythExponent
[ythInt, ythDecimal, ythExponent] => nil ythInt1 => ythInt2
ythInt1Zero => ythInt2Zero
ythInt2 => ythInt3
ythInt2Zero => ythInt3Zero
ythInt3 => ythInt4
ythInt3Zero => ythInt4Zero
ythInt4 => ythInt
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
[ythInt, ythDecimal, ythExponent, ythFraction] => nil
of '1'..'9': of '1'..'9':
[ythInitial, ythMinus] => ythInt ythInitial => ythInt1
ythInt1 => ythInt2
ythInt1Zero => ythInt2Zero
ythInt2 => ythInt3
ythInt2Zero => ythInt3Zero
ythInt3 => ythInt4
ythInt3Zero => ythInt4Zero
[ythInt4, ythMinus] => ythInt
[ythNumE, ythNumEPlusMinus] => ythExponent [ythNumE, ythNumEPlusMinus] => ythExponent
[ythInt, ythDecimal, ythExponent] => nil 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
[ythInt, ythDecimal, ythExponent, ythFraction] => nil
of 'a': of 'a':
ythF => ythLowerFA ythF => ythLowerFA
ythPointN => ythPointNA ythPointN => ythPointNA
@ -174,7 +242,9 @@ template advanceTypeHint(ch: char) {.dirty.} =
of 'S': of 'S':
ythFAL => ythFALS ythFAL => ythFALS
ythYE => ythYES ythYE => ythYES
of 't', 'T': ythInitial => ythT of 't', 'T':
ythInitial => ythT
[ythDay1, ythDay2, ythDay1NoYmd, ythDay2NoYmd] => ythAfterDayT
of 'u': of 'u':
ythN => ythLowerNU ythN => ythLowerNU
ythLowerTR => ythLowerTRU ythLowerTR => ythLowerTRU
@ -182,6 +252,11 @@ template advanceTypeHint(ch: char) {.dirty.} =
ythN => ythNU ythN => ythNU
ythTR => ythTRU ythTR => ythTRU
of 'y', 'Y': ythInitial => ythY of 'y', 'Y': ythInitial => ythY
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: [].} = proc guessType*(scalar: string): TypeHint {.raises: [].} =
## Parse scalar string according to the RegEx table documented at ## Parse scalar string according to the RegEx table documented at
@ -192,8 +267,10 @@ proc guessType*(scalar: string): TypeHint {.raises: [].} =
of ythNULL: result = yTypeNull of ythNULL: result = yTypeNull
of ythTRUE, ythON, ythYES, ythY: result = yTypeBoolTrue of ythTRUE, ythON, ythYES, ythY: result = yTypeBoolTrue
of ythFALSE, ythOFF, ythNO, ythN: result = yTypeBoolFalse of ythFALSE, ythOFF, ythNO, ythN: result = yTypeBoolFalse
of ythInt, yth0: result = yTypeInteger of ythInt1, ythInt2, ythInt3, ythInt4, ythInt, yth0: result = yTypeInteger
of ythDecimal, ythExponent: result = yTypeFloat of ythDecimal, ythExponent: result = yTypeFloat
of ythPointINF: result = yTypeFloatInf of ythPointINF: result = yTypeFloatInf
of ythPointNAN: result = yTypeFloatNaN of ythPointNAN: result = yTypeFloatNaN
of ythDay2, ythSecond2, ythFraction, ythAfterTimeZ, ythTzHour1, ythTzHour2,
ythTzMinute1, ythTzMinute2: result = yTypeTimestamp
else: result = yTypeUnknown else: result = yTypeUnknown

View File

@ -16,7 +16,7 @@
## type. Please consult the serialization guide on the NimYAML website for more ## type. Please consult the serialization guide on the NimYAML website for more
## information. ## information.
import tables, typetraits, strutils, macros, streams import tables, typetraits, strutils, macros, streams, times
import parser, taglib, presenter, stream, ../private/internal, hints import parser, taglib, presenter, stream, ../private/internal, hints
export stream export stream
# *something* in here needs externally visible `==`(x,y: AnchorId), # *something* in here needs externally visible `==`(x,y: AnchorId),
@ -321,6 +321,72 @@ proc representObject*(value: char, ts: TagStyle, c: SerializationContext,
## represents a char value as YAML scalar ## represents a char value as YAML scalar
c.put(scalarEvent("" & value, tag, yAnchorNone)) c.put(scalarEvent("" & value, tag, yAnchorNone))
proc yamlTag*(T: typedesc[Time]): TagId {.inline, raises: [].} = yTagTimestamp
proc constructObject*(s: var YamlStream, c: ConstructionContext,
result: var Time)
{.raises: [YamlConstructionError, YamlStreamError].} =
constructScalarItem(s, item, Time):
if guessType(item.scalarContent) == yTypeTimestamp:
var
tmp = newStringOfCap(60)
pos = 8
c: char
while pos < item.scalarContent.len():
c = item.scalarContent[pos]
if c in {' ', '\t', 'T', 't'}: break
inc(pos)
if pos == item.scalarContent.len():
tmp.add(item.scalarContent)
tmp.add("T00:00:00+00:00")
else:
tmp.add(item.scalarContent[0 .. pos - 1])
if c in {' ', '\t'}:
while true:
inc(pos)
c = item.scalarContent[pos]
if c notin {' ', '\t'}: break
else: inc(pos)
tmp.add("T")
let timeStart = pos
inc(pos, 7)
var fractionStart = -1
while pos < item.scalarContent.len():
c = item.scalarContent[pos]
if c in {'+', '-', 'Z', ' ', '\t'}: break
elif c == '.': fractionStart = pos
inc(pos)
if fractionStart == -1:
tmp.add(item.scalarContent[timeStart .. pos - 1])
else:
tmp.add(item.scalarContent[timeStart .. fractionStart - 1])
if c in {'Z', ' ', '\t'}: tmp.add("+00:00")
else:
tmp.add(c)
inc(pos)
let tzStart = pos
inc(pos)
if pos < item.scalarContent.len() and item.scalarContent[pos] != ':':
inc(pos)
if pos - tzStart == 1: tmp.add('0')
tmp.add(item.scalarContent[tzStart .. pos - 1])
if pos == item.scalarContent.len(): tmp.add(":00")
elif pos + 2 == item.scalarContent.len():
tmp.add(":0")
tmp.add(item.scalarContent[pos + 1])
else:
tmp.add(item.scalarContent[pos .. pos + 2])
let info = tmp.parse("yyyy-M-d'T'H-mm-sszzz")
result = info.toTime()
else:
raise s.constructionError("Not a parsable timestamp: " &
escape(item.scalarContent))
proc representObject*(value: Time, ts: TagStyle, c: SerializationContext,
tag: TagId) {.raises: [ValueError].} =
let tmp = value.getGMTime()
c.put(scalarEvent(tmp.format("yyyy-MM-dd'T'HH:mm:ss'Z'")))
proc yamlTag*[I](T: typedesc[seq[I]]): TagId {.inline, raises: [].} = proc yamlTag*[I](T: typedesc[seq[I]]): TagId {.inline, raises: [].} =
let uri = nimTag("system:seq(" & safeTagUri(yamlTag(I)) & ')') let uri = nimTag("system:seq(" & safeTagUri(yamlTag(I)) & ')')
result = lazyLoadTag(uri) result = lazyLoadTag(uri)
@ -984,6 +1050,8 @@ proc constructChild*[T](s: var YamlStream, c: ConstructionContext,
raise s.constructionError("not implemented!") raise s.constructionError("not implemented!")
of yTypeUnknown: of yTypeUnknown:
possibleTagIds.add(yamlTag(string)) possibleTagIds.add(yamlTag(string))
of yTypeTimestamp:
possibleTagIds.add(yamlTag(Time))
of yTagExclamationMark: of yTagExclamationMark:
possibleTagIds.add(yamlTag(string)) possibleTagIds.add(yamlTag(string))
else: else: