checkpoint failing test after fixing tests checkpoint checkpoint checkpoint re-work asd checkpoint checkpoint checkpoint mix proj checkpoint mix first parser impl checkpoint fix tests re-org parser checkpoint strings fix multiline strings tuples checkpoint maps checkpoint checkpoint checkpoint checkpoint fix weird eof expression parse error checkpoint before typing checkpoint checpoint checkpoint checkpoint checkpoint ids in primitive types checkpoint checkpoint fix tests initial annotation checkpoint checkpoint checkpoint union subtyping conventions refactor - split typer typing tuples checkpoint test refactor checkpoint test refactor parsing atoms checkpoint atoms wip lists checkpoint typing lists checkopint checkpoint wip fixing correct list typing map discussion checkpoint map basic typing fix tests checkpoint checkpoint checkpoint checkpoint fix condition typing fix literal keys in map types checkpoint union types checkpoint union type checkpoint row types discussion & bidirectional typecheck checkpoint basic lambdas checkpoint lambdas typing application wip function application checkpoint checkpoint checkpoint cduce checkpoint checkpoint checkpoint checkpoint checkpoint checkpoint checkpoint
247 lines
9.6 KiB
Elixir
247 lines
9.6 KiB
Elixir
# defmodule Til.TypingSimpleTest do
|
|
# use ExUnit.Case, async: true
|
|
# # lets always alias AstUtils, Typer and Parser for convenience
|
|
# alias Til.AstUtils
|
|
# alias Til.Parser
|
|
# alias Til.Typer
|
|
# # Added alias
|
|
# alias Til.TestHelpers
|
|
#
|
|
# alias MapSet, as: Set
|
|
#
|
|
# describe "simple type inference tests" do
|
|
# test "types a literal integer" do
|
|
# source = "42"
|
|
# {integer_node, typed_nodes_map} = TestHelpers.typecheck_and_get_first_node(source)
|
|
#
|
|
# assert integer_node.ast_node_type == :literal_integer
|
|
#
|
|
# TestHelpers.assert_node_typed_as(integer_node, typed_nodes_map, %{
|
|
# type_kind: :literal,
|
|
# value: 42
|
|
# })
|
|
# end
|
|
#
|
|
# test "types a literal string" do
|
|
# source = "'hello'"
|
|
# {string_node, typed_nodes_map} = TestHelpers.typecheck_and_get_first_node(source)
|
|
#
|
|
# assert string_node.ast_node_type == :literal_string
|
|
#
|
|
# TestHelpers.assert_node_typed_as(string_node, typed_nodes_map, %{
|
|
# type_kind: :literal,
|
|
# value: "hello"
|
|
# })
|
|
# end
|
|
#
|
|
# # test "types a simple s-expression (e.g. function call with literals)" do
|
|
# # source = "(add 1 2)"
|
|
# # {:ok, nodes_map} = Parser.parse(source)
|
|
# #
|
|
# # flunk("Test not implemented: #{inspect(nodes_map)}")
|
|
# # end
|
|
#
|
|
# test "types an s-expression with a symbol lookup in an environment" do
|
|
# source = """
|
|
# (= x 5)
|
|
# (= y x)
|
|
# """
|
|
#
|
|
# # Parse once
|
|
# {:ok, parsed_nodes_map} = Parser.parse(source)
|
|
# # Typecheck once
|
|
# {:ok, typed_nodes_map} = Typer.type_check(parsed_nodes_map)
|
|
#
|
|
# file_node = Enum.find(Map.values(typed_nodes_map), &(&1.ast_node_type == :file))
|
|
# refute is_nil(file_node)
|
|
# assert length(file_node.children) == 2, "Expected two top-level s-expressions"
|
|
#
|
|
# # --- First assignment: (= x 5) ---
|
|
# s_expr_1_node = TestHelpers.get_nth_child_node(typed_nodes_map, 0, file_node.id)
|
|
# assert s_expr_1_node.ast_node_type == :s_expression
|
|
# assert length(s_expr_1_node.children) == 3
|
|
#
|
|
# symbol_x_lhs_node = TestHelpers.get_nth_child_node(typed_nodes_map, 1, s_expr_1_node.id)
|
|
# assert symbol_x_lhs_node.ast_node_type == :symbol
|
|
# assert symbol_x_lhs_node.name == "x"
|
|
#
|
|
# literal_5_node = TestHelpers.get_nth_child_node(typed_nodes_map, 2, s_expr_1_node.id)
|
|
# assert literal_5_node.ast_node_type == :literal_integer
|
|
# assert literal_5_node.value == 5
|
|
#
|
|
# TestHelpers.assert_node_typed_as(literal_5_node, typed_nodes_map, %{
|
|
# type_kind: :literal,
|
|
# value: 5
|
|
# })
|
|
#
|
|
# TestHelpers.assert_node_typed_as(s_expr_1_node, typed_nodes_map, %{
|
|
# type_kind: :literal,
|
|
# value: 5
|
|
# })
|
|
#
|
|
# # --- Second assignment: (= y x) ---
|
|
# s_expr_2_node = TestHelpers.get_nth_child_node(typed_nodes_map, 1, file_node.id)
|
|
# assert s_expr_2_node.ast_node_type == :s_expression
|
|
# assert length(s_expr_2_node.children) == 3
|
|
#
|
|
# symbol_y_lhs_node = TestHelpers.get_nth_child_node(typed_nodes_map, 1, s_expr_2_node.id)
|
|
# assert symbol_y_lhs_node.ast_node_type == :symbol
|
|
# assert symbol_y_lhs_node.name == "y"
|
|
#
|
|
# symbol_x_rhs_node = TestHelpers.get_nth_child_node(typed_nodes_map, 2, s_expr_2_node.id)
|
|
# assert symbol_x_rhs_node.ast_node_type == :symbol
|
|
# assert symbol_x_rhs_node.name == "x"
|
|
#
|
|
# TestHelpers.assert_node_typed_as(symbol_x_rhs_node, typed_nodes_map, %{
|
|
# type_kind: :literal,
|
|
# value: 5
|
|
# })
|
|
#
|
|
# TestHelpers.assert_node_typed_as(s_expr_2_node, typed_nodes_map, %{
|
|
# type_kind: :literal,
|
|
# value: 5
|
|
# })
|
|
#
|
|
# # Assert that 'y' in the environment (and thus its node if we were to look it up again) would be integer.
|
|
# # The symbol_y_lhs_node itself might be typed as :any before the assignment's effect is fully "realized" on it.
|
|
# # The critical part is that the environment passed forward contains y: :til_type_integer.
|
|
# # The final environment is inspected in Typer.type_check, which can be manually verified for now.
|
|
# # For this test, checking the type of the assignment expression (s_expr_2_node) and the RHS (symbol_x_rhs_node) is sufficient.
|
|
# end
|
|
#
|
|
# test "types an if expression with same type in both branches, ambiguous condition" do
|
|
# source = """
|
|
# (= cond_var some_ambiguous_symbol)
|
|
# (if cond_var 1 2)
|
|
# """
|
|
#
|
|
# {if_node, typed_nodes_map} = TestHelpers.typecheck_and_get_nth_node(source, 1)
|
|
#
|
|
# assert if_node.ast_node_type == :s_expression
|
|
#
|
|
# expected_type_1 = %{type_kind: :literal, value: 1}
|
|
# expected_type_2 = %{type_kind: :literal, value: 2}
|
|
#
|
|
# TestHelpers.assert_node_typed_as(if_node, typed_nodes_map, %{
|
|
# type_kind: :union,
|
|
# types: Set.new([expected_type_1, expected_type_2])
|
|
# })
|
|
# end
|
|
#
|
|
# test "types an if expression with different types, ambiguous condition, resulting in a union" do
|
|
# source = """
|
|
# (= cond_var some_ambiguous_symbol)
|
|
# (if cond_var 1 'hello')
|
|
# """
|
|
#
|
|
# {if_node, typed_nodes_map} = TestHelpers.typecheck_and_get_nth_node(source, 1)
|
|
#
|
|
# assert if_node.ast_node_type == :s_expression
|
|
#
|
|
# expected_int_type = %{type_kind: :literal, value: 1}
|
|
# expected_str_type = %{type_kind: :literal, value: "hello"}
|
|
#
|
|
# TestHelpers.assert_node_typed_as(if_node, typed_nodes_map, %{
|
|
# type_kind: :union,
|
|
# types: Set.new([expected_int_type, expected_str_type])
|
|
# })
|
|
# end
|
|
#
|
|
# test "types an if expression with a missing else branch, ambiguous condition (union with nil type)" do
|
|
# source = """
|
|
# (= cond_var some_ambiguous_symbol)
|
|
# (if cond_var 1)
|
|
# """
|
|
#
|
|
# {if_node, typed_nodes_map} = TestHelpers.typecheck_and_get_nth_node(source, 1)
|
|
#
|
|
# assert if_node.ast_node_type == :s_expression
|
|
#
|
|
# expected_int_type = %{type_kind: :literal, value: 1}
|
|
# expected_nil_type = %{type_kind: :literal, value: nil}
|
|
#
|
|
# TestHelpers.assert_node_typed_as(if_node, typed_nodes_map, %{
|
|
# type_kind: :union,
|
|
# types: Set.new([expected_int_type, expected_nil_type])
|
|
# })
|
|
# end
|
|
#
|
|
# test "types an if expression where then branch is nil, missing else, condition true (results in nil type)" do
|
|
# source = """
|
|
# (= x nil)
|
|
# (if true x)
|
|
# """
|
|
#
|
|
# {if_node, typed_nodes_map} = TestHelpers.typecheck_and_get_nth_node(source, 1)
|
|
# assert if_node.ast_node_type == :s_expression
|
|
# expected_nil_type = %{type_kind: :literal, value: nil}
|
|
# TestHelpers.assert_node_typed_as(if_node, typed_nodes_map, expected_nil_type)
|
|
# end
|
|
#
|
|
# test "types an if expression where the condition is true, then branch is 1, missing else (results in `1` type)" do
|
|
# source = """
|
|
# (= x 1)
|
|
# (if true x)
|
|
# """
|
|
#
|
|
# {if_node, typed_nodes_map} = TestHelpers.typecheck_and_get_nth_node(source, 1)
|
|
# assert if_node.ast_node_type == :s_expression
|
|
# expected_type_of_1 = %{type_kind: :literal, value: 1}
|
|
# TestHelpers.assert_node_typed_as(if_node, typed_nodes_map, expected_type_of_1)
|
|
# end
|
|
#
|
|
# test "types an if expression where then and else are nil, condition true (results in nil type)" do
|
|
# source = """
|
|
# (= x nil)
|
|
# (if true x x)
|
|
# """
|
|
#
|
|
# {if_node, typed_nodes_map} = TestHelpers.typecheck_and_get_nth_node(source, 1)
|
|
# assert if_node.ast_node_type == :s_expression
|
|
# expected_nil_type = %{type_kind: :literal, value: nil}
|
|
# TestHelpers.assert_node_typed_as(if_node, typed_nodes_map, expected_nil_type)
|
|
# end
|
|
#
|
|
# test "if expression with statically false condition, missing else (results in nil type)" do
|
|
# source = "(if false 123)"
|
|
# {if_node, typed_nodes_map} = TestHelpers.typecheck_and_get_first_node(source)
|
|
# assert if_node.ast_node_type == :s_expression
|
|
# expected_nil_type = %{type_kind: :literal, value: nil}
|
|
# TestHelpers.assert_node_typed_as(if_node, typed_nodes_map, expected_nil_type)
|
|
# end
|
|
#
|
|
# test "if expression with statically false condition, with else branch (results in else branch type)" do
|
|
# source = "(if false 123 'else_val')"
|
|
# {if_node, typed_nodes_map} = TestHelpers.typecheck_and_get_first_node(source)
|
|
# assert if_node.ast_node_type == :s_expression
|
|
# expected_else_type = %{type_kind: :literal, value: "else_val"}
|
|
# TestHelpers.assert_node_typed_as(if_node, typed_nodes_map, expected_else_type)
|
|
# end
|
|
#
|
|
# test "if expression with truthy non-boolean literal condition type (integer)" do
|
|
# source = """
|
|
# (if 123 'then' 'else')
|
|
# """
|
|
# # Since 123 is truthy, the type of the if expression should be the type of 'then'.
|
|
# {if_node, typed_nodes_map} = TestHelpers.typecheck_and_get_first_node(source)
|
|
# assert if_node.ast_node_type == :s_expression
|
|
#
|
|
# expected_then_type = %{type_kind: :literal, value: "then"}
|
|
# TestHelpers.assert_node_typed_as(if_node, typed_nodes_map, expected_then_type)
|
|
# end
|
|
#
|
|
# test "if expression with truthy non-boolean symbol condition type (typed as integer)" do
|
|
# source = """
|
|
# (= my_int_cond 123)
|
|
# (if my_int_cond 'then' 'else')
|
|
# """
|
|
# # my_int_cond is 123 (truthy), so the type of the if expression should be the type of 'then'.
|
|
# {if_node, typed_nodes_map} = TestHelpers.typecheck_and_get_nth_node(source, 1)
|
|
# assert if_node.ast_node_type == :s_expression
|
|
#
|
|
# expected_then_type = %{type_kind: :literal, value: "then"}
|
|
# TestHelpers.assert_node_typed_as(if_node, typed_nodes_map, expected_then_type)
|
|
# end
|
|
# end
|
|
# end
|