# Some way to debug syntax errors?

**URL:** <https://discourse.julialang.org/t/some-way-to-debug-syntax-errors/86336>\
**Category:** General Usage\
**Tags:** debug, syntax, parser\
**Created:** [August 25, 2022, 3:22pm UTC](https://discourse.julialang.org/t/some-way-to-debug-syntax-errors/86336 "2022-08-25T15:22:14Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [August 25, 2022, 3:22pm UTC](https://discourse.julialang.org/t/some-way-to-debug-syntax-errors/86336/1 "2022-08-25T15:22:14Z")

</div>

I’ve got a syntax error in my code (in a function in a module) and I can’t seem to figure it out. The expression goes something like this:

```julia
ntuple(
  let a = b
    i -> expr(a, i)
  end,
  Val{c}())

```

According to the error message, it seems that Julia expects the entire `let` expression should be a function argument _name_!?

```julia
julia> include("HeterogeneousArrays.jl")
Main.HeterogeneousArrays

julia> include("LayoutTrees.jl")
ERROR: LoadError: syntax: "let dim = dim, axs = axs, ta = ta
    # /home/nsajko/src/gitlab.com/nsajko/BenchmarkVisualization/src/LayoutTrees.jl, line 189
    i -> begin
    # /home/nsajko/src/gitlab.com/nsajko/BenchmarkVisualization/src/LayoutTrees.jl, line 189
    ifelse((i == dim), ta, axs[i])
end
end" is not a valid function argument name around /home/nsajko/src/gitlab.com/nsajko/BenchmarkVisualization/src/LayoutTrees.jl:177
Stacktrace:
 [1] top-level scope
   @ ~/src/gitlab.com/nsajko/BenchmarkVisualization/src/LayoutTrees.jl:164

```

Here’s my code:

> **HeterogeneousArrays.jl**
>
> ```julia
> # Copyright 2022 Neven Sajko. All right reserved.
> 
> module HeterogeneousArrays
> 
> using LinearAlgebra
> 
> export AbstractHeterogeneousArray, HSArray
> 
> # An array with the given axes. Heterogeneous in general, in the sense
> # that different elements can have different types.
> abstract type AbstractHeterogeneousArray{axes} end
> 
> Base.axes(::AbstractHeterogeneousArray{a}) where {a} = a
> 
> Base.axes(::AbstractHeterogeneousArray{a}, i) where {a} = a[i]
> 
> Base.size(::AbstractHeterogeneousArray{a}) where {a} = map(length, a)
> 
> Base.size(::AbstractHeterogeneousArray{a}, i) where {a} = length(a[i])
> 
> Base.length(::AbstractHeterogeneousArray{a}) where {a} = prod(length, a)
> 
> Base.getindex(ar::AbstractHeterogeneousArray{a},
> i::CartesianIndex{n}) where {a, n} = ar[Tuple(i)...]
> 
> # Heterogeneous static array.
> struct HSArray{
> axes, size, len, dim_count,
> ind_helper_mul,
> ind_helper_diff,
> T <: NTuple{len, Any}} <: AbstractHeterogeneousArray{axes}
> 
> data::T
> 
> function indexing_helper_mul(::Val{axes}) where {axes}
> local t = ntuple(i -> length(axes[i + 1]), Val{max(0, length(axes) - 1)}())
> ntuple(
> let t = t
> i -> prod(t[begin+i-1 : end])
> end,
> Val{length(axes)}())
> end
> 
> function HSArray{a}(t::T) where {a, T <: Tuple}
> isa(a, NTuple{n, AbstractUnitRange{Int}} where {n}) ||
> error("axes not a tuple of Int unit ranges")
> local size = map(length, a)
> local len = prod(length, a)
> local dc = length(a)
> local ind_helper_mul = indexing_helper_mul(Val{a}())
> local ind_helper_diff = ntuple(i -> first(a[i]), Val{dc}())
> new{a, size, len, dc, ind_helper_mul, ind_helper_diff, T}(t)
> end
> end
> 
> check_bounds(r::NTuple{n, AbstractUnitRange{Int}},
> i::Vararg{Int, n}) where {n} =
> for p in zip(r, i)
> (p[2] in p[1]) || Base.throw_boundserror(p[1], p[2])
> end
> 
> function Base.getindex(
> ar::HSArray{a, s, l, dc, ihm, ihd},
> i::Vararg{Int, dc}) where {a, s, l, dc, ihm, ihd}
> 
> @boundscheck check_bounds(a, i...)
> local linind = dot(
> ihm,
> ntuple(
> let t = NTuple{dc, Int}(i)
> i -> t[i] - ihd[i]
> end,
> Val{dc}()))
> ar.data[linind + firstindex(())]
> end
> 
> module HeterogeneousArraysWithConstAxes
> 
> using ..HeterogeneousArrays
> using ..HeterogeneousArrays: check_bounds
> 
> export HSArrayWithConstAxes
> 
> # Merges tuples while preserving intra-tuple order, with indices
> # determining where each element goes (which element of the resultant
> # tuple comes from which source tuple).
> #
> # For example, if indices is:
> # (1, 2, 1, 1, 2)
> # and tuples is:
> # ((11, 12, 13), (21, 22))
> # the result is:
> # (11, 21, 12, 13, 22)
> function merged_tuples(::Val{indices}, ::Val{tuples}) where {indices, tuples}
> (length(indices) == sum(length, tuples, init = 0)) || error("mismatched total size")
> all(
> pair -> ==(pair...),
> zip(
> ntuple(
> i -> count(==(i), indices, init = 0),
> Val{length(tuples)}()),
> map(length, tuples))) || error("mismatched size")
> 
> ntuple(
> i ->
> let j = indices[i]
> tuples[j][count(==(j), indices[begin:(i - 1)], init = 0) + 1]
> end,
> Val{length(indices)}())
> end
> 
> struct HSArrayWithConstAxes{
> parent_axis_bool_indices,
> const_axes,
> axes, size, len, dim_count,
> const_axis_bool_indices,
> parent_axis_indices, const_axis_indices,
> parent_axes,
> Parent <: AbstractHeterogeneousArray{parent_axes}} <:
> AbstractHeterogeneousArray{axes}
> 
> parent::Parent
> 
> function HSArrayWithConstAxes{pabi, ca}(ar::HA) where
> {pabi, ca, pa, HA <: AbstractHeterogeneousArray{pa}}
> local ax = merged_tuples(
> Val{ntuple(i -> Int(pabi[i]) + 1, Val{length(pabi)}())}(),
> Val{(ca, pa)}())
> local cabi = ntuple(i -> !pabi[i], Val{length(pabi)}())
> new{pabi, ca,
> ax,
> map(length, ax),
> prod(length, ax),
> length(ax),
> cabi,
> (findall(pabi)...,),
> (findall(cabi)...,),
> pa,
> HA}(ar)
> end
> end
> 
> parent_axis_indices(::HSArrayWithConstAxes{
> pabi,
> ca,
> a, s, l, dc,
> cabi,
> pai}) where {pabi, ca, a, s, l, dc, cabi, pai} =
> pai
> 
> function Base.getindex(
> ar::HSArrayWithConstAxes{pabi, ca, a, s, l, dc},
> i::Vararg{Int, dc}) where {pabi, ca, a, s, l, dc}
> @boundscheck check_bounds(a, i...)
> ar.parent[i[collect(parent_axis_indices(ar))]...]
> end
> 
> end # module HeterogeneousArraysWithConstAxes
> 
> end # module HeterogeneousArrays
> 
> ```

> **LayoutTrees.jl**
>
> ```julia
> # Copyright 2022 Neven Sajko. All right reserved.
> 
> module LayoutTrees
> 
> using ..HeterogeneousArrays
> using ..HeterogeneousArrays.HeterogeneousArraysWithConstAxes
> 
> export
> LayoutTree,
> LinearEquation,
> SystemSolution
> 
> struct Handle
> v::Int
> end
> 
> mutable struct HandleGenerator{first}
> next::Handle
> HandleGenerator{first}() where {first} = new{first}(Handle(first))
> end
> 
> function (hg::HandleGenerator)()
> local ret = hg.next
> hg.next = Handle(hg.next.v + 1)
> ret
> end
> 
> count(hg::HandleGenerator{f}) where {f} =
> hg.next.v - f
> 
> struct LinearEquation{F <: Real}
> lin::Vector{Pair{Handle, F}}
> off::F
> end
> 
> LinearEquation(l::Vector{Pair{Handle, F}}) where {F <: Real} =
> LinearEquation(l, zero(F))
> 
> struct SystemSolution{F <: Real}
> s::Vector{F}
> 
> function SystemSolution(eqs::Vector{LinearEquation{F}}) where {F <: Real}
> local min_handle = minimum(eq -> minimum(v -> v.first.v, eq.lin), eqs)
> local max_handle = maximum(eq -> maximum(v -> v.first.v, eq.lin), eqs)
> 
> isone(min_handle) || error("mnbvcfghj")
> 
> local off = map(eq -> eq.off, eqs)
> local ar = zeros(F, length(eqs), max_handle)
> for (i, eq) in enumerate(eqs)
> for v in eq.lin
> ar[i, v.first.v] = v.second
> end
> end
> new{F}(ar \ off)
> end
> end
> 
> Base.getindex(s::SystemSolution, i::Handle) = s.s[i.v]
> 
> # Length is the number of children.
> struct LayoutTree{
> length, chil_dim_count, pabi,
> T <: Any,
> Children <: HSArrayWithConstAxes{
> pabi, ca, a, s, length, chil_dim_count, cabi, pai, cai, pa,
> <:HSArray{pa}} where {pabi, ca, a, s, cabi, pai, cai, pa}}
> 
> data::T
> 
> children::Children
> 
> LayoutTree(v::T, chil::Chil) where
> {T <: Any,
> pabi, ca, a, s, l, dc, cabi, pai, cai, pa, ps, pl, pdc, ihm, ihd, cl,
> Chil <: HSArrayWithConstAxes{
> pabi, ca, a, s, l, dc, cabi, pai, cai, pa,
> <:HSArray{
> pa, ps, pl, pdc, ihm, ihd,
> <:NTuple{pl, <:LayoutTree{cl, dc, T}}}}} =
> new{l, dc, pabi, T, Chil}(v, chil)
> end
> 
> struct Node{n}
> # Width, height, etc.
> sizes::NTuple{n, Handle}
> 
> # If the node has a single child, this equals the top padding, bottom
> # padding, left padding and right padding, in the 2D case.
> #
> # If the node has more than one child, this is the layout padding.
> padding::Handle
> end
> 
> (g::HandleGenerator)(::Type{Node{n}}) where {n} =
> Node(
> ntuple(
> let g = g
> i -> g()
> end,
> Val{n}()),
> g(),
> g())
> 
> (g::HandleGenerator)(chil::Chil) where
> {pabi, ca, a, s, l, dc, Chil <: HSArrayWithConstAxes{pabi, ca, a, s, l, dc}} =
> LayoutTree(g(Node{dc}), chil)
> 
> const Tree = LayoutTree{len, dc, pabi, Node{dc}} where {len, dc, pabi}
> 
> # If root has no children.
> root_node_equations(t::Tree{0}, ::Int, ::Type{<:Real}) =
> ()
> 
> # If root has a single child.
> root_node_equations(t::Tree{1, dc}, dim::Int, ::Type{F}) where
> {dc, F <: Real} =
> (LinearEquation([
> t.data.sizes[dim] => F(-1),
> t.data.padding => F(2),
> t.children[ntuple(
> let t = t
> j -> first(axes(t, j))
> end,
> Val{dc}())...].data.sizes[dim] => one(F)]),)
> 
> parent_children_padding_equation(
> t::Tree{len, dc, pabi},
> dim::Int,
> ::Type{F}) where {len, dc, pabi, F <: Real} =
> LinearEquation(
> let
> cnt = size(t, dim),
> first_two = (t.data.sizes[dim] => F(-1), t.data.padding => F(cnt - 1))
> 
> ifelse(
> pabi[dim],
> [
> first_two...,
> ntuple(
> let t = t, dim = dim
> i -> (
> t.children[ntuple(
> let t = t, dim = dim, i = i
> j ->
> let a = axes(t, j)
> ifelse(j == dim, a[begin + i - 1], first(a))
> end
> end,
> Val{dc}())...].data.sizes[dim] =>
> one(F))
> end,
> Val{cnt}())...],
> [
> first_two...,
> t.children[ntuple(
> let t = t
> j -> first(axes(t, j))
> end,
> Val{dc}())].data.sizes[dim] => F(cnt)])
> end)
> 
> # If root has more than one child.
> root_node_equations(tree::Tree{len, dc}, dim::Int, ::Type{F}) where
> {len, dc, F <: Real} =
> (
> parent_children_padding_equation(tree, dim, F),
> 
> # Property of an n-dimensional grid:
> # 1. Choose an element of the grid. Its location is defined by n
> # coordinates, one for each dimension. Its shape is defined by
> # n sizes, again one for each dimension.
> # 2. Choose one of the n dimensions. Varying the element's
> # coordinate associated with the chosen dimension keeps the
> # sizes associated with the other n-1 dimensions constant.
> let
> har = tree.children,
> 
> axs = axes(har),
> 
> # Chosen axis
> ca = axs[dim],
> 
> fi = first(ca),
> ta = ca[(begin + 1):end],
> 
> naxs = ntuple(
> let dim = dim, axs = axs, ta = ta
> i -> ifelse(i == dim, ta, axs[i])
> end,
> Val{dc}()),
> 
> cinds = CartesianIndices(naxs)
> 
> ntuple(
> let har = har, dim = dim, fi = fi, cinds = cinds
> i ->
> let
> (j, k) = map(x -> x + 1, divrem(i - 1, dc - 1)),
> d = ifelse(k < dim, k, k + 1),
> t = cinds[j],
> r = CartesianIndex(ntuple(
> let dim = dim, fi = fi, t = t
> i -> ifelse(i == dim, fi, t[i])
> end,
> Val{length(t)}()))
> LinearEquation([
> har[t].data.sizes[d] => one(F),
> har[r].data.sizes[d] => F(-1)])
> end
> end,
> Val{
> length(ta) *
> prod(map(
> a -> prod(length, a, init = 1),
> (axs[begin:(dim - 1)], axs[(dim + 1):end]))) *
> (dc - 1)}())
> end...)
> 
> function add_tree_equations!(
> eqs::Vector{LinearEquation{F}},
> tree::Tree{len, dc}) where {F, len, dc}
> 
> for dim in 1:dc
> append!(eqs, root_node_equations(tree, dim, F))
> end
> 
> let
> har = tree.children,
> cartesian_indices = CartesianIndices(axes(har))
> for cind in (cartesian_indices[i] for i in Base.OneTo(len))
> add_tree_equations!(eqs, har[cind])
> end
> end
> 
> eqs
> end
> 
> end # module LayoutTrees
> 
> ```

Is it perhaps possible to debug this by looking at some intermediate representation?

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [August 25, 2022, 5:12pm UTC](https://discourse.julialang.org/t/some-way-to-debug-syntax-errors/86336/2 "2022-08-25T17:12:46Z")

</div>

Bug report:  
[https://github.com/JuliaLang/julia/issues/46482](https://github.com/JuliaLang/julia/issues/46482)
