nim-stew/stew/shims/macros.nim

292 lines
8.2 KiB
Nim

import
std/macros
export
macros
type
FieldDescription* = object
name*: NimNode
isPublic*: bool
typ*: NimNode
pragmas*: NimNode
caseField*: NimNode
caseBranch*: NimNode
const
nnkPragmaCallKinds = {nnkExprColonExpr, nnkCall, nnkCallStrLit}
proc findPragma*(pragmas: NimNode, pragmaSym: NimNode): NimNode =
for p in pragmas:
if p.kind in {nnkSym, nnkIdent} and eqIdent(p, pragmaSym):
return p
if p.kind in nnkPragmaCallKinds and p.len > 0 and eqIdent(p[0], pragmaSym):
return p
template readPragma*(field: FieldDescription, pragmaName: static string): NimNode =
let p = findPragma(field.pragmas, bindSym(pragmaName))
if p != nil and p.len == 2: p[1] else: p
iterator recordFields*(typeImpl: NimNode): FieldDescription =
# TODO: This doesn't support inheritance yet
let
objectType = typeImpl[2]
recList = objectType[2]
type
RecursionStackItem = tuple
currentNode: NimNode
currentChildItem: int
parentCaseField: NimNode
parentCaseBranch: NimNode
if recList.len > 0:
var traversalStack: seq[RecursionStackItem] = @[
(recList, 0, NimNode(nil), NimNode(nil))
]
template recuseInto(childNode: NimNode,
currentCaseField: NimNode = nil,
currentCaseBranch: NimNode = nil) =
traversalStack.add (childNode, 0, currentCaseField, currentCaseBranch)
while true:
doAssert traversalStack.len > 0
var stackTop = traversalStack[^1]
let recList = stackTop.currentNode
let idx = stackTop.currentChildItem
let n = recList[idx]
inc traversalStack[^1].currentChildItem
if idx == recList.len - 1:
discard traversalStack.pop
case n.kind
of nnkRecWhen:
for i in countdown(n.len - 1, 0):
let branch = n[i]
case branch.kind:
of nnkElifBranch:
recuseInto branch[1]
of nnkElse:
recuseInto branch[0]
else:
doAssert false
continue
of nnkRecCase:
doAssert n.len > 0
for i in countdown(n.len - 1, 1):
let branch = n[i]
case branch.kind
of nnkOfBranch:
recuseInto branch[^1], n[0], branch
of nnkElse:
recuseInto branch[0], n[0], branch
else:
doAssert false
recuseInto newTree(nnkRecCase, n[0]), n[0]
continue
of nnkIdentDefs:
let fieldType = n[^2]
for i in 0 ..< n.len - 2:
var field: FieldDescription
field.name = n[i]
field.typ = fieldType
field.caseField = stackTop.parentCaseField
field.caseBranch = stackTop.parentCaseBranch
if field.name.kind == nnkPragmaExpr:
field.pragmas = field.name[1]
field.name = field.name[0]
if field.name.kind == nnkPostfix:
field.isPublic = true
field.name = field.name[1]
yield field
of nnkNilLit, nnkDiscardStmt, nnkCommentStmt, nnkEmpty:
discard
else:
doAssert false
if traversalStack.len == 0: break
macro field*(obj: typed, fieldName: static string): untyped =
newDotExpr(obj, ident fieldName)
proc skipPragma*(n: NimNode): NimNode =
if n.kind == nnkPragmaExpr: n[0]
else: n
macro hasCustomPragmaFixed*(T: type, field: static string, pragma: typed{nkSym}): untyped =
let
Tresolved = getType(T)[1]
Timpl = getImpl(Tresolved)
for f in recordFields(Timpl):
var fieldName = f.name
# TODO: Fix this in eqIdent
if fieldName.kind == nnkAccQuoted: fieldName = fieldName[0]
if eqIdent(fieldName, field):
return newLit(f.pragmas.findPragma(pragma) != nil)
error "The type " & $Tresolved & " doesn't have a field named " & field
proc humaneTypeName*(typedescNode: NimNode): string =
var t = getType(typedescNode)[1]
if t.kind != nnkBracketExpr:
let tImpl = t.getImpl
if tImpl != nil and tImpl.kind notin {nnkEmpty, nnkNilLit}:
t = tImpl
repr(t)
macro inspectType*(T: typed): untyped =
echo "Inspect type: ", humaneTypeName(T)
# FIXED NewLit
proc newLitFixed*(c: char): NimNode {.compileTime.} =
## produces a new character literal node.
result = newNimNode(nnkCharLit)
result.intVal = ord(c)
proc newLitFixed*(i: int): NimNode {.compileTime.} =
## produces a new integer literal node.
result = newNimNode(nnkIntLit)
result.intVal = i
proc newLitFixed*(i: int8): NimNode {.compileTime.} =
## produces a new integer literal node.
result = newNimNode(nnkInt8Lit)
result.intVal = i
proc newLitFixed*(i: int16): NimNode {.compileTime.} =
## produces a new integer literal node.
result = newNimNode(nnkInt16Lit)
result.intVal = i
proc newLitFixed*(i: int32): NimNode {.compileTime.} =
## produces a new integer literal node.
result = newNimNode(nnkInt32Lit)
result.intVal = i
proc newLitFixed*(i: int64): NimNode {.compileTime.} =
## produces a new integer literal node.
result = newNimNode(nnkInt64Lit)
result.intVal = i
proc newLitFixed*(i: uint): NimNode {.compileTime.} =
## produces a new unsigned integer literal node.
result = newNimNode(nnkUIntLit)
result.intVal = BiggestInt(i)
proc newLitFixed*(i: uint8): NimNode {.compileTime.} =
## produces a new unsigned integer literal node.
result = newNimNode(nnkUInt8Lit)
result.intVal = BiggestInt(i)
proc newLitFixed*(i: uint16): NimNode {.compileTime.} =
## produces a new unsigned integer literal node.
result = newNimNode(nnkUInt16Lit)
result.intVal = BiggestInt(i)
proc newLitFixed*(i: uint32): NimNode {.compileTime.} =
## produces a new unsigned integer literal node.
result = newNimNode(nnkUInt32Lit)
result.intVal = BiggestInt(i)
proc newLitFixed*(i: uint64): NimNode {.compileTime.} =
## produces a new unsigned integer literal node.
result = newNimNode(nnkUInt64Lit)
result.intVal = BiggestInt(i)
proc newLitFixed*(b: bool): NimNode {.compileTime.} =
## produces a new boolean literal node.
result = if b: bindSym"true" else: bindSym"false"
proc newLitFixed*(f: float32): NimNode {.compileTime.} =
## produces a new float literal node.
result = newNimNode(nnkFloat32Lit)
result.floatVal = f
proc newLitFixed*(f: float64): NimNode {.compileTime.} =
## produces a new float literal node.
result = newNimNode(nnkFloat64Lit)
result.floatVal = f
proc newLitFixed*(s: string): NimNode {.compileTime.} =
## produces a new string literal node.
result = newNimNode(nnkStrLit)
result.strVal = s
proc newLitFixed*[N,T](arg: array[N,T]): NimNode {.compileTime.}
proc newLitFixed*[T](arg: seq[T]): NimNode {.compileTime.}
proc newLitFixed*(arg: tuple): NimNode {.compileTime.}
proc newLitFixed*(arg: object): NimNode {.compileTime.} =
result = nnkObjConstr.newTree(arg.type.getTypeInst[1])
for a, b in arg.fieldPairs:
result.add nnkExprColonExpr.newTree( newIdentNode(a), newLitFixed(b) )
proc newLitFixed*[N,T](arg: array[N,T]): NimNode {.compileTime.} =
result = nnkBracket.newTree
for x in arg:
result.add newLitFixed(x)
proc newLitFixed*[T](arg: seq[T]): NimNode {.compileTime.} =
var bracket = nnkBracket.newTree
for x in arg:
bracket.add newLitFixed(x)
result = nnkCall.newTree(
nnkBracketExpr.newTree(
nnkAccQuoted.newTree( bindSym"@" ),
getTypeInst( bindSym"T" )
),
bracket
)
proc newLitFixed*(arg: tuple): NimNode {.compileTime.} =
result = nnkPar.newTree
for a,b in arg.fieldPairs:
result.add nnkExprColonExpr.newTree(newIdentNode(a), newLitFixed(b))
iterator typedParams*(n: NimNode, skip = 0): (NimNode, NimNode) =
let params = n[3]
for i in (1 + skip) ..< params.len:
let paramNodes = params[i]
let paramType = paramNodes[^2]
for j in 0 ..< paramNodes.len - 2:
yield (paramNodes[j], paramType)
macro unpackArgs*(callee: typed, args: untyped): untyped =
result = newCall(callee)
for arg in args:
let arg = if arg.kind == nnkHiddenStdConv: arg[1]
else: arg
if arg.kind == nnkArgList:
for subarg in arg:
result.add subarg
else:
result.add arg
template genCode*(body: untyped) =
iterator generator: NimNode = body
macro payload: untyped =
result = newStmtList()
for node in generator():
result.add node
payload()