===========
macro begin
:language(crystal)
===========

{% begin %}
puts "hello"
{% end %}

---

(expressions
  (macro_begin
    body: (expressions
      (macro_content)
      (macro_content
        (literal_content))
      (macro_content))))

==============
macro verbatim
:language(crystal)
==============

{% verbatim do %}
  {% begin %} {{ foo }} {% end %}
{% end %}

{%verbatim do%}{%end%}

---

(expressions
  (macro_verbatim
    body: (expressions
      (macro_content)
      (macro_begin
        body: (expressions
          (macro_content)
          (macro_expression
            (identifier))
          (macro_content)))
      (macro_content)))
  (macro_verbatim))

========
macro if
:language(crystal)
========

{% if flag?(:unix) %}
  require "lib_c"
{% end %}

macro asdf
  {% if Thing.foo? %}
    1
  {% elsif Thing.bar? %}
    2
  {% else %}
    3
  {% end %}
end

{%if foo?(:bar)%}{%elsif foo?(:baz)%}{%else%}{%end%}

---

(expressions
  (macro_if
    cond: (call
      method: (identifier)
      arguments: (argument_list
        (symbol
          (literal_content))))
    then: (expressions
      (macro_content)
      (macro_content
        (literal_content))
      (macro_content)))
  (macro_def
    name: (identifier)
    body: (expressions
      (macro_content)
      (macro_if
        cond: (call
          receiver: (constant)
          method: (identifier))
        then: (expressions
          (macro_content))
        else: (macro_elsif
          cond: (call
            receiver: (constant)
            method: (identifier))
          then: (expressions
            (macro_content))
          else: (macro_else
            body: (expressions
              (macro_content)))))
      (macro_content)))
  (macro_if
    cond: (call
      method: (identifier)
      arguments: (argument_list
        (symbol
          (literal_content))))
    else: (macro_elsif
      cond: (call
        method: (identifier)
        arguments: (argument_list
          (symbol
            (literal_content))))
      else: (macro_else))))

============
macro unless
:language(crystal)
============
{% unless foo.bar? %}{% end %}

{% unless stuff %}
{{1}}
{% else %}
{{ 2 }}
{% end %}

---

(expressions
  (macro_unless
    cond: (call
      receiver: (identifier)
      method: (identifier)))
  (macro_unless
    cond: (identifier)
    then: (expressions
      (macro_content)
      (macro_expression
        (integer))
      (macro_content))
    else: (macro_else
      body: (expressions
        (macro_content)
        (macro_expression
          (integer))
        (macro_content)))))

=========
macro for
=========

{% for a, b in list %}
  puts "Hello, #{{{ a }}}"
{% end %}

{%for _ in [1,2,3]%}{%end%}

---

(expressions
  (macro_for
    var: (identifier)
    var: (identifier)
    cond: (identifier)
    body: (expressions
      (macro_content)
      (macro_content
        (literal_content)
        (interpolation
          (macro_expression
            (identifier))))
      (macro_content)))
  (macro_for
    var: (underscore)
    cond: (array
      (integer)
      (integer)
      (integer))))

================
macro expression
:language(crystal)
================

{{ puts "hello" }}
{{ a = 1 }}
{ hello: world }

---

(expressions
  (macro_expression
    (call
      method: (identifier)
      arguments: (argument_list
        (string
          (literal_content)))))
  (macro_expression
    (assign
      lhs: (identifier)
      rhs: (integer)))
  (named_tuple
    (named_expr
      name: (identifier)
      (identifier))))

=================
macro expressions
:language(crystal)
=================

{%
  if true
    puts "hello world"
  end
%}

{%%}
{%
# comment
%}

---

(expressions
  (macro_statement
    (expressions
      (if
        cond: (true)
        then: (then
          (call
            method: (identifier)
            arguments: (argument_list
              (string
                (literal_content))))))))
  (macro_statement)
  (macro_statement
    (comment)))

=========
macro def
:language(crystal)
=========

macro name(arg)
  {{ arg }}
end

macro name; {{ puts "hello" }} end

---

(expressions
  (macro_def
    name: (identifier)
    params: (param_list
      (param
        name: (identifier)))
    body: (expressions
      (macro_content)
      (macro_expression
        (identifier))
      (macro_content)))
  (macro_def
    name: (identifier)
    body: (expressions
      (macro_content)
      (macro_expression
        (call
          method: (identifier)
          arguments: (argument_list
            (string
              (literal_content)))))
      (macro_content))))

============================
nested macros and macro vars
:language(crystal)
============================

{% if true %}
  {% for i in [1, 2, 3] %}
    %var = 1 + {{ word }}
    foo = %var + 4
  {% end %}
{% end %}

---

