From 97ef94d3ff7c33a814223589b24c6d9124a142da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= <arne.doering@gmx.net> Date: Fri, 18 Jan 2019 00:45:15 +0100 Subject: [PATCH 1/5] new experimental quoteAst as a potential successor of macros.quote --- lib/experimental/quote2.nim | 85 +++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 lib/experimental/quote2.nim diff --git a/lib/experimental/quote2.nim b/lib/experimental/quote2.nim new file mode 100644 index 0000000000000..173cb08e36fe4 --- /dev/null +++ b/lib/experimental/quote2.nim @@ -0,0 +1,85 @@ +import macros + +proc newTreeExpr(stmtListExpr, exprNode, unquoteIdent: NimNode): NimNode {.compileTime.} = + # stmtList is a buffer to generate statements + if exprNode.kind in nnkLiterals: + result = newCall(bindSym"newLit", exprNode) + elif exprNode.kind == nnkIdent: + result = newCall(bindSym"ident", newLit(exprNode.strVal)) + elif exprNode.kind in nnkCallKinds and exprNode.len == 2 and exprNode[0].eqIdent unquoteIdent: + result = exprNode[1] + elif exprNode.kind == nnkSym: + error("for quoting the ast needs to be untyped", exprNode) + elif exprNode.kind == nnkEmpty: + # bug newTree(nnkEmpty) raises exception: + result = newCall(bindSym"newEmptyNode") + else: + result = genSym(nskLet) + stmtListExpr.add newLetStmt(result, newCall(bindSym"newNimNode", newLit(exprNode.kind))) #, exprNode ) + for child in exprNode: + stmtListExpr.add newCall(bindSym"add", result, newTreeExpr(stmtListExpr, child, unquoteIdent)) + +macro quoteAst(ast: untyped): untyped = + ## Substitute for ``quote do`` but with ``uq`` for unquoting instead of backticks. + result = newNimNode(nnkStmtListExpr) + result.add result.newTreeExpr(ast, ident"uq") + +macro quoteAst(unquoteIdent, ast: untyped): untyped = + unquoteIdent.expectKind nnkIdent + result = newNimNode(nnkStmtListExpr) + result.add result.newTreeExpr(ast, unquoteIdent) + +macro foobar(arg: untyped): untyped = + # simple generation of source code: + result = quoteAst: + echo "Hello world!" + + echo result.treeRepr + + # inject subtrees from local scope, like `` in quote do: + let world = newLit("world") + result = quoteAst: + echo "Hello ", uq(world), "!" + + echo result.treeRepr + + # inject subtree from expression: + result = quoteAst: + echo "Hello ", uq(newLit("world")), "!" + + echo result.treeRepr + + # custom name for unquote in case `uq` should collide with anything. + let x = newLit(123) + result = quoteAst myUnquote: + echo "abc ", myUnquote(x), " ", myUnquote(newLit("xyz")), " ", myUnquote(arg) + + echo result.treeRepr + +let myVal = "Hallo Welt!" +foobar(myVal) + +# example from #10326 + +template id*(val: int) {.pragma.} +macro m1(): untyped = + let x = newLit(10) + let r1 = quote do: + type T1 {.id(`x`).} = object + + let r2 = quoteAst: + type T1 {.id(uq(x)).} = object + + + echo "from #10326:" + echo r1[0][0].treeRepr + echo r2[0][0].treeRepr + +m1() + +macro lineinfoTest(): untyped = + # line info is preserved as if the content of ``quoteAst`` is written in a template + result = quoteAst: + assert(false) + +lineinfoTest() From 6c9a5db120ef44d764ee06cdb8785c34c03db183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= <arne.doering@gmx.net> Date: Wed, 23 Jan 2019 09:33:42 +0100 Subject: [PATCH 2/5] new examples --- compiler/renderer.nim | 7 +- compiler/vm.nim | 38 ++++++++- compiler/vmgen.nim | 7 +- lib/core/macros.nim | 8 ++ lib/experimental/quote2.nim | 153 +++++++++++++++++++++++++++++++++--- 5 files changed, 197 insertions(+), 16 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index ce47cf2197e01..4a41c12525d33 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -999,9 +999,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n, 0) gcomma(g, n, 1) of nkCommand: - accentedName(g, n[0]) - put(g, tkSpaces, Space) - gcomma(g, n, 1) + if n.len > 0: + accentedName(g, n[0]) + put(g, tkSpaces, Space) + gcomma(g, n, 1) of nkExprEqExpr, nkAsgn, nkFastAsgn: gsub(g, n, 0) put(g, tkSpaces, Space) diff --git a/compiler/vm.nim b/compiler/vm.nim index 131501380f1a5..d4659e77521c4 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1498,8 +1498,44 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = internalAssert c.config, false regs[ra].node.info = n.info regs[ra].node.typ = n.typ - of opcNSetLineInfo: + of opcNSetLineInfoObj: + let n = regs[rb].node + assert(n.kind == nkObjConstr) + assert(n.len == 4) + assert(n[1][0].sym.name.s == "filename") + let filename = n[1][1].strVal + assert(n[2][0].sym.name.s == "line") + let line = n[2][1].intVal + assert(n[3][0].sym.name.s == "column") + let column = n[3][1].intVal + + + proc semInstantiationInfo(c: PContext, n: PNode): PNode = + result = newNodeIT(nkTupleConstr, n.info, n.typ) + let idx = expectIntLit(c, n.sons[1]) + let useFullPaths = expectIntLit(c, n.sons[2]) + let info = getInfoContext(c.config, idx) + var filename = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString)) + filename.strVal = if useFullPaths != 0: toFullPath(c.config, info) else: toFilename(c.config, info) + var line = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt)) + line.intVal = toLinenumber(info) + var column = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt)) + column.intVal = toColumn(info) + result.add(filename) + result.add(line) + result.add(column) + + n. + proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile): FileIndex = + proc toAbsolute*(file: string; base: AbsoluteDir): AbsoluteFile = + + regs decodeB(rkNode) + echo regs[rb].kind + debug(regs[rb].node) + regs[ra].node.info = regs[rb].node.info + of opcNCopyLineInfo: + debug(regs[rb].node) regs[ra].node.info = regs[rb].node.info of opcEqIdent: decodeBC(rkInt) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index e7993dfb21270..930dd69765d95 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1231,11 +1231,16 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of "getFile": genUnaryABI(c, n, dest, opcNGetLineInfo, 0) of "getLine": genUnaryABI(c, n, dest, opcNGetLineInfo, 1) of "getColumn": genUnaryABI(c, n, dest, opcNGetLineInfo, 2) + of "setFile": genBinaryStmt(c, n, opcNSetLineInfo) + of "setLine": genBinaryStmt(c, n, opcNSetLineInfo) + of "setColumn": genBinaryStmt(c, n, opcNSetLineInfo) + of "lineInfoObj=": genBinaryStmt(c, n, opcNSetLineInfo) of "copyLineInfo": internalAssert c.config, n.len == 3 unused(c, n, dest) genBinaryStmt(c, n, opcNSetLineInfo) - else: internalAssert c.config, false + else: + internalAssert c.config, false of mNHint: unused(c, n, dest) genBinaryStmt(c, n, opcNHint) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 3a85324bac03a..17cb4e87c525c 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -489,6 +489,14 @@ proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} = proc lineInfo*(arg: NimNode): string {.compileTime.} = $arg.lineInfoObj + +proc `lineInfoObj=`*(n: NimNode; lineinfo: LineInfo) {.magic: "NLineInfo".} + +proc setLine(arg: NimNode; line: int) {.magic: "NLineInfo".} +proc setColumn(arg: NimNode; column: int) {.magic: "NLineInfo".} +proc setFile(arg: NimNode; filename: string) {.magic: "NLineInfo".} + + proc internalParseExpr(s: string): NimNode {. magic: "ParseExprToAst", noSideEffect.} diff --git a/lib/experimental/quote2.nim b/lib/experimental/quote2.nim index 173cb08e36fe4..af9b6ae0a1290 100644 --- a/lib/experimental/quote2.nim +++ b/lib/experimental/quote2.nim @@ -1,33 +1,57 @@ import macros -proc newTreeExpr(stmtListExpr, exprNode, unquoteIdent: NimNode): NimNode {.compileTime.} = + +#template expectNimNode(arg: NimNode): NimNode = arg + +#macro expectNimNode(arg: typed): NimNode = +# error("expressions needs to be of type NimNode", arg) + +proc expectNimNode[T](arg: T): NimNode = arg + +proc newTreeWithLineinfo*(kind: NimNodeKind; lineInfo: LineInfo; children: varargs[NimNode]): NimNode {.compileTime.} = + ## like ``macros.newTree``, just that the first argument is a node to take lineinfo from. + # TODO lineinfo cannot be forwarded to new node. I am forced to drop it here. + result = newNimNode(kind, nil) + result.add(children) + +# TODO restrict unquote to nimnode expressions (error message) + +const forwardLineinfo = false + +proc newTreeExpr(stmtList, exprNode, unquoteIdent: NimNode): NimNode {.compileTime.} = # stmtList is a buffer to generate statements if exprNode.kind in nnkLiterals: result = newCall(bindSym"newLit", exprNode) elif exprNode.kind == nnkIdent: result = newCall(bindSym"ident", newLit(exprNode.strVal)) elif exprNode.kind in nnkCallKinds and exprNode.len == 2 and exprNode[0].eqIdent unquoteIdent: - result = exprNode[1] + result = newCall(bindSym"expectNimNode", exprNode[1]) + echo result.lispRepr elif exprNode.kind == nnkSym: error("for quoting the ast needs to be untyped", exprNode) elif exprNode.kind == nnkEmpty: # bug newTree(nnkEmpty) raises exception: result = newCall(bindSym"newEmptyNode") else: - result = genSym(nskLet) - stmtListExpr.add newLetStmt(result, newCall(bindSym"newNimNode", newLit(exprNode.kind))) #, exprNode ) + if forwardLineInfo: + result = newCall(bindSym"newTreeWithLineinfo", newLit(exprNode.kind), newLit(exprNode.lineinfoObj)) + else: + result = newCall(bindSym"newTree", newLit(exprNode.kind)) for child in exprNode: - stmtListExpr.add newCall(bindSym"add", result, newTreeExpr(stmtListExpr, child, unquoteIdent)) + result.add newTreeExpr(stmtList, child, unquoteIdent) -macro quoteAst(ast: untyped): untyped = +macro quoteAst*(ast: untyped): untyped = ## Substitute for ``quote do`` but with ``uq`` for unquoting instead of backticks. result = newNimNode(nnkStmtListExpr) result.add result.newTreeExpr(ast, ident"uq") -macro quoteAst(unquoteIdent, ast: untyped): untyped = +macro quoteAst*(unquoteIdent, ast: untyped): untyped = unquoteIdent.expectKind nnkIdent - result = newNimNode(nnkStmtListExpr) - result.add result.newTreeExpr(ast, unquoteIdent) + result = newStmtList() + result.add newTreeExpr(result, ast, unquoteIdent) + +proc foo(arg1: int, arg2: string): string = + "xxx" macro foobar(arg: untyped): untyped = # simple generation of source code: @@ -54,6 +78,12 @@ macro foobar(arg: untyped): untyped = result = quoteAst myUnquote: echo "abc ", myUnquote(x), " ", myUnquote(newLit("xyz")), " ", myUnquote(arg) + #result = quoteAst: + # echo uq(bindSym"foo")(123, "abc") + + result = newTree(NimNodeKind(115), newTree(NimNodeKind(26), ident("echo"), newTree( + NimNodeKind(27), expectNimNode(bindSym"foo"), newLit(123), newLit("abc")))) + echo result.treeRepr let myVal = "Hallo Welt!" @@ -70,7 +100,6 @@ macro m1(): untyped = let r2 = quoteAst: type T1 {.id(uq(x)).} = object - echo "from #10326:" echo r1[0][0].treeRepr echo r2[0][0].treeRepr @@ -82,4 +111,106 @@ macro lineinfoTest(): untyped = result = quoteAst: assert(false) -lineinfoTest() +#lineinfoTest() + +# example from #7375 + +macro fails(b: static[bool]): untyped = + echo b + result = newStmtList() + +macro works(b: static[int]): untyped = + echo b + result = newStmtList() + +macro foo(): untyped = + + var b = newLit(false) + + ## Fails + result = quote do: + fails(`b`) + + ## Works + # result = quote do: + # works(`b`) + +foo() + + +# example from #9745 (does not work yet) +# import macros + +# var +# typ {.compiletime.} = newLit("struct ABC") +# name {.compiletime.} = ident("ABC") + +# macro abc(): untyped = +# result = newNimNode(nnkStmtList) + +# let x = quoteAst: +# type +# uq(name) {.importc: uq(typ).} = object + +# echo result.repr + +# abc() + +# example from #7889 + +from streams import newStringStream, readData, writeData +import macros + +macro bindme*(): untyped = + quoteAst: + var tst = "sometext" + var ss = uq(bindSym"newStringStream")("anothertext") + uq(bindSym"writeData")(ss, tst[0].addr, 2) + discard uq(bindSym"readData")(ss, tst[0].addr, 2) # <= comment this out to make compilation successful + +# test.nim +# from binder import bindme +# bindme() + + +# example from #8220 + +macro fooA(): untyped = + result = quoteAst: + let bar = "Hello, World" + echo &"Let's interpolate {bar} in the string" + +foo() + + +# example from #7589 + +macro fooB(x: untyped): untyped = + result = quoteAst: + echo uq(bindSym"==")(3,4) # echo ["false"]' has no type (or is ambiguous) + echo result.treerepr + +# example from #7726 + +import macros + +macro fooC(): untyped = + let a = @[1, 2, 3, 4, 5] + result = quoteAst: + uq(newLit(a.len)) + +macro fooD(): untyped = + let a = @[1, 2, 3, 4, 5] + let len = a.len + result = quoteAst: + uq(newLit(len)) + +macro fooE(): untyped = + let a = @[1, 2, 3, 4, 5] + let len = a.len + result = quoteAst: + uq(newLit(a[2])) + +echo fooC() # Outputs 5 +echo fooD() # Outputs 5 +echo fooE() # Outputs 3 From 36e873c8c4d0eb39f650bbe87b73d3d159dc2298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= <arne.doering@gmx.net> Date: Wed, 23 Jan 2019 15:23:35 +0100 Subject: [PATCH 3/5] nnkCommentStmt --- lib/experimental/quote2.nim | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/experimental/quote2.nim b/lib/experimental/quote2.nim index af9b6ae0a1290..eca016d40f5a5 100644 --- a/lib/experimental/quote2.nim +++ b/lib/experimental/quote2.nim @@ -29,6 +29,8 @@ proc newTreeExpr(stmtList, exprNode, unquoteIdent: NimNode): NimNode {.compileTi echo result.lispRepr elif exprNode.kind == nnkSym: error("for quoting the ast needs to be untyped", exprNode) + elif exprNode.kind == nnkCommentStmt: + result = newCall(bindSym"newCommentStmtNode", newLit(exprNode.strVal)) elif exprNode.kind == nnkEmpty: # bug newTree(nnkEmpty) raises exception: result = newCall(bindSym"newEmptyNode") @@ -214,3 +216,23 @@ macro fooE(): untyped = echo fooC() # Outputs 5 echo fooD() # Outputs 5 echo fooE() # Outputs 3 + + +# example from #10430 + +import macros + +macro commentTest(arg: untyped): untyped = + let tmp = quoteAst: + ## comment 1 + echo "abc" + ## comment 2 + ## still comment 2 + + doAssert tmp.treeRepr == arg.treeRepr + +commentTest: + ## comment 1 + echo "abc" + ## comment 2 + ## still comment 2 From 62e4707651d0e60580413013cd2779746f682d79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= <arne.doering@gmx.net> Date: Thu, 24 Jan 2019 10:16:18 +0100 Subject: [PATCH 4/5] set lineinfoobj --- compiler/vm.nim | 32 ++++---------------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index d4659e77521c4..5bd7e21ed1b64 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1503,39 +1503,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = assert(n.kind == nkObjConstr) assert(n.len == 4) assert(n[1][0].sym.name.s == "filename") - let filename = n[1][1].strVal + var filename = n[1][1].strVal + if not isAbsolute(filename): + filename = parentDir(toFullPath(c.config, c.debug[pc])) / filename assert(n[2][0].sym.name.s == "line") let line = n[2][1].intVal assert(n[3][0].sym.name.s == "column") let column = n[3][1].intVal - - - proc semInstantiationInfo(c: PContext, n: PNode): PNode = - result = newNodeIT(nkTupleConstr, n.info, n.typ) - let idx = expectIntLit(c, n.sons[1]) - let useFullPaths = expectIntLit(c, n.sons[2]) - let info = getInfoContext(c.config, idx) - var filename = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString)) - filename.strVal = if useFullPaths != 0: toFullPath(c.config, info) else: toFilename(c.config, info) - var line = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt)) - line.intVal = toLinenumber(info) - var column = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt)) - column.intVal = toColumn(info) - result.add(filename) - result.add(line) - result.add(column) - - n. - proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile): FileIndex = - proc toAbsolute*(file: string; base: AbsoluteDir): AbsoluteFile = - - regs - decodeB(rkNode) - echo regs[rb].kind - debug(regs[rb].node) - regs[ra].node.info = regs[rb].node.info + regs[ra].node.info = newLineInfo(c.config, AbsoluteFile(filename), line, column) of opcNCopyLineInfo: - debug(regs[rb].node) regs[ra].node.info = regs[rb].node.info of opcEqIdent: decodeBC(rkInt) From f99927f7a22e88d836ff44f77970e93ded475252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= <arne.doering@gmx.net> Date: Thu, 24 Jan 2019 12:53:13 +0100 Subject: [PATCH 5/5] setting lineinfo works --- compiler/vm.nim | 7 +++++-- compiler/vmdef.nim | 2 +- compiler/vmgen.nim | 7 ++----- lib/core/macros.nim | 6 ------ lib/experimental/quote2.nim | 25 ++++++++++++++----------- 5 files changed, 22 insertions(+), 25 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 5bd7e21ed1b64..3951f76f674a1 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -19,6 +19,7 @@ import from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate +from os import isAbsolute, parentDir, `/` from modulegraphs import ModuleGraph, PPassContext @@ -1499,6 +1500,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.info = n.info regs[ra].node.typ = n.typ of opcNSetLineInfoObj: + decodeB(rkNode) let n = regs[rb].node assert(n.kind == nkObjConstr) assert(n.len == 4) @@ -1507,11 +1509,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if not isAbsolute(filename): filename = parentDir(toFullPath(c.config, c.debug[pc])) / filename assert(n[2][0].sym.name.s == "line") - let line = n[2][1].intVal + let line = n[2][1].intVal.int assert(n[3][0].sym.name.s == "column") - let column = n[3][1].intVal + let column = n[3][1].intVal.int regs[ra].node.info = newLineInfo(c.config, AbsoluteFile(filename), line, column) of opcNCopyLineInfo: + decodeB(rkNode) regs[ra].node.info = regs[rb].node.info of opcEqIdent: decodeBC(rkInt) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index a43f8dbba52a8..1a2e5c0d9ea15 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -107,7 +107,7 @@ type opcNError, opcNWarning, opcNHint, - opcNGetLineInfo, opcNSetLineInfo, + opcNGetLineInfo, opcNSetLineInfoObj, opcNCopyLineInfo, opcEqIdent, opcStrToIdent, opcGetImpl, diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 930dd69765d95..353df363b0336 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1231,14 +1231,11 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of "getFile": genUnaryABI(c, n, dest, opcNGetLineInfo, 0) of "getLine": genUnaryABI(c, n, dest, opcNGetLineInfo, 1) of "getColumn": genUnaryABI(c, n, dest, opcNGetLineInfo, 2) - of "setFile": genBinaryStmt(c, n, opcNSetLineInfo) - of "setLine": genBinaryStmt(c, n, opcNSetLineInfo) - of "setColumn": genBinaryStmt(c, n, opcNSetLineInfo) - of "lineInfoObj=": genBinaryStmt(c, n, opcNSetLineInfo) + of "lineInfoObj=": genBinaryStmt(c, n, opcNSetLineInfoObj) of "copyLineInfo": internalAssert c.config, n.len == 3 unused(c, n, dest) - genBinaryStmt(c, n, opcNSetLineInfo) + genBinaryStmt(c, n, opcNCopyLineInfo) else: internalAssert c.config, false of mNHint: diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 17cb4e87c525c..5300766b9e301 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -489,14 +489,8 @@ proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} = proc lineInfo*(arg: NimNode): string {.compileTime.} = $arg.lineInfoObj - proc `lineInfoObj=`*(n: NimNode; lineinfo: LineInfo) {.magic: "NLineInfo".} -proc setLine(arg: NimNode; line: int) {.magic: "NLineInfo".} -proc setColumn(arg: NimNode; column: int) {.magic: "NLineInfo".} -proc setFile(arg: NimNode; filename: string) {.magic: "NLineInfo".} - - proc internalParseExpr(s: string): NimNode {. magic: "ParseExprToAst", noSideEffect.} diff --git a/lib/experimental/quote2.nim b/lib/experimental/quote2.nim index eca016d40f5a5..7e91e40d3aaae 100644 --- a/lib/experimental/quote2.nim +++ b/lib/experimental/quote2.nim @@ -1,22 +1,21 @@ import macros - -#template expectNimNode(arg: NimNode): NimNode = arg - -#macro expectNimNode(arg: typed): NimNode = -# error("expressions needs to be of type NimNode", arg) +# template expectNimNode(arg: NimNode): NimNode = arg +# macro expectNimNode(arg: typed): NimNode = +# error("expressions needs to be of type NimNode", arg) proc expectNimNode[T](arg: T): NimNode = arg -proc newTreeWithLineinfo*(kind: NimNodeKind; lineInfo: LineInfo; children: varargs[NimNode]): NimNode {.compileTime.} = +proc newTreeWithLineinfo*(kind: NimNodeKind; lineinfo: LineInfo; children: varargs[NimNode]): NimNode {.compileTime.} = ## like ``macros.newTree``, just that the first argument is a node to take lineinfo from. # TODO lineinfo cannot be forwarded to new node. I am forced to drop it here. result = newNimNode(kind, nil) + result.lineinfoObj = lineinfo result.add(children) # TODO restrict unquote to nimnode expressions (error message) -const forwardLineinfo = false +const forwardLineinfo = true proc newTreeExpr(stmtList, exprNode, unquoteIdent: NimNode): NimNode {.compileTime.} = # stmtList is a buffer to generate statements @@ -26,7 +25,6 @@ proc newTreeExpr(stmtList, exprNode, unquoteIdent: NimNode): NimNode {.compileTi result = newCall(bindSym"ident", newLit(exprNode.strVal)) elif exprNode.kind in nnkCallKinds and exprNode.len == 2 and exprNode[0].eqIdent unquoteIdent: result = newCall(bindSym"expectNimNode", exprNode[1]) - echo result.lispRepr elif exprNode.kind == nnkSym: error("for quoting the ast needs to be untyped", exprNode) elif exprNode.kind == nnkCommentStmt: @@ -47,6 +45,9 @@ macro quoteAst*(ast: untyped): untyped = result = newNimNode(nnkStmtListExpr) result.add result.newTreeExpr(ast, ident"uq") + echo "quoteAst:" + echo result.repr + macro quoteAst*(unquoteIdent, ast: untyped): untyped = unquoteIdent.expectKind nnkIdent result = newStmtList() @@ -83,11 +84,13 @@ macro foobar(arg: untyped): untyped = #result = quoteAst: # echo uq(bindSym"foo")(123, "abc") - result = newTree(NimNodeKind(115), newTree(NimNodeKind(26), ident("echo"), newTree( - NimNodeKind(27), expectNimNode(bindSym"foo"), newLit(123), newLit("abc")))) - echo result.treeRepr + # result = quoteAst: + # var tmp = 1 + # for x in 0 ..< 100: + # tmp *= 3 + let myVal = "Hallo Welt!" foobar(myVal)