json
nimony/lib/std/json.nim
json — a NIF-backed JSON model for the Nimony stdlib.
Built directly on nifcore, the same in-memory NIF representation the compiler's plugin API uses. A JSON document is not a tree of heap ref JsonNodes; it is a single flat nifcore.TokenBuf, navigated with a JsonNode — a thin wrapper over a nifcore.Cursor. The traversal verbs (into / hasMore / skip) and the builder verbs (openTag / addStrLit / …) are literally the same calls a plugin author uses on a compiler tree — so learning the JSON model and the NIF model is one act, not two. This is the unifying-stdlib idea: the concepts translate directly because the substrate is shared.
Encoding (a JSON value as a nifcore tag-stream)::
null → (null) # 1 TagLit, empty body <err> → (null "message") # parse error: a null carrying a diagnostic true → (true) false → (false) 42 → IntLit 42 # inline atom 3.14 → FloatLit 3.14 "hi" → StrLit "hi" [a,b] → (aconstr a b) {k:v} → (oconstr (kv "k" v))
Three layers, mirroring how nifply relates typed Nim to NIF:
- parse / read —
parseJson/parseFilebuild aJsonTree; navigate
with kind, getStr/getInt/…, len, items, pairs, {}.
- typed map —
toJson(typed Nim → JSON) andfromJson(JSON → typed
Nim), the JSON siblings of nifply's toNif/fromNif. Same reflection magics (internalFieldPairs / internalTypeName), same oconstr/kv/ aconstr tag vocabulary — differing only where idiomatic JSON differs (string keys, enums as name-strings, optional "type" discriminator).
- line info —
parseJsontakes aLineInfoMode; underKeepLineInfo
each node carries a nifcore.LineInfoLit, the same cheap source-position token NIF uses. Building (toJson) carries none — there is no source.
type JsonKind = enum JKNull = (0, "null") JKTrue = (1, "true") JKFalse = (2, "false") JKObject = (3, "oconstr") JKArray = (4, "aconstr") JKKv = (5, "kv")
func dollar`.JsonKind(e: JsonKind): stringtype JsonNodeKind = enum JNull = (0, "JNull") JBool = (1, "JBool") JInt = (2, "JInt") JFloat = (3, "JFloat") JString = (4, "JString") JObject = (5, "JObject") JArray = (6, "JArray")
func dollar`.JsonNodeKind(e: JsonNodeKind): stringtype LineInfoMode = enum DiscardLineInfo = (0, "DiscardLineInfo") KeepLineInfo = (1, "KeepLineInfo") KeepColumnInfo = (2, "KeepColumnInfo")
func dollar`.LineInfoMode(e: LineInfoMode): stringtype JsonOptions = object typeFieldName: string
type JsonTree = object buf: TokenBuf
type JsonNode = object c: Cursor
template tagId(k: JsonKind): TagIdtemplate jsonKind(t: TagId): JsonKindproc createJsonTree(sharedPool: ref Pool.Obj): JsonTreeMint an empty tree. Pass
sharedPoolto intern this tree's string literals (and, underKeep*LineInfo, filenames) into a pool shared with other trees or other NIF adapters — cross-format dedup, the point of the split-pool design. Each tree still gets its own freshTagPoolso theJsonKindcast stays a register move.proc parseJson(stream: ref StreamObj; filename: string; lineInfo: LineInfoMode; sharedPool: ref Pool.Obj): JsonTreeParse a full document off
streaminto an in-memoryJsonTree. UnderKeepLineInfo/KeepColumnInfoevery node records its source position (the filename interned in the tree'spool, deduped ifsharedPoolis passed) — the same cheap mechanism NIF trees use, whichstd/jsoncannot offer.proc parseJson(buffer: string; filename: string; lineInfo: LineInfoMode; sharedPool: ref Pool.Obj): JsonTreeproc parseFile(filename: string; lineInfo: LineInfoMode; sharedPool: ref Pool.Obj): JsonTreeproc root(t: var JsonTree): JsonNodeproc kind(n: JsonNode): JsonNodeKindEffective high-level kind of the node. Coexists with
nifcore.kind(Cursor)as a plain overload becausenis aJsonNode, not aCursor.proc info(n: JsonNode): NifLineInfoSource position recorded for the node, or
NoNifLineInfowhen the tree was parsed withDiscardLineInfo(or built programmatically).proc fileName(n: JsonNode): stringSource filename for the node, or
""when no line info is present.proc getStr(n: JsonNode; default: string): stringproc getInt(n: JsonNode; default: int64): int64proc getFloat(n: JsonNode; default: float64): float64proc getBool(n: JsonNode; default: bool): boolproc errorMsg(n: JsonNode): stringIf the node is an error placeholder (a
nullwith a diagnostic string child,(null "msg")), return that message; otherwise"". A cleannull— and any other node — yields"".proc isError(n: JsonNode): boolTrue when the node is an error placeholder (see
errorMsg).proc errorMsg(t: var JsonTree): stringThe first error message embedded anywhere in the document (depth-first), or
""if it parsed cleanly. Ignore it and malformed nodes simply read asnull; consult it for the diagnostic and source position of the first failure.proc hasError(t: var JsonTree): boolTrue when the document contains any parse error (see
errorMsg).proc len(n: JsonNode): int64Number of elements (array) or members (object). 0 for scalars.
iterator items(n: JsonNode): JsonNodeiterator pairs(n: JsonNode): tuple[string, JsonNode]proc {}(t: var JsonTree; key: string): JsonNodeObject member lookup. Returns a nil-ish node when the root is not an object or the key is absent (
kindof it isJNull).proc $(t: var JsonTree): stringproc toJson(t: var JsonTree; x: int64; opts: JsonOptions)proc toJson(t: var JsonTree; x: float64; opts: JsonOptions)proc toJson(t: var JsonTree; x: string; opts: JsonOptions)proc toJson(t: var JsonTree; x: bool; opts: JsonOptions)proc toJson(t: var JsonTree; x: E; opts: JsonOptions)proc toJson(t: var JsonTree; x: seq[T]; opts: JsonOptions)proc toJson(t: var JsonTree; x: O; opts: JsonOptions)proc toJson(x: T; opts: JsonOptions): JsonTreeEntry point: serialise
xto a freshJsonTree. Distinguished from the recursive workers by arity (1–2 args, no leadingvar JsonTree).proc fromJson(c: var JsonNode; t: typedesc[int64]; opts: JsonOptions): int64proc fromJson(c: var JsonNode; t: typedesc[float64]; opts: JsonOptions): float64proc fromJson(c: var JsonNode; t: typedesc[string]; opts: JsonOptions): stringproc fromJson(c: var JsonNode; t: typedesc[bool]; opts: JsonOptions): boolproc fromJson(c: var JsonNode; t: typedesc[E]; opts: JsonOptions): Eproc fromJson(c: var JsonNode; x: var T; opts: JsonOptions)proc fromJson(c: var JsonNode; t: typedesc[seq[T]]; opts: JsonOptions): seq[T]proc fromJson(c: var JsonNode; t: typedesc[O]; opts: JsonOptions): Oproc fromJson(t: var JsonTree; tt: typedesc[T]; opts: JsonOptions): TEntry point: deserialise the root of
tinto aT.