(expressions
  (macro_if
    cond: (true)
    then: (expressions
      (macro_content)
      (macro_for
        var: (identifier)
        cond: (array
          (integer)
          (integer)
          (integer))
        body: (expressions
          (macro_content)
          (macro_var
            name: (identifier))
          (macro_content)
          (macro_expression
            (identifier))
          (macro_content)
          (macro_var
            name: (identifier))
          (macro_content)))
      (macro_content))))

===============
macro vars cont
===============

var1 = 40
%var1 = 10
var2 = 4321 % var1
var3 = 4321 %var1
var4 = 4321 %%var1
var5 = 4321 % %var1

---

(expressions
  (assign
    lhs: (identifier)
    rhs: (integer))
  (assign
    lhs: (macro_var
      name: (identifier))
    rhs: (integer))
  (assign
    lhs: (identifier)
    rhs: (call
      receiver: (integer)
      method: (operator)
      arguments: (argument_list
        (identifier))))
  (assign
    lhs: (identifier)
    rhs: (call
      receiver: (integer)
      method: (operator)
      arguments: (argument_list
        (identifier))))
  (assign
    lhs: (identifier)
    rhs: (call
      receiver: (integer)
      method: (operator)
      arguments: (argument_list
        (macro_var
          name: (identifier)))))
  (assign
    lhs: (identifier)
    rhs: (call
      receiver: (integer)
      method: (operator)
      arguments: (argument_list
        (macro_var
          name: (identifier))))))

=====================
macro var expressions
:language(crystal)
=====================

macro fresh_vars(*names)
  %var{name,other_name} = 1
  %hello{"world"} = "goodnight"

  def %no_exps
    yield
  end
  %no_exps { puts "in no_exps" }

  {% for name, index in names %}
    %name{index} = {{index}}
    %r(foo)
  {% end %}

  %q(asdf)
end

---

(expressions
  (macro_def
    name: (identifier)
    params: (param_list
      (splat_param
        name: (identifier)))
    body: (expressions
      (macro_content)
      (macro_var
        name: (identifier)
        (identifier)
        (identifier))
      (macro_content)
      (macro_var
        name: (identifier)
        (string
          (literal_content)))
      (macro_content)
      (macro_content
        (literal_content))
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_var
        name: (identifier))
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_var
        name: (identifier))
      (macro_content)
      (macro_content
        (literal_content))
      (macro_content)
      (macro_for
        var: (identifier)
        var: (identifier)
        cond: (identifier)
        body: (expressions
          (macro_content)
          (macro_var
            name: (identifier)
            (identifier))
          (macro_content)
          (macro_expression
            (identifier))
          (macro_content)))
      (macro_content))))

=============================
macro def with nesting syntax
:language(crystal)
=============================
macro nested
  module A
    class B
      abstract	class C
        struct D
          abstract   struct E
            annotation F
            end

            macro G
              begin
                case H
                when I
                  foo J do |_|
                    if K
                      select
                      when L
                      when M
                      end
                    end
                  end
                end
              end
            end
          end

          def N
            unless O
              while P
              end
            end

            until Q
            end
          end

          abstract	def R

          fun S
          end
        end
      end

      lib T
        union U
        end

        enum V
          {{ foo }}
        end
      end
    end
  end
end

--------

(expressions
  (macro_def
    name: (identifier)
    body: (expressions
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_expression
        (identifier))
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content))))

===============
inline macro if
:language(crystal)
===============

{% if flag?(:tracing) %} Crystal::Tracing.init {% end %}
GC.init

---

(expressions
  (macro_if
    cond: (call
      method: (identifier)
      arguments: (argument_list
        (symbol
          (literal_content))))
    then: (expressions
      (macro_content)))
  (call
    receiver: (constant)
    method: (identifier)))

==============
macro exp type
:language(crystal)
==============

{% begin %}
var : {{ "Type" }} = 1
{% end %}

---

(expressions
  (macro_begin
    body: (expressions
      (macro_content)
      (macro_expression
        (string
          (literal_content)))
      (macro_content))))

=============
macro exp def
:language(crystal)
=============

{% begin %}
def {{ "Hello" }}.{{ "world" }}({{ "arg1" }} : {{ "Type1" }}, %my_var : Type2) : {{ "ReturnType" }}
end
{% end %}

---

(expressions
  (macro_begin
    body: (expressions
      (macro_content)
      (macro_expression
        (string
          (literal_content)))
      (macro_content)
      (macro_expression
        (string
          (literal_content)))
      (macro_content)
      (macro_expression
        (string
          (literal_content)))
      (macro_content)
      (macro_expression
        (string
          (literal_content)))
      (macro_content)
      (macro_var
        name: (identifier))
      (macro_content)
      (macro_expression
        (string
          (literal_content)))
      (macro_content))))

==============================
macro exp class / module / lib
:language(crystal)
==============================

{% begin %}
class {{ "MyClass" }} < {{ "MySuper" }}
end

