defmodule Til.ParseAtomTest do use ExUnit.Case, async: true alias Til.Parser import Til.TestHelpers describe "Atom parsing" do test "parses a simple atom" do source = ":hello" {:ok, _nodes_map} = Parser.parse(source) {atom_node, _map} = parse_and_get_first_node(source) assert atom_node.ast_node_type == :literal_atom assert atom_node.value == :hello assert atom_node.raw_string == ":hello" assert atom_node.location == [0, 1, 1, 6, 1, 7] end test "parses an atom with numbers and underscores" do source = ":foo_123_bar" {:ok, _nodes_map} = Parser.parse(source) {atom_node, _map} = parse_and_get_first_node(source) assert atom_node.ast_node_type == :literal_atom assert atom_node.value == :foo_123_bar assert atom_node.raw_string == ":foo_123_bar" end test "parses an atom within an s-expression" do source = "(:an_atom)" {:ok, nodes_map} = Parser.parse(source) s_expr_node = get_first_child_node(nodes_map) atom_node_id = hd(s_expr_node.children) atom_node = get_node_by_id(nodes_map, atom_node_id) assert atom_node.ast_node_type == :literal_atom assert atom_node.value == :an_atom assert atom_node.raw_string == ":an_atom" # Location of :an_atom within () assert atom_node.location == [1, 1, 2, 9, 1, 10] end test "parses multiple atoms in an s-expression" do source = "(:first :second)" {:ok, nodes_map} = Parser.parse(source) s_expr_node = get_first_child_node(nodes_map) first_atom_node = get_node_by_id(nodes_map, Enum.at(s_expr_node.children, 0)) assert first_atom_node.ast_node_type == :literal_atom assert first_atom_node.value == :first assert first_atom_node.raw_string == ":first" second_atom_node = get_node_by_id(nodes_map, Enum.at(s_expr_node.children, 1)) assert second_atom_node.ast_node_type == :literal_atom assert second_atom_node.value == :second assert second_atom_node.raw_string == ":second" end test "parses an atom followed immediately by an opening parenthesis (delimiter)" do source = ":atom_name(foo)" {:ok, nodes_map} = Parser.parse(source) # First child of the file node should be the atom atom_node = get_nth_child_node(nodes_map, 0) assert atom_node.ast_node_type == :literal_atom assert atom_node.value == :atom_name assert atom_node.raw_string == ":atom_name" assert atom_node.location == [0, 1, 1, 10, 1, 11] # Second child should be the s-expression s_expr_node = get_nth_child_node(nodes_map, 1) assert s_expr_node.ast_node_type == :s_expression assert s_expr_node.raw_string == "(foo)" end test "parses an atom at the end of input" do source = " :last_atom " {:ok, nodes_map} = Parser.parse(source) # Use trimmed for helper {atom_node, _map} = parse_and_get_first_node(String.trim(source)) assert atom_node.ast_node_type == :literal_atom assert atom_node.value == :last_atom assert atom_node.raw_string == ":last_atom" # Location needs to be checked against the original source with whitespace file_node = get_file_node_from_map(nodes_map) actual_atom_node_id = hd(file_node.children) actual_atom_node = get_node_by_id(nodes_map, actual_atom_node_id) # " :last_atom " assert actual_atom_node.location == [2, 1, 3, 12, 1, 13] end test "parses atom within a list expression" do source = "[:my_list_atom]" {:ok, nodes_map} = Parser.parse(source) list_expr_node = get_first_child_node(nodes_map) atom_node_id = hd(list_expr_node.children) atom_node = get_node_by_id(nodes_map, atom_node_id) assert atom_node.ast_node_type == :literal_atom assert atom_node.value == :my_list_atom assert atom_node.raw_string == ":my_list_atom" end test "parses atom within a tuple expression" do source = "{:my_tuple_atom}" {:ok, nodes_map} = Parser.parse(source) tuple_expr_node = get_first_child_node(nodes_map) atom_node_id = hd(tuple_expr_node.children) atom_node = get_node_by_id(nodes_map, atom_node_id) assert atom_node.ast_node_type == :literal_atom assert atom_node.value == :my_tuple_atom assert atom_node.raw_string == ":my_tuple_atom" end test "parses atom as a key in a map expression" do source = "m{:key 1}" {:ok, nodes_map} = Parser.parse(source) map_expr_node = get_first_child_node(nodes_map) key_node_id = Enum.at(map_expr_node.children, 0) key_node = get_node_by_id(nodes_map, key_node_id) assert key_node.ast_node_type == :literal_atom assert key_node.value == :key assert key_node.raw_string == ":key" value_node_id = Enum.at(map_expr_node.children, 1) value_node = get_node_by_id(nodes_map, value_node_id) assert value_node.ast_node_type == :literal_integer assert value_node.value == 1 end test "parses atom as a value in a map expression" do source = "m{'string_key' :atom_value}" {:ok, nodes_map} = Parser.parse(source) map_expr_node = get_first_child_node(nodes_map) # string_key_node is child 0 value_node_id = Enum.at(map_expr_node.children, 1) value_node = get_node_by_id(nodes_map, value_node_id) assert value_node.ast_node_type == :literal_atom assert value_node.value == :atom_value assert value_node.raw_string == ":atom_value" end end end