Last active
September 13, 2020 09:09
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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