Awesome, thanks for the suggestions. I ended up independently implementing
your second solution, which I think is the more elegant way to do it. But
it's good to know about the first approach as well.
For anyone who is interested, here's the code I ended up with:
# general case, just return value
expand_names(value) = value
# if it's a symbol, check that it matches, and if so, convert it
function expand_names(sym::Symbol)
m = match(r"_(\d+)", string(sym))
if m !== nothing
return :(children[parseint($(m.captures[1]))])
end
return sym
end
# if it's an expression, recursively go through tree and
# transform all symbols that match '_i'
function expand_names(expr::Expr)
new_args = [expand_names(arg) for arg in expr.args]
return Expr(expr.head, new_args...)
end
Then, you can can run expand_names on any expression/symbol passed via the
braces.
On Friday, March 6, 2015 at 10:58:43 PM UTC-5, Jameson wrote:
>
> oh, that’s reasonably easy then:
>
> macro testfn(expr)
> children = esc(:children)
> syms = [:($(symbol(string("_", i))) = $children[$i]) for i = 1:10]
> return Expr(:let, Expr(:block, esc(expr)), :(children=children), syms..)
> end
>
> which will expand to:
>
> let children=children, _1 = children[1], _2 = children[2], ...
> $expr
> end
>
> (I added the first children=children line, so that the user could
> theoretically assign to a variable named children, without leaking it to
> the enclosing environment. it's not strictly necessary).
>
> -----
>
> alternatively, you could recurse through their expr code and replace all
> variables of the form `_(\d+)` with :(children[\1])
>
> -----
>
>
> On Fri, Mar 6, 2015 at 9:30 PM Abe Schneider [email protected]
> <http://mailto:[email protected]> wrote:
>
> Hmmm, good to know. Thank you.
>>
>> The rationale for doing so is to provide a shortcut for the elements of
>> a variable `children`. Specifically, for a grammar, I might have a rule
>> like:
>>
>> ```
>> @grammar foo begin
>> number = r"[0-9]+" { parseint(children[1]) }
>> end
>> ```
>>
>> What I would like to have instead, to make it more succint is:
>>
>> ```
>> @grammar foo begin
>> number = r"[0-9]+" { parseint(_1) }
>> end
>> ```
>>
>> Originally, just to get things working, I used an `eval`, which while
>> worked, also made the assignment global, which was less than ideal.
>>
>>
>> I'd be curious if anyone has a suggestion on other methods to accomplish
>> this, or if this is outside the scope of what's possible to do with Julia.
>>
>>
>>
>> On Friday, March 6, 2015 at 5:10:07 PM UTC-5, Jameson wrote:
>>
>>> you can't do what you are proposing, by design. a macro cannot do
>>> anything that you cannot express directly, it simply allows you to express
>>> it more succinctly by templating the redundant parts.
>>>
>>> if you want a "set" or "numbered list", use a set or number list.
>>> variables are bad at that sort of task. whereas an Array is very good at it.
>>>
>>> On Fri, Mar 6, 2015 at 8:05 AM Abe Schneider <[email protected]>
>>> wrote:
>>>
>>>> I'm trying to create a set of variables (_1, _2, ...) from items within
>>>> a list in a macro. I have a (much) condensed version of the code:
>>>>
>>>> macro testfn()
>>>> quote
>>>> i = 1
>>>> value = [1, 2, 3]
>>>> $(Expr(:(=), Expr(:symbol, Expr(:string, "_", :i)), :value))
>>>> println(_1)
>>>> end
>>>> end
>>>>
>>>>
>>>> which gives me:
>>>>
>>>> ERROR: syntax: invalid assignment location
>>>>
>>>>
>>>> Any ideas on what might be wrong or the proper way to do this? I would
>>>> like to keep this in the quotes, as in the actual version there's a lot
>>>> more code surrounding the assignment.
>>>>
>>>> I can get things to work with an eval, but I rather avoid the eval, and
>>>> it appears to create a non-local variable.
>>>>
>>>>
>>>> Thanks!
>>>>
>>>
>