================================================================================
Function examples
================================================================================

fn add(x: Int, y: Int) -> Int {
  x + y
}
fn twice(f: fn(t) -> t, x: t) -> t {
  f(f(x))
}
fn inferred_identity(x) {
  x
}
fn replace(
  in string: String,
  each pattern: String,
  with replacement: String
) {
  string.replace(in: string, each: pattern, with: replacement)
}

--------------------------------------------------------------------------------

(source_file
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier)
        type: (type
          name: (type_identifier)))
      (function_parameter
        name: (identifier)
        type: (type
          name: (type_identifier))))
    return_type: (type
      name: (type_identifier))
    body: (block
      (binary_expression
        left: (identifier)
        right: (identifier))))
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier)
        type: (function_type
          parameter_types: (function_parameter_types
            (type_var))
          return_type: (type_var)))
      (function_parameter
        name: (identifier)
        type: (type_var)))
    return_type: (type_var)
    body: (block
      (function_call
        function: (identifier)
        arguments: (arguments
          (argument
            value: (function_call
              function: (identifier)
              arguments: (arguments
                (argument
                  value: (identifier)))))))))
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier)))
    body: (block
      (identifier)))
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        label: (label)
        name: (identifier)
        type: (type
          name: (type_identifier)))
      (function_parameter
        label: (label)
        name: (identifier)
        type: (type
          name: (type_identifier)))
      (function_parameter
        label: (label)
        name: (identifier)
        type: (type
          name: (type_identifier))))
    body: (block
      (function_call
        function: (field_access
          record: (identifier)
          field: (label))
        arguments: (arguments
          (argument
            label: (label)
            value: (identifier))
          (argument
            label: (label)
            value: (identifier))
          (argument
            label: (label)
            value: (identifier)))))))

================================================================================
Public function examples
================================================================================

pub fn add(x: Int, y: Int) -> Int {
  x + y
}
pub fn twice(f: fn(t) -> t, x: t) -> t {
  f(f(x))
}
pub fn inferred_identity(x) {
  x
}
pub fn replace(
  in string: String,
  each pattern: String,
  with replacement: String
) {
  string.replace(in: in, each: each, with: with)
}

--------------------------------------------------------------------------------

(source_file
  (function
    (visibility_modifier)
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier)
        type: (type
          name: (type_identifier)))
      (function_parameter
        name: (identifier)
        type: (type
          name: (type_identifier))))
    return_type: (type
      name: (type_identifier))
    body: (block
      (binary_expression
        left: (identifier)
        right: (identifier))))
  (function
    (visibility_modifier)
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier)
        type: (function_type
          parameter_types: (function_parameter_types
            (type_var))
          return_type: (type_var)))
      (function_parameter
        name: (identifier)
        type: (type_var)))
    return_type: (type_var)
    body: (block
      (function_call
        function: (identifier)
        arguments: (arguments
          (argument
            value: (function_call
              function: (identifier)
              arguments: (arguments
                (argument
                  value: (identifier)))))))))
  (function
    (visibility_modifier)
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier)))
    body: (block
      (identifier)))
  (function
    (visibility_modifier)
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        label: (label)
        name: (identifier)
        type: (type
          name: (type_identifier)))
      (function_parameter
        label: (label)
        name: (identifier)
        type: (type
          name: (type_identifier)))
      (function_parameter
        label: (label)
        name: (identifier)
        type: (type
          name: (type_identifier))))
    body: (block
      (function_call
        function: (field_access
          record: (identifier)
          field: (label))
        arguments: (arguments
          (argument
            label: (label)
            value: (identifier))
          (argument
            label: (label)
            value: (identifier))
          (argument
            label: (label)
            value: (identifier)))))))

================================================================================
Basic functions
================================================================================

