Repository: JuliaLang/julia
File: JuliaSyntax/src/porcelain/syntax_graph.jl (Julia)

Code before:
        push!(needs_else, case)
        needs_else = needs_else[3].args
    end
    push!(needs_else,
          :(throw(ErrorException(string(
              "No match found for `", $st_gs, "` at ", $(string(line)))))))
    return esc(out_blk)
end

function _stm_vcat_to_hcat(p::Expr)
    Meta.isexpr(p, :vcat) || return p
    out = Expr(:hcat)
    for a in p.args
        Meta.isexpr(a, :row) ? append!(out.args, a.args) : push!(out.args, a)
    end
    return out
end

function _stm_destruct_pat(per::Expr)
    pe, r = per.args[1:2]
    Base.remove_linenums!(pe) # errors in lhs of `->` are caught in usage check
    return Meta.isexpr(pe, :tuple) ? (pe.args[1], pe.args[2:end], r) :
        (pe, Expr[], r)
end

function _stm_matches(p::Union{Symbol, Expr}, st, debug=false, indent="")
    p = Meta.isexpr(p, :vcat) ? _stm_vcat_to_hcat(p) : p
    if p isa Symbol
        debug && printstyled(string(indent, p, "=", st, "\n"); color=:yellow)
        return true
    elseif Meta.isexpr(p, (:vect, :hcat))
        p_kind = Kind(p.args[1].args[3])
        kind_ok = p_kind === kind(st)
        if !kind_ok
            debug && printstyled(
                string(indent, "[kind]: ", kind(st), "!=", p_kind, "\n"); color=:red)
            return false
        end
        p_args = filter(e->!(e isa LineNumberNode), p.args)[2:end]
        dots_i = findfirst(x->Meta.isexpr(x, :(...)), p_args)
        dots_start = something(dots_i, length(p_args) + 1)
        n_after_dots = length(p_args) - dots_start # -1 if no dots
        npats = dots_start + n_after_dots
        n_ok = isnothing(dots_i) ? numchildren(st) === npats :
            numchildren(st) >= npats - 1
        if !n_ok
            debug && printstyled(string(
                indent, "[numc]: ", numchildren(st), "!=", npats, "\n"); color=:red)
            return false
        end
        all_ok = true

Review comment (performance):
Hmm, having to copy / filter / scan the input `Expr` every time we pattern-match seems like it'd be slow compared to what I'd expect this macro to expand to (a few `if-else` checks)

Is it possible to make this more like `_stm_assigns` and compute the effective checking logic as an expression?

Code after:
    push!(case_list_tail,
          :(throw(ErrorException(string(
              "No match found for `", $st_gs, "` at ", $(string(line)))))))
    return esc(out_blk)
end

# recursively flatten `vcat` expressions
function _stm_vcat_to_hcat(p::Expr)
    if Meta.isexpr(p, :vcat)
        out = Expr(:hcat)
        for a in p.args
            Meta.isexpr(a, :row) ? append!(out.args, a.args) : push!(out.args, a)
        end
    else
        out = Expr(p.head, p.args...)
    end
    for i in eachindex(out.args)
        out.args[i] = _stm_vcat_to_hcat(out.args[i])
    end
    return out
end
_stm_vcat_to_hcat(x) = x

# return (pat_expr, when_expr|nothing, res_expr)
function _stm_destruct_pat(pcr::Expr)
    pc, r = pcr.args[1:2]
    Base.remove_linenums!(pc) # errors in lhs of `->` are caught in usage check
    (p_vcat, c) = Meta.isexpr(pc, :tuple) ?
        (pc.args[1], pc.args[2].args[2]) : (pc, true)
    return (_stm_vcat_to_hcat(p_vcat), c, r)
end

function _stm_matches_wrapper(p::Expr, st_ex, debug)
    st_gs, k_gs, nc_gs = gensym.("st", "k", "nc")
    Expr(:let, Expr(:block, :($st_gs = $st_ex),
                          :($k_gs = $kind($st_gs)),
                          :($nc_gs = $numchildren($st_gs))),
               _stm_matches(p, st_gs, k_gs, nc_gs, debug))
end

function _stm_matches(p::Expr, st_gs::Symbol, k_gs::Symbol, nc_gs::Symbol, debug)
    pat_k = Kind(p.args[1].args[3])
    out = Expr(:&&, :($pat_k === $k_gs))
    debug && push!(out.args, Expr(:block, :(printstyled(
        string("[kind]: ", $k_gs, "\n"); color=:yellow)), true))

    p_args = p.args[2:end]
    dots_i = findfirst(x->Meta.isexpr(x, :(...)), p_args)
    dots_start = something(dots_i, length(p_args) + 1)
    n_after_dots = length(p_args) - dots_start # -1 if no dots