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
168 lines
6.0 KiB
Elixir
168 lines
6.0 KiB
Elixir
defmodule Til.ListParserTest do
|
|
use ExUnit.Case, async: true
|
|
alias Til.Parser
|
|
import Til.TestHelpers
|
|
|
|
describe "List Parsing" do
|
|
test "parses an empty list []" do
|
|
source = "[]"
|
|
{:ok, nodes_map} = Parser.parse(source)
|
|
file_node = get_file_node_from_map(nodes_map)
|
|
list_node_id = hd(file_node.children)
|
|
list_node = Map.get(nodes_map, list_node_id)
|
|
|
|
assert list_node.ast_node_type == :list_expression
|
|
assert list_node.raw_string == "[]"
|
|
assert list_node.children == []
|
|
assert list_node.parsing_error == nil
|
|
|
|
# Location check
|
|
# "[]"
|
|
# ^ offset 0, line 1, col 1
|
|
# ^ offset 1, line 1, col 2
|
|
# ^ offset 2, line 1, col 3 (end position, exclusive for offset, inclusive for col)
|
|
assert list_node.location == [0, 1, 1, 2, 1, 3]
|
|
|
|
# file_node is already fetched and used to get list_node
|
|
assert file_node.children == [list_node.id]
|
|
end
|
|
|
|
test "parses a list of integers [1 2 3]" do
|
|
source = "[1 2 3]"
|
|
{:ok, nodes_map} = Parser.parse(source)
|
|
file_node = get_file_node_from_map(nodes_map)
|
|
list_node_id = hd(file_node.children)
|
|
list_node = Map.get(nodes_map, list_node_id)
|
|
|
|
assert list_node.ast_node_type == :list_expression
|
|
assert list_node.raw_string == "[1 2 3]"
|
|
assert list_node.parsing_error == nil
|
|
assert length(list_node.children) == 3
|
|
|
|
# Location check
|
|
# "[1 2 3]"
|
|
# ^ offset 0, line 1, col 1
|
|
# ^ offset 7, line 1, col 8
|
|
assert list_node.location == [0, 1, 1, 7, 1, 8]
|
|
|
|
# Check children
|
|
child1_id = Enum.at(list_node.children, 0)
|
|
child2_id = Enum.at(list_node.children, 1)
|
|
child3_id = Enum.at(list_node.children, 2)
|
|
|
|
child1 = Map.get(nodes_map, child1_id)
|
|
child2 = Map.get(nodes_map, child2_id)
|
|
child3 = Map.get(nodes_map, child3_id)
|
|
|
|
assert child1.ast_node_type == :literal_integer
|
|
assert child1.value == 1
|
|
assert child1.raw_string == "1"
|
|
assert child1.parent_id == list_node.id
|
|
# "[1 2 3]"
|
|
# ^ offset 1, line 1, col 2
|
|
# ^ offset 2, line 1, col 3
|
|
assert child1.location == [1, 1, 2, 2, 1, 3]
|
|
|
|
assert child2.ast_node_type == :literal_integer
|
|
assert child2.value == 2
|
|
assert child2.raw_string == "2"
|
|
assert child2.parent_id == list_node.id
|
|
# "[1 2 3]"
|
|
# ^ offset 3, line 1, col 4
|
|
# ^ offset 4, line 1, col 5
|
|
assert child2.location == [3, 1, 4, 4, 1, 5]
|
|
|
|
assert child3.ast_node_type == :literal_integer
|
|
assert child3.value == 3
|
|
assert child3.raw_string == "3"
|
|
assert child3.parent_id == list_node.id
|
|
# "[1 2 3]"
|
|
# ^ offset 5, line 1, col 6
|
|
# ^ offset 6, line 1, col 7
|
|
assert child3.location == [5, 1, 6, 6, 1, 7]
|
|
|
|
# file_node is already fetched and used to get list_node
|
|
assert file_node.children == [list_node.id]
|
|
end
|
|
|
|
test "parses an unclosed list [1 2" do
|
|
source = "[1 2"
|
|
{:ok, nodes_map} = Parser.parse(source)
|
|
file_node = get_file_node_from_map(nodes_map)
|
|
list_node_id = hd(file_node.children)
|
|
list_node = Map.get(nodes_map, list_node_id)
|
|
|
|
assert list_node.ast_node_type == :list_expression
|
|
assert list_node.raw_string == "[1 2" # Raw string is what was consumed for the list
|
|
assert list_node.parsing_error == "Unclosed list"
|
|
assert length(list_node.children) == 2 # Children that were successfully parsed
|
|
|
|
# Location check for the unclosed list node
|
|
# "[1 2"
|
|
# ^ offset 0, line 1, col 1
|
|
# ^ offset 4, line 1, col 5 (end of consumed input for this node)
|
|
assert list_node.location == [0, 1, 1, 4, 1, 5]
|
|
|
|
child1 = get_node_by_id(nodes_map, Enum.at(list_node.children, 0))
|
|
child2 = get_node_by_id(nodes_map, Enum.at(list_node.children, 1))
|
|
|
|
assert child1.value == 1
|
|
assert child2.value == 2
|
|
|
|
# file_node is already fetched and used to get list_node
|
|
assert file_node.children == [list_node.id]
|
|
end
|
|
|
|
test "parses an unexpected closing bracket ] at top level" do
|
|
source = "]"
|
|
{:ok, nodes_map} = Parser.parse(source)
|
|
file_node = get_file_node_from_map(nodes_map)
|
|
error_node_id = hd(file_node.children)
|
|
error_node = Map.get(nodes_map, error_node_id)
|
|
|
|
assert error_node.ast_node_type == :unknown # Or a more specific error type if desired
|
|
assert error_node.raw_string == "]"
|
|
assert error_node.parsing_error == "Unexpected ']'"
|
|
|
|
# Location check
|
|
# "]"
|
|
# ^ offset 0, line 1, col 1
|
|
# ^ offset 1, line 1, col 2
|
|
assert error_node.location == [0, 1, 1, 1, 1, 2]
|
|
|
|
# file_node is already fetched and used to get error_node
|
|
assert file_node.children == [error_node.id]
|
|
end
|
|
|
|
test "parses an unexpected closing bracket ] inside S-expression (foo ])" do
|
|
source = "(foo ])"
|
|
{:ok, nodes_map} = Parser.parse(source)
|
|
file_node = get_file_node_from_map(nodes_map)
|
|
sexpr_node_id = hd(file_node.children) # S-expression is the top-level
|
|
sexpr_node = Map.get(nodes_map, sexpr_node_id)
|
|
|
|
assert sexpr_node.ast_node_type == :s_expression
|
|
assert sexpr_node.raw_string == "(foo ])"
|
|
assert sexpr_node.parsing_error == nil # The S-expression itself is not unclosed
|
|
assert length(sexpr_node.children) == 2 # 'foo' and the error node for ']'
|
|
|
|
# First child 'foo'
|
|
foo_node = get_node_by_id(nodes_map, Enum.at(sexpr_node.children, 0))
|
|
assert foo_node.ast_node_type == :symbol
|
|
assert foo_node.name == "foo"
|
|
|
|
# Second child is the error node for ']'
|
|
error_node = get_node_by_id(nodes_map, Enum.at(sexpr_node.children, 1))
|
|
assert error_node.ast_node_type == :unknown
|
|
assert error_node.raw_string == "]"
|
|
assert error_node.parsing_error == "Unexpected ']'"
|
|
assert error_node.parent_id == sexpr_node.id
|
|
# Location check for ']'
|
|
# "(foo ])"
|
|
# ^ offset 5, line 1, col 6
|
|
# ^ offset 6, line 1, col 7
|
|
assert error_node.location == [5, 1, 6, 6, 1, 7]
|
|
end
|
|
end
|
|
end
|