macros.treeRepr + friends: collapse SymChoice (#18072)
* macros.treeRepr + friends: collapse SymChoice * make repr+friends work with invalid symchoice nodes * address comment
This commit is contained in:
parent
a2b6081256
commit
3cc547f2df
4 changed files with 151 additions and 43 deletions
|
|
@ -81,6 +81,9 @@
|
||||||
|
|
||||||
- Deprecated `proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T]` in `std/algorithm`.
|
- Deprecated `proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T]` in `std/algorithm`.
|
||||||
|
|
||||||
|
- In `std/macros`, `treeRepr,lispRepr,astGenRepr` now represent SymChoice nodes in a collapsed way,
|
||||||
|
use `-d:nimLegacyMacrosCollapseSymChoice` to get previous behavior.
|
||||||
|
|
||||||
- The configuration subsystem now allows for `-d:release` and `-d:danger` to work as expected.
|
- The configuration subsystem now allows for `-d:release` and `-d:danger` to work as expected.
|
||||||
The downside is that these defines now have custom logic that doesn't apply for
|
The downside is that these defines now have custom logic that doesn't apply for
|
||||||
other defines.
|
other defines.
|
||||||
|
|
|
||||||
|
|
@ -856,6 +856,29 @@ proc nestList*(op: NimNode; pack: NimNode; init: NimNode): NimNode =
|
||||||
for i in countdown(pack.len - 1, 0):
|
for i in countdown(pack.len - 1, 0):
|
||||||
result = newCall(op, pack[i], result)
|
result = newCall(op, pack[i], result)
|
||||||
|
|
||||||
|
proc eqIdent*(a: string; b: string): bool {.magic: "EqIdent", noSideEffect.}
|
||||||
|
## Style insensitive comparison.
|
||||||
|
|
||||||
|
proc eqIdent*(a: NimNode; b: string): bool {.magic: "EqIdent", noSideEffect.}
|
||||||
|
## Style insensitive comparison. `a` can be an identifier or a
|
||||||
|
## symbol. `a` may be wrapped in an export marker
|
||||||
|
## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`),
|
||||||
|
## these nodes will be unwrapped.
|
||||||
|
|
||||||
|
proc eqIdent*(a: string; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
|
||||||
|
## Style insensitive comparison. `b` can be an identifier or a
|
||||||
|
## symbol. `b` may be wrapped in an export marker
|
||||||
|
## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`),
|
||||||
|
## these nodes will be unwrapped.
|
||||||
|
|
||||||
|
proc eqIdent*(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
|
||||||
|
## Style insensitive comparison. `a` and `b` can be an
|
||||||
|
## identifier or a symbol. Both may be wrapped in an export marker
|
||||||
|
## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`),
|
||||||
|
## these nodes will be unwrapped.
|
||||||
|
|
||||||
|
const collapseSymChoice = not defined(nimLegacyMacrosCollapseSymChoice)
|
||||||
|
|
||||||
proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indented = false) {.benign.} =
|
proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indented = false) {.benign.} =
|
||||||
if level > 0:
|
if level > 0:
|
||||||
if indented:
|
if indented:
|
||||||
|
|
@ -883,8 +906,21 @@ proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indent
|
||||||
res.add(" " & $n.strVal.newLit.repr)
|
res.add(" " & $n.strVal.newLit.repr)
|
||||||
of nnkNone:
|
of nnkNone:
|
||||||
assert false
|
assert false
|
||||||
|
elif n.kind in {nnkOpenSymChoice, nnkClosedSymChoice} and collapseSymChoice:
|
||||||
|
res.add(" " & $n.len)
|
||||||
|
if n.len > 0:
|
||||||
|
var allSameSymName = true
|
||||||
|
for i in 0..<n.len:
|
||||||
|
if n[i].kind != nnkSym or not eqIdent(n[i], n[0]):
|
||||||
|
allSameSymName = false
|
||||||
|
break
|
||||||
|
if allSameSymName:
|
||||||
|
res.add(" " & $n[0].strVal.newLit.repr)
|
||||||
|
else:
|
||||||
|
for j in 0 ..< n.len:
|
||||||
|
n[j].treeTraverse(res, level+1, isLisp, indented)
|
||||||
else:
|
else:
|
||||||
for j in 0 .. n.len-1:
|
for j in 0 ..< n.len:
|
||||||
n[j].treeTraverse(res, level+1, isLisp, indented)
|
n[j].treeTraverse(res, level+1, isLisp, indented)
|
||||||
|
|
||||||
if isLisp:
|
if isLisp:
|
||||||
|
|
@ -932,6 +968,10 @@ proc astGenRepr*(n: NimNode): string {.benign.} =
|
||||||
of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkIdent, nnkSym:
|
of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkIdent, nnkSym:
|
||||||
res.add(n.strVal.newLit.repr)
|
res.add(n.strVal.newLit.repr)
|
||||||
of nnkNone: assert false
|
of nnkNone: assert false
|
||||||
|
elif n.kind in {nnkOpenSymChoice, nnkClosedSymChoice} and collapseSymChoice:
|
||||||
|
res.add(", # unrepresentable symbols: " & $n.len)
|
||||||
|
if n.len > 0:
|
||||||
|
res.add(" " & n[0].strVal.newLit.repr)
|
||||||
else:
|
else:
|
||||||
res.add(".newTree(")
|
res.add(".newTree(")
|
||||||
for j in 0..<n.len:
|
for j in 0..<n.len:
|
||||||
|
|
@ -1394,27 +1434,6 @@ proc copy*(node: NimNode): NimNode =
|
||||||
## An alias for `copyNimTree<#copyNimTree,NimNode>`_.
|
## An alias for `copyNimTree<#copyNimTree,NimNode>`_.
|
||||||
return node.copyNimTree()
|
return node.copyNimTree()
|
||||||
|
|
||||||
proc eqIdent*(a: string; b: string): bool {.magic: "EqIdent", noSideEffect.}
|
|
||||||
## Style insensitive comparison.
|
|
||||||
|
|
||||||
proc eqIdent*(a: NimNode; b: string): bool {.magic: "EqIdent", noSideEffect.}
|
|
||||||
## Style insensitive comparison. `a` can be an identifier or a
|
|
||||||
## symbol. `a` may be wrapped in an export marker
|
|
||||||
## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`),
|
|
||||||
## these nodes will be unwrapped.
|
|
||||||
|
|
||||||
proc eqIdent*(a: string; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
|
|
||||||
## Style insensitive comparison. `b` can be an identifier or a
|
|
||||||
## symbol. `b` may be wrapped in an export marker
|
|
||||||
## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`),
|
|
||||||
## these nodes will be unwrapped.
|
|
||||||
|
|
||||||
proc eqIdent*(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
|
|
||||||
## Style insensitive comparison. `a` and `b` can be an
|
|
||||||
## identifier or a symbol. Both may be wrapped in an export marker
|
|
||||||
## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`),
|
|
||||||
## these nodes will be unwrapped.
|
|
||||||
|
|
||||||
proc expectIdent*(n: NimNode, name: string) {.since: (1,1).} =
|
proc expectIdent*(n: NimNode, name: string) {.since: (1,1).} =
|
||||||
## Check that `eqIdent(n,name)` holds true. If this is not the
|
## Check that `eqIdent(n,name)` holds true. If this is not the
|
||||||
## case, compilation aborts with an error message. This is useful
|
## case, compilation aborts with an error message. This is useful
|
||||||
|
|
|
||||||
|
|
@ -2,34 +2,69 @@
|
||||||
|
|
||||||
import macros
|
import macros
|
||||||
|
|
||||||
template plus(a, b: untyped): untyped {.dirty} =
|
block:
|
||||||
a + b
|
template plus(a, b: untyped): untyped {.dirty} =
|
||||||
|
a + b
|
||||||
|
|
||||||
macro call(e: untyped): untyped =
|
macro call(e: untyped): untyped =
|
||||||
result = newCall("foo", newStrLitNode("bar"))
|
result = newCall("foo", newStrLitNode("bar"))
|
||||||
|
|
||||||
macro dumpAST(n: untyped): untyped =
|
macro dumpAST(n: untyped): string =
|
||||||
# dump AST as a side-effect and return the inner node
|
var msg = ""
|
||||||
let n = callsite()
|
msg.add "lispRepr:\n" & n.lispRepr & "\n"
|
||||||
echo n.lispRepr
|
msg.add "treeRepr:\n" & n.treeRepr & "\n"
|
||||||
echo n.treeRepr
|
|
||||||
|
|
||||||
var plusAst = getAst(plus(1, 2))
|
var plusAst = getAst(plus(1, 2))
|
||||||
echo plusAst.lispRepr
|
msg.add "lispRepr:\n" & n.lispRepr & "\n"
|
||||||
|
|
||||||
var callAst = getAst(call(4))
|
var callAst = getAst(call(4))
|
||||||
echo callAst.lispRepr
|
msg.add "callAst.lispRepr:\n" & callAst.lispRepr & "\n"
|
||||||
|
|
||||||
var e = parseExpr("foo(bar + baz)")
|
var e = parseExpr("foo(bar + baz)")
|
||||||
echo e.lispRepr
|
msg.add "e.lispRepr:\n" & e.lispRepr & "\n"
|
||||||
|
result = msg.newLit
|
||||||
|
|
||||||
result = n[1]
|
let a = dumpAST:
|
||||||
|
proc add(x, y: int): int =
|
||||||
|
return x + y
|
||||||
|
const foo = 3
|
||||||
|
|
||||||
dumpAST:
|
doAssert a == """
|
||||||
proc add(x, y: int): int =
|
lispRepr:
|
||||||
return x + y
|
(StmtList (ProcDef (Ident "add") (Empty) (Empty) (FormalParams (Ident "int") (IdentDefs (Ident "x") (Ident "y") (Ident "int") (Empty))) (Empty) (Empty) (StmtList (ReturnStmt (Infix (Ident "+") (Ident "x") (Ident "y"))))) (ConstSection (ConstDef (Ident "foo") (Empty) (IntLit 3))))
|
||||||
|
treeRepr:
|
||||||
proc sub(x, y: int): int = return x - y
|
StmtList
|
||||||
|
ProcDef
|
||||||
|
Ident "add"
|
||||||
|
Empty
|
||||||
|
Empty
|
||||||
|
FormalParams
|
||||||
|
Ident "int"
|
||||||
|
IdentDefs
|
||||||
|
Ident "x"
|
||||||
|
Ident "y"
|
||||||
|
Ident "int"
|
||||||
|
Empty
|
||||||
|
Empty
|
||||||
|
Empty
|
||||||
|
StmtList
|
||||||
|
ReturnStmt
|
||||||
|
Infix
|
||||||
|
Ident "+"
|
||||||
|
Ident "x"
|
||||||
|
Ident "y"
|
||||||
|
ConstSection
|
||||||
|
ConstDef
|
||||||
|
Ident "foo"
|
||||||
|
Empty
|
||||||
|
IntLit 3
|
||||||
|
lispRepr:
|
||||||
|
(StmtList (ProcDef (Ident "add") (Empty) (Empty) (FormalParams (Ident "int") (IdentDefs (Ident "x") (Ident "y") (Ident "int") (Empty))) (Empty) (Empty) (StmtList (ReturnStmt (Infix (Ident "+") (Ident "x") (Ident "y"))))) (ConstSection (ConstDef (Ident "foo") (Empty) (IntLit 3))))
|
||||||
|
callAst.lispRepr:
|
||||||
|
(Call (Ident "foo") (StrLit "bar"))
|
||||||
|
e.lispRepr:
|
||||||
|
(Call (Ident "foo") (Infix (Ident "+") (Ident "bar") (Ident "baz")))
|
||||||
|
"""
|
||||||
|
|
||||||
macro fun() =
|
macro fun() =
|
||||||
let n = quote do:
|
let n = quote do:
|
||||||
|
|
@ -79,3 +114,48 @@ block: # repr
|
||||||
doAssert repr2((1, 2, 3.0)) == "(1, 2, 3.0)"
|
doAssert repr2((1, 2, 3.0)) == "(1, 2, 3.0)"
|
||||||
doAssert repr2((1)) == "(1)"
|
doAssert repr2((1)) == "(1)"
|
||||||
doAssert repr2((1+2)) == "(1 + 2)"
|
doAssert repr2((1+2)) == "(1 + 2)"
|
||||||
|
|
||||||
|
block: # treeRepr
|
||||||
|
macro treeRepr2(a: untyped): string = newLit a.treeRepr
|
||||||
|
macro treeRepr3(a: typed): string = newLit a.treeRepr
|
||||||
|
|
||||||
|
doAssert treeRepr2(1+1 == 2) == """
|
||||||
|
Infix
|
||||||
|
Ident "=="
|
||||||
|
Infix
|
||||||
|
Ident "+"
|
||||||
|
IntLit 1
|
||||||
|
IntLit 1
|
||||||
|
IntLit 2"""
|
||||||
|
|
||||||
|
proc baz() = discard
|
||||||
|
proc baz(a: int) = discard
|
||||||
|
proc baz(a: float) = discard
|
||||||
|
|
||||||
|
doAssert treeRepr3(baz()) == """
|
||||||
|
Call
|
||||||
|
Sym "baz""""
|
||||||
|
|
||||||
|
let a = treeRepr3(block:
|
||||||
|
proc bar(a: auto) = baz())
|
||||||
|
doAssert a == """
|
||||||
|
BlockStmt
|
||||||
|
Empty
|
||||||
|
ProcDef
|
||||||
|
Sym "bar"
|
||||||
|
Empty
|
||||||
|
GenericParams
|
||||||
|
Sym "a:type"
|
||||||
|
FormalParams
|
||||||
|
Empty
|
||||||
|
IdentDefs
|
||||||
|
Sym "a"
|
||||||
|
Sym "auto"
|
||||||
|
Empty
|
||||||
|
Empty
|
||||||
|
Bracket
|
||||||
|
Empty
|
||||||
|
Empty
|
||||||
|
StmtList
|
||||||
|
Call
|
||||||
|
OpenSymChoice 3 "baz""""
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,9 @@
|
||||||
|
#[
|
||||||
|
xxx macros tests need to be reorganized to makes sure each API is tested once
|
||||||
|
See also:
|
||||||
|
tests/macros/tdumpast.nim for treeRepr + friends
|
||||||
|
]#
|
||||||
|
|
||||||
import std/macros
|
import std/macros
|
||||||
|
|
||||||
block: # hasArgOfName
|
block: # hasArgOfName
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue