# Identifiers

abc_123_ABC
_fn!
ρ; φ; z
ℝ
x′
θ̄
logŷ
ϵ
ŷ
∫
🙋
🦀

==>
Program(
  Identifier,
  Identifier,
  Identifier,
  Identifier,
  Identifier,
  Identifier,
  Identifier,
  Identifier,
  Identifier,
  Identifier,
  Identifier,
  Identifier,
  Identifier,
  Identifier,
)


# Keywords as identifiers - Valid variable names

where = Any

where where where

==>
Program(
  Assignment(Identifier, AssignmentOp, Identifier),
  BinaryExpression(Identifier, where, Type(Identifier)),
)


# Field expressions

foo.x
bar.x.y.z

Base.:+
Base.:(==)
df."a"
Base.r"foo"

(a[1].b().c).d

==>
Program(
  FieldExpression(Identifier, Field(Identifier)),
  FieldExpression(
    FieldExpression(
      FieldExpression(
        Identifier,
        Field(Identifier),
      ),
      Field(Identifier),
    )
    Field(Identifier),
  ),
  FieldExpression(Identifier, Field(Symbol(Operator(UnaryPlusOp)))),
  FieldExpression(Identifier, Field(QuoteExpression(ParenExpression(Operator(ComparisonOp))))),
  FieldExpression(Identifier, Field(StringLiteral)),
  FieldExpression(Identifier, Field(NsStringLiteral(Identifier))),

  FieldExpression(
    ParenExpression(
      FieldExpression(
        CallExpression(
          FieldExpression(
            IndexExpression(Identifier, VectorExpression(IntegerLiteral)),
            Field(Identifier)
          ),
          Arguments
        ),
        Field(Identifier)
      )
    ),
    Field(Identifier)
  )
)


# Keywords as identifiers - keywords as field names

obj.if
obj.begin
obj.end

==>
Program(
  FieldExpression(Identifier, Field(Identifier)),
  FieldExpression(Identifier, Field(Identifier)),
  FieldExpression(Identifier, Field(Identifier)),
)


# Index expressions

a[]
a[1, :]
"foo"[1]
xs[i += 1]

==>
Program(
  IndexExpression(Identifier, VectorExpression()),
  IndexExpression(Identifier, VectorExpression(IntegerLiteral, Operator(Colon))),
  IndexExpression(StringLiteral, VectorExpression(IntegerLiteral)),
  IndexExpression(Identifier, VectorExpression(Assignment(Identifier, UpdateOp, IntegerLiteral))),
),


# Keywords as identifiers - begin/end indexing

xs[begin]
xs[begin + 1]
xs[end]
xs[end - 1]

==>
Program(
  IndexExpression(Identifier, VectorExpression(begin)),
  IndexExpression(Identifier, VectorExpression(BinaryExpression(begin, UnaryPlusOp, IntegerLiteral))),
  IndexExpression(Identifier, VectorExpression(end)),
  IndexExpression(Identifier, VectorExpression(BinaryExpression(end, UnaryPlusOp, IntegerLiteral))),
)


# Keywords as identifiers - begin/end indexing with range operators

xs[begin:end]
xs[begin..end]
data[begin:2:end]

==>
Program(
  IndexExpression(Identifier, VectorExpression(BinaryExpression(begin, Colon, end))),
  IndexExpression(Identifier, VectorExpression(BinaryExpression(begin, EllipsisOp, end))),
  IndexExpression(Identifier, VectorExpression(BinaryExpression(BinaryExpression(begin, Colon, IntegerLiteral), Colon, end))),
)


# Keywords as identifiers - begin/end indexing nested in larger expressions - issue #15

data[max(begin, i - 1)]
data[min(end, i + 1)]
x[(begin)]
x[f(a = end)]
x[(() -> begin)()]

==>
Program(
  IndexExpression(Identifier, VectorExpression(CallExpression(Identifier, Arguments(begin, BinaryExpression(Identifier, UnaryPlusOp, IntegerLiteral))))),
  IndexExpression(Identifier, VectorExpression(CallExpression(Identifier, Arguments(end, BinaryExpression(Identifier, UnaryPlusOp, IntegerLiteral))))),
  IndexExpression(Identifier, VectorExpression(ParenExpression(begin))),
  IndexExpression(Identifier, VectorExpression(CallExpression(Identifier, Arguments(KwArg(Identifier, AssignmentOp, end))))),
  IndexExpression(Identifier, VectorExpression(CallExpression(ParenExpression(ArrowFunctionExpression(TupleExpression, begin)), Arguments))),
)


# Compound statements inside index brackets

x[let
  2
end]
x[if a
  1
else
  2
end]

==>
Program(
  IndexExpression(Identifier, VectorExpression(LetStatement(let, IntegerLiteral, end))),
  IndexExpression(Identifier, VectorExpression(IfStatement(if, Condition(Identifier), IntegerLiteral, ElseClause(else, IntegerLiteral), end))),
)


# Type parametrized expressions

Vector{Int}
T{S, U}
Vector{<:Number}
$(usertype){T}

## Gen.jl DSL with tilde syntax
{:x} ~ normal(0, 1)

==>
Program(
  ParametrizedExpression(Identifier, BraceExpression(Identifier)),
  ParametrizedExpression(Identifier, BraceExpression(Identifier, Identifier)),
  ParametrizedExpression(Identifier, BraceExpression(UnaryExpression(TypeComparisonOp, Identifier))),
  ParametrizedExpression(
    InterpExpression(ParenExpression(Identifier)),
    BraceExpression(Identifier)
  ),
  LineComment,
  Assignment(
    BraceExpression(Symbol(Identifier)),
    TildeOp,
    CallExpression(Identifier, Arguments(IntegerLiteral, IntegerLiteral))
  )
)



