let seems to work the same in injected and provided code bodies. const however only works properly in injected bodies, not with provided code bodies.
Using a development version of nim gotten from git today, downloaded/built right before running the below test.
Nim Compiler Version 0.11.3 (2015-07-28) [FreeBSD: amd64]
Copyright (c) 2006-2015 by Andreas Rumpf
git hash: 8913e82f4505cb542c06545696ef687a52cd080c
active boot switches: -d:release
import macros, strutils
macro works1 (name: expr, body: stmt) : stmt {.immediate.} =
var newbody = parseStmt """const injected = "$1" """ % $name
newbody.add body
newbody.add parseStmt """echo "injected: ", injected"""
newbody.add parseStmt """echo (if injected == "$1": "good" else: "bad") """ % $name
newbody.add parseStmt """echo "expected: ", "$1\n" """ % $name
result = newProc (name, body=newbody)
macro uses3 (name1: expr, name2: expr, how: expr, body: stmt) : stmt {.immediate.} =
result = parseStmt ""
var newbody1 = parseStmt """$2 injected = "$1" """ % [$name1, $how]
newbody1.add body
newbody1.add parseStmt """echo "injected: ", injected"""
newbody1.add parseStmt """echo (if injected == "$1": "good" else: "bad") """ % $name1
newbody1.add parseStmt """echo "expected: ", "$1\n" """ % $name1
result.add newProc (name1, body=newbody1)
var newbody2 = parseStmt """$2 injected = "$1" """ % [$name2, $how]
newbody2.add body
newbody2.add parseStmt """echo "injected: ", injected"""
newbody2.add parseStmt """echo (if injected == "$1": "good" else: "bad") """ % $name2
newbody2.add parseStmt """echo "expected: ", "$1\n" """ % $name2
result.add newProc (name2, body=newbody2)
works1 abc0:
echo "got: ", injected
uses3 abc1, def1, "let":
echo "got: ", injected
uses3 ghi1, jkl1, "const":
echo "got: ", injected
abc0 ()
abc1 ()
def1 ()
ghi1 ()
jkl1 ()
results in this output:
got: abc0
injected: abc0
good
expected: abc0
got: abc1
injected: abc1
good
expected: abc1
got: def1
injected: def1
good
expected: def1
got: ghi1
injected: ghi1
good
expected: ghi1
got: ghi1
injected: jkl1
good
expected: jkl1
All appears well, except for the last got : message. It should be got: jkl1 rather than got: ghi1. The injected code body got the right value, the provided code body did not.
The uses3 macro invocation that works uses let injected =
The uses3 macro invocation that fails uses const injected =
Alright, I tried to simplify it, but I can't seem to be able to pass expr and stmt parameters to compile time functions (and return stmt parameters from compile time functions) in order to further factor the code, but that is a different issue.
Anyway, I'm trying to create multiple parameterized procedures (parameterization left out of these examples) that use a provided code body, for each macro injected procedure.
I'm providing a code body to the macro that is injected into the middle of the code body produced by the macro.
As it turns out, this may not really be a const vs let issue. This is an issue of symbol references appearing in the provided code body. It does not matter if const or let is used, when I produce more than one procedure from the provided code body, the ones after the first generated procedure have the some values that were meant for the first generated procedure.
This is only incorrect for the provided code body, not any injected code.
import macros, strutils
macro uses3 (name1: expr, name2: expr, how: expr, body: stmt) : stmt {.immediate.} =
result = parseStmt ""
var newbody1 = parseStmt """$2 injected = "$1" """ % [$name1, $how]
newbody1.add parseStmt """let injected2 = "$1" """ % $name1
newbody1.add parseStmt """const how = "$1" """ % $how
newbody1.add body
newbody1.add parseStmt """echo "injected: ", injected, "\n" """
result.add newProc (name1, body=newbody1)
var newbody2 = parseStmt """$2 injected = "$1" """ % [$name2, $how]
newbody2.add parseStmt """let injected2 = "$1" """ % $name1
newbody2.add parseStmt """const how = "$1" """ % $how
newbody2.add body
newbody2.add parseStmt """echo "what was injected: ", injected, "\n" """
result.add newProc (name2, body=newbody2)
uses3 abc1, def1, "let":
echo "in provided code body using '", how, "' got ", injected
echo "expected???: ", injected2
uses3 ghi1, jkl1, "const":
echo "in provided code body using '", how, "' got ", injected
echo "expected???: ", injected2
abc1 ()
def1 ()
ghi1 ()
jkl1 ()
This is the resulting output from the compiler:
$ nim c constvslet2.nim
...
Hint: used config file '/etc/nim.cfg' [Conf]
Hint: system [Processing]
Hint: constvslet2 [Processing]
Hint: macros [Processing]
Hint: strutils [Processing]
Hint: parseutils [Processing]
lib/nim/core/macros.nim(300, 7) Hint: 'how' is declared but not used [XDeclaredButNotUsed]
lib/nim/core/macros.nim(300, 5) Hint: 'injected2' is declared but not used [XDeclaredButNotUsed]
lib/nim/core/macros.nim(300, 7) Hint: 'how' is declared but not used [XDeclaredButNotUsed]
lib/nim/core/macros.nim(300, 5) Hint: 'injected2' is declared but not used [XDeclaredButNotUsed]
[Linking]
Hint: operation successful (17286 lines compiled; 0.159 sec total; 17.179MB; Debug Build) [SuccessX]
First off, 'how' and 'injected2' are used.
Here is the output of the above program:
in provided code body using 'let' got abc1
expected???: abc1
injected: abc1
in provided code body using 'let' got def1
expected???: abc1
what was injected: def1
in provided code body using 'const' got ghi1
expected???: ghi1
injected: ghi1
in provided code body using 'const' got ghi1
expected???: ghi1
what was injected: jkl1
That is not the output I expected. What I expected was:
in provided code body using 'let' got abc1
expected???: abc1
injected: abc1
in provided code body using 'let' got def1
expected???: def1
what was injected: def1
in provided code body using 'const' got ghi1
expected???: ghi1
injected: ghi1
in provided code body using 'const' got jkl1
expected???: jkl1
what was injected: jkl1
Difference between what I expected and what I got:
--- got.txt 2015-07-30 10:20:04.326375005 -0500
+++ expected.txt 2015-07-30 10:19:22.883779033 -0500
@@ -3,13 +3,13 @@
injected: abc1
in provided code body using 'let' got def1
-expected???: abc1
+expected???: def1
what was injected: def1
in provided code body using 'const' got ghi1
expected???: ghi1
injected: ghi1
-in provided code body using 'const' got ghi1
-expected???: ghi1
+in provided code body using 'const' got jkl1
+expected???: jkl1
what was injected: jkl1
I had a typo in my second example which skewed the results, but that did not completely correct the problem.
However, making a new body using a copyNimTree of the first body gives me the results I was expecting.
Thanks.
Using copyNimTree allowed me to solve the other issue I was having. stmt can't be passed to and be returned from a {.compileTime.} proc, but NimNode can.
I was able to factor the second example provided, so now I can clean up existing code I have.
import macros, strutils
proc commonUses3 (body: NimNode, name:string, how:string) : NimNode {.compileTime.} =
result = parseStmt """$2 injected = "$1" """ % [name, how]
result.add parseStmt """let injected2 = "$1" """ % name
result.add parseStmt """const how = "$1" """ % how
result.add body.copyNimTree
result.add parseStmt """echo "injected: ", injected, "\n" """
macro uses3 (name1: expr, name2: expr, how: expr, body: stmt) : stmt {.immediate.} =
result = newStmtList ()
result.add newProc (name1, body=commonUses3 (body, $name1, $how))
result.add newProc (name2, body=commonUses3 (body, $name2, $how))
uses3 abc1, def1, "let":
echo "in provided code body using '", how, "' got ", injected
echo "expected???: ", injected2
uses3 ghi1, jkl1, "const":
echo "in provided code body using '", how, "' got ", injected
echo "expected???: ", injected2
abc1 ()
def1 ()
ghi1 ()
jkl1 ()
That gives me the output I expect:
in provided code body using 'let' got abc1
expected???: abc1
injected: abc1
in provided code body using 'let' got def1
expected???: def1
injected: def1
in provided code body using 'const' got ghi1
expected???: ghi1
injected: ghi1
in provided code body using 'const' got jkl1
expected???: jkl1
injected: jkl1