fn unfinished() {}
fn str() {
  "Hello, World!"
}
fn integer() {
  42
}
fn float() {
  12.34
}
fn record() {
  Cat(name: "Nubi")
}
fn remote_record() {
  cats.Cat(name: "Nubi")
}
fn var(x) {
  x
}
fn funcall() {
  myfun()
}
fn unfinished() {
  todo as "Finish me!"
}
fn do_panic() {
  panic
  panic as "aaaah!"
}
fn tuple(x) {
  #(x, 1)
}
fn list(x) {
  [1, 2, ..x]
}
fn bit_string() {
  <<0:4, 1:3, 1:1>>
}
fn return_fun(x) {
  fn(y: Int) { x + y }
}
fn block() {
  {
    1 + 1
    "Hello, World!"
  }
}
fn foob(x, y) {
  case x, y {
    1, 2 | 3, 4 -> True
    _else -> False
  }
}
fn assert_assignment() {
  let assert Ok(a) = Ok(1)
  let assert x = {
    1 + 1
    "Hello, World!"
  }
  let assert y = x
  let assert #(x, _) = #(1, 2)
  let assert Ok(_) = Ok(10) as "This never fails"
  let assert 10 = 11 as { "message1" <> "message2" }
}
fn assignment() {
  let x = {
    1 + 1
    "Hello, World!"
  }
  let y = x
  let #(x, _) = #(1, 2)
}
fn assertions() {
  assert True && False
  assert add(1, 2) == 3
  assert !some_value
  assert {
    10
    11
  } != 10
  assert result.is_ok(Ok(10))
}
fn update(x) {
  Cat(..x, name: "Nubi", cuteness: 1000 + 1001)
  animals.Dog(..myfun(), name: "Rey", cuteness: 1950)
}
fn tuple_access(x) {
  x.1
}
fn field_access(x) {
  x.name
}

--------------------------------------------------------------------------------

