mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-21 23:05:27 +00:00
Accept nnkTypeSection from typedef macro pragmas (#19168)
This commit is contained in:
29
changelog.md
29
changelog.md
@@ -13,7 +13,36 @@
|
||||
|
||||
## 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
|
||||
|
||||
|
||||
@@ -1112,7 +1112,12 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
|
||||
if name.kind == nkPragmaExpr:
|
||||
let rewritten = applyTypeSectionPragmas(c, name[1], typeDef)
|
||||
if rewritten != nil:
|
||||
typeSection[i] = rewritten
|
||||
case rewritten.kind
|
||||
of nkTypeDef:
|
||||
typeSection[i] = rewritten
|
||||
of nkTypeSection:
|
||||
typeSection.sons[i .. i] = rewritten.sons
|
||||
else: illFormedAst(rewritten, c.config)
|
||||
typeDefLeftSidePass(c, typeSection, i)
|
||||
return
|
||||
pragma(c, s, name[1], typePragmas)
|
||||
@@ -1143,16 +1148,19 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
|
||||
proc typeSectionLeftSidePass(c: PContext, n: PNode) =
|
||||
# process the symbols on the left side for the whole type section, before
|
||||
# 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]
|
||||
when defined(nimsuggest):
|
||||
if c.config.cmd == cmdIdeTools:
|
||||
inc c.inTypeContext
|
||||
suggestStmt(c, a)
|
||||
dec c.inTypeContext
|
||||
if a.kind == nkCommentStmt: continue
|
||||
if a.kind != nkTypeDef: illFormedAst(a, c.config)
|
||||
typeDefLeftSidePass(c, n, i)
|
||||
case a.kind
|
||||
of nkCommentStmt: discard
|
||||
of nkTypeDef: typeDefLeftSidePass(c, n, i)
|
||||
else: illFormedAst(a, c.config)
|
||||
inc i
|
||||
|
||||
proc checkCovariantParamsUsages(c: PContext; genericType: PType) =
|
||||
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`
|
||||
AST node capturing both the left-hand side and right-hand side of the
|
||||
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.
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user