# DSL help with nested macros

**URL:** <https://discourse.julialang.org/t/dsl-help-with-nested-macros/28267>\
**Category:** General Usage\
**Tags:** metaprogramming\
**Created:** [September 1, 2019, 5:02pm UTC](https://discourse.julialang.org/t/dsl-help-with-nested-macros/28267 "2019-09-01T17:02:29Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![tk3369](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tk3369/32/2824_2.png) [@tk3369](https://discourse.julialang.org/u/tk3369)\
**Post date:** [September 1, 2019, 5:02pm UTC](https://discourse.julialang.org/t/dsl-help-with-nested-macros/28267/1 "2019-09-01T17:02:29Z")

</div>

Hi all. I’m learning how to implement DSL using macros and some help would be appreciated. In particular, I would like to create a DSL for [L-System](https://en.wikipedia.org/wiki/L-system).

The purpose of DSL is clean syntax. So here’s what I’m thinking in regards to the algae example:

```julia
@lsys begin
    @start A
    @rule A = AB
    @rule B = A
end

```

and I would like to transform it into the following Julia code:

```julia
model = LModel("A")
add_rule(model, "A", split("AB", ""))
add_rule(model, "B", split("A", ""))

```

I can create the `@start` and `@rule` macros but I’m not sure how to pass the `model` variable around. The `@start` macro generates a random variable name for `model` and likewise for `@rule` macro.

See [LSystem.jl](https://github.com/tk3369/LSystem.jl) repo for the current implementation. I am open to suggestions including tweaking the DSL itself to make it easier. Thanks

---

<div class="post-metadata">

**Author:** ![dfdx](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dfdx/32/120_2.png) [@dfdx](https://discourse.julialang.org/u/dfdx)\
**Post date:** [September 1, 2019, 9:31pm UTC](https://discourse.julialang.org/t/dsl-help-with-nested-macros/28267/2 "2019-09-01T21:31:07Z")

</div>

Do I understand correctly, that the main questions are:

1. How to capture the name of the model variable created by `var = LModel(name)` expression?
2. How to pass this name to `add_rule()` calls?

If so, here’s one simple way to implement `@lsys` which does the trick:

```julia
# let's say, @start and `rule` generated the following expression which is passed to @lsys
ex = quote 
       # say, @start generated name `model_123`
       model_123 = LModel("A")
       # initially using dummy name __model_var__
       add_rule( __model_var__ , "A", split("AB", "")) 
       add_rule( __model_var__ , "B", split("A", ""))
end

using Espresso

# Espresso.findex() traverses AST and finds all subexpressions 
# matching the pattern "_var = LModel(_)"
# (any symbol starting with underscore is considered a placeholder)
model_subexs = findex(:(_var = LModel(_)), ex)
# ==> 1-element Array{Any,1}:
# ==> :(model_123 = LModel("A"))

# model variable is in 
model_var = model_subexs[1].args[1]
# ==> :model_123

# now we can substitute all occurrences of ` __model_var__ ` with actual model var name
subs(ex, Dict(: __model_var__ => model_var))
# ==> quote
# ==> model_123 = LModel("A")
# ==> add_rule(model_123, "A", split("AB", ""))
# ==> add_rule(model_123, "B", split("A", ""))
# ==> end

```

---

<div class="post-metadata">

**Author:** ![tk3369](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tk3369/32/2824_2.png) [@tk3369](https://discourse.julialang.org/u/tk3369)\
**Post date:** [September 2, 2019, 4:11am UTC](https://discourse.julialang.org/t/dsl-help-with-nested-macros/28267/3 "2019-09-02T04:11:08Z")

</div>

That’s a great idea! Thanks!

By the way, since I’m already using MacroTools, I took your idea and implemented using `@capture` and `postwalk`.

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [September 2, 2019, 10:51am UTC](https://discourse.julialang.org/t/dsl-help-with-nested-macros/28267/4 "2019-09-02T10:51:01Z")

</div>

Good to hear there will be an L-systems package, what’s your use case?

---

<div class="post-metadata">

**Author:** ![tk3369](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tk3369/32/2824_2.png) [@tk3369](https://discourse.julialang.org/u/tk3369)\
**Post date:** [September 2, 2019, 6:52pm UTC](https://discourse.julialang.org/t/dsl-help-with-nested-macros/28267/5 "2019-09-02T18:52:40Z")

</div>

Not a real use case but I’m planning to use it as an example in my upcoming book _Hands On Design Patterns for Julia_ 🙂

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [September 2, 2019, 7:26pm UTC](https://discourse.julialang.org/t/dsl-help-with-nested-macros/28267/6 "2019-09-02T19:26:26Z")

</div>

Another approach would be to mimic how it’s done in [`@sync`](https://github.com/JuliaLang/julia/blob/9a8b2fd72b675bb8a5bf0322943ee9451787b86a/base/task.jl#L314-L324) and [`@async`](https://github.com/JuliaLang/julia/blob/9a8b2fd72b675bb8a5bf0322943ee9451787b86a/base/task.jl#L332-L344) (i.e., create a binding with special `gensym`’ed variable [`sync_varname`](https://github.com/JuliaLang/julia/blob/9a8b2fd72b675bb8a5bf0322943ee9451787b86a/base/task.jl#L305) in `@sync` using `let`).

It would be something like

```julia
const model_varname = gensym(:model)

macro lsys(block)
    var = esc(sync_varname)
    quote
        let $var
            $(esc(block))
        end
    end
end

macro start(name)
    var = esc(model_varname)
    quote
        $var = LModel($(esc(name)))
    end
end

```

and so on (untested). You can also use `@isdefined` macro in `@start` to throw a user-friendly error when it’s used outside `@lsys`.
