Then, the statement " if bool_expr is true" seems a bit odd as it suggests to me that you’re trying to evaluate that expression at macro-expansion time. But at that time it will probably not be defined.
Thanks - I’m happy to evaluate that anywhere, so the macro can return a whole if statement. But yes, in one of my attempts I was getting the error that I was trying to evaluate a non-bool (an expr).
What I’m trying to do is to overcome the default behaviour where the if statement returns the last executed expression - and instead wrap all the expressions inside the if block into an array and return the array.
I didn’t put the begin block as I’m open to any approach which yields the result. For ex passing a function with a do block is also fine.
Aright, do a dump of your input expression and one of your desired output expression:
julia> dump(:(@retall if boolex
a = 6
b = 7
c = a+b
end))
Expr
head: Symbol macrocall
args: Array{Any}((3,))
1: Symbol @all
2: LineNumberNode
line: Int64 1
file: Symbol REPL[6]
3: Expr
...
and
julia> dump(quote
a = 6
b = 7
c = a+b
[a,b,c]
end)
Expr
head: Symbol block
args: Array{Any}((8,))
1: LineNumberNode
line: Int64 2
file: Symbol REPL[8]
2: Expr
head: Symbol =
args: Array{Any}((2,))
...
Then figure out how to transform one nested datatype into the other. (Remember, meta-programming is nothing but nested data-structure gymnastics).
Also, you can enlist the help of MacroTools.jl. Although, there is probably nothing wrong with getting your hands dirty (i.e. not use MacroTools) until you’re a bit more experienced.
It’s for Genie’s templating engine which embeds Julia code into HTML. The template code looks like this:
<% if bool_expr %>
<h1>Hello!</h1>
<h2>Welcome to foo</h2>
<p>Foo is the best</p>
<% end %>
This is converted by the templating engine into:
if bool_expr
h1("Hello!")
h2("Welcome to foo")
p("Foo is the best")
end
This has the effect that only the last expression, p("Foo is the best") aka <p>Foo is the best</p> gets rendered.
Yes, now the only solution is to write:
<% if bool_expr
[ %>
<h1>Hello!</h1>
<h2>Welcome to foo</h2>
<p>Foo is the best</p>
<% ]end %>
However, this catches people off guard and I need to explain why this happens, especially as this is the only place where this brackets syntax is needed. It would be simpler to put in the docs that they need to use @if similar to how they need to use @foreach.
That’s a great point. I would’ve preferred to rely on the compiler instead of me searching, matching, and massaging strings, which is more error prone. But it does have the advantage that it doesn’t introduce new DSL constructs. Thanks!
Edit 1
To be honest, my starting assumption was that it will be super easy to capture the body of the @if block, slap some square brackets around it, and return it. It wasn’t…
It isn’t that hard - its what my example does above. The body of the if statement is just the second argument of the if Expr (the first argument is the condition and the head is the “if”). My example just swaps out the body block with an array of its own arguments (which are the specific lines in the body block) so instead of just executing the body, it returns a list of what it was going to execute
If you try getting rid of the if part, things go off track, ie:
@if 1==1
"hi"
3
end
which is what I would like the users to work with.
Still, your proposed solution would be great combined with @yuyichao’s suggestion of simply modifying the template code before parsing, ie I could replace if cond with @test if cond which is less error prone than inserting the brackets into the template code.
the workflow is: a) template (HTML with embeded Julia “snippets”) => b) parsed template (pure Julia code) => c) rendered template (the pure HTML output of executing the pure Julia code at #b)
since the template engine generates the Julia code at #b), it can generate the if block wrapped in square brackets - is that right?
If I understand correctly per above, I would strongly prefer to avoid that, as parsing the block of code and isolating the body of the if expression is error prone. Now I use a pretty robust mechanism where I parse the DOM of the template (#a) and only work at element level. If I start parsing multiple elements as multi line strings I run into issues like nested blocks of Julia code within the if code and I have to keep track of the correct closing end - and this gets messy fast.
julia> @if true begin
h1("Hello!")
h2("Welcome to foo")
p("Foo is the best")
end
3-element Array{String,1}:
"<h1>Hello!</h1>"
"<h2>Welcome to foo</h2>"
"<p>Foo is the best</p>"
Of course, having the begin might offend your sensibilities, but I kinda like it.
Edit: As @yuyichao pointed out, the macro can be written in more julian form as
@eval macro $(Symbol("if"))(cond, body)
out_body = [Expr(:block, arg) for arg in body.args if !(arg isa LineNumberNode)]
quote
if $cond
[$(out_body...)]
end
end |> esc
end
Instead. (Again, as I said in another thread, you almost never need to completely throw out expression literal and interpolation just because you can’t construct one particular level)