(source_file
  (function
    name: (identifier)
    parameters: (function_parameters)
    body: (block))
  (function
    name: (identifier)
    parameters: (function_parameters)
    body: (block
      (string
        (quoted_content))))
  (function
    name: (identifier)
    parameters: (function_parameters)
    body: (block
      (integer)))
  (function
    name: (identifier)
    parameters: (function_parameters)
    body: (block
      (float)))
  (function
    name: (identifier)
    parameters: (function_parameters)
    body: (block
      (record
        name: (constructor_name)
        arguments: (arguments
          (argument
            label: (label)
            value: (string
              (quoted_content)))))))
  (function
    name: (identifier)
    parameters: (function_parameters)
    body: (block
      (record
        name: (remote_constructor_name
          module: (identifier)
          name: (constructor_name))
        arguments: (arguments
          (argument
            label: (label)
            value: (string
              (quoted_content)))))))
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier)))
    body: (block
      (identifier)))
  (function
    name: (identifier)
    parameters: (function_parameters)
    body: (block
      (function_call
        function: (identifier)
        arguments: (arguments))))
  (function
    name: (identifier)
    parameters: (function_parameters)
    body: (block
      (todo
        message: (string
          (quoted_content)))))
  (function
    name: (identifier)
    parameters: (function_parameters)
    body: (block
      (panic)
      (panic
        message: (string
          (quoted_content)))))
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier)))
    body: (block
      (tuple
        (identifier)
        (integer))))
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier)))
    body: (block
      (list
        (integer)
        (integer)
        spread: (identifier))))
  (function
    name: (identifier)
    parameters: (function_parameters)
    body: (block
      (bit_string
        (bit_string_segment
          value: (integer)
          options: (bit_string_segment_options
            (integer)))
        (bit_string_segment
          value: (integer)
          options: (bit_string_segment_options
            (integer)))
        (bit_string_segment
          value: (integer)
          options: (bit_string_segment_options
            (integer))))))
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier)))
    body: (block
      (anonymous_function
        parameters: (function_parameters
          (function_parameter
            name: (identifier)
            type: (type
              name: (type_identifier))))
        body: (block
          (binary_expression
            left: (identifier)
            right: (identifier))))))
  (function
    name: (identifier)
    parameters: (function_parameters)
    body: (block
      (block
        (binary_expression
          left: (integer)
          right: (integer))
        (string
          (quoted_content)))))
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier))
      (function_parameter
        name: (identifier)))
    body: (block
      (case
        subjects: (case_subjects
          (identifier)
          (identifier))
        clauses: (case_clauses
          (case_clause
            patterns: (case_clause_patterns
              (case_clause_pattern
                (integer)
                (integer))
              (case_clause_pattern
                (integer)
                (integer)))
            value: (record
              name: (constructor_name)))
          (case_clause
            patterns: (case_clause_patterns
              (case_clause_pattern
                (discard)))
            value: (record
              name: (constructor_name)))))))
  (function
    name: (identifier)
    parameters: (function_parameters)
    body: (block
      (let_assert
        pattern: (record_pattern
          name: (constructor_name)
          arguments: (record_pattern_arguments
            (record_pattern_argument
              pattern: (identifier))))
        value: (record
          name: (constructor_name)
          arguments: (arguments
            (argument
              value: (integer)))))
      (let_assert
        pattern: (identifier)
        value: (block
          (binary_expression
            left: (integer)
            right: (integer))
          (string
            (quoted_content))))
      (let_assert
        pattern: (identifier)
        value: (identifier))
      (let_assert
        pattern: (tuple_pattern
          (identifier)
          (discard))
        value: (tuple
          (integer)
              (integer)))
          (let_assert
            pattern: (record_pattern
              name: (constructor_name)
              arguments: (record_pattern_arguments
                (record_pattern_argument
                  pattern: (discard))))
            value: (record
              name: (constructor_name)
              arguments: (arguments
                (argument
                  value: (integer))))
            message: (string
              (quoted_content)))
          (let_assert
            pattern: (integer)
            value: (integer)
            message: (block
              (binary_expression
                left: (string
                  (quoted_content))
                right: (string
                  (quoted_content)))))))
  (function
    name: (identifier)
    parameters: (function_parameters)
    body: (block
      (let
        pattern: (identifier)
        value: (block
          (binary_expression
            left: (integer)
            right: (integer))
          (string
            (quoted_content))))
      (let
        pattern: (identifier)
        value: (identifier))
      (let
        pattern: (tuple_pattern
          (identifier)
          (discard))
        value: (tuple
          (integer)
          (integer)))))
  (function
    name: (identifier)
    parameters: (function_parameters)
    body: (block
      (assert
        value: (binary_expression
          left: (record
            name: (constructor_name))
          right: (record
            name: (constructor_name))))
      (assert
        value: (binary_expression
          left: (function_call
            function: (identifier)
            arguments: (arguments
              (argument
                value: (integer))
              (argument
                value: (integer))))
          right: (integer)))
      (assert
        value: (boolean_negation
          (identifier)))
      (assert
        value: (binary_expression
          left: (block
            (integer)
            (integer))
          right: (integer)))
      (assert
        value: (function_call
          function: (field_access
            record: (identifier)
            field: (label))
          arguments: (arguments
            (argument
              value: (record
                name: (constructor_name)
                arguments: (arguments
                  (argument
                    value: (integer))))))))))
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier)))
    body: (block
      (record_update
        constructor: (constructor_name)
        spread: (identifier)
        arguments: (record_update_arguments
          (record_update_argument
            label: (label)
            value: (string
              (quoted_content)))
          (record_update_argument
            label: (label)
            value: (binary_expression
              left: (integer)
              right: (integer)))))
      (record_update
        constructor: (remote_constructor_name
          module: (identifier)
          name: (constructor_name))
        spread: (function_call
          function: (identifier)
          arguments: (arguments))
        arguments: (record_update_arguments
          (record_update_argument
            label: (label)
            value: (string
              (quoted_content)))
          (record_update_argument
            label: (label)
            value: (integer))))))
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier)))
    body: (block
      (tuple_access
        tuple: (identifier)
        index: (integer))))
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier)))
    body: (block
      (field_access
        record: (identifier)
        field: (label)))))

================================================================================
Cases
================================================================================

fn trial(x, y, z) {
  case x {
    1 -> 2
    2 -> 3
    _ -> 4
  }

  case y {
    #(z, _) | #(_, z) if z.1 == 5 -> True
    #(_, z) if z.2 >= 4 < 5 || z.1 > 6 -> True
  }

  case #(x, y) {
    #(1, 2) -> #(3, 4)
    _ -> #(5, 6)
  }

  case z {
    Uri(scheme: Some("https"), ..) -> True
    _ -> False
  }
}

--------------------------------------------------------------------------------

