From 6402433d2a62865ab6b929bd629fac34a8a833d0 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Wed, 26 Oct 2016 18:32:54 +0200 Subject: [PATCH] Implemented setDefaultValue. --- test/tserialization.nim | 24 +++++++++++- yaml/serialization.nim | 81 +++++++++++++++++++++++++++++++++-------- 2 files changed, 88 insertions(+), 17 deletions(-) diff --git a/test/tserialization.nim b/test/tserialization.nim index b049ae4..f701f12 100644 --- a/test/tserialization.nim +++ b/test/tserialization.nim @@ -54,6 +54,9 @@ type of deD: neverThere: int + WithDefault = object + a, b, c, d: string + markAsTransient(NonVariantWithTransient, a) markAsTransient(NonVariantWithTransient, c) @@ -61,7 +64,8 @@ markAsTransient(VariantWithTransient, gTemporary) markAsTransient(VariantWithTransient, cTemporary) markAsTransient(VariantWithTransient, neverThere) - +setDefaultValue(WithDefault, b, "b") +setDefaultValue(WithDefault, d, "d") proc `$`(v: BetterInt): string {.borrow.} proc `==`(left, right: BetterInt): bool {.borrow.} @@ -579,6 +583,24 @@ next: assert(result[1].next == result[2]) assert(result[2].next == result[0]) + test "Load object with default values": + let input = "a: abc\nc: dce" + var result: WithDefault + load(input, result) + assert result.a == "abc" + assert result.b == "b" + assert result.c == "dce" + assert result.d == "d" + + test "Load object with partly default values": + let input = "a: abc\nb: bcd\nc: cde" + var result: WithDefault + load(input, result) + assert result.a == "abc" + assert result.b == "bcd" + assert result.c == "cde" + assert result.d == "d" + test "Load nil values": let input = newStringStream("- ~\n- !!str ~") var result: seq[ref string] diff --git a/yaml/serialization.nim b/yaml/serialization.nim index 89f72dd..99c559a 100644 --- a/yaml/serialization.nim +++ b/yaml/serialization.nim @@ -536,24 +536,45 @@ proc checkDuplicate(s: NimNode, tName: string, name: string, i: int, newLit("While constructing " & tName & ": Duplicate field: " & escape(name)))))) -proc checkMissing(s: NimNode, tName: string, name: string, i: int, - matched: NimNode): NimNode {.compileTime.} = - result = newIfStmt((newCall("not", newNimNode(nnkBracketExpr).add(matched, - newLit(i))), newNimNode(nnkRaiseStmt).add(newCall( - bindSym("constructionError"), s, newLit("While constructing " & - tName & ": Missing field: " & escape(name)))))) - -proc markAsFound(i: int, matched: NimNode): NimNode {.compileTime.} = - newAssignment(newNimNode(nnkBracketExpr).add(matched, newLit(i)), - newLit(true)) - let implicitVariantObjectMarker {.compileTime.} = newIdentNode(":implicitVariantObject") transientBitvectorProc {.compileTime.} = newIdentNode(":transientObject") + defaultBitvectorProc {.compileTime.} = + newIdentNode(":defaultBitvector") + defaultValueGetter {.compileTime.} = + newIdentNode(":defaultValueGetter") -var transientVectors {.compileTime.} = newSeq[set[int16]]() +var + transientVectors {.compileTime.} = newSeq[set[int16]]() + defaultVectors {.compileTime.} = newSeq[set[int16]]() + +proc addDefaultOr(tName: string, i: int, o: NimNode, + field, elseBranch, defaultValues: NimNode): NimNode {.compileTime.} = + let + dbp = defaultBitvectorProc + t = newIdentNode(tName) + result = quote do: + when compiles(`dbp`(`t`)): + when `i` in defaultVectors[`dbp`(`t`)]: + `o`.`field` = `defaultValues`.`field` + else: `elseBranch` + else: `elseBranch` + +proc checkMissing(s: NimNode, t: typedesc, tName: string, field: NimNode, + i: int, matched, o, defaultValues: NimNode): + NimNode {.compileTime.} = + result = newIfStmt((newCall("not", newNimNode(nnkBracketExpr).add(matched, + newLit(i))), addDefaultOr(tName, i, o, field, + newNimNode(nnkRaiseStmt).add(newCall( + bindSym("constructionError"), s, newLit("While constructing " & + tName & ": Missing field: " & escape($field)))), defaultValues))) + echo result.repr + +proc markAsFound(i: int, matched: NimNode): NimNode {.compileTime.} = + newAssignment(newNimNode(nnkBracketExpr).add(matched, newLit(i)), + newLit(true)) proc ifNotTransient(tSym: NimNode, fieldIndex: int, content: openarray[NimNode], elseError: bool = false, s: NimNode = nil, tName, fName: string = nil): @@ -570,7 +591,12 @@ proc ifNotTransient(tSym: NimNode, fieldIndex: int, content: openarray[NimNode], macro ensureAllFieldsPresent(s: YamlStream, t: typedesc, tIndex: int, o: typed, matched: typed): typed = - result = newStmtList() + let + dbp = defaultBitvectorProc + defaultValues = genSym(nskConst, "defaultValues") + result = quote do: + when compiles(`dbp`(`t`)): + const `defaultValues` = `defaultValueGetter`(`t`) let tDecl = getType(t) tName = $tDecl[1] @@ -578,7 +604,8 @@ macro ensureAllFieldsPresent(s: YamlStream, t: typedesc, tIndex: int, o: typed, var field = 0 for child in tDesc[2].children: if child.kind == nnkRecCase: - result.add(checkMissing(s, tName, $child[0], field, matched)) + result.add(checkMissing(s, t, tName, child[0], field, matched, o, + defaultValues)) for bIndex in 1 .. len(child) - 1: let discChecks = newStmtList() var @@ -594,13 +621,14 @@ macro ensureAllFieldsPresent(s: YamlStream, t: typedesc, tIndex: int, o: typed, else: internalError("Unexpected child kind: " & $child[bIndex].kind) for item in child[bIndex][recListIndex].children: inc(field) - discChecks.add(checkMissing(s, tName, $item, field, matched)) + discChecks.add(checkMissing(s, t, tName, item, field, matched, o, + defaultValues)) result.add(ifNotTransient(tIndex, field, [newIfStmt((infix(newDotExpr(o, newIdentNode($child[0])), "in", curValues), discChecks))])) else: result.add(ifNotTransient(tIndex, field, - [checkMissing(s, tName, $child, field, matched)])) + [checkMissing(s, t, tName, child, field, matched, o, defaultValues)])) inc(field) macro fetchTransientIndex(t: typedesc, tIndex: untyped): typed = @@ -1288,3 +1316,24 @@ macro markAsTransient*(t: typedesc, field: untyped): typed = static: transientVectors.add({}) static: transientVectors[`transientBitvectorProc`(`t`)].incl(getFieldIndex(`t`, `field`)) + +macro setDefaultValue*(t: typedesc, field: untyped, value: typed): typed = + let + dSym = genSym(nskVar, ":default") + nextBitvectorIndex = defaultVectors.len + result = quote do: + when compiles(`transientBitvectorProc`(`t`)): + {.fatal: "Cannot set default value of transient field".} + elif compiles(`implicitVariantObjectMarker`(`t`)): + {.fatal: "Cannot set default value of implicit variant objects.".} + when not compiles(`defaultValueGetter`(`t`)): + var `dSym` {.compileTime.}: `t` + template `defaultValueGetter`(t: typedesc[`t`]): auto = + `dSym` + proc `defaultBitvectorProc`*(myType: typedesc[`t`]): int + {.compileTime.} = `nextBitvectorIndex` + static: defaultVectors.add({}) + static: + `defaultValueGetter`(`t`).`field` = `value` + defaultVectors[`defaultBitvectorProc`(`t`)].incl(getFieldIndex(`t`, `field`)) + echo result.repr \ No newline at end of file