Skip to content

Commit

Permalink
Replace expectX templates with assertX templates
Browse files Browse the repository at this point in the history
The assertX templates do not take a code block like the other ones did.
Using these templates required some specialized code inside of the
blocks and couldn't take advantage of Nim's implicit returns, which
leads to cleaner code (in my opinion).
  • Loading branch information
tanelso2 committed Nov 19, 2024
1 parent b139d72 commit 0a44192
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 153 deletions.
49 changes: 22 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,43 +219,37 @@ will output:

```nim
proc ofYaml(n: YNode; t: typedesc[E]): E =
case n.kind
of ynString:
case n.strVal
of $eStr:
eStr
of $eInt:
eInt
else:
raise newException(ValueError, "unknown kind: " & n.strVal)
assertYString n
case n.strVal
of $eStr:
eStr
of $eInt:
eInt
else:
raise newException(ValueError, "expected string YNode")
raise newException(ValueError, "unknown kind: " & n.strVal)
proc toYaml(x: E): YNode =
result = newYString($x)
proc ofYaml(n: YNode; t: typedesc[V]): V =
case n.kind
of ynMap:
let kind = n.get("kind", typedesc[E])
case kind
of eStr:
result = V(kind: kind, c: n.get("c", typedesc[string]),
s: n.get("s", typedesc[string]))
of eInt:
result = V(kind: kind, c: n.get("c", typedesc[string]),
i: n.get("i", typedesc[int]))
else:
raise newException(ValueError, "expected map YNode")
assertYMap n
let kind = n.get("kind", typedesc[E])
case kind
of eStr:
V(kind: kind, c: n.get("c", typedesc[string]),
s: n.get("s", typedesc[string]))
of eInt:
V(kind: kind, c: n.get("c", typedesc[string]),
i: n.get("i", typedesc[int]))
proc toYaml(x: V): YNode =
case x.kind
result = case x.kind
of eStr:
result = newYMapRemoveNils([("kind", toYaml(x.kind)), ("c", toYaml(x.c)),
("s", toYaml(x.s))])
newYMapRemoveNils([("kind", toYaml(x.kind)), ("c", toYaml(x.c)),
("s", toYaml(x.s))])
of eInt:
result = newYMapRemoveNils([("kind", toYaml(x.kind)), ("c", toYaml(x.c)),
("i", toYaml(x.i))])
newYMapRemoveNils([("kind", toYaml(x.kind)), ("c", toYaml(x.c)),
("i", toYaml(x.i))])
```

# Comparison with [NimYAML](https://github.com/flyx/NimYAML)
Expand Down Expand Up @@ -314,6 +308,7 @@ I had trouble when using NimYAML where I was structuring my types to better work
NimYAML is more strict about types whereas Yanyl is more akin to duck-typing. If an object has the fields expected, Yanyl will stuff it into the type you specified.

**Note**: some of the things I thought were NimYAML's intended behavior may actually be bugs, so I will work on reporting those properly instead of just spinning off my own library.
**Note 2024-11-18:** some of these things may have been fixed in more recent versions of NimYAML. It has been a while since I checked in on its behavior with types and generating code.

## Explicit declarations
As a newbie to Nim macro programming, I wasn't sure how or where NimYAML was creating functions. Yanyl makes it clear with its `derive` macros. It is more boilerplate for the developer, but I think it makes it clear and easier to debug.
Expand Down
114 changes: 51 additions & 63 deletions src/yanyl/codegen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ proc mkTypedesc(t: NimNode): NimNode =

proc getValueForField(f: Field, obj: NimNode): NimNode =
mkYNodeGetCall(
obj,
f.getName(),
obj,
f.getName(),
mkTypedesc(f.getT()))

proc pubIdent(s: string): NimNode =
Expand All @@ -42,7 +42,7 @@ proc mkObjTypeConsFieldParam(f: Field, obj: NimNode): NimNode =
proc mkOfYamlForObjType(t: NimNode, fields: seq[Field]): NimNode =
let retType = t
let nodeParam = newIdentDefs(ident("n"), ident("YNode"))
let typeParam = newIdentDefs(ident("t"),
let typeParam = newIdentDefs(ident("t"),
nnkBracketExpr.newTree(
ident "typedesc",
retType
Expand All @@ -53,20 +53,18 @@ proc mkOfYamlForObjType(t: NimNode, fields: seq[Field]): NimNode =
params=[retType, nodeParam, typeParam],
body=nnkStmtList.newTree(
nnkCommand.newTree(
ident("expectYMap"),
n,
newStmtList(
nnkAsgn.newTree(
ident("result"),
nnkObjConstr.newTree(
concat(
@[retType],
fields.mapIt(mkObjTypeConsFieldParam(it, n)))))))))
ident("assertYMap"),
n
),
nnkObjConstr.newTree(
concat(
@[retType],
fields.mapIt(mkObjTypeConsFieldParam(it, n))))))