(source_file
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier))
      (function_parameter
        name: (identifier))
      (function_parameter
        name: (identifier)))
    body: (block
      (case
        subjects: (case_subjects
          (identifier))
        clauses: (case_clauses
          (case_clause
            patterns: (case_clause_patterns
              (case_clause_pattern
                (integer)))
            value: (integer))
          (case_clause
            patterns: (case_clause_patterns
              (case_clause_pattern
                (integer)))
            value: (integer))
          (case_clause
            patterns: (case_clause_patterns
              (case_clause_pattern
                (discard)))
            value: (integer))))
      (case
        subjects: (case_subjects
          (identifier))
        clauses: (case_clauses
          (case_clause
            patterns: (case_clause_patterns
              (case_clause_pattern
                (tuple_pattern
                  (identifier)
                  (discard)))
              (case_clause_pattern
                (tuple_pattern
                  (discard)
                  (identifier))))
            guard: (case_clause_guard
              (binary_expression
                left: (tuple_access
                  tuple: (identifier)
                  index: (integer))
                right: (integer)))
            value: (record
              name: (constructor_name)))
          (case_clause
            patterns: (case_clause_patterns
              (case_clause_pattern
                (tuple_pattern
                  (discard)
                  (identifier))))
            guard: (case_clause_guard
              (binary_expression
                left: (binary_expression
                  left: (binary_expression
                    left: (tuple_access
                      tuple: (identifier)
                      index: (integer))
                    right: (integer))
                  right: (integer))
                right: (binary_expression
                  left: (tuple_access
                    tuple: (identifier)
                    index: (integer))
                  right: (integer))))
            value: (record
              name: (constructor_name)))))
      (case
        subjects: (case_subjects
          (tuple
            (identifier)
            (identifier)))
        clauses: (case_clauses
          (case_clause
            patterns: (case_clause_patterns
              (case_clause_pattern
                (tuple_pattern
                  (integer)
                  (integer))))
            value: (tuple
              (integer)
              (integer)))
          (case_clause
            patterns: (case_clause_patterns
              (case_clause_pattern
                (discard)))
            value: (tuple
              (integer)
              (integer)))))
      (case
        subjects: (case_subjects
          (identifier))
        clauses: (case_clauses
          (case_clause
            patterns: (case_clause_patterns
              (case_clause_pattern
                (record_pattern
                  name: (constructor_name)
                  arguments: (record_pattern_arguments
                    (record_pattern_argument
                      label: (label)
                      pattern: (record_pattern
                        name: (constructor_name)
                        arguments: (record_pattern_arguments
                          (record_pattern_argument
                            pattern: (string
                              (quoted_content))))))
                    (pattern_spread)))))
            value: (record
              name: (constructor_name)))
          (case_clause
            patterns: (case_clause_patterns
              (case_clause_pattern
                (discard)))
            value: (record
              name: (constructor_name))))))))

================================================================================
Let expressions
================================================================================

let foo: fn(Int) -> Int = fn(x) { x }
let fun_ref = float.to_string

--------------------------------------------------------------------------------

(source_file
  (let
    pattern: (identifier)
    type: (function_type
      parameter_types: (function_parameter_types
        (type
          name: (type_identifier)))
      return_type: (type
        name: (type_identifier)))
    value: (anonymous_function
      parameters: (function_parameters
        (function_parameter
          name: (identifier)))
      body: (block
        (identifier))))
  (let
    pattern: (identifier)
    value: (field_access
      record: (identifier)
      field: (label))))

================================================================================
Complex binary expressions
================================================================================

fn complicated(x, y) {
  x > y && x >= y || x + y |> fun() < x * y + 1
}

--------------------------------------------------------------------------------

(source_file
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier))
      (function_parameter
        name: (identifier)))
    body: (block
      (binary_expression
        left: (binary_expression
          left: (binary_expression
            left: (identifier)
            right: (identifier))
          right: (binary_expression
            left: (identifier)
            right: (identifier)))
        right: (binary_expression
          left: (binary_expression
            left: (binary_expression
              left: (identifier)
              right: (identifier))
            right: (function_call
              function: (identifier)
              arguments: (arguments)))
          right: (binary_expression
            left: (binary_expression
              left: (identifier)
              right: (identifier))
            right: (integer)))))))

================================================================================
Complex nesting of field and tuple access
================================================================================

fn complex_data_fun(x, y) {
  y.barbaz.2.thing
  x.1.qux.10
  x.2.foobar.1("Hello", 1)
  y.quux.2.quuz(12.34, "a")
}

