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:
Kaushal Modi
2018-10-27 09:10:05 -04:00
committed by Dominik Picheta
parent dd252ce640
commit f8cef575b3
3 changed files with 109 additions and 72 deletions

View File

@@ -38,6 +38,9 @@
### 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

View File

@@ -732,72 +732,56 @@ proc nestList*(theProc: NimIdent, x: NimNode): NimNode {.compileTime, deprecated
for i in countdown(L-3, 0):
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.} =
## Convert the AST `n` to a human-readable tree-like string.
##
## See also `repr`, `lispRepr`, and `astGenRepr`.
n.treeTraverse(result, isLisp = false, indented = true)
proc traverse(res: var string, level: int, n: NimNode) {.benign.} =
for i in 0..level-1: 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, 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,
proc lispRepr*(n: NimNode; indented = false): string {.compileTime, benign.} =
## Convert the AST ``n`` to a human-readable lisp-like string.
##
## See also `repr`, `treeRepr`, and `astGenRepr`.
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, ")")
## See also ``repr``, ``treeRepr``, and ``astGenRepr``.
n.treeTraverse(result, isLisp = true, indented = indented)
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
## astGenRepr:
## echo "Hello world"
##
## Would output:
##
## .. code-block:: nim
## nnkStmtList.newTree(
## nnkCommand.newTree(
## newIdentNode("echo"),
## newLit("Hello world")
## )
## )
##
## See also `repr`, `treeRepr`, and `lispRepr`.
## See also ``repr``, ``treeRepr``, and ``lispRepr``.
const
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
## 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
## tree and to discover what kind of nodes must be created to represent
## 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
## 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
## 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
## Deprecated. Use `dumpTree` instead.

View File

@@ -14,7 +14,7 @@ var nodeSeq {.compileTime.} = newSeq[NimNode](2)
proc checkNode(arg: NimNode; name: string): void {. compileTime .} =
echo "checking ", name
assertEq arg.lispRepr , "StmtList(DiscardStmt(Empty()))"
assertEq arg.lispRepr, """(StmtList (DiscardStmt (Empty)))"""
node = arg
nodeArray = [arg]
@@ -24,12 +24,12 @@ proc checkNode(arg: NimNode; name: string): void {. compileTime .} =
seqAppend.add(arg) # bit this creates a copy
arg.add newCall(ident"echo", newLit("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 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 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 arg.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 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[1].lispRepr, """(StmtList (DiscardStmt (Empty)) (Call (Ident "echo") (StrLit "Hello World")))"""
echo "OK"