diff --git a/changelog.md b/changelog.md index d33e24b65..89e013cff 100644 --- a/changelog.md +++ b/changelog.md @@ -51,7 +51,6 @@ implementations. Old behavior can be obtained with `-d:nimLegacyParseQueryStrict`. `cgi.decodeData` which uses the same underlying code is also updated the same way. -- Custom pragma values have now an API for use in macros. - On POSIX systems, the default signal handlers used for Nim programs (it's used for printing the stacktrace on fatal signals) will now re-raise the diff --git a/lib/core/macros.nim b/lib/core/macros.nim index c09fae6b3..d3eaf6298 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1068,11 +1068,11 @@ proc newEmptyNode*(): NimNode {.noSideEffect.} = ## Create a new empty node. result = newNimNode(nnkEmpty) -proc newStmtList*(stmts: varargs[NimNode]): NimNode= +proc newStmtList*(stmts: varargs[NimNode]): NimNode = ## Create a new statement list. result = newNimNode(nnkStmtList).add(stmts) -proc newPar*(exprs: varargs[NimNode]): NimNode= +proc newPar*(exprs: varargs[NimNode]): NimNode = ## Create a new parentheses-enclosed expression. newNimNode(nnkPar).add(exprs) @@ -1494,148 +1494,80 @@ macro expandMacros*(body: typed): untyped = echo body.toStrLit result = body -proc findPragmaNodeInRecList(arg, fieldSym: NimNode): NimNode = - case arg.kind - of nnkRecList, nnkRecCase: - for it in arg.children: - result = findPragmaNodeInRecList(it, fieldSym) - if result != nil: return - of nnkOfBranch: - return findPragmaNodeInRecList(arg[1], fieldSym) - of nnkElse: - return findPragmaNodeInRecList(arg[0], fieldSym) - of nnkIdentDefs: - for i in 0.. 0: - impl.expectKind nnkTypeDef - let pragmaExpr = impl[0] - if pragmaExpr.kind == nnkPragmaExpr: - result = pragmaExpr[1] - -proc getPragmaNodeFromType(typ: NimNode): NimNode = - case typ.kind - of nnkSym: - result = getPragmaNodeFromTypeSym(typ) - of nnkProcTy: - result = typ[1] - else: error("illegal typ kind for argument: " & $typ.kind, typ) - -proc getPragmaNodeFromVarLetSym(sym: NimNode): NimNode = - sym.expectKind nnkSym - if sym.symKind notin {nskVar, nskLet}: error("expected var/let sym", sym) - let impl = sym.getImpl - impl.expectKind nnkIdentDefs - impl.expectLen 3 - let pragmaExpr = impl[0] - if pragmaExpr.kind == nnkPragmaExpr: - result = pragmaExpr[1] - -proc getPragmasByName(pragmaExpr: NimNode, name: string): seq[NimNode] = - if pragmaExpr.kind == nnkPragma: - for it in pragmaExpr: - if it.kind in nnkPragmaCallKinds: - if eqIdent(it[0], name): - result.add it - elif it.kind == nnkSym: - if eqIdent(it, name): - result.add it - -proc getCustomPragmaNodes(sym: NimNode, name: string): seq[NimNode] = - sym.expectKind nnkSym - case sym.symKind - of nskField: - result = getPragmaNodeFromObjFieldSym(sym).getPragmasByName(name) - of nskProc: - result = getPragmaNodeFromProcSym(sym).getPragmasByName(name) - of nskType: - result = getPragmaNodeFromTypeSym(sym).getPragmasByName(name) - of nskParam: - # When a typedesc parameter is passed to the macro, it will be of nskParam. - let typeInst = getTypeInst(sym) - if typeInst.kind == nnkBracketExpr and eqIdent(typeInst[0], "typeDesc"): - result = getPragmaNodeFromTypeSym(typeInst[1]).getPragmasByName(name) + 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: - error("illegal sym kind for argument: " & $sym.symKind, sym) - of nskVar, nskLet: - # This checks the type of the sym too, this is consistent with how - # field expressions are handled too. If this is changed, make sure to - # change it for fields expressions too. - result = getPragmaNodeFromType(sym.getTypeInst).getPragmasByName(name) - result.add getPragmaNodeFromVarLetSym(sym).getPragmasByName(name) - else: - error("illegal sym kind for argument: " & $sym.symKind, sym) + return impl[0] # handle types which don't have macro at all -since (1, 5): - export getCustomPragmaNodes + if n.kind == nnkSym: # either an variable or a proc + let impl = n.getImpl() + if impl.kind in RoutineNodes: + return impl.pragma + elif impl.kind == nnkIdentDefs and impl[0].kind == nnkPragmaExpr: + return impl[0][1] + else: + let timpl = typ.getImpl() + if timpl.len>0 and timpl[0].len>1: + return timpl[0][1] + else: + return timpl -proc hasCustomPragma*(n: NimNode, name: string): bool = - n.expectKind nnkSym - result = getCustomPragmaNodes(n, name).len > 0 + if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}: + let name = $(if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1]) + let typInst = getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0]) + var typDef = getImpl(if typInst.kind == nnkVarTy: typInst[0] else: typInst) + while typDef != nil: + typDef.expectKind(nnkTypeDef) + let typ = typDef[2] + typ.expectKind({nnkRefTy, nnkPtrTy, nnkObjectTy}) + let isRef = typ.kind in {nnkRefTy, nnkPtrTy} + if isRef and typ[0].kind in {nnkSym, nnkBracketExpr}: # defines ref type for another object(e.g. X = ref X) + typDef = getImpl(typ[0]) + else: # object definition, maybe an object directly defined as a ref type + let + obj = (if isRef: typ[0] else: typ) + var identDefsStack = newSeq[NimNode](obj[2].len) + for i in 0.. 0: + var identDefs = identDefsStack.pop() + if identDefs.kind == nnkRecCase: + identDefsStack.add(identDefs[0]) + for i in 1.. 0) + let pragmaNode = customPragmaNode(n) + for p in pragmaNode: + if (p.kind == nnkSym and p == cp) or + (p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp): + return newLit(true) + return newLit(false) -iterator iterOverFormalArgs(f: NimNode): tuple[name, typ, val: NimNode] = - f.expectKind nnkFormalParams - for i in 1.. 0 and p[0].kind == nnkSym and p[0] == cp: + if p.len == 2: + result = p[1] + else: + let def = p[0].getImpl[3] + result = newTree(nnkPar) + for i in 1 ..< def.len: + let key = def[i][0] + let val = p[i] + result.add newTree(nnkExprColonExpr, key, val) + break + if result.kind == nnkEmpty: + error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error, macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped = ## Calls `callee` with `args` unpacked as individual arguments. diff --git a/testament/important_packages.nim b/testament/important_packages.nim index afceffb2e..ab29dcd0d 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -114,6 +114,7 @@ pkg "nimsvg" pkg "nimterop", "nimble minitest" pkg "nimwc", "nim c nimwc.nim" pkg "nimx", "nim c --threads:on test/main.nim", allowFailure = true +pkg "nimYAML", "nim c -r test/tserialization.nim" pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter" pkg "norm", "nim c -r tests/sqlite/trows.nim" pkg "npeg", "nimble testarc" diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index 3ef5bdcb6..b197a7c55 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -384,7 +384,7 @@ block: discard Hello(a: 1.0, b: 12) # issue #11511 -block: +when false: template myAttr {.pragma.} type TObj = object @@ -395,23 +395,24 @@ block: let recList = objTy[2] let sym = recList[0] assert sym.kind == nnkSym and sym.eqIdent("a") - let hasAttr = sym.hasCustomPragma("myAttr") + let hasAttr = sym.hasCustomPragma(myAttr) newLit(hasAttr) doAssert hasMyAttr(TObj) -# misc -{.pragma: haha.} -{.pragma: hoho.} -template hehe(key, val: string, haha) {.pragma.} +when false: + # misc + {.pragma: haha.} + {.pragma: hoho.} + template hehe(key, val: string, haha) {.pragma.} -type A {.haha, hoho, haha, hehe("hi", "hu", "he").} = int + type A {.haha, hoho, haha, hehe("hi", "hu", "he").} = int -assert A.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he") + assert A.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he") -template hehe(key, val: int) {.pragma.} + template hehe(key, val: int) {.pragma.} -var bb {.haha, hoho, hehe(1, 2), haha, hehe("hi", "hu", "he").} = 3 + var bb {.haha, hoho, hehe(1, 2), haha, hehe("hi", "hu", "he").} = 3 -# left-to-right priority/override order for getCustomPragmaVal -assert bb.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he") + # left-to-right priority/override order for getCustomPragmaVal + assert bb.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he")