add generics

This commit is contained in:
paul committed 2026-10-23 02:13:30 -04:00
1 parent 79533e9537
commit 2204d58102
22 files changed
+47358 -37579

No files matched your search

+270 -13
View File
@@ -196,10 +196,10 @@ mod tests {
}
#[test]
fn exposes_wrappers_and_untyped_inputs() {
let source = "present: i32?: some 1\n\
fn exposes_sum_spelled_optionals_and_untyped_inputs() {
let source = "present: i32?: 1->|.some\n\
double :: @ value { value * 2 }\n\
unwrap :: @ result { match result { ok inner => inner; err _ => 0 } }\n";
unwrap :: @ result { match result { |.ok inner => inner; |.err _ => 0 } }\n";
let tree = parse(source);
assert!(
!tree.root_node().has_error(),
@@ -209,10 +209,22 @@ mod tests {
let present = tree.root_node().named_child(0).unwrap();
let wrapped = present.child_by_field_name("value").unwrap();
assert_eq!(wrapped.kind(), "unary_expression");
assert_eq!(wrapped.kind(), "pipeline_expression");
assert_eq!(
wrapped
.child_by_field_name("operator")
wrapped.child_by_field_name("input").unwrap().kind(),
"signed_numeric_literal"
);
let stage = wrapped.named_child(1).unwrap();
assert_eq!(stage.kind(), "direct_pipeline_stage");
let constructor = stage
.child_by_field_name("callable")
.unwrap()
.named_child(0)
.unwrap();
assert_eq!(constructor.kind(), "sum_constructor");
assert_eq!(
constructor
.child_by_field_name("variant")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
@@ -233,25 +245,270 @@ mod tests {
let body = function.child_by_field_name("body").unwrap();
let arm = body.named_child(0).unwrap().named_child(1).unwrap();
let pattern = arm.child_by_field_name("pattern").unwrap();
let wrapper = pattern.named_child(0).unwrap();
assert_eq!(wrapper.kind(), "wrapper_pattern");
let variant = pattern.named_child(0).unwrap();
assert_eq!(variant.kind(), "sum_pattern");
assert_eq!(
wrapper
.child_by_field_name("wrapper")
variant
.child_by_field_name("variant")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"ok"
);
assert_eq!(
variant.child_by_field_name("payload").unwrap().kind(),
"identifier"
);
}
#[test]
fn rejects_removed_wrapper_forms() {
fn binds_same_line_stages_tighter_than_operators() {
let source = "mixed :: -3->add_ten + 2 * 3->double - {1 + 1}->double\n";
let tree = parse(source);
assert!(
!tree.root_node().has_error(),
"{}",
tree.root_node().to_sexp()
);
let declaration = tree.root_node().named_child(0).unwrap();
let subtraction = declaration.child_by_field_name("value").unwrap();
assert_eq!(subtraction.kind(), "binary_expression");
assert_eq!(
subtraction
.child_by_field_name("operator")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"-"
);
let addition = subtraction.child_by_field_name("left").unwrap();
assert_eq!(addition.kind(), "binary_expression");
let negation = addition.child_by_field_name("left").unwrap();
assert_eq!(negation.kind(), "unary_expression");
let negated = negation.child_by_field_name("operand").unwrap();
assert_eq!(negated.kind(), "pipeline_expression");
assert_eq!(
negated
.child_by_field_name("input")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"3"
);
let product = addition.child_by_field_name("right").unwrap();
assert_eq!(product.kind(), "binary_expression");
assert_eq!(
product.child_by_field_name("right").unwrap().kind(),
"pipeline_expression"
);
let grouped = subtraction.child_by_field_name("right").unwrap();
assert_eq!(grouped.kind(), "pipeline_expression");
assert_eq!(
grouped.child_by_field_name("input").unwrap().kind(),
"block"
);
}
#[test]
fn applies_continuation_stages_to_the_whole_preceding_expression() {
let source = "flat := value->first\n->second\nwhole := a + b->f\n->g\n";
let tree = parse(source);
assert!(
!tree.root_node().has_error(),
"{}",
tree.root_node().to_sexp()
);
let flat = tree
.root_node()
.named_child(0)
.unwrap()
.child_by_field_name("value")
.unwrap();
assert_eq!(flat.kind(), "pipeline_expression");
assert_eq!(
flat.child_by_field_name("input").unwrap().kind(),
"identifier"
);
assert_eq!(flat.named_child(1).unwrap().kind(), "direct_pipeline_stage");
assert_eq!(
flat.named_child(2).unwrap().kind(),
"continuation_pipeline_stage"
);
assert_eq!(flat.named_child_count(), 3);
let whole = tree
.root_node()
.named_child(1)
.unwrap()
.child_by_field_name("value")
.unwrap();
assert_eq!(whole.kind(), "pipeline_expression");
let head = whole.child_by_field_name("input").unwrap();
assert_eq!(head.kind(), "binary_expression");
assert_eq!(
head.child_by_field_name("right").unwrap().kind(),
"pipeline_expression"
);
assert_eq!(
whole.named_child(1).unwrap().kind(),
"continuation_pipeline_stage"
);
}
#[test]
fn exposes_generic_binders_constraints_and_output_types() {
let source = "Show :: <>{ to_str: str; ...Display }\n\
pick: <T: Show> (T, T) -> T: @ (first, second): (T, T) { first }\n\
Card :: &{ label: Output<make_label: Show>: }\n";
let tree = parse(source);
assert!(
!tree.root_node().has_error(),
"{}",
tree.root_node().to_sexp()
);
let show = tree.root_node().named_child(0).unwrap();
let definition = show.child_by_field_name("value").unwrap();
assert_eq!(definition.kind(), "type_definition");
let constraint = definition.named_child(0).unwrap();
assert_eq!(constraint.kind(), "constraint_body");
let entry = constraint.named_child(0).unwrap();
assert_eq!(entry.kind(), "constraint_entry");
assert_eq!(
entry
.child_by_field_name("name")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"to_str"
);
assert_eq!(
entry.child_by_field_name("output").unwrap().kind(),
"primitive_type"
);
let spread = constraint.named_child(1).unwrap();
assert_eq!(spread.kind(), "constraint_spread");
assert_eq!(
spread
.child_by_field_name("constraint")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"Display"
);
let pick = tree.root_node().named_child(1).unwrap();
let binder = pick.child_by_field_name("binder").unwrap();
assert_eq!(binder.kind(), "binder");
let parameter = binder.named_child(0).unwrap();
assert_eq!(parameter.kind(), "binder_entry");
assert_eq!(
parameter
.child_by_field_name("type")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"T"
);
assert_eq!(
parameter
.child_by_field_name("constraint")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"Show"
);
assert_eq!(
pick.child_by_field_name("type").unwrap().kind(),
"function_type"
);
assert_eq!(
pick.child_by_field_name("value").unwrap().kind(),
"function_literal"
);
let card = tree.root_node().named_child(2).unwrap();
let field = card
.child_by_field_name("value")
.unwrap()
.named_child(0)
.unwrap()
.named_child(0)
.unwrap();
assert_eq!(field.kind(), "product_field");
let output = field.child_by_field_name("type").unwrap();
assert_eq!(output.kind(), "output_type");
assert_eq!(
output
.child_by_field_name("function")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"make_label"
);
assert_eq!(
output
.child_by_field_name("output")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"Show"
);
}
#[test]
fn exposes_import_filters() {
let source = "use math.{sqrt for f64, pi}\nuse .shapes.area for Circle\npub { area for Circle; sqrt }\n";
let tree = parse(source);
assert!(
!tree.root_node().has_error(),
"{}",
tree.root_node().to_sexp()
);
let group = tree.root_node().named_child(0).unwrap();
assert_eq!(group.kind(), "use_declaration");
let member = group.named_child(1).unwrap();
assert_eq!(member.kind(), "import_member");
let filter = member.named_child(1).unwrap();
assert_eq!(filter.kind(), "import_filter");
assert_eq!(
filter.child_by_field_name("type").unwrap().kind(),
"primitive_type"
);
assert_eq!(group.named_child(2).unwrap().named_child_count(), 1);
let single = tree.root_node().named_child(1).unwrap();
assert_eq!(single.named_child(1).unwrap().kind(), "import_filter");
let publication = tree.root_node().named_child(2).unwrap();
assert_eq!(publication.kind(), "pub_declaration");
let filtered = publication.named_child(0).unwrap();
assert_eq!(filtered.kind(), "pub_entry");
assert_eq!(
filtered
.child_by_field_name("name")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"area"
);
assert_eq!(filtered.named_child(1).unwrap().kind(), "import_filter");
assert_eq!(publication.named_child(1).unwrap().named_child_count(), 1);
}
#[test]
fn rejects_removed_wrapper_forms_and_reserved_words() {
for source in [
"old: i32?: 1->|.some\n",
"present: i32?: some 1\n",
"old :: @ value: i32 -> i32 ! str { value! }\n",
"old :: @ value: str -> i32 ! str { !value }\n",
"some := 1\n",
"context := 1\n",
] {
assert!(parse(source).root_node().has_error(), "accepted {source}");
}