proc mkOfYamlForTupleType(t: NimNode, fields: seq[Field]): NimNode =
let retType = t
let nodeParam = newIdentDefs(ident("n"), ident("YNode"))
let typeParam = newIdentDefs(ident("t"),
let typeParam = newIdentDefs(ident("t"),
nnkBracketExpr.newTree(
ident "typedesc",
retType
Expand All @@ -77,13 +75,11 @@ proc mkOfYamlForTupleType(t: NimNode, fields: seq[Field]): NimNode =
params=[retType, nodeParam, typeParam],
body=nnkStmtList.newTree(
nnkCommand.newTree(
ident("expectYMap"),
n,
newStmtList(
nnkAsgn.newTree(
ident("result"),
nnkTupleConstr.newTree(
fields.mapIt(mkObjTypeConsFieldParam(it, n))))))))
ident "assertYMap",
n
),
nnkTupleConstr.newTree(
fields.mapIt(mkObjTypeConsFieldParam(it, n)))))

proc mkObjTypeTableField(f: Field, obj: NimNode): NimNode =
nnkExprColonExpr.newTree(
Expand Down Expand Up @@ -145,10 +141,7 @@ proc mkEnumOfBranch(val: EnumVal): NimNode =
ident(val.name)
),
newStmtList(
nnkAsgn.newTree(
ident("result"),
ident(val.name)
)
ident(val.name)
)
)

Expand Down Expand Up @@ -179,22 +172,22 @@ proc mkOfYamlForEnumType(t: NimNode, vals: seq[EnumVal]): NimNode =
params=[retType, nodeParam, typeParam],
body=nnkStmtList.newTree(
nnkCommand.newTree(
ident("expectYString"),
n,
newStmtList(
nnkCaseStmt.newTree(
concat(@[
nnkDotExpr.newTree(
n, ident("strVal")
)
],
vals.mapIt(mkEnumOfBranch(it)),
@[elseBranch])
)))))

proc mkOfYamlForVariantType(t: NimNode,
common: seq[Field],
discrim: Field,
ident "assertYString",
n
),
nnkCaseStmt.newTree(
concat(@[
nnkDotExpr.newTree(
n, ident("strVal")
)
],
vals.mapIt(mkEnumOfBranch(it)),
@[elseBranch])
)))

proc mkOfYamlForVariantType(t: NimNode,
common: seq[Field],
discrim: Field,
variants: seq[NimVariant]): NimNode =
let retType = t
let n = ident("n")
Expand All @@ -205,13 +198,11 @@ proc mkOfYamlForVariantType(t: NimNode,
nnkOfBranch.newTree(
ident(v.name),
newStmtList(
nnkAsgn.newTree(
ident("result"),
nnkObjConstr.newTree(
concat(
@[t,
newColonExpr(kind, kind)],
neededFields.mapIt(mkObjTypeConsFieldParam(it, n)))))))
nnkObjConstr.newTree(
concat(
@[t,
newColonExpr(kind, kind)],
neededFields.mapIt(mkObjTypeConsFieldParam(it, n))))))

let nodeParam = newIdentDefs(n, ident("YNode"))
let typedescType = nnkBracketExpr.newTree(
Expand All @@ -226,31 +217,28 @@ proc mkOfYamlForVariantType(t: NimNode,
params=[retType, nodeParam, typeParam],
body=newStmtList(
nnkCommand.newTree(
ident("expectYMap"),
n,
newStmtList(
newLetStmt(kind,
getValueForField(discrim, n)),
nnkCaseStmt.newTree(
concat(@[kind], ofBranches))))))
ident "assertYMap",
n
),
newLetStmt(kind,
getValueForField(discrim, n)),
nnkCaseStmt.newTree(
concat(@[kind], ofBranches))))

proc mkToYamlForVariantType(t: NimNode,
common: seq[Field],
discrim: Field,
common: seq[Field],
discrim: Field,
variants: seq[NimVariant]): NimNode =
let retType = ident("YNode")
let obj = ident("x")
proc mkVariantOfBranch(v: NimVariant): NimNode =
let neededFields = @[discrim] & common & v.fields
nnkOfBranch.newTree(
ident(v.name),
nnkAsgn.newTree(
ident("result"),
newCall(
ident("newYMapRemoveNils"),
nnkTableConstr.newTree(
neededFields.mapIt(mkObjTypeTableField(it, obj))
)
newCall(
ident("newYMapRemoveNils"),
nnkTableConstr.newTree(
neededFields.mapIt(mkObjTypeTableField(it, obj))
)
)
)
Expand Down
Loading

0 comments on commit 0a44192

Please sign in to comment.