# Function call expressions

f()
g("hi", 2)
h(d...)
new{typeof(xs)}(xs)
f(g())

==>
Program(
  CallExpression(Identifier, Arguments),
  CallExpression(Identifier, Arguments(StringLiteral, IntegerLiteral)),
  CallExpression(Identifier, Arguments(SplatExpression(Identifier))),
  CallExpression(
    ParametrizedExpression(
      Identifier,
      BraceExpression(CallExpression(Identifier, Arguments(Identifier)))
    ),
    Arguments(Identifier)
  ),
  CallExpression(
    Identifier,
    Arguments(CallExpression(Identifier, Arguments))
  )
)


# Function call expressions - Broadcasting

f.(xs)
foo!.(ys)
.⋆(ys)
.!isnan.(zs)

==>
Program(
  CallExpression(Identifier, Arguments(Identifier)),
  CallExpression(Identifier, Arguments(Identifier)),
  CallExpression(Operator(TimesOp), Arguments(Identifier)),
  UnaryExpression(UnaryOp, CallExpression(Identifier, Arguments(Identifier))),
)


# Function call expressions - Keyword arguments

f(e; f = g)
g(arg; kwarg)
h(; x, y)

==>
Program(
  CallExpression(
    Identifier,
    Arguments(
      Identifier,
      KeywordArguments(KwArg(Identifier, AssignmentOp, Identifier))
    ),
  ),
  CallExpression(
    Identifier,
    Arguments(
      Identifier,
      KeywordArguments(Identifier)
    ),
  ),
  CallExpression(
    Identifier,
    Arguments(
      KeywordArguments(Identifier, Identifier)
    ),
  ),
)


# Function call expressions with do blocks

reduce(xs) do x, (y,(z,w))
  f(x, y)
end

==>
Program(
  CallExpression(
    Identifier,
    Arguments(Identifier),
    DoClause(
      do,
      Parameters(Identifier, 
        TupleExpression(Identifier, TupleExpression(Identifier, Identifier)),
      ),
      CallExpression(
        Identifier,
        Arguments(Identifier, Identifier),
      ),
      end,
    )
  )
)


# Function call expressions with do blocks - no parameters

f() do; end

==>
Program(
  CallExpression(
    Identifier,
    Arguments,
    DoClause(
      do,
      Parameters,
      end
    )
  )
)


# Function call expressions with do blocks - single line without semicolon

f() do x end

==>
Program(
  CallExpression(
    Identifier,
    Arguments,
    DoClause(
      do,
      Parameters(Identifier),
      end
    )
  )
)


# Function call expressions with do blocks - typed parameter

f(xs) do x::Int; end

==>
Program(
  CallExpression(
    Identifier,
    Arguments(Identifier),
    DoClause(
      do,
      Parameters(BinaryExpression(Identifier, SubTypeOp, Type(Identifier))),
      end
    )
  )
)


# Function call expressions with do blocks - typed and exploded parameter

f(xs) do (x,y)::Int; end

==>
Program(
  CallExpression(
    Identifier,
    Arguments(Identifier),
    DoClause(
      do,
      Parameters(
        BinaryExpression(
          TupleExpression(Identifier, Identifier),
          SubTypeOp,
          Type(Identifier),
        )
      ),
      end
    )
  )
)


# Quote expressions and symbols

:foo
:if

:(x; y)
:(x, y)
:[x, y, z]

:+
:->
:(=)

==>
Program(
  Symbol(Identifier),
  Symbol(Identifier),

  QuoteExpression(ParenExpression(Identifier, Identifier)),
  QuoteExpression(TupleExpression(Identifier, Identifier)),
  QuoteExpression(VectorExpression(Identifier, Identifier, Identifier)),

  Symbol(Operator(UnaryPlusOp)),
  Symbol(Operator),
  QuoteExpression(ParenExpression(Operator(AssignmentOp))),
)


# Keywords as identifiers - Quoted keywords

:if

if x == :end
  1
else
  2
end


==>
Program(
  Symbol(Identifier),
  IfStatement(
    if
    Condition(BinaryExpression(Identifier, ComparisonOp, Symbol(Identifier)))
    IntegerLiteral,
    ElseClause(else, IntegerLiteral)
    end
  )
)


# Interpolation expressions

$foo
$obj.field
$(obj.field)
$f(x)
$f[1, 2]

==>
Program(
  InterpExpression(Identifier),
  FieldExpression(InterpExpression(Identifier), Field(Identifier)),
  InterpExpression(ParenExpression(FieldExpression(Identifier, Field(Identifier)))),
  CallExpression(InterpExpression(Identifier), Arguments(Identifier)),
  IndexExpression(InterpExpression(Identifier), VectorExpression(IntegerLiteral, IntegerLiteral)),
)


# Interpolation expressions - Interpolation in hcat # BROKEN

#=
[x $y z]

= =>
Program(
  MatrixExpression(MatrixRow(Identifier, InterpExpression(Identifier), Identifier)),
)
=#
==>Program(BlockComment)


# Adjoint expressions

[u, v]'
(x, y)'
A'[i]
f'(x)
:a'
a' * b

==>
Program(
  AdjointExpression(VectorExpression(Identifier, Identifier)),
  AdjointExpression(TupleExpression(Identifier, Identifier)),
  IndexExpression(AdjointExpression(Identifier), VectorExpression(Identifier)),
  CallExpression(AdjointExpression(Identifier), Arguments(Identifier)),
  AdjointExpression(Symbol(Identifier))
  BinaryExpression(AdjointExpression(Identifier), TimesOp, Identifier),
)

