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:
Timothee Cour 2021-06-04 21:58:26 -07:00 • committed by GitHub
commit 3cc547f2df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 151 additions and 43 deletions

View file

@ -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.

View file

@ -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: else:
for j in 0 .. n.len-1: for j in 0 ..< n.len:
n[j].treeTraverse(res, level+1, isLisp, indented)
else:
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

View file

@ -2,34 +2,69 @@
import macros import macros
template plus(a, b: untyped): untyped {.dirty} = block:
template plus(a, b: untyped): untyped {.dirty} =
a + b 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:
dumpAST:
proc add(x, y: int): int = proc add(x, y: int): int =
return x + y return x + y
const foo = 3
proc sub(x, y: int): int = return x - y doAssert a == """
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))))
treeRepr:
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""""

View file

@ -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