--------------------------------------------------------------------------------

(source_file
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier))
      (function_parameter
        name: (identifier)))
    body: (block
      (field_access
        record: (tuple_access
          tuple: (field_access
            record: (identifier)
            field: (label))
          index: (integer))
        field: (label))
      (tuple_access
        tuple: (field_access
          record: (tuple_access
            tuple: (identifier)
            index: (integer))
          field: (label))
        index: (integer))
      (function_call
        function: (tuple_access
          tuple: (field_access
            record: (tuple_access
              tuple: (identifier)
              index: (integer))
            field: (label))
          index: (integer))
        arguments: (arguments
          (argument
            value: (string
              (quoted_content)))
          (argument
            value: (integer))))
      (function_call
        function: (field_access
          record: (tuple_access
            tuple: (field_access
              record: (identifier)
              field: (label))
            index: (integer))
          field: (label))
        arguments: (arguments
          (argument
            value: (float))
          (argument
            value: (string
              (quoted_content))))))))

================================================================================
Unusual function invocations
================================================================================

fn weird(x) {
  x(name: "Nubi")
  fn (x) { x + 1 }(3)
  {
    let fun = fn (x) { x + 1 }
  }(3)
  case 5 {
    5 -> fn (x) { x + 1 }
  }(3)
  returns_fun(1)(5)
}

--------------------------------------------------------------------------------

(source_file
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier)))
    body: (block
      (function_call
        function: (identifier)
        arguments: (arguments
          (argument
            label: (label)
            value: (string
              (quoted_content)))))
      (function_call
        function: (anonymous_function
          parameters: (function_parameters
            (function_parameter
              name: (identifier)))
          body: (block
            (binary_expression
              left: (identifier)
              right: (integer))))
        arguments: (arguments
          (argument
            value: (integer))))
      (function_call
        function: (block
          (let
            pattern: (identifier)
            value: (anonymous_function
              parameters: (function_parameters
                (function_parameter
                  name: (identifier)))
              body: (block
                (binary_expression
                  left: (identifier)
                  right: (integer))))))
        arguments: (arguments
          (argument
            value: (integer))))
      (function_call
        function: (case
          subjects: (case_subjects
            (integer))
          clauses: (case_clauses
            (case_clause
              patterns: (case_clause_patterns
                (case_clause_pattern
                  (integer)))
              value: (anonymous_function
                parameters: (function_parameters
                  (function_parameter
                    name: (identifier)))
                body: (block
                  (binary_expression
                    left: (identifier)
                    right: (integer)))))))
        arguments: (arguments
          (argument
            value: (integer))))
      (function_call
        function: (function_call
          function: (identifier)
          arguments: (arguments
            (argument
              value: (integer))))
        arguments: (arguments
          (argument
            value: (integer)))))))

================================================================================
Various discard variables
================================================================================

fn ignores(foo, _bar) {
  let _baz = 4
  qux(foo, _hole)
}

--------------------------------------------------------------------------------

(source_file
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier))
      (function_parameter
        name: (discard)))
    body: (block
      (let
        pattern: (discard)
        value: (integer))
      (function_call
        function: (identifier)
        arguments: (arguments
          (argument
            value: (identifier))
          (argument
            value: (hole)))))))

================================================================================
Weird lists
================================================================================

fn lists(x) {
  []
  [1,]
  [1 ..x]
  [1,..x]
}

--------------------------------------------------------------------------------

(source_file
  (function
    name: (identifier)
    parameters: (function_parameters
      (function_parameter
        name: (identifier)))
    body: (block
      (list)
      (list
        (integer))
      (list
        (integer)
        spread: (identifier))
      (list
        (integer)
        spread: (identifier)))))

================================================================================
Comment in string
================================================================================

io.println("// hello world!\n")

--------------------------------------------------------------------------------

(source_file
  (function_call
    function: (field_access
      record: (identifier)
      field: (label))
    arguments: (arguments
      (argument
        value: (string
          (quoted_content)
          (escape_sequence))))))

================================================================================
Call with label shorthand
================================================================================

wibble(arg1:, arg2:)

--------------------------------------------------------------------------------

(source_file
  (function_call
    (identifier)
    (arguments
      (argument
        (label))
      (argument
        (label)))))