struct {{ "MyStruct" }}
end

enum {{ "MyEnum" }}
end

lib {{ "MyLib" }}
end
{% end %}

---

(expressions
  (macro_begin
    body: (expressions
      (macro_content)
      (macro_expression
        (string
          (literal_content)))
      (macro_content)
      (macro_expression
        (string
          (literal_content)))
      (macro_content)
      (macro_expression
        (string
          (literal_content)))
      (macro_content)
      (macro_expression
        (string
          (literal_content)))
      (macro_content)
      (macro_expression
        (string
          (literal_content)))
      (macro_content))))

==============================
comments in macros
:language(crystal)
==============================
macro asdf
  foo
  # comments containing nesting keywords don't matter
  # end
  # begin
  # macro
  # def
end

# comments inside a macro are terminated by {%end%}
{% begin %}
  foo # comment {% end %}; 5
---

(expressions
  (macro_def
    name: (identifier)
    body: (expressions
      (macro_content)))
  (comment)
  (macro_begin
    body: (expressions
      (macro_content)))
  (integer))

=====================
single line macro def
:language(crystal)
=====================
macro foo() puts 1 end
---

(expressions
  (macro_def
    name: (identifier)
    body: (expressions
      (macro_content))))

================================================
macro nesting when identifiers end with keywords
:language(crystal)
================================================
macro asdf()
  if g_class!
    while g_begin
    end
  end
end
---

(expressions
  (macro_def
    name: (identifier)
    body: (expressions
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content))))

=====================================
nesting after macro exp
:language(crystal)
=====================================
macro foo
  {{ foo }} = if a; :a else :b end
  {% bar %} unless baz

  {{ foo }} begin b
  end

  {{ foo }} if a
  {{ bar }} unless b
end
----

(expressions
  (macro_def
    name: (identifier)
    body: (expressions
      (macro_content)
      (macro_expression
        (identifier))
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_statement
        (expressions
          (identifier)))
      (macro_content)
      (macro_expression
        (identifier))
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_expression
        (identifier))
      (macro_content)
      (macro_expression
        (identifier))
      (macro_content))))

================================================================
macro comments containing expression followed by nesting keyword
:language(crystal)
================================================================
macro foo
  a
  # {% adsf %} end
  # {{
    foo
  }} begin b
  # {{ foo }} if false
end
----

(expressions
  (macro_def
    name: (identifier)
    body: (expressions
      (macro_content)
      (macro_statement
        (expressions
          (identifier)))
      (macro_content)
      (macro_expression
        (identifier))
      (macro_content)
      (macro_expression
        (identifier))
      (macro_content))))

===================================
macro state is reset between macros
:language(crystal)
===================================
macro foo() puts 1 end
macro bar() if true; puts "yes"; end end
---

(expressions
  (macro_def
    name: (identifier)
    body: (expressions
      (macro_content)))
  (macro_def
    name: (identifier)
    body: (expressions
      (macro_content)
      (macro_content)
      (macro_content)
      (macro_content
        (literal_content))
      (macro_content)
      (macro_content)
      (macro_content))))

=======================
macro exp part of ident
:language(crystal)
=======================

{% begin %}
key.to_{{method.id}}?
{% end %}

---

(expressions
  (macro_begin
    body: (expressions
      (macro_content)
      (macro_expression
        (call
          receiver: (identifier)
          method: (identifier)))
      (macro_content))))

==================================
interpolation with macro variables
==================================
macro foo
  "#{%full_name}"
end
---

(expressions
  (macro_def
    name: (identifier)
    body: (expressions
      (macro_content)
      (macro_content
        (interpolation
          (macro_var
            name: (identifier))))
      (macro_content))))

========================
escaped macro expression
========================

macro name
  \{% if true %}
    puts "hello"
  \{% end %}
end

---

(expressions
  (macro_def
    name: (identifier)
    body: (expressions
      (macro_content)
      (macro_if
        cond: (true)
        then: (expressions
          (macro_content)
          (macro_content
            (literal_content))
          (macro_content)))
      (macro_content))))

==========================================
macro expressions in string interpolation
==========================================
{% for byte, i in bytes %}
  # See `Log#{{method.id}}`.
  "#{1}"
  "#{{1}}"
  "#{{{1}}}"
  "#{{{byte}}.to_s(16)}"
{% end %}

macro yield_decoded_chunk_bytes(*bytes, chunk_pos)
  Error.new("#{{{foo}}} 0x#{{{byte}}.to_s(16)} #{{{chunk_pos}} + {{i}}}")
  # See `Log#{{method.id}}`.
  "#{1}"
  "#{{1}}"
  "#{{{1}}}"
end
---

