Accept nnkTypeSection from typedef macro pragmas (#19168)
This commit is contained in:
parent
eb5358dcdb
commit
1b143f5e79
4 changed files with 109 additions and 5 deletions
29
changelog.md
29
changelog.md
|
|
@ -13,7 +13,36 @@
|
||||||
|
|
||||||
## Language changes
|
## Language changes
|
||||||
|
|
||||||
|
- Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`,
|
||||||
|
allowing multiple type definitions to be injected in place of the original type definition.
|
||||||
|
|
||||||
|
```nim
|
||||||
|
import macros
|
||||||
|
|
||||||
|
macro multiply(amount: static int, s: untyped): untyped =
|
||||||
|
let name = $s[0].basename
|
||||||
|
result = newNimNode(nnkTypeSection)
|
||||||
|
for i in 1 .. amount:
|
||||||
|
result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2]))
|
||||||
|
|
||||||
|
type
|
||||||
|
Foo = object
|
||||||
|
Bar {.multiply: 3.} = object
|
||||||
|
x, y, z: int
|
||||||
|
Baz = object
|
||||||
|
|
||||||
|
# becomes
|
||||||
|
|
||||||
|
type
|
||||||
|
Foo = object
|
||||||
|
Bar1 = object
|
||||||
|
x, y, z: int
|
||||||
|
Bar2 = object
|
||||||
|
x, y, z: int
|
||||||
|
Bar3 = object
|
||||||
|
x, y, z: int
|
||||||
|
Baz = object
|
||||||
|
```
|
||||||
|
|
||||||
## Compiler changes
|
## Compiler changes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1112,7 +1112,12 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
|
||||||
if name.kind == nkPragmaExpr:
|
if name.kind == nkPragmaExpr:
|
||||||
let rewritten = applyTypeSectionPragmas(c, name[1], typeDef)
|
let rewritten = applyTypeSectionPragmas(c, name[1], typeDef)
|
||||||
if rewritten != nil:
|
if rewritten != nil:
|
||||||
|
case rewritten.kind
|
||||||
|
of nkTypeDef:
|
||||||
typeSection[i] = rewritten
|
typeSection[i] = rewritten
|
||||||
|
of nkTypeSection:
|
||||||
|
typeSection.sons[i .. i] = rewritten.sons
|
||||||
|
else: illFormedAst(rewritten, c.config)
|
||||||
typeDefLeftSidePass(c, typeSection, i)
|
typeDefLeftSidePass(c, typeSection, i)
|
||||||
return
|
return
|
||||||
pragma(c, s, name[1], typePragmas)
|
pragma(c, s, name[1], typePragmas)
|
||||||
|
|
@ -1143,16 +1148,19 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
|
||||||
proc typeSectionLeftSidePass(c: PContext, n: PNode) =
|
proc typeSectionLeftSidePass(c: PContext, n: PNode) =
|
||||||
# process the symbols on the left side for the whole type section, before
|
# process the symbols on the left side for the whole type section, before
|
||||||
# we even look at the type definitions on the right
|
# we even look at the type definitions on the right
|
||||||
for i in 0..<n.len:
|
var i = 0
|
||||||
|
while i < n.len: # n may grow due to type pragma macros
|
||||||
var a = n[i]
|
var a = n[i]
|
||||||
when defined(nimsuggest):
|
when defined(nimsuggest):
|
||||||
if c.config.cmd == cmdIdeTools:
|
if c.config.cmd == cmdIdeTools:
|
||||||
inc c.inTypeContext
|
inc c.inTypeContext
|
||||||
suggestStmt(c, a)
|
suggestStmt(c, a)
|
||||||
dec c.inTypeContext
|
dec c.inTypeContext
|
||||||
if a.kind == nkCommentStmt: continue
|
case a.kind
|
||||||
if a.kind != nkTypeDef: illFormedAst(a, c.config)
|
of nkCommentStmt: discard
|
||||||
typeDefLeftSidePass(c, n, i)
|
of nkTypeDef: typeDefLeftSidePass(c, n, i)
|
||||||
|
else: illFormedAst(a, c.config)
|
||||||
|
inc i
|
||||||
|
|
||||||
proc checkCovariantParamsUsages(c: PContext; genericType: PType) =
|
proc checkCovariantParamsUsages(c: PContext; genericType: PType) =
|
||||||
var body = genericType[^1]
|
var body = genericType[^1]
|
||||||
|
|
|
||||||
|
|
@ -7755,6 +7755,7 @@ This is translated to:
|
||||||
This is translated to a call to the `schema` macro with a `nnkTypeDef`
|
This is translated to a call to the `schema` macro with a `nnkTypeDef`
|
||||||
AST node capturing both the left-hand side and right-hand side of the
|
AST node capturing both the left-hand side and right-hand side of the
|
||||||
definition. The macro can return a potentially modified `nnkTypeDef` tree
|
definition. The macro can return a potentially modified `nnkTypeDef` tree
|
||||||
|
or multiple `nnkTypeDef` trees contained in a `nnkTypeSection` node
|
||||||
which will replace the original row in the type section.
|
which will replace the original row in the type section.
|
||||||
|
|
||||||
When multiple macro pragmas are applied to the same definition, the
|
When multiple macro pragmas are applied to the same definition, the
|
||||||
|
|
|
||||||
66
tests/pragmas/ttypedef_macro.nim
Normal file
66
tests/pragmas/ttypedef_macro.nim
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
import macros
|
||||||
|
|
||||||
|
macro makeref(s): untyped =
|
||||||
|
expectKind s, nnkTypeDef
|
||||||
|
result = newTree(nnkTypeDef, s[0], s[1], newTree(nnkRefTy, s[2]))
|
||||||
|
|
||||||
|
type
|
||||||
|
Obj {.makeref.} = object
|
||||||
|
a: int
|
||||||
|
|
||||||
|
doAssert Obj is ref
|
||||||
|
doAssert Obj(a: 3)[].a == 3
|
||||||
|
|
||||||
|
macro multiply(amount: static int, s): untyped =
|
||||||
|
let name = $s[0].basename
|
||||||
|
result = newNimNode(nnkTypeSection)
|
||||||
|
for i in 1 .. amount:
|
||||||
|
result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2]))
|
||||||
|
|
||||||
|
type
|
||||||
|
Foo = object
|
||||||
|
Bar {.multiply: 2.} = object
|
||||||
|
x, y, z: int
|
||||||
|
Baz = object
|
||||||
|
|
||||||
|
let bar1 = Bar1(x: 1, y: 2, z: 3)
|
||||||
|
let bar2 = Bar2(x: bar1.x, y: bar1.y, z: bar1.z)
|
||||||
|
doAssert Bar1 isnot Bar2
|
||||||
|
doAssert not declared(Bar)
|
||||||
|
doAssert not declared(Bar3)
|
||||||
|
|
||||||
|
# https://github.com/nim-lang/RFCs/issues/219
|
||||||
|
|
||||||
|
macro inferKind(td): untyped =
|
||||||
|
let name = $td[0].basename
|
||||||
|
var rhs = td[2]
|
||||||
|
while rhs.kind in {nnkPtrTy, nnkRefTy}: rhs = rhs[0]
|
||||||
|
if rhs.kind != nnkObjectTy:
|
||||||
|
result = td
|
||||||
|
else:
|
||||||
|
for n in rhs[^1]:
|
||||||
|
if n.kind == nnkRecCase and n[0][^2].eqIdent"_":
|
||||||
|
let kindTypeName = ident(name & "Kind")
|
||||||
|
let en = newTree(nnkEnumTy, newEmptyNode())
|
||||||
|
for i in 1 ..< n.len:
|
||||||
|
let branch = n[i]
|
||||||
|
if branch.kind == nnkOfBranch:
|
||||||
|
for j in 0 ..< branch.len - 1:
|
||||||
|
en.add(branch[j])
|
||||||
|
n[0][^2] = kindTypeName
|
||||||
|
return newTree(nnkTypeSection,
|
||||||
|
newTree(nnkTypeDef, kindTypeName, newEmptyNode(), en),
|
||||||
|
td)
|
||||||
|
|
||||||
|
type Node {.inferKind.} = ref object
|
||||||
|
case kind: _
|
||||||
|
of opValue: value: int
|
||||||
|
of opAdd, opSub, opMul, opCall: kids: seq[Node]
|
||||||
|
|
||||||
|
doAssert opValue is NodeKind
|
||||||
|
let node = Node(kind: opMul, kids: @[
|
||||||
|
Node(kind: opValue, value: 3),
|
||||||
|
Node(kind: opValue, value: 5)
|
||||||
|
])
|
||||||
|
doAssert node.kind == opMul
|
||||||
|
doAssert node.kids[0].value * node.kids[1].value == 15
|
||||||
Loading…
Add table
Add a link
Reference in a new issue