# Modules

module A

baremodule B end

module C
end

end

==>
Program(
  ModuleDefinition(
    module,
    Identifier,
    ModuleDefinition(baremodule, Identifier, end),
    ModuleDefinition(module, Identifier, end),
    end
  )
)


# Abstract type definitions

abstract type T end

abstract type T{N} end
abstract type T{X, Y,} end
abstract type T{S <: U} end

abstract type T <: S end
abstract type T{N} <: S end

==>
Program(
  AbstractDefinition(abstract, type, TypeHead(Identifier), end),

  AbstractDefinition(abstract, type, TypeHead(ParametrizedExpression(Identifier, BraceExpression(Identifier))), end),
  AbstractDefinition(abstract, type, TypeHead(ParametrizedExpression(Identifier, BraceExpression(Identifier, Identifier))), end),
  AbstractDefinition(abstract, type, TypeHead(ParametrizedExpression(Identifier, BraceExpression(BinaryExpression(Identifier, TypeComparisonOp, Identifier)))), end),

  AbstractDefinition(abstract, type, TypeHead(BinaryExpression(Identifier, TypeComparisonOp, Identifier)), end),
  AbstractDefinition(abstract, type, TypeHead(BinaryExpression(ParametrizedExpression(Identifier, BraceExpression(Identifier)), TypeComparisonOp, Identifier)), end),
)


# Primitive type definitions

primitive type T 8 end
primitive type T <: S 16 end
primitive type Ptr{T} 32 end

==>
Program(
  PrimitiveDefinition(primitive, type, TypeHead(Identifier), IntegerLiteral, end),
  PrimitiveDefinition(primitive, type, TypeHead(BinaryExpression(Identifier, TypeComparisonOp, Identifier)), IntegerLiteral, end),
  PrimitiveDefinition(primitive, type, TypeHead(ParametrizedExpression(Identifier, BraceExpression(Identifier))), IntegerLiteral, end),
)


# Struct definitions

struct Unit end

struct MyInt; field::Int end

==>
Program(
  StructDefinition(struct, TypeHead(Identifier), end),
  StructDefinition(struct, TypeHead(Identifier), BinaryExpression(Identifier, SubTypeOp, Type(Identifier)), end),
)


# Mutable struct definitions

mutable struct Bar
  baz
  qux::Float64
end

==>
Program(
  StructDefinition(
    mutable, struct,
    TypeHead(Identifier),
    Identifier,
    BinaryExpression(Identifier, SubTypeOp, Type(Identifier)),
    end
  ),
)


# Parametric struct definitions

struct Point{T}
  x::T
  y::T
end

==>
Program(
  StructDefinition(
    struct,
    TypeHead(ParametrizedExpression(Identifier, BraceExpression(Identifier))),
    BinaryExpression(Identifier, SubTypeOp, Type(Identifier)),
    BinaryExpression(Identifier, SubTypeOp, Type(Identifier)),
    end
  ),
)


# Parametric struct definitions with subtyping

struct Rational{T<:Integer} <: Real
  num::T
  den::T
end

==>
Program(
  StructDefinition(
    struct,
    TypeHead(
      BinaryExpression(
        ParametrizedExpression(Identifier, BraceExpression(BinaryExpression(Identifier, TypeComparisonOp, Identifier))),
        TypeComparisonOp,
        Identifier
      )
    ),
    BinaryExpression(Identifier, SubTypeOp, Type(Identifier)),
    BinaryExpression(Identifier, SubTypeOp, Type(Identifier)),
    end
  ),
)


# Parametric struct fields

mutable struct Foo <: Bar
  asd::Vector{Bar}
end

==>
Program(
  StructDefinition(
    mutable, struct,
    TypeHead(BinaryExpression(Identifier, TypeComparisonOp, Identifier)),
    BinaryExpression(Identifier, SubTypeOp, Type(ParametrizedExpression(Identifier, BraceExpression(Identifier)))),
    end
  ),
)


# Function definitions

function nop() end

function I(x) x end

function Base.rand(n::MyInt)
    return 4
end

function Γ(z)
    gamma(z)
end

==>
Program(
  FunctionDefinition(function, Signature(CallExpression(Identifier, Arguments)), end),
  FunctionDefinition(function, Signature(CallExpression(Identifier, Arguments(Identifier))), Identifier, end),
  FunctionDefinition(
    function,
    Signature(
      CallExpression(
        FieldExpression(Identifier, Field(Identifier)),
        Arguments(BinaryExpression(Identifier, SubTypeOp, Type(Identifier)))
      )
    ),
    ReturnStatement(return, IntegerLiteral),
    end,
  ),
  FunctionDefinition(
    function,
    Signature(CallExpression(Identifier, Arguments(Identifier))),
    CallExpression(Identifier, Arguments(Identifier)),
    end,
  )
)

# Function definitions - Operators

function ⊕(x, y)
    x + y
end

===>
Program(
  FunctionDefinition(
    function,
    Signature(CallExpression(Operator(PlusOp), Arguments(Identifier, Identifier))),
    BinaryExpression(Identifier, UnaryPlusOp, Identifier),
    end,
  )
)


# Function definitions - Callable structs

function (foo::Foo)()
end

==>
Program(
  FunctionDefinition(
    function,
    Signature(CallExpression(ParenExpression(BinaryExpression(Identifier, SubTypeOp, Type(Identifier))), Arguments)),
    end,
  )
)


