fixes merge conflicts

This commit is contained in:
Andreas Rumpf 2018-09-11 17:27:47 +02:00
commit f7d1902043
887 changed files with 48670 additions and 42944 deletions

View file

@ -8,12 +8,16 @@
#
include "system/inclrtl"
include "system/helpers"
## This module contains the interface to the compiler's abstract syntax
## tree (`AST`:idx:). Macros operate on this tree.
## .. include:: ../../doc/astspec.txt
# If you look for the implementation of the magic symbol
# ``{.magic: "Foo".}``, search for `mFoo` and `opcFoo`.
type
NimNodeKind* = enum
nnkNone, nnkEmpty, nnkIdent, nnkSym,
@ -76,7 +80,8 @@ type
nnkGotoState,
nnkState,
nnkBreakState,
nnkFuncDef
nnkFuncDef,
nnkTupleConstr
NimNodeKinds* = set[NimNodeKind]
NimTypeKind* = enum # some types are no longer used, see ast.nim
@ -118,13 +123,10 @@ type
## use ``ident"abc"``.
NimSymObj = object # hidden
NimSym* = ref NimSymObj
NimSym* {.deprecated.} = ref NimSymObj
## represents a Nim *symbol* in the compiler; a *symbol* is a looked-up
## *ident*.
{.deprecated: [TNimrodNodeKind: NimNodeKind, TNimNodeKinds: NimNodeKinds,
TNimrodTypeKind: NimTypeKind, TNimrodSymKind: NimSymKind,
TNimrodIdent: NimIdent, PNimrodSymbol: NimSym].}
const
nnkLiterals* = {nnkCharLit..nnkNilLit}
@ -134,25 +136,23 @@ const
proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.}
## constructs an identifier from the string `s`
## **Deprecated since version 0.18.0**: Use ``toNimIdent`` instead.
## **Deprecated since version 0.18.0**: Use ``ident`` or ``newIdentNode`` instead.
proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.}
proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.}
## constructs an identifier from the string `s`
## **Deprecated since version 0.18.1**; Use ``ident`` or ``newIdentNode`` instead.
proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.}
## converts a Nim identifier to a string
proc `$`*(s: NimSym): string {.magic: "IdentToStr", noSideEffect.}
## converts a Nim symbol to a string
proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect.}
proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect, deprecated.}
## compares two Nim identifiers
## **Deprecated since version 0.18.1**; Use ``==`` on ``NimNode`` instead.
proc `==`*(a, b: NimNode): bool {.magic: "EqNimrodNode", noSideEffect.}
## compares two Nim nodes
proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect.}
proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect, deprecated.}
## compares two Nim symbols
## **Deprecated since version 0.18.1**; Use ```==`(NimNode,NimNode)`` instead.
proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} =
## compares two Nim nodes' types. Return true if the types are the same,
@ -195,8 +195,53 @@ proc kind*(n: NimNode): NimNodeKind {.magic: "NKind", noSideEffect.}
proc intVal*(n: NimNode): BiggestInt {.magic: "NIntVal", noSideEffect.}
proc floatVal*(n: NimNode): BiggestFloat {.magic: "NFloatVal", noSideEffect.}
proc symbol*(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect.}
proc ident*(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect.}
proc ident*(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect, deprecated.} =
## **Deprecated since version 0.18.1**; All functionality is defined on ``NimNode``.
proc symbol*(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect, deprecated.}
## **Deprecated since version 0.18.1**; All functionality is defined on ``NimNode``.
proc getImpl*(s: NimSym): NimNode {.magic: "GetImpl", noSideEffect, deprecated: "use `getImpl: NimNode -> NimNode` instead".}
when defined(nimSymKind):
proc symKind*(symbol: NimNode): NimSymKind {.magic: "NSymKind", noSideEffect.}
proc getImpl*(symbol: NimNode): NimNode {.magic: "GetImpl", noSideEffect.}
proc strVal*(n: NimNode): string {.magic: "NStrVal", noSideEffect.}
## retrieve the implementation of `symbol`. `symbol` can be a
## routine or a const.
proc `$`*(i: NimIdent): string {.magic: "NStrVal", noSideEffect, deprecated.}
## converts a Nim identifier to a string
## **Deprecated since version 0.18.1**; Use ``strVal`` instead.
proc `$`*(s: NimSym): string {.magic: "NStrVal", noSideEffect, deprecated.}
## converts a Nim symbol to a string
## **Deprecated since version 0.18.1**; Use ``strVal`` instead.
else: # bootstrapping substitute
proc getImpl*(symbol: NimNode): NimNode =
symbol.symbol.getImpl
proc strValOld(n: NimNode): string {.magic: "NStrVal", noSideEffect.}
proc `$`*(s: NimSym): string {.magic: "IdentToStr", noSideEffect.}
proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.}
proc strVal*(n: NimNode): string =
if n.kind == nnkIdent:
$n.ident
elif n.kind == nnkSym:
$n.symbol
else:
n.strValOld
when defined(nimHasSymOwnerInMacro):
proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect.}
## accepts node of kind nnkSym and returns its owner's symbol.
## result is also mnde of kind nnkSym if owner exists otherwise
## nnkNilLit is returned
proc getType*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
## with 'getType' you can access the node's `type`:idx:. A Nim type is
@ -214,26 +259,65 @@ proc getType*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
proc typeKind*(n: NimNode): NimTypeKind {.magic: "NGetType", noSideEffect.}
## Returns the type kind of the node 'n' that should represent a type, that
## means the node should have been obtained via `getType`.
## means the node should have been obtained via ``getType``.
proc getTypeInst*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
## Like getType except it includes generic parameters for a specific instance
proc getTypeInst*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} =
## Returns the `type`:idx: of a node in a form matching the way the
## type instance was declared in the code.
runnableExamples:
type
Vec[N: static[int], T] = object
arr: array[N, T]
Vec4[T] = Vec[4, T]
Vec4f = Vec4[float32]
var a: Vec4f
var b: Vec4[float32]
var c: Vec[4, float32]
macro dumpTypeInst(x: typed): untyped =
newLit(x.getTypeInst.repr)
doAssert(dumpTypeInst(a) == "Vec4f")
doAssert(dumpTypeInst(b) == "Vec4[float32]")
doAssert(dumpTypeInst(c) == "Vec[4, float32]")
proc getTypeInst*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
## Like getType except it includes generic parameters for a specific instance
## Version of ``getTypeInst`` which takes a ``typedesc``.
proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
## Like getType except it includes generic parameters for the implementation
proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} =
## Returns the `type`:idx: of a node in a form matching the implementation
## of the type. Any intermediate aliases are expanded to arrive at the final
## type implementation. You can instead use ``getImpl`` on a symbol if you
## want to find the intermediate aliases.
runnableExamples:
type
Vec[N: static[int], T] = object
arr: array[N, T]
Vec4[T] = Vec[4, T]
Vec4f = Vec4[float32]
var a: Vec4f
var b: Vec4[float32]
var c: Vec[4, float32]
macro dumpTypeImpl(x: typed): untyped =
newLit(x.getTypeImpl.repr)
let t = """
object
arr: array[0 .. 3, float32]
"""
doAssert(dumpTypeImpl(a) == t)
doAssert(dumpTypeImpl(b) == t)
doAssert(dumpTypeImpl(c) == t)
proc getTypeImpl*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
## Like getType except it includes generic parameters for the implementation
proc strVal*(n: NimNode): string {.magic: "NStrVal", noSideEffect.}
## Version of ``getTypeImpl`` which takes a ``typedesc``.
proc `intVal=`*(n: NimNode, val: BiggestInt) {.magic: "NSetIntVal", noSideEffect.}
proc `floatVal=`*(n: NimNode, val: BiggestFloat) {.magic: "NSetFloatVal", noSideEffect.}
proc `symbol=`*(n: NimNode, val: NimSym) {.magic: "NSetSymbol", noSideEffect.}
proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect.}
proc `symbol=`*(n: NimNode, val: NimSym) {.magic: "NSetSymbol", noSideEffect, deprecated.}
## **Deprecated since version 0.18.1**; Generate a new ``NimNode`` with ``genSym`` instead.
proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect, deprecated.}
## **Deprecated since version 0.18.1**; Generate a new ``NimNode`` with ``ident(string)`` instead.
#proc `typ=`*(n: NimNode, typ: typedesc) {.magic: "NSetType".}
# this is not sound! Unfortunately forbidding 'typ=' is not enough, as you
# can easily do:
@ -255,18 +339,13 @@ proc newNimNode*(kind: NimNodeKind,
proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.}
proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.}
proc getImpl*(s: NimSym): NimNode {.magic: "GetImpl", noSideEffect.} =
## retrieve the implementation of a symbol `s`. `s` can be a routine or a
## const.
discard
proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.}
## writes an error message at compile time
proc warning*(msg: string) {.magic: "NWarning", benign.}
proc warning*(msg: string, n: NimNode = nil) {.magic: "NWarning", benign.}
## writes a warning message at compile time
proc hint*(msg: string) {.magic: "NHint", benign.}
proc hint*(msg: string, n: NimNode = nil) {.magic: "NHint", benign.}
## writes a hint message at compile time
proc newStrLitNode*(s: string): NimNode {.compileTime, noSideEffect.} =
@ -294,11 +373,9 @@ proc newIdentNode*(i: NimIdent): NimNode {.compileTime.} =
result = newNimNode(nnkIdent)
result.ident = i
proc newIdentNode*(i: string): NimNode {.compileTime.} =
## creates an identifier node from `i`
result = newNimNode(nnkIdent)
result.ident = toNimIdent i
proc newIdentNode*(i: string): NimNode {.magic: "StrToIdent", noSideEffect.}
## creates an identifier node from `i`. It is simply an alias for
## ``ident(string)``. Use that, it's shorter.
type
BindSymRule* = enum ## specifies how ``bindSym`` behaves
@ -312,24 +389,34 @@ type
{.deprecated: [TBindSymRule: BindSymRule].}
proc bindSym*(ident: string, rule: BindSymRule = brClosed): NimNode {.
proc bindSym*(ident: string | NimNode, rule: BindSymRule = brClosed): NimNode {.
magic: "NBindSym", noSideEffect.}
## creates a node that binds `ident` to a symbol node. The bound symbol
## may be an overloaded symbol.
## if `ident` is a NimNode, it must have nkIdent kind.
## If ``rule == brClosed`` either an ``nkClosedSymChoice`` tree is
## returned or ``nkSym`` if the symbol is not ambiguous.
## If ``rule == brOpen`` either an ``nkOpenSymChoice`` tree is
## returned or ``nkSym`` if the symbol is not ambiguous.
## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is
## returned even if the symbol is not ambiguous.
##
## experimental feature:
## use {.experimental: "dynamicBindSym".} to activate it
## if called from template / regular code, `ident` and `rule` must be
## constant expression / literal value.
## if called from macros / compile time procs / static blocks,
## `ident` and `rule` can be VM computed value.
proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {.
magic: "NGenSym", noSideEffect.}
## generates a fresh symbol that is guaranteed to be unique. The symbol
## needs to occur in a declaration context.
proc callsite*(): NimNode {.magic: "NCallSite", benign.}
proc callsite*(): NimNode {.magic: "NCallSite", benign,
deprecated: "use varargs[untyped] in the macro prototype instead".}
## returns the AST of the invocation expression that invoked this macro.
## **Deprecated since version 0.18.1**.
proc toStrLit*(n: NimNode): NimNode {.compileTime.} =
## converts the AST `n` to the concrete Nim code and wraps that
@ -342,7 +429,8 @@ type
line*,column*: int
proc `$`*(arg: Lineinfo): string =
result = arg.filename & "(" & $arg.line & ", " & $arg.column & ")"
# BUG: without `result = `, gives compile error
result = lineInfoToString(arg.filename, arg.line, arg.column)
#proc lineinfo*(n: NimNode): LineInfo {.magic: "NLineInfo", noSideEffect.}
## returns the position the node appears in the original source file
@ -352,7 +440,11 @@ proc getLine(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.}
proc copyLineInfo*(arg: NimNode, info: NimNode) {.magic: "NLineInfo", noSideEffect.}
## copy lineinfo from info node
proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} =
## returns ``LineInfo`` of ``n``, using absolute path for ``filename``
result.filename = n.getFile
result.line = n.getLine
result.column = n.getColumn
@ -464,9 +556,11 @@ proc newCall*(theProc: NimNode,
result.add(args)
proc newCall*(theProc: NimIdent,
args: varargs[NimNode]): NimNode {.compileTime.} =
args: varargs[NimNode]): NimNode {.compileTime, deprecated.} =
## produces a new call node. `theProc` is the proc that is called with
## the arguments ``args[0..]``.
## **Deprecated since version 0.18.1**; Use ``newCall(string, ...)``,
## or ``newCall(NimNode, ...)`` instead.
result = newNimNode(nnkCall)
result.add(newIdentNode(theProc))
result.add(args)
@ -594,17 +688,30 @@ proc newLit*(s: string): NimNode {.compileTime.} =
result = newNimNode(nnkStrLit)
result.strVal = s
proc nestList*(theProc: NimIdent,
x: NimNode): NimNode {.compileTime.} =
## nests the list `x` into a tree of call expressions:
## ``[a, b, c]`` is transformed into ``theProc(a, theProc(c, d))``.
proc nestList*(op: NimNode; pack: NimNode): NimNode {.compileTime.} =
## nests the list `pack` into a tree of call expressions:
## ``[a, b, c]`` is transformed into ``op(a, op(c, d))``.
## This is also known as fold expression.
if pack.len < 1:
error("`nestList` expects a node with at least 1 child")
result = pack[^1]
for i in countdown(pack.len - 2, 0):
result = newCall(op, pack[i], result)
proc nestList*(op: NimNode; pack: NimNode; init: NimNode): NimNode {.compileTime.} =
## nests the list `pack` into a tree of call expressions:
## ``[a, b, c]`` is transformed into ``op(a, op(c, d))``.
## This is also known as fold expression.
result = init
for i in countdown(pack.len - 1, 0):
result = newCall(op, pack[i], result)
proc nestList*(theProc: NimIdent, x: NimNode): NimNode {.compileTime, deprecated.} =
## **Deprecated since version 0.18.1**; Use one of ``nestList(NimNode, ...)`` instead.
var L = x.len
result = newCall(theProc, x[L-2], x[L-1])
for i in countdown(L-3, 0):
# XXX the 'copyNimTree' here is necessary due to a bug in the evaluation
# engine that would otherwise create an endless loop here. :-(
# This could easily user code and so should be fixed in evals.nim somehow.
result = newCall(theProc, x[i], copyNimTree(result))
result = newCall(theProc, x[i], result)
proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
## Convert the AST `n` to a human-readable tree-like string.
@ -616,13 +723,11 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
res.add(($n.kind).substr(3))
case n.kind
of nnkEmpty: discard # same as nil node in this representation
of nnkNilLit: res.add(" nil")
of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal)
of nnkIdent: res.add(" ident\"" & $n.ident & '"')
of nnkSym: res.add(" \"" & $n.symbol & '"')
of nnkStrLit..nnkTripleStrLit, nnkIdent, nnkSym:
res.add(" " & $n.strVal.newLit.repr)
of nnkNone: assert false
else:
for j in 0..n.len-1:
@ -641,13 +746,11 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} =
add(result, "(")
case n.kind
of nnkEmpty: discard # same as nil node in this representation
of nnkNilLit: add(result, "nil")
of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
of nnkIdent: add(result, "ident\"" & $n.ident & '"')
of nnkSym: add(result, $n.symbol)
of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkident, nnkSym:
add(result, n.strVal.newLit.repr)
of nnkNone: assert false
else:
if n.len > 0:
@ -678,54 +781,27 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
## See also `repr`, `treeRepr`, and `lispRepr`.
const
NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone}
NodeKinds = {nnkEmpty, nnkIdent, nnkSym, nnkNone, nnkCommentStmt}
LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit}
proc escape(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect.} =
## Functions copied from strutils
proc toHex(x: BiggestInt, len: Positive): string {.noSideEffect, rtl.} =
const
HexChars = "0123456789ABCDEF"
var
t = x
result = newString(len)
for j in countdown(len-1, 0):
result[j] = HexChars[int(t and 0xF)]
t = t shr 4
# handle negative overflow
if t == 0 and x < 0: t = -1
result = newStringOfCap(s.len + s.len shr 2)
result.add(prefix)
for c in items(s):
case c
of '\0'..'\31', '\128'..'\255':
add(result, "\\x")
add(result, toHex(ord(c), 2))
of '\\': add(result, "\\\\")
of '\'': add(result, "\\'")
of '\"': add(result, "\\\"")
else: add(result, c)
add(result, suffix)
proc traverse(res: var string, level: int, n: NimNode) {.benign.} =
for i in 0..level-1: res.add " "
if n.kind in NodeKinds:
res.add("new" & ($n.kind).substr(3) & "Node(")
elif n.kind in LitKinds:
res.add("newLit(")
elif n.kind == nnkNilLit:
res.add("newNilLit()")
else:
res.add($n.kind)
case n.kind
of nnkEmpty: discard
of nnkNilLit: res.add("nil")
of nnkEmpty, nnkNilLit: discard
of nnkCharLit: res.add("'" & $chr(n.intVal) & "'")
of nnkIntLit..nnkInt64Lit: res.add($n.intVal)
of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal)
of nnkStrLit..nnkTripleStrLit: res.add($n.strVal.escape())
of nnkIdent: res.add(($n.ident).escape())
of nnkSym: res.add(($n.symbol).escape())
of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkIdent, nnkSym:
res.add(n.strVal.newLit.repr)
of nnkNone: assert false
else:
res.add(".newTree(")
@ -769,11 +845,10 @@ macro dumpAstGen*(s: untyped): untyped = echo s.astGenRepr
## See `dumpTree`.
macro dumpTreeImm*(s: untyped): untyped {.deprecated.} = echo s.treeRepr
## Deprecated.
## Deprecated. Use `dumpTree` instead.
macro dumpLispImm*(s: untyped): untyped {.deprecated.} = echo s.lispRepr
## Deprecated.
## Deprecated. Use `dumpLisp` instead.
proc newEmptyNode*(): NimNode {.compileTime, noSideEffect.} =
## Create a new empty node
@ -1019,28 +1094,21 @@ proc `body=`*(someProc: NimNode, val: NimNode) {.compileTime.} =
proc basename*(a: NimNode): NimNode {.compiletime, benign.}
proc `$`*(node: NimNode): string {.compileTime.} =
## Get the string of an identifier node
case node.kind
of nnkIdent:
result = $node.ident
of nnkPostfix:
result = $node.basename.ident & "*"
of nnkStrLit..nnkTripleStrLit:
result = node.basename.strVal & "*"
of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkSym, nnkIdent:
result = node.strVal
of nnkSym:
result = $node.symbol
of nnkOpenSymChoice, nnkClosedSymChoice:
result = $node[0]
of nnkAccQuoted:
result = $node[0]
of nnkCommentStmt:
result = node.strVal
else:
badNodeKind node.kind, "$"
proc ident*(name: string): NimNode {.compileTime,inline.} = newIdentNode(name)
proc ident*(name: string): NimNode {.magic: "StrToIdent", noSideEffect.}
## Create a new ident node from a string
iterator items*(n: NimNode): NimNode {.inline.} =
@ -1131,40 +1199,57 @@ proc copy*(node: NimNode): NimNode {.compileTime.} =
## An alias for copyNimTree().
return node.copyNimTree()
proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} =
proc toLower(c: char): char {.inline.} =
if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A')))
else: result = c
var i = 0
var j = 0
# first char is case sensitive
if a[0] != b[0]: return 1
while true:
while a[i] == '_': inc(i)
while b[j] == '_': inc(j) # BUGFIX: typo
var aa = toLower(a[i])
var bb = toLower(b[j])
result = ord(aa) - ord(bb)
if result != 0 or aa == '\0': break
inc(i)
inc(j)
when defined(nimVmEqIdent):
proc eqIdent*(a: string; b: string): bool {.magic: "EqIdent", noSideEffect.}
## Style insensitive comparison.
proc eqIdent*(a, b: string): bool = cmpIgnoreStyle(a, b) == 0
## Check if two idents are identical.
proc eqIdent*(a: NimNode; b: string): bool {.magic: "EqIdent", noSideEffect.}
## Style insensitive comparison.
## ``a`` can be an identifier or a symbol.
proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} =
## Check if node is some identifier node (``nnkIdent``, ``nnkSym``, etc.)
## is the same as ``s``. Note that this is the preferred way to check! Most
## other ways like ``node.ident`` are much more error-prone, unfortunately.
case node.kind
of nnkIdent:
result = node.ident == toNimIdent s
of nnkSym:
result = eqIdent($node.symbol, s)
of nnkOpenSymChoice, nnkClosedSymChoice:
result = eqIdent($node[0], s)
else:
result = false
proc eqIdent*(a: string; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
## Style insensitive comparison.
## ``b`` can be an identifier or a symbol.
proc eqIdent*(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
## Style insensitive comparison.
## ``a`` and ``b`` can be an identifier or a symbol.
else:
# this procedure is optimized for native code, it should not be compiled to nimVM bytecode.
proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} =
proc toLower(c: char): char {.inline.} =
if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A')))
else: result = c
var i = 0
var j = 0
# first char is case sensitive
if a[0] != b[0]: return 1
while true:
while a[i] == '_': inc(i)
while b[j] == '_': inc(j) # BUGFIX: typo
var aa = toLower(a[i])
var bb = toLower(b[j])
result = ord(aa) - ord(bb)
if result != 0 or aa == '\0': break
inc(i)
inc(j)
proc eqIdent*(a, b: string): bool = cmpIgnoreStyle(a, b) == 0
## Check if two idents are identical.
proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} =
## Check if node is some identifier node (``nnkIdent``, ``nnkSym``, etc.)
## is the same as ``s``. Note that this is the preferred way to check! Most
## other ways like ``node.ident`` are much more error-prone, unfortunately.
case node.kind
of nnkSym, nnkIdent:
result = eqIdent(node.strVal, s)
of nnkOpenSymChoice, nnkClosedSymChoice:
result = eqIdent($node[0], s)
else:
result = false
proc hasArgOfName*(params: NimNode; name: string): bool {.compiletime.}=
## Search nnkFormalParams for an argument.
@ -1215,33 +1300,81 @@ macro expandMacros*(body: typed): untyped =
echo result.toStrLit
proc customPragmaNode(n: NimNode): NimNode =
expectKind(n, {nnkSym, nnkDotExpr})
if n.kind == nnkSym:
let sym = n.symbol.getImpl()
sym.expectRoutine()
result = sym.pragma
elif n.kind == nnkDotExpr:
let typDef = getImpl(getTypeInst(n[0]).symbol)
typDef.expectKind(nnkTypeDef)
typDef[2].expectKind(nnkObjectTy)
let recList = typDef[2][2]
for identDefs in recList:
for i in 0 .. identDefs.len - 3:
if identDefs[i].kind == nnkPragmaExpr and
identDefs[i][0].kind == nnkIdent and $identDefs[i][0] == $n[1]:
return identDefs[i][1]
expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkCheckedFieldExpr})
let
typ = n.getTypeInst()
if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy:
return typ[1][1]
elif typ.typeKind == ntyTypeDesc:
let impl = typ[1].getImpl()
if impl[0].kind == nnkPragmaExpr:
return impl[0][1]
else:
return impl[0] # handle types which don't have macro at all
if n.kind == nnkSym: # either an variable or a proc
let impl = n.getImpl()
if impl.kind in RoutineNodes:
return impl.pragma
else:
return typ.getImpl()[0][1]
if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}:
let name = (if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1])
var typDef = getImpl(getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0]))
while typDef != nil:
typDef.expectKind(nnkTypeDef)
typDef[2].expectKind({nnkRefTy, nnkPtrTy, nnkObjectTy})
let isRef = typDef[2].kind in {nnkRefTy, nnkPtrTy}
if isRef and typDef[2][0].kind in {nnkSym, nnkBracketExpr}: # defines ref type for another object(e.g. X = ref X)
typDef = getImpl(typDef[2][0])
else: # object definition, maybe an object directly defined as a ref type
let
obj = (if isRef: typDef[2][0] else: typDef[2])
var identDefsStack = newSeq[NimNode](obj[2].len)
for i in 0..<identDefsStack.len: identDefsStack[i] = obj[2][i]
while identDefsStack.len > 0:
var identDefs = identDefsStack.pop()
if identDefs.kind == nnkRecCase:
identDefsStack.add(identDefs[0])
for i in 1..<identDefs.len:
# if it is and empty branch, skip
if identDefs[i][0].kind == nnkNilLit: continue
if identDefs[i][1].kind == nnkIdentDefs:
identDefsStack.add(identDefs[i][1])
else: # nnkRecList
for j in 0..<identDefs[i][1].len:
identDefsStack.add(identDefs[i][1][j])
else:
for i in 0 .. identDefs.len - 3:
if identDefs[i].kind == nnkPragmaExpr and
identDefs[i][0].kind == nnkIdent and $identDefs[i][0] == $name:
return identDefs[i][1]
if obj[1].kind == nnkOfInherit: # explore the parent object
typDef = getImpl(obj[1][0])
else:
typDef = nil
macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped =
## Expands to `true` if expression `n` which is expected to be `nnkDotExpr`
## has custom pragma `cp`.
## (if checking a field), a proc or a type has custom pragma `cp`.
##
## See also `getCustomPragmaVal`.
##
## .. code-block:: nim
## template myAttr() {.pragma.}
## type
## MyObj = object
## myField {.myAttr.}: int
##
## proc myProc() {.myAttr.} = discard
##
## var o: MyObj
## assert(o.myField.hasCustomPragma(myAttr) == 0)
## assert(o.myField.hasCustomPragma(myAttr))
## assert(myProc.hasCustomPragma(myAttr))
let pragmaNode = customPragmaNode(n)
for p in pragmaNode:
if (p.kind == nnkSym and p == cp) or
@ -1251,20 +1384,25 @@ macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped =
macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
## Expands to value of custom pragma `cp` of expression `n` which is expected
## to be `nnkDotExpr`.
## to be `nnkDotExpr`, a proc or a type.
##
## See also `hasCustomPragma`
##
## .. code-block:: nim
## template serializationKey(key: string) {.pragma.}
## type
## MyObj = object
## MyObj {.serializationKey: "mo".} = object
## myField {.serializationKey: "mf".}: int
## var o: MyObj
## assert(o.myField.getCustomPragmaVal(serializationKey) == "mf")
## assert(o.getCustomPragmaVal(serializationKey) == "mo")
## assert(MyObj.getCustomPragmaVal(serializationKey) == "mo")
let pragmaNode = customPragmaNode(n)
for p in pragmaNode:
if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp:
return p[1]
return newEmptyNode()
error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error,
when not defined(booting):
@ -1285,3 +1423,7 @@ macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped =
result = newCall(callee)
for i in 0 ..< args.len:
result.add args[i]
proc getProjectPath*(): string = discard
## Returns the path to the currently compiling project