(expressions
  (macro_for
    var: (identifier)
    var: (identifier)
    cond: (identifier)
    body: (expressions
      (macro_content)
      (macro_expression
        (call
          receiver: (identifier)
          method: (identifier)))
      (macro_content)
      (macro_content
        (interpolation
          (integer)))
      (macro_content)
      (macro_content
        (interpolation
          (tuple
            (integer))))
      (macro_content)
      (macro_content
        (interpolation
          (macro_expression
            (integer))))
      (macro_content)
      (macro_content
        (interpolation
          (call
            receiver: (macro_expression
              (identifier))
            method: (identifier)
            arguments: (argument_list
              (integer)))))
      (macro_content)))
  (macro_def
    name: (identifier)
    params: (param_list
      (splat_param
        name: (identifier))
      (param
        name: (identifier)))
    body: (expressions
      (macro_content)
      (macro_content
        (interpolation
          (macro_expression
            (identifier)))
        (literal_content)
        (interpolation
          (call
            receiver: (macro_expression
              (identifier))
            method: (identifier)
            arguments: (argument_list
              (integer))))
        (literal_content)
        (interpolation
          (call
            receiver: (macro_expression
              (identifier))
            method: (operator)
            arguments: (argument_list
              (macro_expression
                (identifier))))))
      (macro_content)
      (macro_expression
        (call
          receiver: (identifier)
          method: (identifier)))
      (macro_content)
      (macro_content
        (interpolation
          (integer)))
      (macro_content)
      (macro_content
        (interpolation
          (tuple
            (integer))))
      (macro_content)
      (macro_content
        (interpolation
          (macro_expression
            (integer))))
      (macro_content))))

===========================================
splat and double splat in macro expressions
:language(crystal)
===========================================
{%begin%}
  {{ *{Int32} }}
  {{ **{a: Int32} }}
{%end%}
---

(expressions
  (macro_begin
    body: (expressions
      (macro_content)
      (macro_expression
        (splat
          (tuple
            (constant))))
      (macro_content)
      (macro_expression
        (double_splat
          (named_tuple
            (named_expr
              name: (identifier)
              (constant)))))
      (macro_content))))

===============================================
nested string interpolation in macro expression
===============================================
macro strings
  "#{{{{"#{a.inspect}"}}}}"
end
----

(expressions
  (macro_def
    name: (identifier)
    body: (expressions
      (macro_content)
      (macro_content
        (interpolation
          (macro_expression
            (tuple
              (string
                (interpolation
                  (call
                    receiver: (identifier)
                    method: (identifier))))))))
      (macro_content))))

===============================
inline modifier statements in macro expression
:language(crystal)
===============================
macro foo
  {{ skip_file if compare_versions(Crystal::VERSION, "1.3.0") >= 0 }}

  {% debug unless env("NO_DEBUG") %}
end
---

(expressions
  (macro_def
    name: (identifier)
    body: (expressions
      (macro_content)
      (macro_expression
        (modifier_if
          then: (identifier)
          cond: (call
            receiver: (call
              method: (identifier)
              arguments: (argument_list
                (constant)
                (string
                  (literal_content))))
            method: (operator)
            arguments: (argument_list
              (integer)))))
      (macro_content)
      (macro_statement
        (expressions
          (modifier_unless
            then: (identifier)
            cond: (call
              method: (identifier)
              arguments: (argument_list
                (string
                  (literal_content)))))))
      (macro_content))))

========================
macro var as block param
:language(crystal)
========================

{% begin %}
  method do |%buffer|
    puts %buffer
  end
{% end %}

---

(expressions
  (macro_begin
    body: (expressions
      (macro_content)
      (macro_var
        name: (identifier))
      (macro_content)
      (macro_var
        name: (identifier))
      (macro_content))))

=============================
def identifier with macro exp
:language(crystal)
=============================

{% begin %}
  def hello_{{ "world" }}(a, b)
  end
{% end %}

---

(expressions
  (macro_begin
    body: (expressions
      (macro_content)
      (macro_expression
        (string
          (literal_content)))
      (macro_content))))

=====================
ivar macro expression
:language(crystal)
=====================

{% begin %}
  @{{ name }} = %bar{name}
{% end %}

---

(expressions
  (macro_begin
    body: (expressions
      (macro_content)
      (macro_expression
        (identifier))
      (macro_content)
      (macro_var
        name: (identifier)
        (identifier))
      (macro_content))))

===============================
pseudo-methods with macro nodes
:language(crystal)
===============================

{% begin %}
  a.is_a?({{type}})
{% end %}

---

(expressions
  (macro_begin
    body: (expressions
      (macro_content)
      (macro_expression
        (identifier))
      (macro_content))))

======================
macro var in call args
:language(crystal)
======================

{% begin %}
  method.call %mvar
{% end %}

---

(expressions
  (macro_begin
    body: (expressions
      (macro_content)
      (macro_var
        name: (identifier))
      (macro_content))))