# function definitions - Zero method definitions

function f end

==>
Program(
  FunctionDefinition(
    function,
    Signature(Identifier),
    end,
  )
)


# Function definitions - Anonymous functions

function fix2(f, x)
    return function(y)
        f(x, y)
    end
end

==>
Program(
  FunctionDefinition(
    function,
    Signature(CallExpression(Identifier, Arguments(Identifier, Identifier))),
    ReturnStatement(
      return,
      FunctionDefinition(
        function,
        Signature(TupleExpression(Identifier)),
        CallExpression(Identifier, Arguments(Identifier, Identifier)),
        end,
      )
    )
    end,
  )
)


# Function definitions - Anonymous functions with no parameters

function() end

function()
  code
end

==>
Program(
  FunctionDefinition(function, Signature(TupleExpression), end),
  FunctionDefinition(function, Signature(TupleExpression), Identifier, end),
)


# Function definitions - Return type annotations # FIXME

function f()::T end

function f()::Int
  code
end

==>
Program(
  FunctionDefinition(
    function,
    Signature(
      CallExpression(Identifier, Arguments),
    ),
    UnaryExpression(
      SubTypeOp, Type(Identifier),
    ),
    end,
  ),
  FunctionDefinition(
    function,
    Signature(
        CallExpression(Identifier, Arguments),
    ),
    UnaryExpression(
      SubTypeOp, Type(Identifier),
    ),
    Identifier,
    end,
  )
)


# Function definitions - Signature with `where` expression # FIXME

function f()::T where T
end

function f(n::N)::S where {N <: Number, S}
end

==>
Program(
  FunctionDefinition(
    function,
    Signature(
        CallExpression(Identifier, Arguments),
    )
    UnaryExpression(
      SubTypeOp,
      Type(
        BinaryExpression(
          Identifier,
          where,
          Type(Identifier),
        )
      )
    )
    end
  ),
  FunctionDefinition(
    function,
    Signature(
      CallExpression(Identifier, Arguments(BinaryExpression(Identifier, SubTypeOp, Type(Identifier)))),
    )
    UnaryExpression(
      SubTypeOp,
      Type(
        BinaryExpression(
          Identifier,
          where,
          Type(BraceExpression(BinaryExpression(Identifier, TypeComparisonOp, Identifier), Identifier)),
        )
      )
    ),
    end
  )
)


# Function definitions - Typed parameters

function name(arg::T, ::S)
  code
end

==>
Program(
  FunctionDefinition(
    function,
    Signature(
      CallExpression(
        Identifier,
        Arguments(
          BinaryExpression(Identifier, SubTypeOp, Type(Identifier)),
          UnaryExpression(SubTypeOp, Type(Identifier))
        )
      )
    )
    Identifier,
    end
  )
)


# Function definitions - Typed parameters with FieldExpression type

function name(param::Type.Some) end

==>
Program(
  FunctionDefinition(
    function,
    Signature(
      CallExpression(
        Identifier,
        Arguments(BinaryExpression(Identifier, SubTypeOp, Type(FieldExpression(Identifier, Field(Identifier))))),
      )
    )
    end
  )
)


# Function definitions - optional parameters

function f(x, y=2)
  x, y
end

==>
Program(
  FunctionDefinition(
    function,
    Signature(
      CallExpression(Identifier, Arguments(Identifier, KwArg(Identifier, AssignmentOp, IntegerLiteral)))
    ),
    OpenTuple(Identifier, Identifier),
    end
  )
)


# Function definitions - exploded parameters

function f((x,(y1,y2)), (u,v)=2)
  x, y
end

==>
Program(
  FunctionDefinition(
    function,
    Signature(
      CallExpression(Identifier, Arguments(
        TupleExpression(Identifier,TupleExpression(Identifier,Identifier)), 
        KwArg(TupleExpression(Identifier,Identifier), AssignmentOp, IntegerLiteral),
      ))
    ),
    OpenTuple(Identifier, Identifier),
    end
  )
)



# Function definitions - keyword parameters

function a(; x, y) end
==>
Program(
  FunctionDefinition(
    function,
    Signature(
      CallExpression(Identifier, Arguments(KeywordArguments(Identifier, Identifier)))
    ),
    end
  )
)


# Function definitions - Anonymous function with keyword parameters # BROKEN

#=
function (x; y) end
= =>
Program(
  FunctionDefinition(
    function,
    Signature(TupleExpression(Identifier, Identifier)),
    end
  )
)
=#
==>Program(BlockComment)


# Macro definitions

macro name(s::Symbol)
    String(s)
end

macro count(args...); length(args) end

macro Mod.mac() end

macro wow end

==>
Program(
  MacroDefinition(
    macro,
    Signature(CallExpression(Identifier, Arguments(BinaryExpression(Identifier, SubTypeOp, Type(Identifier))))),
    CallExpression(Identifier, Arguments(Identifier)),
    end
  ),
  MacroDefinition(
    macro,
    Signature(CallExpression(Identifier, Arguments(SplatExpression(Identifier)))),
    CallExpression(Identifier, Arguments(Identifier)),
    end
  ),
  MacroDefinition(
    macro,
    Signature(CallExpression(FieldExpression(Identifier, Field(Identifier)), Arguments)),
    end
  ),
  MacroDefinition(
    macro,
    Signature(Identifier),
    end
  )
)
