Skip to content

Commit 2aaae6a

Browse files
committed
cps: implement shims for docgen
Currently CPS simply left the procedures untouched during docgen. This causes problems when code for `whelp` gets sem-ed, breaking docgen. This commit implements a couple docgen shims: - `cps` now result in a `{.cps: baseType.}` pragma tagged to the resulting procedure and will show up in the generated documentation. - `whelp` will generate `nil` continuation for the base type of a cps proc. - `whelp` will generate a `Callback` with a factory type that yields a the base continuation type, not the environment type. This should work for most cases when docgen are concerned. This worked well enough for `insideout` docs to be generated successfully under NimSkull.
1 parent a04b82f commit 2aaae6a

2 files changed

Lines changed: 99 additions & 2 deletions

File tree

cps.nim

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import std/[genasts, deques]
2-
import cps/[spec, transform, rewrites, hooks, exprs, normalizedast, callbacks]
2+
import cps/[docgen, spec, transform, rewrites, hooks, exprs, normalizedast, callbacks]
33
import std/macros except newStmtList, newTree
44

55
export Continuation, ContinuationProc, ContinuationFn, State, Callback
@@ -80,7 +80,8 @@ macro cps*(tipe: typed, n: untyped): untyped =
8080
## When applied to a procedure, rewrites the procedure into a
8181
## continuation form. When applied to a procedure type definition,
8282
## rewrites the type into a callback form.
83-
when defined(nimdoc): return n
83+
when defined(nimdoc): return cpsDocAnnotate(tipe, n)
84+
8485
# add the application of the typed transformation pass
8586
n.addPragma:
8687
nnkExprColonExpr.newTree(bindSym"cpsTyped", tipe)
@@ -139,6 +140,7 @@ macro whelp*(call: typed): untyped =
139140
## If you pass `whelp` a continuation procedure *symbol* instead, the
140141
## result is a `Callback` which you can use to create many individual
141142
## continuations or recover the `result` of an extant continuation.
143+
when defined(nimdoc): return docWhelp(call)
142144
result =
143145
case call.kind
144146
of nnkSym:

cps/docgen.nim

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
when defined(nimdoc):
2+
import std/macros except newTree, newStmtList
3+
import callbacks, normalizedast, spec
4+
5+
template cps(tipe: typed) {.pragma.}
6+
7+
proc cpsDocAnnotate*(tipe, n: NimNode): NimNode =
8+
## Annotate `n` with a `cps` pragma that will show up in doc
9+
n.addPragma:
10+
nnkExprColonExpr.newTree(bindSym"cps", tipe)
11+
result = n
12+
13+
proc getCpsBase(n: NormNode, origin: NormNode = n): NormNode =
14+
## recover the symbol of the cps base type or generate error ast
15+
case n.kind
16+
of NormalCallNodes:
17+
getCpsBase(n[0], origin)
18+
of nnkSym:
19+
getCpsBase(n.getImpl, origin)
20+
of nnkProcDef:
21+
if n.hasPragma "borrow":
22+
getCpsBase(n.last)
23+
elif n.hasPragma "cps":
24+
pragmaArgument(n, "cps")
25+
else:
26+
error "procedure " & n.name.strVal & " doesn't seem to be a cps call", origin
27+
normalizedast.newCall("typeOf", n)
28+
else:
29+
error "procedure doesn't seem to be a cps call", origin
30+
## XXX: darn ambiguous calls
31+
normalizedast.newCall("typeOf", n)
32+
33+
proc whelpCall(call: Call): NormNode =
34+
let base = getCpsBase(call)
35+
result = newStmtList()
36+
for param in call[1..^1]:
37+
# Generate a let statement for every param.
38+
# This makes sure that effects generated by those params will be recorded
39+
# by docgen.
40+
result.add:
41+
nnkLetSection.newTree(
42+
nnkIdentDefs.newTree(
43+
nnkPragmaExpr.newTree(
44+
nskLet.genSym"forEffectsOnly",
45+
nnkPragma.newTree(ident"used")
46+
),
47+
newEmptyNode(),
48+
param
49+
)
50+
)
51+
52+
result.add:
53+
newCall(base, newNilLit()) # Add the "supposed" whelp result
54+
55+
proc whelpCallback(sym: Sym): NormNode =
56+
let
57+
impl = sym.getImpl.ProcDef
58+
base = getCpsBase(impl)
59+
60+
var params = nnkFormalParams.newTree(copy base)
61+
for param in impl.callingParams:
62+
var idefs = nnkIdentDefs.newTree()
63+
64+
# Add names
65+
for names in param[0..^3]:
66+
idefs.add desym(names.Sym)
67+
68+
# Add type
69+
if param[^2].kind == nnkEmpty:
70+
idefs.add getTypeInst(param[^1])
71+
else:
72+
idefs.add param[^2]
73+
74+
# Make sure no "default" params are here
75+
idefs.add newEmptyNode()
76+
params.add idefs
77+
78+
let factory = nnkProcTy.newTree(params, nnkPragma.newTree ident"nimcall")
79+
let returnType = NormNode: copyOrVoid(NimNode impl.returnParam)
80+
81+
result = newCall(
82+
nnkBracketExpr.newTree(
83+
bindSym"Callback",
84+
copy base,
85+
returnType,
86+
factory
87+
)
88+
)
89+
90+
proc docWhelp*(call: NimNode): NimNode =
91+
## Fake `whelp` implementation for docgen
92+
if call.kind == nnkSym:
93+
whelpCallback(call.Sym)
94+
else:
95+
whelpCall(normalizeCall call)

0 commit comments

Comments
 (0)