From 17de740d0dbc7df9887bf52dd3be952135037f2f Mon Sep 17 00:00:00 2001 From: saem Date: Sat, 5 Aug 2023 12:41:37 -0700 Subject: [PATCH 01/16] fix: `ast_query.containsNode` ignore `nkError` `containsNode` previously attempted to traverse `nkError` nodes, resulting in run time error as there is no child nodes field (`sons`). --- compiler/ast/ast_query.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/ast/ast_query.nim b/compiler/ast/ast_query.nim index b6783e55d89..0a807e5abe1 100644 --- a/compiler/ast/ast_query.nim +++ b/compiler/ast/ast_query.nim @@ -331,7 +331,7 @@ proc hasNilSon*(n: PNode): bool = proc containsNode*(n: PNode, kinds: TNodeKinds): bool = if n == nil: return case n.kind - of nkEmpty..nkNilLit: result = n.kind in kinds + of nkEmpty..nkNilLit, nkError: result = n.kind in kinds else: for i in 0.. Date: Sat, 5 Aug 2023 12:41:56 -0700 Subject: [PATCH 02/16] formatting --- compiler/sem/semfields.nim | 6 ------ compiler/sem/semtypes.nim | 4 +--- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/compiler/sem/semfields.nim b/compiler/sem/semfields.nim index 3e3dc50578f..968db9d7013 100644 --- a/compiler/sem/semfields.nim +++ b/compiler/sem/semfields.nim @@ -81,7 +81,6 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) = if call.len > 2: localReport(c.c.config, forLoop.info, reportAst( rsemParallelFieldsDisallowsCase, call)) - return # iterate over the selector: semForObjectFields(c, typ[0], forLoop, father) @@ -102,7 +101,6 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) = of nkRecList: for t in items(typ): semForObjectFields(c, t, forLoop, father) - else: semReportIllformedAst(c.c.config, typ, { nkRecList, nkRecCase, nkNilLit, nkSym}) @@ -114,7 +112,6 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = var trueSymbol = systemModuleSym(c.graph, getIdent(c.cache, "true")) if trueSymbol == nil: localReport(c.config, n.info, reportStr(rsemSystemNeeds, "true")) - trueSymbol = newSym( skUnknown, getIdent(c.cache, "true"), nextSymId c.idgen, getCurrOwner(c), n.info) @@ -130,14 +127,12 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = rsemWrongNumberOfVariables, expected = call.len - 1 + ord(m == mFieldPairs), got = n.len - 2)) - return result const skippedTypesForFields = abstractVar - {tyTypeDesc} + tyUserTypeClasses var tupleTypeA = skipTypes(call[1].typ, skippedTypesForFields) if tupleTypeA.kind notin {tyTuple, tyObject}: localReport(c.config, n.info, reportSem(rsemNoObjectOrTupleType)) - return result for i in 1.. 0) and (obj[0] != nil): addInheritedFields(c, check, pos, obj[0].skipGenericInvocation) addInheritedFieldsAux(c, check, pos, obj.n) From de2e970604ea81acb3da367091e6ef6d2cc04005 Mon Sep 17 00:00:00 2001 From: saem Date: Sat, 5 Aug 2023 12:42:24 -0700 Subject: [PATCH 03/16] trivial: import style fix --- tests/stdlib/formats/tjsonmacro.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/stdlib/formats/tjsonmacro.nim b/tests/stdlib/formats/tjsonmacro.nim index 779c0ce6513..5c33d851176 100644 --- a/tests/stdlib/formats/tjsonmacro.nim +++ b/tests/stdlib/formats/tjsonmacro.nim @@ -3,7 +3,7 @@ discard """ targets: "c js" """ -import json, strutils, options, tables +import std/[json, strutils, options, tables] # The definition of the `%` proc needs to be here, since the `% c` calls below # can only find our custom `%` proc for `Pix` if defined in global scope. From fd8e35f94b19ce95b57c0aadfa011a8de1833348 Mon Sep 17 00:00:00 2001 From: saem Date: Sat, 5 Aug 2023 12:43:45 -0700 Subject: [PATCH 04/16] remove `macros` type api usage in `std/json` little ugly, but this is the first pass proving it out for `object` and `tuple` types. --- lib/pure/json.nim | 143 ++++++++++++---------------------------------- 1 file changed, 38 insertions(+), 105 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index d1d2b4744d2..72b240d14b8 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1046,10 +1046,17 @@ template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind], ] raise newException(JsonKindError, msg) -when defined(nimFixedForwardGeneric): +when defined(nimFixedForwardGeneric): # xxx: this seems unnecessary with the updated bootstrap macro isRefSkipDistinct*(arg: typed): untyped = ## internal only, do not use + # xxx: why can't we: + # ``` + # import std/typetraits + # proc isRefBase*(t: typedesc[distinct]): bool = + # t.distinctBase(true) is ref + # ``` + # instead? var impl = getTypeImpl(arg) if impl.kind == nnkBracketExpr and impl[0].eqIdent("typeDesc"): impl = getTypeImpl(impl[1]) @@ -1193,6 +1200,36 @@ when defined(nimFixedForwardGeneric): dst = some(default(T)) initFromJson(dst.get, jsonNode, jsonPath) + from std/typetraits import isNamedTuple + + proc initFromJson[T: object|tuple](dst: var T, jsonNode: JsonNode, jsonPath: var string) = + let originalJsonPathLen = jsonPath.len ## so we can trucate/reset after + when T is tuple: + when T.isNamedTuple: + # TODO: nimvm workaround for distinct? + jsonPath.add "." + for name, value in dst.fieldPairs: + jsonPath.add name + var val = value + initFromJson(val, jsonNode.getOrDefault(name), jsonPath) + value = val + jsonPath.setLen(jsonPath.len - name.len) + jsonPath.setLen originalJsonPathLen + else: + error("Use a named tuple instead of: " & T.repr) + elif T is object: + jsonPath.add "." + for name, value in dst.fieldPairs: + # xxx: might need unsafe assign due to kind fields + jsonPath.add name + var val = value + initFromJson(val, jsonNode.getOrDefault(name), jsonPath) + value = val + jsonPath.setLen(jsonPath.len - name.len) + jsonPath.setLen originalJsonPathLen + else: + error("Unreachable type: " & T.repr) + macro assignDistinctImpl[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string) = let typInst = getTypeInst(dst) let typImpl = getTypeImpl(dst) @@ -1210,110 +1247,6 @@ when defined(nimFixedForwardGeneric): proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) = assignDistinctImpl(dst, jsonNode, jsonPath) - proc detectIncompatibleType(typeExpr, lineinfoNode: NimNode) = - if typeExpr.kind == nnkTupleConstr: - error("Use a named tuple instead of: " & typeExpr.repr, lineinfoNode) - - proc foldObjectBody(dst, typeNode, tmpSym, jsonNode, jsonPath, originalJsonPathLen: NimNode) = - case typeNode.kind - of nnkEmpty: - discard - of nnkRecList, nnkTupleTy: - for it in typeNode: - foldObjectBody(dst, it, tmpSym, jsonNode, jsonPath, originalJsonPathLen) - - of nnkIdentDefs: - typeNode.expectLen 3 - let fieldSym = typeNode[0] - let fieldNameLit = newLit(fieldSym.strVal) - let fieldPathLit = newLit("." & fieldSym.strVal) - let fieldType = typeNode[1] - - # Detecting incompatiple tuple types in `assignObjectImpl` only - # would be much cleaner, but the ast for tuple types does not - # contain usable type information. - detectIncompatibleType(fieldType, fieldSym) - - dst.add quote do: - jsonPath.add `fieldPathLit` - when nimvm: - when isRefSkipDistinct(`tmpSym`.`fieldSym`): - # workaround #12489 - var tmp: `fieldType` - initFromJson(tmp, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`) - `tmpSym`.`fieldSym` = tmp - else: - initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`) - else: - initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`) - jsonPath.setLen `originalJsonPathLen` - - of nnkRecCase: - let kindSym = typeNode[0][0] - let kindNameLit = newLit(kindSym.strVal) - let kindPathLit = newLit("." & kindSym.strVal) - let kindType = typeNode[0][1] - let kindOffsetLit = newLit(uint(getOffset(kindSym))) - dst.add quote do: - var kindTmp: `kindType` - jsonPath.add `kindPathLit` - initFromJson(kindTmp, `jsonNode`[`kindNameLit`], `jsonPath`) - jsonPath.setLen `originalJsonPathLen` - when defined js: - `tmpSym`.`kindSym` = kindTmp - else: - when nimvm: - `tmpSym`.`kindSym` = kindTmp - else: - # fuck it, assign kind field anyway - ((cast[ptr `kindType`](cast[uint](`tmpSym`.addr) + `kindOffsetLit`))[]) = kindTmp - dst.add nnkCaseStmt.newTree(nnkDotExpr.newTree(tmpSym, kindSym)) - for i in 1 ..< typeNode.len: - foldObjectBody(dst, typeNode[i], tmpSym, jsonNode, jsonPath, originalJsonPathLen) - - of nnkOfBranch, nnkElse: - let ofBranch = newNimNode(typeNode.kind) - for i in 0 ..< typeNode.len-1: - ofBranch.add copyNimTree(typeNode[i]) - let dstInner = newNimNode(nnkStmtListExpr) - foldObjectBody(dstInner, typeNode[^1], tmpSym, jsonNode, jsonPath, originalJsonPathLen) - # resOuter now contains the inner stmtList - ofBranch.add dstInner - dst[^1].expectKind nnkCaseStmt - dst[^1].add ofBranch - - of nnkObjectTy: - typeNode[0].expectKind nnkEmpty - typeNode[1].expectKind {nnkEmpty, nnkOfInherit} - if typeNode[1].kind == nnkOfInherit: - let base = typeNode[1][0] - var impl = getTypeImpl(base) - while impl.kind in {nnkRefTy, nnkPtrTy}: - impl = getTypeImpl(impl[0]) - foldObjectBody(dst, impl, tmpSym, jsonNode, jsonPath, originalJsonPathLen) - let body = typeNode[2] - foldObjectBody(dst, body, tmpSym, jsonNode, jsonPath, originalJsonPathLen) - - else: - error("unhandled kind: " & $typeNode.kind, typeNode) - - macro assignObjectImpl[T](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - let typeSym = getTypeInst(dst) - let originalJsonPathLen = genSym(nskLet, "originalJsonPathLen") - result = newStmtList() - result.add quote do: - let `originalJsonPathLen` = len(`jsonPath`) - if typeSym.kind in {nnkTupleTy, nnkTupleConstr}: - # both, `dst` and `typeSym` don't have good lineinfo. But nothing - # else is available here. - detectIncompatibleType(typeSym, dst) - foldObjectBody(result, typeSym, dst, jsonNode, jsonPath, originalJsonPathLen) - else: - foldObjectBody(result, typeSym.getTypeImpl, dst, jsonNode, jsonPath, originalJsonPathLen) - - proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - assignObjectImpl(dst, jsonNode, jsonPath) - proc to*[T](node: JsonNode, t: typedesc[T]): T = ## `Unmarshals`:idx: the specified node into the object type specified. ## From 5cbef8f8a32e6a8b7c890e8cd1eeb6a46a9777af Mon Sep 17 00:00:00 2001 From: saem Date: Tue, 8 Aug 2023 12:20:52 -0700 Subject: [PATCH 05/16] add `nkError` wrapping to `semfields` --- compiler/sem/semfields.nim | 2 ++ compiler/sem/semstmts.nim | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/sem/semfields.nim b/compiler/sem/semfields.nim index 968db9d7013..1b3e35e8dbe 100644 --- a/compiler/sem/semfields.nim +++ b/compiler/sem/semfields.nim @@ -174,3 +174,5 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = stmts.add(b) else: result = stmts + if containsNode(stmts, {nkError}): + result = c.config.wrapError(result) diff --git a/compiler/sem/semstmts.nim b/compiler/sem/semstmts.nim index 05c5e0234fe..78ba6646695 100644 --- a/compiler/sem/semstmts.nim +++ b/compiler/sem/semstmts.nim @@ -1638,12 +1638,13 @@ proc semFor(c: PContext, n: PNode; flags: TExprFlags): PNode = n[^2] = implicitIterator(c, "pairs", n[^2]) else: localReport(c.config, n[^2], reportSem(rsemForExpectsIterator)) - result = semForVars(c, n, flags) else: result = semForVars(c, n, flags) - # propagate any enforced VoidContext: - if n[^1].typ == c.enforceVoidContext: + if result.kind == nkError: + discard # do nothing + elif n[^1].typ == c.enforceVoidContext: + # propagate any enforced VoidContext: result.typ = c.enforceVoidContext elif efInTypeof in flags: result.typ = result.lastSon.typ From 1c98d0c62a88975494f30c21e571150f5ce067b9 Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Fri, 11 Aug 2023 17:16:11 -0700 Subject: [PATCH 06/16] use error pragmas with generics will remove the `tjsonmacro_reject` test --- lib/pure/json.nim | 4 ++-- tests/stdlib/formats/tjsonmacro_reject.nim | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 72b240d14b8..49a7254a1c8 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1216,7 +1216,7 @@ when defined(nimFixedForwardGeneric): # xxx: this seems unnecessary with the upd jsonPath.setLen(jsonPath.len - name.len) jsonPath.setLen originalJsonPathLen else: - error("Use a named tuple instead of: " & T.repr) + {.error("Use a named tuple instead of: " & T.repr).} elif T is object: jsonPath.add "." for name, value in dst.fieldPairs: @@ -1228,7 +1228,7 @@ when defined(nimFixedForwardGeneric): # xxx: this seems unnecessary with the upd jsonPath.setLen(jsonPath.len - name.len) jsonPath.setLen originalJsonPathLen else: - error("Unreachable type: " & T.repr) + {.error("Unreachable type: " & T.repr).} macro assignDistinctImpl[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string) = let typInst = getTypeInst(dst) diff --git a/tests/stdlib/formats/tjsonmacro_reject.nim b/tests/stdlib/formats/tjsonmacro_reject.nim index ada365d7d4c..e9cb35fc38b 100644 --- a/tests/stdlib/formats/tjsonmacro_reject.nim +++ b/tests/stdlib/formats/tjsonmacro_reject.nim @@ -1,10 +1,9 @@ discard """ errormsg: "Use a named tuple instead of: (string, float)" - file: "tjsonmacro_reject.nim" - line: 11 + file: "json.nim" """ -import json +import std/json type Car = object From aded7e9a1cef83513ddb389cbbc3d49d1b41c542 Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Fri, 11 Aug 2023 17:25:08 -0700 Subject: [PATCH 07/16] move json test --- tests/stdlib/formats/tjsonmacro.nim | 9 +++++++++ tests/stdlib/formats/tjsonmacro_reject.nim | 17 ----------------- 2 files changed, 9 insertions(+), 17 deletions(-) delete mode 100644 tests/stdlib/formats/tjsonmacro_reject.nim diff --git a/tests/stdlib/formats/tjsonmacro.nim b/tests/stdlib/formats/tjsonmacro.nim index 5c33d851176..ed09e3dbb42 100644 --- a/tests/stdlib/formats/tjsonmacro.nim +++ b/tests/stdlib/formats/tjsonmacro.nim @@ -646,6 +646,15 @@ proc testJson() = ] doAssert (%* a).to(a.typeof) == a + block: + type + Car = object + engine: (string, float) + model: string + let j = """{"engine": {"name": "V8", "capacity": 5.5}, "model": "Skyline"}""" + let parsed = parseJson(j) + doAssert not compiles(to(parsed, Car)) + testJson() static: diff --git a/tests/stdlib/formats/tjsonmacro_reject.nim b/tests/stdlib/formats/tjsonmacro_reject.nim deleted file mode 100644 index e9cb35fc38b..00000000000 --- a/tests/stdlib/formats/tjsonmacro_reject.nim +++ /dev/null @@ -1,17 +0,0 @@ -discard """ - errormsg: "Use a named tuple instead of: (string, float)" - file: "json.nim" -""" - -import std/json - -type - Car = object - engine: (string, float) - model: string - -let j = """ - {"engine": {"name": "V8", "capacity": 5.5}, model: "Skyline"} -""" -let parsed = parseJson(j) -echo(to(parsed, Car)) From cb56b6026219071fb6054796b4c3167296f284ac Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Fri, 11 Aug 2023 17:28:23 -0700 Subject: [PATCH 08/16] remove `nimFixedForwardGeneric` --- compiler/front/condsyms.nim | 1 - lib/pure/json.nim | 444 ++++++++++++++++++------------------ lib/std/jsonutils.nim | 17 +- 3 files changed, 222 insertions(+), 240 deletions(-) diff --git a/compiler/front/condsyms.nim b/compiler/front/condsyms.nim index 22f3ab979d6..3105815e53c 100644 --- a/compiler/front/condsyms.nim +++ b/compiler/front/condsyms.nim @@ -37,7 +37,6 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasStyleChecks") defineSymbol("nimToOpenArrayCString") defineSymbol("nimHasUsed") - defineSymbol("nimFixedForwardGeneric") defineSymbol("nimnomagic64") defineSymbol("nimNewShiftOps") defineSymbol("nimHasCursor") diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 49a7254a1c8..f415cb75102 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1046,181 +1046,167 @@ template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind], ] raise newException(JsonKindError, msg) -when defined(nimFixedForwardGeneric): # xxx: this seems unnecessary with the updated bootstrap - - macro isRefSkipDistinct*(arg: typed): untyped = - ## internal only, do not use - # xxx: why can't we: - # ``` - # import std/typetraits - # proc isRefBase*(t: typedesc[distinct]): bool = - # t.distinctBase(true) is ref - # ``` - # instead? - var impl = getTypeImpl(arg) - if impl.kind == nnkBracketExpr and impl[0].eqIdent("typeDesc"): - impl = getTypeImpl(impl[1]) - while impl.kind == nnkDistinctTy: - impl = getTypeImpl(impl[0]) - result = newLit(impl.kind == nnkRefTy) - - # The following forward declarations don't work in older versions of Nim - - # forward declare all initFromJson - - proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) - proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[S, T](dst: var array[S, T]; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T](dst: var Table[string, T]; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T](dst: var OrderedTable[string, T]; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) - - # initFromJson definitions - - proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JString, JNull}, jsonPath) - # since strings don't have a nil state anymore, this mapping of - # JNull to the default string is questionable. `none(string)` and - # `some("")` have the same potentional json value `JNull`. - if jsonNode.kind == JNull: - dst = "" - else: - dst = jsonNode.str - - proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JBool}, jsonPath) - dst = jsonNode.bval - - proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string) = - if jsonNode == nil: - raise newException(KeyError, "key not found: " & jsonPath) - dst = jsonNode.copy - - proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) = - when T is uint|uint64 or (not defined(js) and int.sizeof == 4): - verifyJsonKind(jsonNode, {JInt, JString}, jsonPath) - case jsonNode.kind - of JString: - let x = parseBiggestUInt(jsonNode.str) - dst = cast[T](x) - else: - dst = T(jsonNode.num) - else: - verifyJsonKind(jsonNode, {JInt}, jsonPath) - dst = cast[T](jsonNode.num) - - proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - if jsonNode.kind == JString: - case jsonNode.str - of "nan": - let b = NaN - dst = T(b) - # dst = NaN # would fail some tests because range conversions would cause CT error - # in some cases; but this is not a hot-spot inside this branch and backend can optimize this. - of "inf": - let b = Inf - dst = T(b) - of "-inf": - let b = -Inf - dst = T(b) - else: raise newException(JsonKindError, "expected 'nan|inf|-inf', got " & jsonNode.str) - else: - verifyJsonKind(jsonNode, {JInt, JFloat}, jsonPath) - if jsonNode.kind == JFloat: - dst = T(jsonNode.fnum) - else: - dst = T(jsonNode.num) - - proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JString}, jsonPath) - dst = parseEnum[T](jsonNode.getStr) - - proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JArray}, jsonPath) - dst.setLen jsonNode.len - let orignalJsonPathLen = jsonPath.len - for i in 0 ..< jsonNode.len: - jsonPath.add '[' - jsonPath.addInt i - jsonPath.add ']' - initFromJson(dst[i], jsonNode[i], jsonPath) - jsonPath.setLen orignalJsonPathLen - - proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JArray}, jsonPath) - let originalJsonPathLen = jsonPath.len - for i in 0 ..< jsonNode.len: - jsonPath.add '[' - jsonPath.addInt i - jsonPath.add ']' - initFromJson(dst[i.S], jsonNode[i], jsonPath) # `.S` for enum indexed arrays - jsonPath.setLen originalJsonPathLen +macro isRefSkipDistinct*(arg: typed): untyped = + ## internal only, do not use + # xxx: why can't we: + # ``` + # import std/typetraits + # proc isRefBase*(t: typedesc[distinct]): bool = + # t.distinctBase(true) is ref + # ``` + # instead? + var impl = getTypeImpl(arg) + if impl.kind == nnkBracketExpr and impl[0].eqIdent("typeDesc"): + impl = getTypeImpl(impl[1]) + while impl.kind == nnkDistinctTy: + impl = getTypeImpl(impl[0]) + result = newLit(impl.kind == nnkRefTy) + +# The following forward declarations don't work in older versions of Nim + +# forward declare all initFromJson + +proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) +proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[S, T](dst: var array[S, T]; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T](dst: var Table[string, T]; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T](dst: var OrderedTable[string, T]; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) + +# initFromJson definitions + +proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JString, JNull}, jsonPath) + # since strings don't have a nil state anymore, this mapping of + # JNull to the default string is questionable. `none(string)` and + # `some("")` have the same potentional json value `JNull`. + if jsonNode.kind == JNull: + dst = "" + else: + dst = jsonNode.str - proc initFromJson[T](dst: var Table[string,T]; jsonNode: JsonNode; jsonPath: var string) = - dst = initTable[string, T]() - verifyJsonKind(jsonNode, {JObject}, jsonPath) - let originalJsonPathLen = jsonPath.len - for key in keys(jsonNode.fields): - jsonPath.add '.' - jsonPath.add key - initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath) - jsonPath.setLen originalJsonPathLen +proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JBool}, jsonPath) + dst = jsonNode.bval - proc initFromJson[T](dst: var OrderedTable[string,T]; jsonNode: JsonNode; jsonPath: var string) = - dst = initOrderedTable[string,T]() - verifyJsonKind(jsonNode, {JObject}, jsonPath) - let originalJsonPathLen = jsonPath.len - for key in keys(jsonNode.fields): - jsonPath.add '.' - jsonPath.add key - initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath) - jsonPath.setLen originalJsonPathLen +proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string) = + if jsonNode == nil: + raise newException(KeyError, "key not found: " & jsonPath) + dst = jsonNode.copy - proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JObject, JNull}, jsonPath) - if jsonNode.kind == JNull: - dst = nil +proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) = + when T is uint|uint64 or (not defined(js) and int.sizeof == 4): + verifyJsonKind(jsonNode, {JInt, JString}, jsonPath) + case jsonNode.kind + of JString: + let x = parseBiggestUInt(jsonNode.str) + dst = cast[T](x) + else: + dst = T(jsonNode.num) + else: + verifyJsonKind(jsonNode, {JInt}, jsonPath) + dst = cast[T](jsonNode.num) + +proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) = + if jsonNode.kind == JString: + case jsonNode.str + of "nan": + let b = NaN + dst = T(b) + # dst = NaN # would fail some tests because range conversions would cause CT error + # in some cases; but this is not a hot-spot inside this branch and backend can optimize this. + of "inf": + let b = Inf + dst = T(b) + of "-inf": + let b = -Inf + dst = T(b) + else: raise newException(JsonKindError, "expected 'nan|inf|-inf', got " & jsonNode.str) + else: + verifyJsonKind(jsonNode, {JInt, JFloat}, jsonPath) + if jsonNode.kind == JFloat: + dst = T(jsonNode.fnum) else: - dst = new(T) - initFromJson(dst[], jsonNode, jsonPath) + dst = T(jsonNode.num) + +proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JString}, jsonPath) + dst = parseEnum[T](jsonNode.getStr) + +proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JArray}, jsonPath) + dst.setLen jsonNode.len + let orignalJsonPathLen = jsonPath.len + for i in 0 ..< jsonNode.len: + jsonPath.add '[' + jsonPath.addInt i + jsonPath.add ']' + initFromJson(dst[i], jsonNode[i], jsonPath) + jsonPath.setLen orignalJsonPathLen + +proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JArray}, jsonPath) + let originalJsonPathLen = jsonPath.len + for i in 0 ..< jsonNode.len: + jsonPath.add '[' + jsonPath.addInt i + jsonPath.add ']' + initFromJson(dst[i.S], jsonNode[i], jsonPath) # `.S` for enum indexed arrays + jsonPath.setLen originalJsonPathLen + +proc initFromJson[T](dst: var Table[string,T]; jsonNode: JsonNode; jsonPath: var string) = + dst = initTable[string, T]() + verifyJsonKind(jsonNode, {JObject}, jsonPath) + let originalJsonPathLen = jsonPath.len + for key in keys(jsonNode.fields): + jsonPath.add '.' + jsonPath.add key + initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath) + jsonPath.setLen originalJsonPathLen + +proc initFromJson[T](dst: var OrderedTable[string,T]; jsonNode: JsonNode; jsonPath: var string) = + dst = initOrderedTable[string,T]() + verifyJsonKind(jsonNode, {JObject}, jsonPath) + let originalJsonPathLen = jsonPath.len + for key in keys(jsonNode.fields): + jsonPath.add '.' + jsonPath.add key + initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath) + jsonPath.setLen originalJsonPathLen + +proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JObject, JNull}, jsonPath) + if jsonNode.kind == JNull: + dst = nil + else: + dst = new(T) + initFromJson(dst[], jsonNode, jsonPath) - proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) = - if jsonNode != nil and jsonNode.kind != JNull: - when T is ref: - dst = some(new(T)) - else: - dst = some(default(T)) - initFromJson(dst.get, jsonNode, jsonPath) - - from std/typetraits import isNamedTuple - - proc initFromJson[T: object|tuple](dst: var T, jsonNode: JsonNode, jsonPath: var string) = - let originalJsonPathLen = jsonPath.len ## so we can trucate/reset after - when T is tuple: - when T.isNamedTuple: - # TODO: nimvm workaround for distinct? - jsonPath.add "." - for name, value in dst.fieldPairs: - jsonPath.add name - var val = value - initFromJson(val, jsonNode.getOrDefault(name), jsonPath) - value = val - jsonPath.setLen(jsonPath.len - name.len) - jsonPath.setLen originalJsonPathLen - else: - {.error("Use a named tuple instead of: " & T.repr).} - elif T is object: +proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) = + if jsonNode != nil and jsonNode.kind != JNull: + when T is ref: + dst = some(new(T)) + else: + dst = some(default(T)) + initFromJson(dst.get, jsonNode, jsonPath) + +from std/typetraits import isNamedTuple + +proc initFromJson[T: object|tuple](dst: var T, jsonNode: JsonNode, jsonPath: var string) = + let originalJsonPathLen = jsonPath.len ## so we can trucate/reset after + when T is tuple: + when T.isNamedTuple: + # TODO: nimvm workaround for distinct? jsonPath.add "." for name, value in dst.fieldPairs: - # xxx: might need unsafe assign due to kind fields jsonPath.add name var val = value initFromJson(val, jsonNode.getOrDefault(name), jsonPath) @@ -1228,61 +1214,73 @@ when defined(nimFixedForwardGeneric): # xxx: this seems unnecessary with the upd jsonPath.setLen(jsonPath.len - name.len) jsonPath.setLen originalJsonPathLen else: - {.error("Unreachable type: " & T.repr).} - - macro assignDistinctImpl[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string) = - let typInst = getTypeInst(dst) - let typImpl = getTypeImpl(dst) - let baseTyp = typImpl[0] - - result = quote do: - when nimvm: - # workaround #12282 - var tmp: `baseTyp` - initFromJson( tmp, `jsonNode`, `jsonPath`) - `dst` = `typInst`(tmp) - else: - initFromJson( `baseTyp`(`dst`), `jsonNode`, `jsonPath`) - - proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - assignDistinctImpl(dst, jsonNode, jsonPath) - - proc to*[T](node: JsonNode, t: typedesc[T]): T = - ## `Unmarshals`:idx: the specified node into the object type specified. - ## - ## Known limitations: - ## - ## * Heterogeneous arrays are not supported. - ## * Sets in object variants are not supported. - ## * Not nil annotations are not supported. - ## - runnableExamples: - let jsonNode = parseJson(""" - { - "person": { - "name": "Nimmer", - "age": 21 - }, - "list": [1, 2, 3, 4] - } - """) - - type - Person = object - name: string - age: int - - Data = object - person: Person - list: seq[int] - - var data = to(jsonNode, Data) - doAssert data.person.name == "Nimmer" - doAssert data.person.age == 21 - doAssert data.list == @[1, 2, 3, 4] - - var jsonPath = "" - initFromJson(result, node, jsonPath) + {.error("Use a named tuple instead of: " & T.repr).} + elif T is object: + jsonPath.add "." + for name, value in dst.fieldPairs: + # xxx: might need unsafe assign due to kind fields + jsonPath.add name + var val = value + initFromJson(val, jsonNode.getOrDefault(name), jsonPath) + value = val + jsonPath.setLen(jsonPath.len - name.len) + jsonPath.setLen originalJsonPathLen + else: + {.error("Unreachable type: " & T.repr).} + +macro assignDistinctImpl[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string) = + let typInst = getTypeInst(dst) + let typImpl = getTypeImpl(dst) + let baseTyp = typImpl[0] + + result = quote do: + when nimvm: + # workaround #12282 + var tmp: `baseTyp` + initFromJson( tmp, `jsonNode`, `jsonPath`) + `dst` = `typInst`(tmp) + else: + initFromJson( `baseTyp`(`dst`), `jsonNode`, `jsonPath`) + +proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) = + assignDistinctImpl(dst, jsonNode, jsonPath) + +proc to*[T](node: JsonNode, t: typedesc[T]): T = + ## `Unmarshals`:idx: the specified node into the object type specified. + ## + ## Known limitations: + ## + ## * Heterogeneous arrays are not supported. + ## * Sets in object variants are not supported. + ## * Not nil annotations are not supported. + ## + runnableExamples: + let jsonNode = parseJson(""" + { + "person": { + "name": "Nimmer", + "age": 21 + }, + "list": [1, 2, 3, 4] + } + """) + + type + Person = object + name: string + age: int + + Data = object + person: Person + list: seq[int] + + var data = to(jsonNode, Data) + doAssert data.person.name == "Nimmer" + doAssert data.person.age == 21 + doAssert data.list == @[1, 2, 3, 4] + + var jsonPath = "" + initFromJson(result, node, jsonPath) when false: import std/os diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index 95a13b4f025..9fea21cc5c7 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -34,22 +34,7 @@ import std/macros from std/enumutils import symbolName from std/typetraits import OrdinalEnum -when not defined(nimFixedForwardGeneric): - # xxx remove pending csources_v1 update >= 1.2.0 - proc to[T](node: JsonNode, t: typedesc[T]): T = - when T is string: node.getStr - elif T is bool: node.getBool - else: static: doAssert false, $T # support as needed (only needed during bootstrap) - proc isNamedTuple(T: typedesc): bool = # old implementation - when T isnot tuple: result = false - else: - var t: T - for name, _ in t.fieldPairs: - when name == "Field0": return compiles(t.Field0) - else: return true - return false -else: - proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} +proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} type Joptions* = object # xxx rename FromJsonOptions From 4cd751b23780b66f439a0b90c8ee006897caffe1 Mon Sep 17 00:00:00 2001 From: saem Date: Mon, 14 Aug 2023 11:53:36 -0700 Subject: [PATCH 09/16] remove `isRefSkipDistinct` --- lib/pure/json.nim | 16 ---------------- tests/stdlib/formats/tjson.nim | 26 -------------------------- 2 files changed, 42 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index f415cb75102..d7e27c98ddc 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1046,22 +1046,6 @@ template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind], ] raise newException(JsonKindError, msg) -macro isRefSkipDistinct*(arg: typed): untyped = - ## internal only, do not use - # xxx: why can't we: - # ``` - # import std/typetraits - # proc isRefBase*(t: typedesc[distinct]): bool = - # t.distinctBase(true) is ref - # ``` - # instead? - var impl = getTypeImpl(arg) - if impl.kind == nnkBracketExpr and impl[0].eqIdent("typeDesc"): - impl = getTypeImpl(impl[1]) - while impl.kind == nnkDistinctTy: - impl = getTypeImpl(impl[0]) - result = newLit(impl.kind == nnkRefTy) - # The following forward declarations don't work in older versions of Nim # forward declare all initFromJson diff --git a/tests/stdlib/formats/tjson.nim b/tests/stdlib/formats/tjson.nim index 8581add25bc..a9ae7839c83 100644 --- a/tests/stdlib/formats/tjson.nim +++ b/tests/stdlib/formats/tjson.nim @@ -234,32 +234,6 @@ block: doAssert res == fragments -# test isRefSkipDistinct -type - MyRef = ref object - MyObject = object - MyDistinct = distinct MyRef - MyOtherDistinct = distinct MyRef - -var x0: ref int -var x1: MyRef -var x2: MyObject -var x3: MyDistinct -var x4: MyOtherDistinct - -doAssert isRefSkipDistinct(x0) -doAssert isRefSkipDistinct(x1) -doAssert not isRefSkipDistinct(x2) -doAssert isRefSkipDistinct(x3) -doAssert isRefSkipDistinct(x4) - - -doAssert isRefSkipDistinct(ref int) -doAssert isRefSkipDistinct(MyRef) -doAssert not isRefSkipDistinct(MyObject) -doAssert isRefSkipDistinct(MyDistinct) -doAssert isRefSkipDistinct(MyOtherDistinct) - let x = parseJson("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999") doAssert x.kind == JString From 59ca6e9d6b1939f168fe13e2abc3539e7dd385c8 Mon Sep 17 00:00:00 2001 From: saem Date: Mon, 14 Aug 2023 12:01:08 -0700 Subject: [PATCH 10/16] remove type macros for `distinct` handling --- lib/pure/json.nim | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index d7e27c98ddc..3f84e6640bd 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1182,7 +1182,7 @@ proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var strin dst = some(default(T)) initFromJson(dst.get, jsonNode, jsonPath) -from std/typetraits import isNamedTuple +from std/typetraits import isNamedTuple, distinctBase proc initFromJson[T: object|tuple](dst: var T, jsonNode: JsonNode, jsonPath: var string) = let originalJsonPathLen = jsonPath.len ## so we can trucate/reset after @@ -1212,22 +1212,8 @@ proc initFromJson[T: object|tuple](dst: var T, jsonNode: JsonNode, jsonPath: var else: {.error("Unreachable type: " & T.repr).} -macro assignDistinctImpl[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string) = - let typInst = getTypeInst(dst) - let typImpl = getTypeImpl(dst) - let baseTyp = typImpl[0] - - result = quote do: - when nimvm: - # workaround #12282 - var tmp: `baseTyp` - initFromJson( tmp, `jsonNode`, `jsonPath`) - `dst` = `typInst`(tmp) - else: - initFromJson( `baseTyp`(`dst`), `jsonNode`, `jsonPath`) - -proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - assignDistinctImpl(dst, jsonNode, jsonPath) +proc initFromJson[T: distinct](dst: var T, jsonNode: JsonNode, jsonPath: var string) = + initFromJson(distinctBase dst, jsonNode, jsonPath) proc to*[T](node: JsonNode, t: typedesc[T]): T = ## `Unmarshals`:idx: the specified node into the object type specified. From a9dd3857aa4f601a41aa6a7b0b171b5f8b76afd8 Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Wed, 16 Aug 2023 11:32:30 -0700 Subject: [PATCH 11/16] remove out of date comment Co-authored-by: zerbina <100542850+zerbina@users.noreply.github.com> --- lib/pure/json.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 3f84e6640bd..18b9ac5c3a2 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1046,7 +1046,6 @@ template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind], ] raise newException(JsonKindError, msg) -# The following forward declarations don't work in older versions of Nim # forward declare all initFromJson From 2c2978fc4023628b353603d1b6cdf9f9e8bc460a Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Wed, 16 Aug 2023 11:32:54 -0700 Subject: [PATCH 12/16] remove out of date TODO Co-authored-by: zerbina <100542850+zerbina@users.noreply.github.com> --- lib/pure/json.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 18b9ac5c3a2..648f1153511 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1187,7 +1187,6 @@ proc initFromJson[T: object|tuple](dst: var T, jsonNode: JsonNode, jsonPath: var let originalJsonPathLen = jsonPath.len ## so we can trucate/reset after when T is tuple: when T.isNamedTuple: - # TODO: nimvm workaround for distinct? jsonPath.add "." for name, value in dst.fieldPairs: jsonPath.add name From f7da4bf81a6abf9b2cb4d0c4fc7325760dcb1200 Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Wed, 16 Aug 2023 11:33:07 -0700 Subject: [PATCH 13/16] remove out of date comment Co-authored-by: zerbina <100542850+zerbina@users.noreply.github.com> --- lib/pure/json.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 648f1153511..ea2bfb60d17 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1200,7 +1200,6 @@ proc initFromJson[T: object|tuple](dst: var T, jsonNode: JsonNode, jsonPath: var elif T is object: jsonPath.add "." for name, value in dst.fieldPairs: - # xxx: might need unsafe assign due to kind fields jsonPath.add name var val = value initFromJson(val, jsonNode.getOrDefault(name), jsonPath) From 7dea08dea42fc57dfe1884ae67de4bd7ee22b02f Mon Sep 17 00:00:00 2001 From: saem Date: Wed, 16 Aug 2023 11:38:08 -0700 Subject: [PATCH 14/16] split `initFromJons` for `object` and `tuple` --- lib/pure/json.nim | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index ea2bfb60d17..c7e5e72b79c 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1183,21 +1183,9 @@ proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var strin from std/typetraits import isNamedTuple, distinctBase -proc initFromJson[T: object|tuple](dst: var T, jsonNode: JsonNode, jsonPath: var string) = +proc initFromJson[T: tuple](dst: var T, jsonNode: JsonNode, jsonPath: var string) = let originalJsonPathLen = jsonPath.len ## so we can trucate/reset after - when T is tuple: - when T.isNamedTuple: - jsonPath.add "." - for name, value in dst.fieldPairs: - jsonPath.add name - var val = value - initFromJson(val, jsonNode.getOrDefault(name), jsonPath) - value = val - jsonPath.setLen(jsonPath.len - name.len) - jsonPath.setLen originalJsonPathLen - else: - {.error("Use a named tuple instead of: " & T.repr).} - elif T is object: + when T.isNamedTuple: jsonPath.add "." for name, value in dst.fieldPairs: jsonPath.add name @@ -1207,7 +1195,18 @@ proc initFromJson[T: object|tuple](dst: var T, jsonNode: JsonNode, jsonPath: var jsonPath.setLen(jsonPath.len - name.len) jsonPath.setLen originalJsonPathLen else: - {.error("Unreachable type: " & T.repr).} + {.error("Use a named tuple instead of: " & T.repr).} + +proc initFromJson[T: object](dst: var T, jsonNode: JsonNode, jsonPath: var string) = + let originalJsonPathLen = jsonPath.len ## so we can trucate/reset after + jsonPath.add "." + for name, value in dst.fieldPairs: + jsonPath.add name + var val = value + initFromJson(val, jsonNode.getOrDefault(name), jsonPath) + value = val + jsonPath.setLen(jsonPath.len - name.len) + jsonPath.setLen originalJsonPathLen proc initFromJson[T: distinct](dst: var T, jsonNode: JsonNode, jsonPath: var string) = initFromJson(distinctBase dst, jsonNode, jsonPath) From 593373793edc0ed6805d4bc6d6dc453bda4d1993 Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Thu, 17 Aug 2023 15:37:47 -0700 Subject: [PATCH 15/16] `containsNode` correctly traverses types Co-authored-by: zerbina <100542850+zerbina@users.noreply.github.com> --- compiler/ast/ast_query.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/ast/ast_query.nim b/compiler/ast/ast_query.nim index e066f35921f..0d01ecbb36f 100644 --- a/compiler/ast/ast_query.nim +++ b/compiler/ast/ast_query.nim @@ -331,7 +331,7 @@ proc hasNilSon*(n: PNode): bool = proc containsNode*(n: PNode, kinds: TNodeKinds): bool = if n == nil: return case n.kind - of nkEmpty..nkNilLit, nkError: result = n.kind in kinds + of nkWithoutSons: result = n.kind in kinds else: for i in 0.. Date: Fri, 18 Aug 2023 12:28:05 -0700 Subject: [PATCH 16/16] fix `initFromJson` forward declarations --- lib/pure/json.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index c7e5e72b79c..0e81616a658 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1062,7 +1062,8 @@ proc initFromJson[T](dst: var OrderedTable[string, T]; jsonNode: JsonNode; jsonP proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) -proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T: tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T: object](dst: var T; jsonNode: JsonNode; jsonPath: var string) # initFromJson definitions