includes :assert :file 'parser/parser' 'parser/sexp' 'parser/var_assigner'

add_results setup name: "Var assigner tests" {
  ast = { input, expected_ast |
    parsed = brat_parser.parse input, :program, :fully
    assert parsed, "Failed to parse '#{input}'"

    input_ast = var_assigner.new(parsed.ast).assign

    true? input_ast.nodes.length == 1
      { assert_equal expected_ast, input_ast.nodes.last }
      { assert_equal expected_ast, input_ast }
  }

  test "local assignment" {
    ast "a = 1" s[:local_var_assign, :a, s[:number, 1]]
  }

  test "local reassignment" {
    ast "a = 1; a = 2" s[:program,
                          s[:local_var_assign, :a, s[:number, 1]],
                          s[:local_var_reassign, :a, s[:number, 2]]]
  }

  test "local use" {
    ast "a = 1; a" s[:program,
                      s[:local_var_assign, :a, s[:number, 1]],
                      s[:get_local_value, :a]]
  }

  test "assign in :function" {
    ast "{ a = 1 }" s[:function, [], [s[:local_var_assign, :a, s[:number, 1]]]]
  }

  test "use arg" {
    ast "{ a | a }" s[:function, [s[:arg, :a]], [s[:get_local_value, :a]]]
  }

  test "use default arg" {
    ast "{ a = 1 | a }" s[:function, [s[:def_arg, :a, s[:number, 1]]], [s[:get_local_value, :a]]]
  }

  test "use var arg" {
    ast "{ *a | a[0] }" s[:function, [s[:var_arg, :a]], [s[:call, s[:get_local_value, :a], :get, [s[:number, 0]]]]]
  }

  test "reassign arg" {
    ast "{ a | a = 1 }" s[:function, [s[:arg, :a]], [s[:local_var_reassign, :a, s[:number, 1]]]]
  }

  test ":function scope" {
    ast "{ a = 1}; a" s[:program,
                        s[:function, [], [s[:local_var_assign, :a, s[:number, 1]]]],
                        s[:get_value, :a]]
  }

  test "use upvalue" {
    ast "a = 1; { a }" s[:program,
                          s[:local_var_assign, :a, s[:number, 1]],
                          s[:function, [], [s[:get_up_value, :a]]]]
  }

  test "upvalue reassign" {
    ast "a = 1; { a = 2 }" s[:program,
                              s[:local_var_assign, :a, s[:number, 1]],
                              s[:function, [], [s[:upvar_assign, :a, s[:number, 2]]]]]
  }

  test "arg shadow" {
    ast "a = 1; { a | a }" s[:program,
                              s[:local_var_assign, :a, s[:number, 1]],
                              s[:function, [s[:arg, :a]], [s[:get_local_value, :a]]]]
  }
}