-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New quote ast 2 #11823
New quote ast 2 #11823
Conversation
Why is the use of I'm worried about the compilation speed impact of the approach taken here. If this makes it to macros.nim, it will:
It also introduces an alternative code path to do the same thing, which will be a source of inconsistencies and bugs when the rules of the language change (for the record, this is part of the reason why |
I avoided getAst, because I think the usage of a local template and getAst is what causes most of the problems in This is also a continuation of my PR with the
I am not doing heavy lifting in the VM, it is much more of a light lifting. I translate a block of code into a block of code that generates this code. This is what some people manually write because they don't want to deal with But the vm runs out of registers quickly and I didn't figure out exactly why, yet. This is more concerning to me right now. |
fa09c62
to
1e37ab3
Compare
@zah I wanted to avoid getAst because there were several issues with getAst. One of them is that it erases comment statements. I found a workaround for that one. And in combination with dirty templates getAst seems to be becoma feasible. To fix the comment statement problem of getAst the documentation generator should be rewritten, and I don't know if that is worth the effort right now. I just hope that template instantiation won't get more hacks. By now I removed most of the features that at one point I implemented and left only what I think is a bare bone |
8b9813a
to
1fc3e0a
Compare
Why did you decide to avoid Timothee's approach where there is an extra argument controlling the dirtiness of the template? It seems to me that this can still be useful from time to time and it will make life easier for people switching to the new API. Otherwise, can you please add a test for a nested use of
template genCode*(body: untyped) =
iterator generator: NimNode = body
macro payload: untyped =
result = newStmtList()
for node in generator():
result.add node
payload()
proc main =
genCode:
for i in 1..10:
yield newCall("echo", newLit(i)) Such blocks have a relatively wide-spread use in the Boo language. |
@zah, at all costs the temporary template may not bind to local variables automatically if not explicitly requested to do so. This is one of the biggest problems that For the generator expression, yes nice idea. I had a similar idea for when I still wanted to implement quoting with the unquote operator. But the whole idea of unquote as an expression did not work out at all. Nim does not allow arbitrary expressions everywhere. Yes you could use it to generate statement lists like in your example, but you would not be able to use it to fill up fiends in type definitions. And now since all genrating expressions have been forced to be written before the let body = newStmtList()
for i in 1..10:
let i = newLit(i)
body.addQuoteAst(i):
echo i
result = quoteast(body):
proc main() =
body I am thinking of a shortcut to inject newLit more easily though: let body = newStmtList()
for i in 1..10:
# prefix @ to inject newLit for i
body.addQuoteAst(@i):
echo i I am also thinking about other prefixes, for example |
Well, I'm looking forward to try the new quote replacements in our codebase. I'll try to provide some feedback regarding how painful it is to switch from non-dirty to dirty by default. |
let's compare this PR with #11722 on a simple example: #main.nim
import ./t0672c
echo foobar(1+2, true)
#t0672c.nim
import macros
import tables
type Kar = ref object
proc `$`(a: Kar): string = "custom"
when defined case_quoteAst: # using this PR #11823
macro foobar*(arg: untyped, b: static bool): untyped =
let b = newLit b
let newTable = bindSym"newTable"
let `[]=` = bindSym("[]=")
let mySet = bindSym"[]="
let myGet = bindSym"[]"
let pairs = bindSym"pairs"
let Kar = bindSym"Kar"
let `$` = bindSym"$"
result = quoteAst(arg, b, newTable, `[]=`, mySet, myGet, pairs, Kar, `$`):
let t = newTable[string, Kar]()
# t["foo1"] = Kar() # Error: type mismatch => doesn't work with `[]=`, ditto with `[]`
mySet(t, "foo1", Kar())
for k,v in pairs(t): echo (k,$v)
(arg, b, myGet(t,"foo1"))
when defined case_genAst2: # using PR #11722
macro foobar*(arg: untyped, b: static bool): untyped =
result = genAst(arg, b):
let t = newTable[string, Kar]()
t["foo1"] = Kar()
for k,v in pairs(t): echo (k,$v)
(arg, b, t["foo1"])
As for #10430 (Comments are removed in quote do expressions), this PR doesn't have that issue, unlike |
@timotheecour but that is not a fair comparison, |
Here the focus is not on how little is to do when migrating, it is about how straight forward is it. Is everything that needs to be changed caught in an error message that the programmer can understand? Is it obvious what to do?
What do you mean? What is caller scope hijacking? Please explain what you mean.
Yea this is a bummer, good that you found it, I will fix it. The problem is
Can you provide an example for this? I don't see that problem.
Yea, that is an issue that I understand. The problem that I have is, In a macro I can only declare a tepmlate in the scope where a template is expanded. In this scope all locals variables are in scope. I don't know how to shield my template from these local symbols other than making the template dirty.
Yea I found a workaround for it. |
#11722 has examples showing the CT error msgs the user will encounter when he misuses
you have to rename
import macros
macro fun*(): untyped =
proc `$`(a: ref int): string = "foo"
# let `$` = bindSym"$" # Error: redefinition of '$';
let toString = bindSym"$" # have to rename
result = quoteAst(toString):
let b = new int
discard toString(b) so that makes it awkward for local operators, eg
# main.nim
import randomimport, mylib
fun()
# randomimport.nim
proc `$`*(a: ref int): string = "foo1" # hijacks `$`
# mylib.nim
import macros
proc `$`(a: ref int): string = "foo2"
macro fun*(): untyped =
result = quoteAst():
let b = new int
echo $b
let `$` = bindSym"$"
result = quoteAst(): but in complex examples, with lots of
it uses non-dirty templates so |
In that case my solution will show the error message "undeclared identifier
Local procedures are for when the procedure accesses the environment (closure). This is exactly what bound symbols should never do. When you put
Your example won't compile unless |
This pull request has been automatically marked as stale because it has not had recent activity. If you think it is still a valid PR, please rebase it on the latest devel; otherwise it will be closed. Thank you for your contributions. |
This pull request superseeds my old pull request from here, therefore it is also an implementation of my proposal nim-lang/RFCs#122.
My old pull request uses an
uq(expr)
syntax to inject an expression into the quoted AST. I replaced that syntax with explicit symbol injection. This syntax was proposed from #11722, this circomvents the problem that theuq(expr)
syntax isn't allowed everywhere (explained in the "Limitations" section of the mentioned RFC).As a side effect, this PR also introduces a proc to manually set the lineinfo for a NimNode.
The biggest fix compared to
quote do
is that identifiers are not automatically captured. This means clean error messages when you forget to capture your variables. Clean error messages when they have the incorrect type, and no automatically doing the wrong thing for you. This also means that things are a little bit more verbose, the usage ofnewLit
andbindSym
is enforced, it is not automatically done for you. I think in the future it is sane to add a shortcut syntax for the most common patterns. For example*myVal
formyVal = newLit(myVal)
and@myProc
formyProc = bindSym"myProc"
. But that is just syntactic sugar and not part of this initial PR.