205 lines
5.7 KiB
Python
205 lines
5.7 KiB
Python
"""
|
|
Tests of calf.parser
|
|
"""
|
|
|
|
import calf.parser as cp
|
|
from conftest import parametrize
|
|
import pytest
|
|
|
|
|
|
@parametrize(
|
|
"text",
|
|
[
|
|
'"',
|
|
'"foo bar',
|
|
'"""foo bar',
|
|
'"""foo bar"',
|
|
],
|
|
)
|
|
def test_bad_strings_raise(text):
|
|
"""Tests asserting we won't let obviously bad strings fly."""
|
|
# FIXME (arrdem 2021-03-13):
|
|
# Can we provide this behavior in the lexer rather than in the parser?
|
|
with pytest.raises(ValueError):
|
|
next(cp.parse_buffer(text))
|
|
|
|
|
|
@parametrize(
|
|
"text",
|
|
[
|
|
"[1.0",
|
|
"(1.0",
|
|
"{1.0",
|
|
],
|
|
)
|
|
def test_unterminated_raises(text):
|
|
"""Tests asserting that we don't let unterminated collections parse."""
|
|
with pytest.raises(cp.CalfMissingCloseParseError):
|
|
next(cp.parse_buffer(text))
|
|
|
|
|
|
@parametrize(
|
|
"text",
|
|
[
|
|
"[{]",
|
|
"[(]",
|
|
"({)",
|
|
"([)",
|
|
"{(}",
|
|
"{[}",
|
|
],
|
|
)
|
|
def test_unbalanced_raises(text):
|
|
"""Tests asserting that we don't let missmatched collections parse."""
|
|
with pytest.raises(cp.CalfUnexpectedCloseParseError):
|
|
next(cp.parse_buffer(text))
|
|
|
|
|
|
@parametrize(
|
|
"buff, value",
|
|
[
|
|
('"foo"', "foo"),
|
|
('"foo\tbar"', "foo\tbar"),
|
|
('"foo\n\rbar"', "foo\n\rbar"),
|
|
('"foo\\"bar\\""', 'foo"bar"'),
|
|
('"""foo"""', "foo"),
|
|
('"""foo"bar"baz"""', 'foo"bar"baz'),
|
|
],
|
|
)
|
|
def test_strings_round_trip(buff, value):
|
|
assert next(cp.parse_buffer(buff)) == value
|
|
|
|
|
|
@parametrize(
|
|
"text, element_types",
|
|
[
|
|
# Integers
|
|
("(1)", ["INTEGER"]),
|
|
("( 1 )", ["INTEGER"]),
|
|
("(,1,)", ["INTEGER"]),
|
|
("(1\n)", ["INTEGER"]),
|
|
("(\n1\n)", ["INTEGER"]),
|
|
("(1, 2, 3, 4)", ["INTEGER", "INTEGER", "INTEGER", "INTEGER"]),
|
|
# Floats
|
|
("(1.0)", ["FLOAT"]),
|
|
("(1.0e0)", ["FLOAT"]),
|
|
("(1e0)", ["FLOAT"]),
|
|
("(1e0)", ["FLOAT"]),
|
|
# Symbols
|
|
("(foo)", ["SYMBOL"]),
|
|
("(+)", ["SYMBOL"]),
|
|
("(-)", ["SYMBOL"]),
|
|
("(*)", ["SYMBOL"]),
|
|
("(foo-bar)", ["SYMBOL"]),
|
|
("(+foo-bar+)", ["SYMBOL"]),
|
|
("(+foo-bar+)", ["SYMBOL"]),
|
|
("( foo bar )", ["SYMBOL", "SYMBOL"]),
|
|
# Keywords
|
|
("(:foo)", ["KEYWORD"]),
|
|
("( :foo )", ["KEYWORD"]),
|
|
("(\n:foo\n)", ["KEYWORD"]),
|
|
("(,:foo,)", ["KEYWORD"]),
|
|
("(:foo :bar)", ["KEYWORD", "KEYWORD"]),
|
|
("(:foo :bar 1)", ["KEYWORD", "KEYWORD", "INTEGER"]),
|
|
# Strings
|
|
('("foo", "bar", "baz")', ["STRING", "STRING", "STRING"]),
|
|
# Lists
|
|
("([] [] ())", ["SQLIST", "SQLIST", "LIST"]),
|
|
],
|
|
)
|
|
def test_parse_list(text, element_types):
|
|
"""Test we can parse various lists of contents."""
|
|
l_t = next(cp.parse_buffer(text, discard_whitespace=True))
|
|
assert l_t.type == "LIST"
|
|
assert [t.type for t in l_t] == element_types
|
|
|
|
|
|
@parametrize(
|
|
"text, element_types",
|
|
[
|
|
# Integers
|
|
("[1]", ["INTEGER"]),
|
|
("[ 1 ]", ["INTEGER"]),
|
|
("[,1,]", ["INTEGER"]),
|
|
("[1\n]", ["INTEGER"]),
|
|
("[\n1\n]", ["INTEGER"]),
|
|
("[1, 2, 3, 4]", ["INTEGER", "INTEGER", "INTEGER", "INTEGER"]),
|
|
# Floats
|
|
("[1.0]", ["FLOAT"]),
|
|
("[1.0e0]", ["FLOAT"]),
|
|
("[1e0]", ["FLOAT"]),
|
|
("[1e0]", ["FLOAT"]),
|
|
# Symbols
|
|
("[foo]", ["SYMBOL"]),
|
|
("[+]", ["SYMBOL"]),
|
|
("[-]", ["SYMBOL"]),
|
|
("[*]", ["SYMBOL"]),
|
|
("[foo-bar]", ["SYMBOL"]),
|
|
("[+foo-bar+]", ["SYMBOL"]),
|
|
("[+foo-bar+]", ["SYMBOL"]),
|
|
("[ foo bar ]", ["SYMBOL", "SYMBOL"]),
|
|
# Keywords
|
|
("[:foo]", ["KEYWORD"]),
|
|
("[ :foo ]", ["KEYWORD"]),
|
|
("[\n:foo\n]", ["KEYWORD"]),
|
|
("[,:foo,]", ["KEYWORD"]),
|
|
("[:foo :bar]", ["KEYWORD", "KEYWORD"]),
|
|
("[:foo :bar 1]", ["KEYWORD", "KEYWORD", "INTEGER"]),
|
|
# Strings
|
|
('["foo", "bar", "baz"]', ["STRING", "STRING", "STRING"]),
|
|
# Lists
|
|
("[[] [] ()]", ["SQLIST", "SQLIST", "LIST"]),
|
|
],
|
|
)
|
|
def test_parse_sqlist(text, element_types):
|
|
"""Test we can parse various 'square' lists of contents."""
|
|
l_t = next(cp.parse_buffer(text, discard_whitespace=True))
|
|
assert l_t.type == "SQLIST"
|
|
assert [t.type for t in l_t] == element_types
|
|
|
|
|
|
@parametrize(
|
|
"text, element_pairs",
|
|
[
|
|
("{}", []),
|
|
("{:foo 1}", [["KEYWORD", "INTEGER"]]),
|
|
("{:foo 1, :bar 2}", [["KEYWORD", "INTEGER"], ["KEYWORD", "INTEGER"]]),
|
|
("{foo 1, bar 2}", [["SYMBOL", "INTEGER"], ["SYMBOL", "INTEGER"]]),
|
|
("{foo 1, bar -2}", [["SYMBOL", "INTEGER"], ["SYMBOL", "INTEGER"]]),
|
|
("{foo 1, bar -2e0}", [["SYMBOL", "INTEGER"], ["SYMBOL", "FLOAT"]]),
|
|
("{foo ()}", [["SYMBOL", "LIST"]]),
|
|
("{foo []}", [["SYMBOL", "SQLIST"]]),
|
|
("{foo {}}", [["SYMBOL", "DICT"]]),
|
|
('{"foo" {}}', [["STRING", "DICT"]]),
|
|
],
|
|
)
|
|
def test_parse_dict(text, element_pairs):
|
|
"""Test we can parse various mappings."""
|
|
d_t = next(cp.parse_buffer(text, discard_whitespace=True))
|
|
assert d_t.type == "DICT"
|
|
assert [[t.type for t in pair] for pair in d_t.value] == element_pairs
|
|
|
|
|
|
@parametrize("text", ["{1}", "{1, 2, 3}", "{:foo}", "{:foo :bar :baz}"])
|
|
def test_parse_bad_dict(text):
|
|
"""Assert that dicts with missmatched pairs don't parse."""
|
|
with pytest.raises(Exception):
|
|
next(cp.parse_buffer(text))
|
|
|
|
|
|
@parametrize(
|
|
"text",
|
|
[
|
|
"()",
|
|
"(1 1.1 1e2 -2 foo :foo foo/bar :foo/bar [{},])",
|
|
"{:foo bar, :baz [:qux]}",
|
|
"'foo",
|
|
"'[foo bar :baz 'qux, {}]",
|
|
"#foo []",
|
|
"^{} bar",
|
|
],
|
|
)
|
|
def test_examples(text):
|
|
"""Shotgun examples showing we can parse some stuff."""
|
|
|
|
assert list(cp.parse_buffer(text))
|