Hello,
I use the following thing in my few repos: https://nim-lang.org/docs/manual_experimental.html#term-rewriting-macros which I suppose very useful to make a some optimizations,
but I met strange behavior here: https://github.com/inv2004/soa
> nimble test
...
no opt
opt a
no opt
I expected no opt; opt a; opt b
If I swap last two lines in test then I have no opt; opt b; no opt - looks like it works for the first line only
Cannot understand why it works for the first line only - is it bug or what?
Nim Compiler Version 1.6.8 [Linux: amd64]
Can you provide a better MWE ? I had to lookup the objects M and S in your source code, I don't know what is check and could not run your test in https://play.nim-lang.org/ With your post, I don't know what you want to achieve, Here is the full code found with your repo:
import macros
import strformat
import strutils
import sequtils
type
M* = object
a*: int
b*: string
S = object
a: seq[int]
b: seq[string]
proc getFieldsRec(t: NimNode): seq[(string, string)] =
let obj = getImpl(t)[2]
if obj[1].kind != nnkEmpty:
result.add getFieldsRec(obj[1][0])
let typeFields = obj[2]
for f in typeFields.children:
assert f.kind == nnkIdentDefs
var fieldNames = newSeq[string]()
for ff in f.children:
case ff.kind
of nnkIdent: fieldNames.add ff.strVal
of nnkSym:
for x in fieldNames:
result.add (x, ff.strVal)
of nnkEmpty: discard
else: raise newException(Exception, "unexpected construction: " & $ff.treeRepr)
proc parseExprs(code: seq[string]): NimNode =
# echo code.join "\n"
parseExpr(code.join("\n"))
macro defineST*(T: typedesc, st: untyped) =
result = newStmtList()
let fields = getFieldsRec(getType(T)[1])
var code = newSeq[string]()
# type
code.add fmt"type {st} = object"
for (f, t) in fields:
code.add fmt" {f}: seq[{t}]"
result.add parseExprs code
#len
code.setLen 0
code.add fmt"proc len(st: {st}): int ="
code.add fmt" st.{fields[0][0]}.len"
result.add parseExprs code
# insert
code.setLen 0
code.add fmt"proc add(st: var {st}, v: {T}) ="
for (f, t) in fields:
code.add fmt" st.{f}.add v.{f}"
result.add parseExprs code
# `[]`
code.setLen 0
code.add fmt"proc `[]`(st: {st}, i: int): M ="
code.add " echo \"no opt\""
code.add " M("
code.add fields.mapIt(fmt" {it[0]}: st.{it[0]}[i]").join(",\n")
code.add " )"
result.add parseExprs code
# optimizations
for (f, t) in fields:
code.setLen 0
code.add fmt"""template opt{f}{{`[]`(st,i).{f}}}(st: S, i: int): {t} ="""
code.add fmt""" echo "opt ", "{f}" """
code.add fmt""" st.{f}[i]"""
result.add parseExprs code
echo repr result
template opta{
`[]`(st, i).a}(st: S; i: int): int =
echo "opt ", "a"
st.a[i]
template optb{
`[]`(st, i).b}(st: S; i: int): string =
echo "opt ", "b"
st.b[i]
var st = S()
st.add M(a: 1, b: "aaa")
st.add M(a: 2, b: "bbb")
check st.len == 2
assert st[1] == M(a:2, b:"bbb")
assert st[0].a == 1
assert st[1].b == "bbb"
I could not reproduce your output, I got a type mismatch.@dlesnoff Minimal example is here: https://play.nim-lang.org/#ix=4cKQ and it works
So, I cannot understand what is wrong not with my minimal example, but with my template generation
var str = @["proc f() ="]
for f in ff:
str.add fmt" {f} += 1"
work with ast is also much more complex here, I understand it's advantages, but just decided to try the way for the first time - and wonder that it was 10x times faster
This works fine:
when true:
type M = object
a: int
b: string
type S = object
a: seq[int]
b: seq[string]
proc `[]`(st: S; i: int): M =
M(a: st.a[i], b: st.b[i])
template opta{`[]`(st, i).a}(st: S; i: int): int =
echo "opt [", i, "].a"
st.a[i]
template optb{`[]`(st, i).b}(st: S; i: int): string =
echo "opt [", i, "].b"
st.b[i]
var st = S(a: @[1, 2], b: @["aaa", "bbb"])
assert st.a.len == 2
assert st[1] == M(a:2, b:"bbb")
assert st[0].a == 1
assert st[1].b == "bbb"
It doesn't work with unittest. Why I don't know either, life is too short to dig through unittest. ;-)