wmtools/nimbledeps/pkgs2/argparse-4.0.1-e9c2ebe3f74b1dfc4df773686ae6dab7638a8662/argparse/macrohelp.nim

268 lines
No EOL
7.3 KiB
Nim

import macros
import strformat
import strutils
import sequtils; export sequtils
type
UnfinishedObjectTypeDef* = object
root*: NimNode
insertion*: NimNode
UnfinishedCase* = object
root*: NimNode
cases*: seq[NimNode]
elsebody*: NimNode
UnfinishedIf = object
root*: NimNode
elsebody*: NimNode
InsertionPoint = object
parent*: NimNode
child*: NimNode
const ident* = (proc(s: string): NimNode)(ident)
const newIdentNode* = (proc(s: string): NimNode)(newIdentNode)
proc replaceNodes*(ast: NimNode): NimNode =
## Replace NimIdent and NimSym by a fresh ident node
##
## Use with the results of ``quote do: ...`` to get
## ASTs without symbol resolution having been done already.
proc inspect(node: NimNode): NimNode =
case node.kind:
of nnkIdent:
if "`gensym" in node.strVal:
return ident(node.strVal.split("`")[0])
else:
return ident(node.strVal)
of nnkSym:
return ident(node.strVal)
of nnkEmpty:
return node
of nnkLiterals:
return node
of nnkOpenSymChoice:
return inspect(node[0])
else:
var rTree = node.kind.newTree()
for child in node:
rTree.add inspect(child)
return rTree
result = inspect(ast)
proc parentOf*(node: NimNode, name:string): InsertionPoint =
## Recursively search for an ident node of the given name and return
## the parent of that node.
var stack:seq[NimNode] = @[node]
while stack.len > 0:
var n = stack.pop()
for child in n.children:
if child.kind == nnkIdent and child.strVal == name:
return InsertionPoint(parent:n, child:child)
else:
stack.add(child)
error("node not found: " & name)
proc parentOf*(node: NimNode, child:NimNode): InsertionPoint =
## Recursively search for an ident node of the given name and return
## the parent of that node.
var stack:seq[NimNode] = @[node]
while stack.len > 0:
var n = stack.pop()
for c in n.children:
if c == child:
return InsertionPoint(parent:n, child:c)
else:
stack.add(c)
error("node not found: " & child.repr)
proc getInsertionPoint*(node: var NimNode, name:string): InsertionPoint =
## Return a node pair that you can replace with something else
return node.parentOf(name)
proc clear*(point: InsertionPoint):int =
var i = 0
for child in point.parent.children:
if child == point.child:
break
inc(i)
point.parent.del(i, 1)
result = i
proc replace*(point: InsertionPoint, newnode: NimNode) =
## Replace the child
let i = point.clear()
point.parent.insert(i, newnode)
proc newObjectTypeDef*(name: string, isref:bool = false): UnfinishedObjectTypeDef {.compileTime.} =
## Creates:
## root ->
## type
## {name} = object
## insertion -> ...
##
var insertion = newNimNode(nnkRecList)
var objectty = nnkObjectTy.newTree(
newEmptyNode(),
newEmptyNode(),
insertion,
)
if isref:
objectty = nnkRefTy.newTree(objectty)
var root = newNimNode(nnkTypeSection).add(
newNimNode(nnkTypeDef).add(
ident(name),
newEmptyNode(),
objectty
)
)
result = UnfinishedObjectTypeDef(root: root, insertion: insertion)
proc addObjectField*(objtypedef: UnfinishedObjectTypeDef, name: string, kind: NimNode) {.compileTime.} =
## Adds a field to an object definition created by newObjectTypeDef
objtypedef.insertion.add(newIdentDefs(
newNimNode(nnkPostfix).add(
ident("*"),
ident(name),
),
kind,
newEmptyNode(),
))
proc addObjectField*(objtypedef: UnfinishedObjectTypeDef, name: string, kind: string, isref: bool = false) {.compileTime.} =
## Adds a field to an object definition created by newObjectTypeDef
if isref:
addObjectField(objtypedef, name, nnkRefTy.newTree(ident(kind)))
else:
addObjectField(objtypedef, name, ident(kind))
#--------------------------------------------------------------
# case statements
#--------------------------------------------------------------
proc newCaseStatement*(key: NimNode): ref UnfinishedCase =
## Create a new, unfinished case statement. Call `finalize` to finish it.
##
## case(`key`)
new(result)
result.root = nnkCaseStmt.newTree(key)
proc newCaseStatement*(key: string): ref UnfinishedCase =
return newCaseStatement(ident(key))
proc add*(n: ref UnfinishedCase, opt: seq[NimNode], body: NimNode) =
## Adds a branch to an UnfinishedCase
##
## Usage:
## var c = newCaseStatement("foo")
## c.add(@[newLit("apple"), newLit("banana")], quote do:
## echo "apple or banana"
## )
var branch = nnkOfBranch.newTree()
for node in opt:
branch.add(node)
branch.add(body)
n.cases.add(branch)
proc add*(n: ref UnfinishedCase, opt: NimNode, body: NimNode) =
## Adds a branch to an UnfinishedCase
n.add(@[opt], body)
proc add*(n: ref UnfinishedCase, opt:string, body: NimNode) =
## Adds a branch to an UnfinishedCase
##
## c.add("foo", quote do:
## echo "value was foo"
## )
n.add(@[newStrLitNode(opt)], body)
proc add*(n: ref UnfinishedCase, opts: seq[string], body: NimNode) =
## Adds a branch to an UnfinishedCase
##
## c.add(@["foo", "foo-also"], quote do:
## echo "value was foo"
## )
n.add(opts.mapIt(newStrLitNode(it)), body)
proc add*(n: ref UnfinishedCase, opt:int, body: NimNode) =
## Adds an integer branch to an UnfinishedCase
add(n, @[newLit(opt)], body)
proc hasElse*(n: ref UnfinishedCase): bool =
not n.elsebody.isNil
proc addElse*(n: ref UnfinishedCase, body: NimNode) =
## Add an else: to an UnfinishedCase
n.elsebody = body
proc isValid*(n: ref UnfinishedCase): bool =
return n.cases.len > 0 or n.elsebody != nil
proc finalize*(n: ref UnfinishedCase): NimNode =
if n.cases.len > 0:
for branch in n.cases:
n.root.add(branch)
if n.elsebody != nil:
n.root.add(nnkElse.newTree(n.elsebody))
result = n.root
else:
result = n.elsebody
#--------------------------------------------------------------
# if statements
#--------------------------------------------------------------
proc newIfStatement*(): ref UnfinishedIf =
## Create an unfinished if statement.
new(result)
result.root = nnkIfStmt.newTree()
proc add*(n: ref UnfinishedIf, cond: NimNode, body: NimNode) =
## Add a branch to an if statement
##
## var f = newIfStatement()
## f.add()
add(n.root, nnkElifBranch.newTree(
cond,
body,
))
proc addElse*(n: ref UnfinishedIf, body: NimNode) =
## Add an else: to an UnfinishedIf
n.elsebody = body
proc isValid*(n: ref UnfinishedIf): bool =
return n.root.len > 0 or n.elsebody != nil
proc finalize*(n: ref UnfinishedIf): NimNode =
## Finish an If statement
result = n.root
if n.root.len == 0:
# This "if" is only an "else"
result = n.elsebody
elif n.elsebody != nil:
result.add(nnkElse.newTree(n.elsebody))
proc nimRepr*(n:NimNode): string =
case n.kind
of nnkStmtList:
var lines:seq[string]
for child in n:
lines.add(child.nimRepr)
result = lines.join("\L")
of nnkCommand:
let name = n[0].nimRepr
var args:seq[string]
for i, child in n:
if i == 0:
continue
args.add(child.nimRepr)
echo n.lispRepr
let arglist = args.join(", ")
result = &"{name}({arglist})"
of nnkIdent:
result = n.strVal
of nnkStrLit:
result = "[" & n.strVal & "]"
else:
result = &"<unknown {n.kind} {n.lispRepr}>"