# Surface AST to surface AST lowering/desugaring?

**URL:** https://discourse.julialang.org/t/surface-ast-to-surface-ast-lowering-desugaring/28067
**Category:** General Usage
**Tags:** question, metaprogramming
**Created:** [August 27, 2019, 9:44pm UTC](https://discourse.julialang.org/t/surface-ast-to-surface-ast-lowering-desugaring/28067 "2019-08-27T21:44:42Z")
**Posts on this page:** 8
**Page:** 1

<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: [August 27, 2019, 9:44pm UTC](https://discourse.julialang.org/t/surface-ast-to-surface-ast-lowering-desugaring/28067/1 "2019-08-27T21:44:42Z")

</div>

Is there any function/package to do surface AST to surface AST lowering? I’m aware of `Meta.lower` but it does not produce a surface AST that can be used as an output of a macro.

The only relevant thing I know is @c42f’s WIP on moving lowering from flisp to Julia ([https://github.com/JuliaLang/julia/pull/32201](https://github.com/JuliaLang/julia/pull/32201)). But it sounds like it’s going to take sometime for all of the things listed to happen.

An example usage I have in mind is macro `@~` of [LazyArrays.jl](https://github.com/JuliaArrays/LazyArrays.jl). It can handle `@~ vcat(A, B)` but not `@~ [A; B]` at the moment. I’ve been thinking that it’d be easy to support this kind of fancy syntax if there is a way to lower expression `[A; B]` to `vcat(A, B)` etc.

I’ll probably write a subset of lowering myself if there is no out-of-the-box solution but it’d be nice to know if there is already a solution. I’m also wondering if it makes sense to use Cassette and operate on the IR level to do something like `LazyArrays.@~`.

---

<div class="post-metadata">

### Author: ![cscherrer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cscherrer/32/7631_2.png) [@cscherrer](https://discourse.julialang.org/u/cscherrer)
#### Post date: [August 27, 2019, 11:31pm UTC](https://discourse.julialang.org/t/surface-ast-to-surface-ast-lowering-desugaring/28067/2 "2019-08-27T23:31:25Z")

</div>

I’m using [MLStyle.jl](https://github.com/thautwarm/MLStyle.jl) to do something along these lines for my package. I have a function `canonical` that takes an `Expr` and returns another, and has rewrite rules like this:

```julia
:($f($(args...))) => begin
    rf = r(f)
    rx = map(r,args)
    :($rf($(rx...)))
end

```

The first line in the function is `r=canonical`, in order for all the recursions to work out.

---

<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: [August 28, 2019, 12:42am UTC](https://discourse.julialang.org/t/surface-ast-to-surface-ast-lowering-desugaring/28067/3 "2019-08-28T00:42:34Z")

</div>

Yeah, using something like MLStyle.jl and MacroTools.jl is useful when manipulating AST. But I was looking for more out-of-the-box solution.

To demonstrate what I need, I cooked up a package that does what I need [GroundEffects.jl](https://github.com/tkf/GroundEffects.jl) but it likely does not cover many cases.

```julia
julia> GroundEffects.lower(:[A B; C D])
:((hvcat)((2, 2), A, B, C, D))

julia> GroundEffects.lower(:[A B])
:((hcat)(A, B))

julia> GroundEffects.lower(:(y .+= f(x)))
:((Base.Broadcast.materialize!)(y, (Base.Broadcast.broadcasted)(+, y, f(x))))

julia> GroundEffects.lower(:(g(y) .+= f(x)))
quote
    var"##lhs#402" = g(y)
    (Base.Broadcast.materialize!)(var"##lhs#402", (Base.Broadcast.broadcasted)(+, var"##lhs#402", f(x)))
end

```

---

<div class="post-metadata">

### Author: ![c42f](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/c42f/32/52842_2.png) [@c42f](https://discourse.julialang.org/u/c42f)
#### Post date: [August 28, 2019, 6:50am UTC](https://discourse.julialang.org/t/surface-ast-to-surface-ast-lowering-desugaring/28067/4 "2019-08-28T06:50:43Z")

</div>

I’m not aware of anything which does this, but it’s an interesting question: can we have an “initial desugaring” pass which doesn’t introduce any of the lowered AST forms and can be used by macros which don’t want to deal with some surface syntax complexities?

The tricky thing is that correct desugaring almost always introduces some intermediate lowered forms. For example, `[a;b]` is lowered to `Base.vcat(a,b)` where the `Base.vcat` is actually `GlobalRef(Base, :vcat)` rather than the surface syntax `:(Base.vcat)`.

---

<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: [August 28, 2019, 7:05am UTC](https://discourse.julialang.org/t/surface-ast-to-surface-ast-lowering-desugaring/28067/5 "2019-08-28T07:05:38Z")

</div>

ATM my solution is to just directly put function object `Base.vcat` in `Expr`. I don’t know if that’s a valid strategy in Julia core but I am not aiming _that_ high. Is `Base.vcat` a good approximation of `GlobalRef(Base, :vcat)`? Can it produce incorrect result? Maybe in a `baremodule`?

---

<div class="post-metadata">

### Author: ![c42f](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/c42f/32/52842_2.png) [@c42f](https://discourse.julialang.org/u/c42f)
#### Post date: [August 28, 2019, 7:43am UTC](https://discourse.julialang.org/t/surface-ast-to-surface-ast-lowering-desugaring/28067/6 "2019-08-28T07:43:31Z")

</div>

> [@tkf](#):
>
> Is `Base.vcat` a good approximation of `GlobalRef(Base, :vcat)` ? Can it produce incorrect result?

I don’t know the definitive story for how `GlobalRef` is used in the implementation. `GlobalRef`s are a “partially symbolic” reference to a given _name_ within a known module _instance_. If a const binding for that name exists in the module during inference it’s resolved immediately. Otherwise the binding is looked up at runtime.

For your use case I think inserting the value of `Base.vcat` eagerly into the AST should be fine.

---

<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: [August 28, 2019, 8:21am UTC](https://discourse.julialang.org/t/surface-ast-to-surface-ast-lowering-desugaring/28067/7 "2019-08-28T08:21:44Z")

</div>

I see, actual lowering has to handle non-const global.

---

<div class="post-metadata">

### Author: ![c42f](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/c42f/32/52842_2.png) [@c42f](https://discourse.julialang.org/u/c42f)
#### Post date: [August 28, 2019, 8:35am UTC](https://discourse.julialang.org/t/surface-ast-to-surface-ast-lowering-desugaring/28067/8 "2019-08-28T08:35:55Z")

</div>

Yes that’s one reason why `GlobalRef` exists, but I think there’s some other benefits I don’t really appreciate (serialization maybe?)
