template createVar(x, v: untyped): untyped =
var x = v
createVar("name", 100)
I try above code, but got this error message.
Error: identifier expected, but found '"name"'
I think you need to use a macro to convert the string to a identifier. This is something I hacked together that's pretty clean:
import macros
macro createVar(varName: string, value: untyped): untyped =
let newVar = ident(varName.strVal) # create the new identifier from varName
result = quote do:
var `newVar` = `value`
createVar("name", 100)
createVar("cool", "macro")
echo name, cool
May I ask what you plan to use this functionality for? :)
template createVar(name, value) =
var name {.inject.} = value
createVar(variable, 100)
:)
I'm trying to filter data with implicit lambda parameter, but still not lucky
import macros
type
Person = object
name: string
age: int
macro createVar(varName: string, value: untyped): untyped =
let newVar = ident(varName.strVal) # create the new identifier from varName
result = quote do:
let `newVar` {.inject.} = `value`
template createAllVar(it: untyped): untyped =
for k, v in fieldPairs it :
createVar(k, v)
template filter(s, pred: untyped): untyped =
var result = newSeq[typeof(s[0])]()
for it {.inject.} in items(s):
createAllVar it
if pred: result.add(it)
result
let people = @[ Person(name: "wk", age: 20), Person(name: "jw", age: 22)]
let rs = filter(people, age > 20 and name == "jw")
echo rs
Error
/Users/wk/Source/nim-syntax/concept/ImplicitLambaParameter.nim(10, 12) Hint: 'name' is declared but not used [XDeclaredButNotUsed]
/Users/wk/Source/nim-syntax/concept/ImplicitLambaParameter.nim(10, 12) Hint: 'age' is declared but not used [XDeclaredButNotUsed]
/Users/wk/Source/nim-syntax/concept/ImplicitLambaParameter.nim(25, 25) Error: undeclared identifier: 'age'
I see, the reason you get the undeclared identifier error is because of scopes. When you define a var in a for loop it can't be accessed from outside. This is a simplified example:
for i in 0 .. 10:
var x = 1
echo x # Error: undeclared identifier: 'x'
Second reason is that templates have hygiene which means variables defined in a template can't be accessed from the outside. There are two solutions to this, injecting the variables or marking the template with the {.dirty.}
pragma which removes all hygiene. But the scope thing is the one that's limiting you in this case.
I can't come up with anything from the top of my head right now but I'll try to figure something out :/
I think I've got a solution: we write a macro that iterates over the fields of it and creates all the assignments at compiletime. Will be gone for an hour or two though.
Just one question, will it always be an object?
Here you go, I had to use even more macro magic but it works (for objects at least). I tried to write some descriptive comments to describe what happens but the summary is that we basically unrolls the loop in createAllVar at compiletime so we don't have the issues with scopes:
import macros
type
Person = object
name: string
age: int
proc nameToVarAssign(it: NimNode, fieldName: string): NimNode =
let varIdent = ident(fieldName) # create the identifier for the field and the variable
let dotExpr = newDotExpr(it, varIdent) # create it.field expression
result = quote do: # create the assignment expression
var `varIdent` = `dotExpr` # var field = it.field
macro createAllVar(it: typed): untyped =
result = newStmtList()
let fieldList = it.getType[2] # this is the list of field names
for fieldName in fieldList: # iterate over all fields and generate the variable assignments
result.add(nameToVarAssign(it, fieldName.strVal))
template filter(s, pred: untyped): untyped =
var result = newSeq[typeof(s[0])]()
for it {.inject.} in items(s):
createAllVar it
if pred: result.add(it)
result
let people = @[ Person(name: "wk", age: 20), Person(name: "jw", age: 22)]
let rs = filter(people, age > 20 and name == "jw")
echo rs