Improve dumpLisp macro (#9515)
* Improve dumpLisp macro
- Remove commas from the lisp representation of the AST.
- Make the dumpLisp output "pretty" and indented.
- Improve docs of `dumpTree` and `dumpLisp` macros.
With:
dumpLisp:
echo "Hello, World!"
Output before this commit:
StmtList(Command(Ident("echo"), StrLit("Hello, World!")))
Output after this commit:
(StmtList
(Command
(Ident "echo")
(StrLit "Hello, World!")))
* Re-use the traverse proc inside treeRepr for lispRepr too
- Add module-local `treeTraverse` proc.
- Also fix treeRepr/dumpTree not printing nnkCommentStmt node contents.
* More doc string updates
* Allow unindented lispRepr output for tests
* Update a test affected by the lispRepr change
* Fix dumpTree
* Add note about lispRepr and dumpLisp to changelog [ci skip]
This commit is contained in:
parent
dd252ce640
commit
f8cef575b3
3 changed files with 109 additions and 72 deletions
|
|
@ -38,6 +38,9 @@
|
||||||
|
|
||||||
### Library changes
|
### Library changes
|
||||||
|
|
||||||
|
- The string output of `macros.lispRepr` proc has been tweaked
|
||||||
|
slightly. The `dumpLisp` macro in this module now outputs an
|
||||||
|
indented proper Lisp, devoid of commas.
|
||||||
|
|
||||||
### Language additions
|
### Language additions
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -732,72 +732,56 @@ proc nestList*(theProc: NimIdent, x: NimNode): NimNode {.compileTime, deprecated
|
||||||
for i in countdown(L-3, 0):
|
for i in countdown(L-3, 0):
|
||||||
result = newCall(theProc, x[i], result)
|
result = newCall(theProc, x[i], result)
|
||||||
|
|
||||||
|
proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indented = false) {.benign.} =
|
||||||
|
if level > 0:
|
||||||
|
if indented:
|
||||||
|
res.add("\n")
|
||||||
|
for i in 0 .. level-1:
|
||||||
|
if isLisp:
|
||||||
|
res.add(" ") # dumpLisp indentation
|
||||||
|
else:
|
||||||
|
res.add(" ") # dumpTree indentation
|
||||||
|
else:
|
||||||
|
res.add(" ")
|
||||||
|
|
||||||
|
if isLisp:
|
||||||
|
res.add("(")
|
||||||
|
res.add(($n.kind).substr(3))
|
||||||
|
|
||||||
|
case n.kind
|
||||||
|
of nnkEmpty, nnkNilLit:
|
||||||
|
discard # same as nil node in this representation
|
||||||
|
of nnkCharLit .. nnkInt64Lit:
|
||||||
|
res.add(" " & $n.intVal)
|
||||||
|
of nnkFloatLit .. nnkFloat64Lit:
|
||||||
|
res.add(" " & $n.floatVal)
|
||||||
|
of nnkStrLit .. nnkTripleStrLit, nnkCommentStmt, nnkIdent, nnkSym:
|
||||||
|
res.add(" " & $n.strVal.newLit.repr)
|
||||||
|
of nnkNone:
|
||||||
|
assert false
|
||||||
|
else:
|
||||||
|
for j in 0 .. n.len-1:
|
||||||
|
n[j].treeTraverse(res, level+1, isLisp, indented)
|
||||||
|
|
||||||
|
if isLisp:
|
||||||
|
res.add(")")
|
||||||
|
|
||||||
proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
|
proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
|
||||||
## Convert the AST `n` to a human-readable tree-like string.
|
## Convert the AST `n` to a human-readable tree-like string.
|
||||||
##
|
##
|
||||||
## See also `repr`, `lispRepr`, and `astGenRepr`.
|
## See also `repr`, `lispRepr`, and `astGenRepr`.
|
||||||
|
n.treeTraverse(result, isLisp = false, indented = true)
|
||||||
|
|
||||||
proc traverse(res: var string, level: int, n: NimNode) {.benign.} =
|
proc lispRepr*(n: NimNode; indented = false): string {.compileTime, benign.} =
|
||||||
for i in 0..level-1: res.add " "
|
## Convert the AST ``n`` to a human-readable lisp-like string.
|
||||||
res.add(($n.kind).substr(3))
|
|
||||||
|
|
||||||
case n.kind
|
|
||||||
of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
|
|
||||||
of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
|
|
||||||
of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
|
|
||||||
of nnkStrLit..nnkTripleStrLit, nnkIdent, nnkSym:
|
|
||||||
res.add(" " & $n.strVal.newLit.repr)
|
|
||||||
of nnkNone: assert false
|
|
||||||
else:
|
|
||||||
for j in 0..n.len-1:
|
|
||||||
res.add "\n"
|
|
||||||
traverse(res, level + 1, n[j])
|
|
||||||
|
|
||||||
result = ""
|
|
||||||
traverse(result, 0, n)
|
|
||||||
|
|
||||||
proc lispRepr*(n: NimNode): string {.compileTime, benign.} =
|
|
||||||
## Convert the AST `n` to a human-readable lisp-like string,
|
|
||||||
##
|
##
|
||||||
## See also `repr`, `treeRepr`, and `astGenRepr`.
|
## See also ``repr``, ``treeRepr``, and ``astGenRepr``.
|
||||||
|
n.treeTraverse(result, isLisp = true, indented = indented)
|
||||||
result = ($n.kind).substr(3)
|
|
||||||
add(result, "(")
|
|
||||||
|
|
||||||
case n.kind
|
|
||||||
of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
|
|
||||||
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
|
|
||||||
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
|
|
||||||
of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkident, nnkSym:
|
|
||||||
add(result, n.strVal.newLit.repr)
|
|
||||||
of nnkNone: assert false
|
|
||||||
else:
|
|
||||||
if n.len > 0:
|
|
||||||
add(result, lispRepr(n[0]))
|
|
||||||
for j in 1..n.len-1:
|
|
||||||
add(result, ", ")
|
|
||||||
add(result, lispRepr(n[j]))
|
|
||||||
|
|
||||||
add(result, ")")
|
|
||||||
|
|
||||||
proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
|
proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
|
||||||
## Convert the AST `n` to the code required to generate that AST. So for example
|
## Convert the AST ``n`` to the code required to generate that AST.
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
## See also ``repr``, ``treeRepr``, and ``lispRepr``.
|
||||||
## astGenRepr:
|
|
||||||
## echo "Hello world"
|
|
||||||
##
|
|
||||||
## Would output:
|
|
||||||
##
|
|
||||||
## .. code-block:: nim
|
|
||||||
## nnkStmtList.newTree(
|
|
||||||
## nnkCommand.newTree(
|
|
||||||
## newIdentNode("echo"),
|
|
||||||
## newLit("Hello world")
|
|
||||||
## )
|
|
||||||
## )
|
|
||||||
##
|
|
||||||
## See also `repr`, `treeRepr`, and `lispRepr`.
|
|
||||||
|
|
||||||
const
|
const
|
||||||
NodeKinds = {nnkEmpty, nnkIdent, nnkSym, nnkNone, nnkCommentStmt}
|
NodeKinds = {nnkEmpty, nnkIdent, nnkSym, nnkNone, nnkCommentStmt}
|
||||||
|
|
@ -842,26 +826,76 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
|
||||||
|
|
||||||
macro dumpTree*(s: untyped): untyped = echo s.treeRepr
|
macro dumpTree*(s: untyped): untyped = echo s.treeRepr
|
||||||
## Accepts a block of nim code and prints the parsed abstract syntax
|
## Accepts a block of nim code and prints the parsed abstract syntax
|
||||||
## tree using the `treeRepr` function. Printing is done *at compile time*.
|
## tree using the ``treeRepr`` proc. Printing is done *at compile time*.
|
||||||
##
|
##
|
||||||
## You can use this as a tool to explore the Nim's abstract syntax
|
## You can use this as a tool to explore the Nim's abstract syntax
|
||||||
## tree and to discover what kind of nodes must be created to represent
|
## tree and to discover what kind of nodes must be created to represent
|
||||||
## a certain expression/statement.
|
## a certain expression/statement.
|
||||||
|
|
||||||
macro dumpLisp*(s: untyped): untyped = echo s.lispRepr
|
|
||||||
## Accepts a block of nim code and prints the parsed abstract syntax
|
|
||||||
## tree using the `lispRepr` function. Printing is done *at compile time*.
|
|
||||||
##
|
##
|
||||||
## See `dumpTree`.
|
## For example:
|
||||||
|
##
|
||||||
|
## .. code-block:: nim
|
||||||
|
## dumpTree:
|
||||||
|
## echo "Hello, World!"
|
||||||
|
##
|
||||||
|
## Outputs:
|
||||||
|
##
|
||||||
|
## .. code-block::
|
||||||
|
## StmtList
|
||||||
|
## Command
|
||||||
|
## Ident "echo"
|
||||||
|
## StrLit "Hello, World!"
|
||||||
|
##
|
||||||
|
## Also see ``dumpAstGen`` and ``dumpLisp``.
|
||||||
|
|
||||||
|
macro dumpLisp*(s: untyped): untyped = echo s.lispRepr(indented = true)
|
||||||
|
## Accepts a block of nim code and prints the parsed abstract syntax
|
||||||
|
## tree using the ``lispRepr`` proc. Printing is done *at compile time*.
|
||||||
|
##
|
||||||
|
## You can use this as a tool to explore the Nim's abstract syntax
|
||||||
|
## tree and to discover what kind of nodes must be created to represent
|
||||||
|
## a certain expression/statement.
|
||||||
|
##
|
||||||
|
## For example:
|
||||||
|
##
|
||||||
|
## .. code-block:: nim
|
||||||
|
## dumpLisp:
|
||||||
|
## echo "Hello, World!"
|
||||||
|
##
|
||||||
|
## Outputs:
|
||||||
|
##
|
||||||
|
## .. code-block::
|
||||||
|
## (StmtList
|
||||||
|
## (Command
|
||||||
|
## (Ident "echo")
|
||||||
|
## (StrLit "Hello, World!")))
|
||||||
|
##
|
||||||
|
## Also see ``dumpAstGen`` and ``dumpTree``.
|
||||||
|
|
||||||
macro dumpAstGen*(s: untyped): untyped = echo s.astGenRepr
|
macro dumpAstGen*(s: untyped): untyped = echo s.astGenRepr
|
||||||
## Accepts a block of nim code and prints the parsed abstract syntax
|
## Accepts a block of nim code and prints the parsed abstract syntax
|
||||||
## tree using the `astGenRepr` function. Printing is done *at compile time*.
|
## tree using the ``astGenRepr`` proc. Printing is done *at compile time*.
|
||||||
##
|
##
|
||||||
## You can use this as a tool to write macros quicker by writing example
|
## You can use this as a tool to write macros quicker by writing example
|
||||||
## outputs and then copying the snippets into the macro for modification.
|
## outputs and then copying the snippets into the macro for modification.
|
||||||
##
|
##
|
||||||
## See `dumpTree`.
|
## For example:
|
||||||
|
##
|
||||||
|
## .. code-block:: nim
|
||||||
|
## dumpAstGen:
|
||||||
|
## echo "Hello, World!"
|
||||||
|
##
|
||||||
|
## Outputs:
|
||||||
|
##
|
||||||
|
## .. code-block:: nim
|
||||||
|
## nnkStmtList.newTree(
|
||||||
|
## nnkCommand.newTree(
|
||||||
|
## newIdentNode("echo"),
|
||||||
|
## newLit("Hello, World!")
|
||||||
|
## )
|
||||||
|
## )
|
||||||
|
##
|
||||||
|
## Also see ``dumpTree`` and ``dumpLisp``.
|
||||||
|
|
||||||
macro dumpTreeImm*(s: untyped): untyped {.deprecated.} = echo s.treeRepr
|
macro dumpTreeImm*(s: untyped): untyped {.deprecated.} = echo s.treeRepr
|
||||||
## Deprecated. Use `dumpTree` instead.
|
## Deprecated. Use `dumpTree` instead.
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ var nodeSeq {.compileTime.} = newSeq[NimNode](2)
|
||||||
proc checkNode(arg: NimNode; name: string): void {. compileTime .} =
|
proc checkNode(arg: NimNode; name: string): void {. compileTime .} =
|
||||||
echo "checking ", name
|
echo "checking ", name
|
||||||
|
|
||||||
assertEq arg.lispRepr , "StmtList(DiscardStmt(Empty()))"
|
assertEq arg.lispRepr, """(StmtList (DiscardStmt (Empty)))"""
|
||||||
|
|
||||||
node = arg
|
node = arg
|
||||||
nodeArray = [arg]
|
nodeArray = [arg]
|
||||||
|
|
@ -24,12 +24,12 @@ proc checkNode(arg: NimNode; name: string): void {. compileTime .} =
|
||||||
seqAppend.add(arg) # bit this creates a copy
|
seqAppend.add(arg) # bit this creates a copy
|
||||||
arg.add newCall(ident"echo", newLit("Hello World"))
|
arg.add newCall(ident"echo", newLit("Hello World"))
|
||||||
|
|
||||||
assertEq arg.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))"""
|
assertEq arg.lispRepr, """(StmtList (DiscardStmt (Empty)) (Call (Ident "echo") (StrLit "Hello World")))"""
|
||||||
assertEq node.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))"""
|
assertEq node.lispRepr, """(StmtList (DiscardStmt (Empty)) (Call (Ident "echo") (StrLit "Hello World")))"""
|
||||||
assertEq nodeArray[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))"""
|
assertEq nodeArray[0].lispRepr, """(StmtList (DiscardStmt (Empty)) (Call (Ident "echo") (StrLit "Hello World")))"""
|
||||||
assertEq nodeSeq[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))"""
|
assertEq nodeSeq[0].lispRepr, """(StmtList (DiscardStmt (Empty)) (Call (Ident "echo") (StrLit "Hello World")))"""
|
||||||
assertEq seqAppend[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))"""
|
assertEq seqAppend[0].lispRepr, """(StmtList (DiscardStmt (Empty)) (Call (Ident "echo") (StrLit "Hello World")))"""
|
||||||
assertEq seqAppend[1].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))"""
|
assertEq seqAppend[1].lispRepr, """(StmtList (DiscardStmt (Empty)) (Call (Ident "echo") (StrLit "Hello World")))"""
|
||||||
|
|
||||||
echo "OK"
|
echo "OK"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue