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.
import "../yaml"
import unittest, strutils, streams, tables
import unittest, strutils, streams, tables, times
type
MyTuple = tuple
@ -208,6 +208,14 @@ suite "Serialization":
var output = dump(input, tsNone, asTidy, blockOnly)
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":
let input = newStringStream(" - a\n - b")
var result: seq[string]

View File

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

View File

@ -16,7 +16,7 @@
## type. Please consult the serialization guide on the NimYAML website for more
## information.
import tables, typetraits, strutils, macros, streams
import tables, typetraits, strutils, macros, streams, times
import parser, taglib, presenter, stream, ../private/internal, hints
export stream
# *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
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: [].} =
let uri = nimTag("system:seq(" & safeTagUri(yamlTag(I)) & ')')
result = lazyLoadTag(uri)
@ -984,6 +1050,8 @@ proc constructChild*[T](s: var YamlStream, c: ConstructionContext,
raise s.constructionError("not implemented!")
of yTypeUnknown:
possibleTagIds.add(yamlTag(string))
of yTypeTimestamp:
possibleTagIds.add(yamlTag(Time))
of yTagExclamationMark:
possibleTagIds.add(yamlTag(string))
else: