Skip to content

Instantly share code, notes, and snippets.

@sogaiu
Last active September 13, 2020 09:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sogaiu/45684afcbc37818b4651bcc550616ee9 to your computer and use it in GitHub Desktop.
Save sogaiu/45684afcbc37818b4651bcc550616ee9 to your computer and use it in GitHub Desktop.
testing tree-sitter-clojure with hypothesis (some support for characters, keywords, lists, numbers, strings, and symbols)
# clone tree-sitter-clojure into vendor dir and follow setup
# instructions in README for setup before executing this file
import re
from hypothesis import assume, given, example, note, reject
from hypothesis import settings, Verbosity
from hypothesis.strategies import booleans, characters, integers, text
from hypothesis.strategies import composite, just, lists, one_of, sampled_from
from tree_sitter import Language, Parser
# grammatical pieces from tree-sitter-clojure (1 + 10 + 17 + 2 = 31)
#
# + below here means good first draft attempt has been made
#
# subatomic (1)
# auto_resolve_marker
#
# atomic (10)
# boolean
# + character
# comment
# + keyword
# nil
# + number
# regex
# + string
# + symbol
# symbolic_value
#
# compound (17)
# discard_expr
# list
# map
# vector
# set
# anon_func
# read_cond
# read_cond_splicing
# namespaced_map
# var_quote_form
# eval_form
# tagged_literal
# syntax_quote_form
# quote_form
# unquote_splicing_form
# unquote_form
# deref_form
#
# compound but not standalone (2)
# metadata
# old_metadata
# may need to create build subdir...
so_with_langs_path = 'build/my-languages.so'
Language.build_library(
so_with_langs_path,
['vendor/tree-sitter-clojure']
)
CLJ_LANGUAGE = Language(so_with_langs_path, 'clojure')
parser = Parser()
parser.set_language(CLJ_LANGUAGE)
def node_text(source, node):
return source[node.start_byte:node.end_byte]
def to_ascii(n, cap):
if n <= 9:
return chr(n + 48)
else:
if cap:
offset = 55
else:
offset = 87
return chr(n + offset)
@composite
def radix_number_as_str(draw):
sign = draw(one_of(just(""), just("+"), just("-")))
#
radix = draw(integers(min_value=2, max_value=36))
#
r_or_R = draw(one_of(just("R"), just("r")))
# XXX: too many digits is a problem
n = draw(integers(min_value=1, max_value=20))
pre_digits = draw(lists(elements=integers(min_value=0,
max_value=radix - 1),
min_size=n, max_size=n))
caps = draw(lists(elements=booleans(),
min_size=n, max_size=n))
digits = map(to_ascii, pre_digits, caps)
#
return f'{sign}{radix}{r_or_R}{"".join(digits)}'
@composite
def hex_number_as_str(draw):
sign = draw(one_of(just(""), just("+"), just("-")))
#
x_or_X = draw(one_of(just("X"), just("x")))
# XXX: too many digits is a problem
n = draw(integers(min_value=1, max_value=20))
pre_digits = draw(lists(elements=integers(min_value=0,
max_value=15),
min_size=n, max_size=n))
caps = draw(lists(elements=booleans(),
min_size=n, max_size=n))
digits = map(to_ascii, pre_digits, caps)
#
end_n = draw(one_of(just(""), just("N")))
#
return f'{sign}0{x_or_X}{"".join(digits)}{end_n}'
@composite
def octal_number_as_str(draw):
sign = draw(one_of(just(""), just("+"), just("-")))
# XXX: too many digits is a problem
n = draw(integers(min_value=1, max_value=20))
pre_digits = draw(lists(elements=integers(min_value=0,
max_value=7),
min_size=n, max_size=n))
digits = map(lambda d: chr(d + 48), pre_digits)
#
m_or_n = draw(one_of(just(""), just("M"), just("N")))
#
return f'{sign}0{"".join(digits)}{m_or_n}'
@composite
def ratio_as_str(draw):
sign = draw(one_of(just(""), just("+"), just("-")))
#
p = draw(integers(min_value=0))
q = draw(integers(min_value=1))
#
return f'{sign}{p}/{q}'
@composite
def double_as_str(draw):
sign = draw(one_of(just(""), just("+"), just("-")))
#
left_of_dot = draw(integers(min_value=0))
#
has_dot_part = draw(booleans())
if has_dot_part:
dot_part = f'.{draw(integers(min_value=0))}'
else:
dot_part = ""
#
has_exp_part = draw(booleans())
if has_exp_part:
e_or_E = draw(one_of(just("E"), just("e")))
exp_sign = draw(one_of(just(""), just("+"), just("-")))
exp_num = draw(integers(min_value=0))
exp_part = f'{e_or_E}{exp_sign}{exp_num}'
else:
exp_part = ""
#
end_m = draw(one_of(just(""), just("M")))
#
return f'{sign}{left_of_dot}{dot_part}{exp_part}{end_m}'
@composite
def number_as_str(draw):
number = draw(one_of(radix_number_as_str(),
hex_number_as_str(),
octal_number_as_str(),
ratio_as_str(),
double_as_str(),
integer_as_str()))
#
return number
@composite
def integer_as_str(draw):
sign = draw(one_of(just(""), just("+"), just("-")))
#
i = draw(integers(min_value=0))
#
m_or_n = draw(one_of(just(""), just("M"), just("N")))
#
return f'{sign}{i}{m_or_n}'
@composite
def unqualified_symbol_as_str(draw):
# XXX: ignoring non-ascii
ok_in_head = ("*", "+", "!", "-", "_",
"?", "<", ">", "=",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
"u", "v", "w", "x", "y", "z",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
"K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z",
"/",
".")
ok_in_body = ("*", "+", "!", "-", "_", "'",
"?", "<", ">", "=",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
"u", "v", "w", "x", "y", "z",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
"K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
".",
":",
"#")
#
n = draw(integers(min_value=0, max_value=19))
#
head_char = draw(sampled_from(ok_in_head))
# slash by itself can be in a symbol, but for unqualified symbols,
# this is only allowed if the symbol is exactly slash (division)
if head_char == "/":
m = 0
else:
m = n
body_chars = \
draw(lists(elements=sampled_from(ok_in_body),
min_size=m, max_size=m))
sym_body = "".join(body_chars)
# ensure the colon-releated constraints are enforced
# XXX: :: is reserved for keywords, but "ending in a colon"
# is reserved for future use in clojure...what happens
# if that ends up being used?
assume(not(re.search("::", sym_body)))
assume(not(re.search(":$", sym_body)))
# ensure a number hasn't been generated
# XXX: this is incomplete and quite possibly a fair bit of work to
# get right as it may require weeding out all possible number
# representations...
if n > 0:
if (head_char == "+") or (head_char == "-"):
assume(not(re.search("^[0-9]$", sym_body[0])))
#
return f'{head_char}{sym_body}'
@composite
def qualified_symbol_as_str(draw):
# XXX: ignoring on-ascii
ok_in_head = ("*", "+", "!", "-", "_",
"?", "<", ">", "=",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
"u", "v", "w", "x", "y", "z",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
"K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z",
".")
ok_in_body = ("*", "+", "!", "-", "_", "'",
"?", "<", ">", "=",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
"u", "v", "w", "x", "y", "z",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
"K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
".",
":",
"#")
#
n = draw(integers(min_value=0, max_value=19))
#
head_char = draw(sampled_from(ok_in_head))
body_chars = \
draw(lists(elements=sampled_from(ok_in_body),
min_size=n, max_size=n))
sym_body = "".join(body_chars)
# ensure the colon-releated constraints are enforced
# XXX: :: is reserved for keywords, but "ending in a colon"
# is reserved for future use in clojure...what happens
# if that ends up being used?
assume(not(re.search("::", sym_body)))
assume(not(re.search(":$", sym_body)))
# ensure a number hasn't been generated
# XXX: this is incomplete and quite possibly a fair bit of work to
# get right as it may require weeding out all possible number
# representations...
if n > 0:
if (head_char == "+") or (head_char == "-"):
assume(not(re.search("^[0-9]$", sym_body[0])))
#
uq_sym = draw(unqualified_symbol_as_str())
#
return f'{head_char}{sym_body}/{uq_sym}'
@composite
def unqualified_keyword_no_sigil_as_str(draw):
# XXX: ignoring non-ascii
ok_in_head = ("*", "+", "!", "-", "_",
"?", "<", ">", "=",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
"u", "v", "w", "x", "y", "z",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
"K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z",
# https://clojure.atlassian.net/browse/TCHECK-155
"/",
".")
ok_in_body = ("*", "+", "!", "-", "_", "'",
"?", "<", ">", "=",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
"u", "v", "w", "x", "y", "z",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
"K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
".",
":",
"#")
#
n = draw(integers(min_value=0, max_value=19))
#
head_char = draw(sampled_from(ok_in_head))
# XXX: :/ is recognized as a keyword via clojure's repl, however:
# https://clojure.atlassian.net/browse/TCHECK-155
if head_char == "/":
m = 0
else:
m = n
body_chars = \
draw(lists(elements=sampled_from(ok_in_body),
min_size=m, max_size=m))
kwd_body = "".join(body_chars)
# ensure the colon-releated constraints are enforced
# XXX: :: is not allowed in the body, and "ending in a colon"
# is reserved for future use in clojure...what happens
# if that ends up being used?
assume(not(re.search("::", kwd_body)))
assume(not(re.search(":$", kwd_body)))
#
return f'{head_char}{kwd_body}'
@composite
def unqualified_keyword_as_str(draw):
uq_kwd_no_sig = draw(unqualified_keyword_no_sigil_as_str())
#
return f':{uq_kwd_no_sig}'
@composite
def unqualified_auto_resolved_keyword_as_str(draw):
uq_kwd_no_sig = draw(unqualified_keyword_no_sigil_as_str())
# clojure repl rejects ::/
assume(uq_kwd_no_sig != "/")
#
return f'::{uq_kwd_no_sig}'
@composite
def keyword_ns_as_str(draw):
# XXX: ignoring non-ascii
ok_in_head = ("*", "+", "!", "-", "_",
"?", "<", ">", "=",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
"u", "v", "w", "x", "y", "z",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
"K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z",
".")
ok_in_body = ("*", "+", "!", "-", "_", "'",
"?", "<", ">", "=",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
"u", "v", "w", "x", "y", "z",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
"K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
".",
":",
"#")
#
n = draw(integers(min_value=0, max_value=19))
#
head_char = draw(sampled_from(ok_in_head))
body_chars = \
draw(lists(elements=sampled_from(ok_in_body),
min_size=n, max_size=n))
kwd_body = "".join(body_chars)
# ensure the colon-releated constraints are enforced
# XXX: :: is not allowed in the body, and "ending in a colon"
# is reserved for future use in clojure...what happens
# if that ends up being used?
assume(not(re.search("::", kwd_body)))
assume(not(re.search(":$", kwd_body)))
#
return f'{head_char}{kwd_body}'
@composite
def qualified_keyword_as_str(draw):
kwd_ns = draw(keyword_ns_as_str())
uq_kwd_no_sig = draw(unqualified_keyword_no_sigil_as_str())
#
return f':{kwd_ns}/{uq_kwd_no_sig}'
@composite
def qualified_auto_resolved_keyword_as_str(draw):
kwd_ns = draw(keyword_ns_as_str())
uq_kwd_no_sig = draw(unqualified_keyword_no_sigil_as_str())
#
return f'::{kwd_ns}/{uq_kwd_no_sig}'
@composite
def auto_resolved_keyword_as_str(draw):
kwd = draw(one_of(unqualified_auto_resolved_keyword_as_str(),
qualified_auto_resolved_keyword_as_str()))
return kwd
@composite
def keyword_as_str(draw):
kwd = draw(one_of(unqualified_auto_resolved_keyword_as_str(),
unqualified_keyword_as_str(),
qualified_auto_resolved_keyword_as_str(),
qualified_keyword_as_str()))
return kwd
@composite
def any_character_as_str(draw):
# XXX: will include surrogates which seem to lead to problems
# but unclear whether that's in tree-sitter or python...
#character = draw(characters())
character = draw(text(min_size=1, max_size=1))
# XXX: tree-sitter cannot handle null byte (0)
assume(character != '\x00')
#
return f'\\{character}'
@composite
def named_character_as_str(draw):
named_char = draw(one_of(just("backspace"),
just("formfeed"),
just("newline"),
just("return"),
just("space"),
just("tab")))
#
return f'\\{named_char}'
@composite
def octal_character_as_str(draw):
n = draw(integers(min_value=1, max_value=3))
#
if n == 3:
first_digit = draw(integers(min_value=0, max_value=3))
else:
first_digit = draw(integers(min_value=0, max_value=7))
rest_digits = map(to_ascii,
draw(lists(elements=integers(min_value=0,
max_value=7),
min_size=n-1, max_size=n-1)),
draw(lists(elements=booleans(),
min_size=n-1, max_size=n-1)))
#
return f'\\o{to_ascii(first_digit, False)}{"".join(rest_digits)}'
@composite
def unicode_quad_character_as_str(draw):
pre_digits = draw(lists(elements=integers(min_value=0,
max_value=15),
min_size=4, max_size=4))
caps = draw(lists(elements=booleans(),
min_size=4, max_size=4))
quad = map(to_ascii, pre_digits, caps)
#
return f'\\u{"".join(quad)}'
@composite
def character_as_str(draw):
character = draw(one_of(any_character_as_str(),
named_character_as_str(),
octal_character_as_str(),
unicode_quad_character_as_str()))
#
return character
@composite
def string_as_str(draw):
n = draw(integers(min_value=0, max_value=100))
#
not_tween_delims = ('"', '\\')
#
chars = \
draw(lists(elements=characters(blacklist_characters=not_tween_delims,
min_codepoint=32, max_codepoint=127),
min_size=n, max_size=n))
#
# XXX: what about escape sequences? e.g. \", \\, etc.
return f'"{"".join(chars)}"'
@composite
def integer_list(draw):
n = draw(integers(min_value=0, max_value=20))
#
int_strs = draw(lists(elements=integer_as_str(),
min_size=n, max_size=n))
#
return int_strs
@composite
def number_list(draw):
n = draw(integers(min_value=0, max_value=20))
#
num_strs = draw(lists(elements=number_as_str(),
min_size=n, max_size=n))
#
return num_strs
# XXX
@composite
def hetero_as_str(draw):
het = draw(one_of(auto_resolved_keyword_as_str(),
# XXX: boolean
character_as_str(),
keyword_as_str(),
# XXX: nil
number_as_str(),
# XXX: regex
string_as_str(),
symbol_str(),
# XXX: symbolic value
))
return het
# XXX: should return not just values but their types too?
@composite
def hetero_list(draw):
n = draw(integers(min_value=0, max_value=20))
#
hets = draw(lists(elements=hetero_as_str(),
min_size=n, max_size=n))
#
return hets
@settings(verbosity=Verbosity.verbose)
@given(hex_number_as_str())
def test_parses_hex_as_number(hex_num_str):
tree = parser.parse(bytes(hex_num_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "number"
assert hex_num_str == node_text(hex_num_str, child)
@settings(verbosity=Verbosity.verbose)
@given(octal_number_as_str())
def test_parses_octal_as_number(oct_num_str):
tree = parser.parse(bytes(oct_num_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "number"
assert oct_num_str == node_text(oct_num_str, child)
@settings(verbosity=Verbosity.verbose)
@given(radix_number_as_str())
def test_parses_radix_as_number(radix_num_str):
tree = parser.parse(bytes(radix_num_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "number"
assert radix_num_str == node_text(radix_num_str, child)
@settings(verbosity=Verbosity.verbose)
@given(ratio_as_str())
def test_parses_ratio_as_number(ratio_str):
tree = parser.parse(bytes(ratio_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "number"
assert ratio_str == node_text(ratio_str, child)
@settings(verbosity=Verbosity.verbose)
@given(double_as_str())
def test_parses_double_as_number(dbl_str):
tree = parser.parse(bytes(dbl_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "number"
assert dbl_str == node_text(dbl_str, child)
@settings(verbosity=Verbosity.verbose)
@given(integer_as_str())
def test_parses_integer_as_number(int_str):
tree = parser.parse(bytes(int_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "number"
assert int_str == node_text(int_str, child)
@settings(verbosity=Verbosity.verbose)
@given(number_as_str())
def test_parses_number(num_str):
tree = parser.parse(bytes(num_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "number"
assert num_str == node_text(num_str, child)
@settings(verbosity=Verbosity.verbose)
@given(unqualified_symbol_as_str())
def test_parses_unqualified_symbol(symbol_str):
tree = parser.parse(bytes(symbol_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "symbol"
assert symbol_str == node_text(symbol_str, child)
@settings(verbosity=Verbosity.verbose)
@given(qualified_symbol_as_str())
def test_parses_qualified_symbol(symbol_str):
tree = parser.parse(bytes(symbol_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "symbol"
assert symbol_str == node_text(symbol_str, child)
@settings(verbosity=Verbosity.verbose)
@given(unqualified_keyword_as_str())
def test_parses_unqualified_keyword(keyword_str):
tree = parser.parse(bytes(keyword_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "keyword"
assert keyword_str == node_text(keyword_str, child)
@settings(verbosity=Verbosity.verbose)
@given(qualified_keyword_as_str())
def test_parses_qualified_keyword(keyword_str):
tree = parser.parse(bytes(keyword_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "keyword"
assert keyword_str == node_text(keyword_str, child)
@settings(verbosity=Verbosity.verbose)
@given(unqualified_auto_resolved_keyword_as_str())
def test_parses_unqualified_auto_resolved_keyword(keyword_str):
tree = parser.parse(bytes(keyword_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "keyword"
assert keyword_str == node_text(keyword_str, child)
@settings(verbosity=Verbosity.verbose)
@given(qualified_auto_resolved_keyword_as_str())
def test_parses_qualified_auto_resolved_keyword(keyword_str):
tree = parser.parse(bytes(keyword_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "keyword"
assert keyword_str == node_text(keyword_str, child)
@settings(verbosity=Verbosity.verbose)
@given(any_character_as_str())
def test_parses_any_character(any_character_as_str):
tree = parser.parse(bytes(any_character_as_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "character"
assert any_character_as_str == node_text(any_character_as_str, child)
@settings(verbosity=Verbosity.verbose)
@given(named_character_as_str())
def test_parses_named_character(named_character_as_str):
tree = parser.parse(bytes(named_character_as_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "character"
assert named_character_as_str == node_text(named_character_as_str, child)
@settings(verbosity=Verbosity.verbose)
@given(octal_character_as_str())
def test_parses_octal_character(octal_character_as_str):
tree = parser.parse(bytes(octal_character_as_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "character"
assert octal_character_as_str == node_text(octal_character_as_str, child)
@settings(verbosity=Verbosity.verbose)
@given(unicode_quad_character_as_str())
def test_parses_unicode_quad_character(unicode_quad_character_as_str):
tree = parser.parse(bytes(unicode_quad_character_as_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "character"
assert unicode_quad_character_as_str == \
node_text(unicode_quad_character_as_str, child)
@settings(verbosity=Verbosity.verbose)
@given(character_as_str())
def test_parses_character(character_as_str):
tree = parser.parse(bytes(character_as_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "character"
assert character_as_str == \
node_text(character_as_str, child)
@settings(verbosity=Verbosity.verbose)
@given(string_as_str())
def test_parses_string(string_str):
tree = parser.parse(bytes(string_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
child = root_node.children[0]
assert child.type == "string"
assert string_str == node_text(string_str, child)
# XXX: pairs of things?
#
# e.g. symbol and number, number and symbol, number and number, etc.
@settings(verbosity=Verbosity.verbose)
@given(integer_list())
def test_parses_integer_list(int_strs):
n = len(int_strs)
int_list_str = f'({" ".join(int_strs)})'
tree = parser.parse(bytes(int_list_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
the_list_node = root_node.children[0]
assert the_list_node.type == "list"
cnt = 0
for child in the_list_node.children:
if child.is_named:
child_text = node_text(int_list_str, child)
assert child_text == int_strs[cnt]
cnt += 1
assert child.type == "number"
assert n == cnt
@settings(verbosity=Verbosity.verbose)
@given(number_list())
def test_parses_number_list(num_strs):
n = len(num_strs)
num_list_str = f'({" ".join(num_strs)})'
tree = parser.parse(bytes(num_list_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
the_list_node = root_node.children[0]
assert the_list_node.type == "list"
cnt = 0
for child in the_list_node.children:
if child.is_named:
child_text = node_text(num_list_str, child)
assert child_text == num_strs[cnt]
cnt += 1
assert child.type == "number"
assert n == cnt
# XXX: need the type of thing being passed in too...
@settings(verbosity=Verbosity.verbose)
@given(hetero_list())
def test_parses_hetero_list(hetero_strs):
n = len(hetero_strs)
hetero_list_str = f'({" ".join(hetero_strs)})'
tree = parser.parse(bytes(num_list_str, "utf8"))
root_node = tree.root_node
assert 1 == len(root_node.children)
the_list_node = root_node.children[0]
assert the_list_node.type == "list"
cnt = 0
for child in the_list_node.children:
if child.is_named:
child_text = node_text(num_list_str, child)
assert child_text == num_strs[cnt]
cnt += 1
assert child.type == "number"
assert n == cnt
if __name__ == "__main__":
test_parses_hex_as_number()
test_parses_octal_as_number()
test_parses_radix_as_number()
test_parses_ratio_as_number()
test_parses_double_as_number()
test_parses_integer_as_number()
test_parses_number()
#
test_parses_unqualified_symbol()
test_parses_qualified_symbol()
#
test_parses_unqualified_keyword()
test_parses_qualified_keyword()
test_parses_unqualified_auto_resolved_keyword()
test_parses_qualified_auto_resolved_keyword()
#
test_parses_any_character()
test_parses_named_character()
test_parses_octal_character()
test_parses_unicode_quad_character()
test_parses_character()
#
test_parses_string()
#
#test_parses_list_of_ints()
#test_parses_integer_list()
test_parses_number_list()
#
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment