//! LScript language support for the Tree-sitter parsing library. use tree_sitter_language::LanguageFn; unsafe extern "C" { fn tree_sitter_lscript() -> *const (); } /// The Tree-sitter language function for LScript. pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_lscript) }; /// Generated descriptions of the grammar's named node types. pub const NODE_TYPES: &str = include_str!("../../src/node-types.json"); /// The default syntax-highlighting query. pub const HIGHLIGHTS_QUERY: &str = include_str!("../../queries/highlights.scm"); #[cfg(test)] mod tests { fn parse(source: &str) -> tree_sitter::Tree { let mut parser = tree_sitter::Parser::new(); parser .set_language(&super::LANGUAGE.into()) .expect("generated LScript grammar must load"); parser .parse(source, None) .expect("parser must produce a tree") } #[test] fn loads_and_parses_lscript() { let tree = parse("answer :: 42\n"); assert!(!tree.root_node().has_error()); } #[test] fn exposes_stable_expression_fields_without_precedence_wrappers() { let source = "answer := left + right * -value\n"; let tree = parse(source); let declaration = tree.root_node().named_child(0).unwrap(); let addition = declaration.child_by_field_name("value").unwrap(); assert_eq!(addition.kind(), "binary_expression"); assert_eq!( addition .child_by_field_name("operator") .unwrap() .utf8_text(source.as_bytes()) .unwrap(), "+" ); assert_eq!( addition.child_by_field_name("left").unwrap().kind(), "identifier" ); let multiplication = addition.child_by_field_name("right").unwrap(); assert_eq!(multiplication.kind(), "binary_expression"); let negation = multiplication.child_by_field_name("right").unwrap(); assert_eq!(negation.kind(), "unary_expression"); assert_eq!( negation.child_by_field_name("operand").unwrap().kind(), "identifier" ); } #[test] fn exposes_meaningful_type_constructor_fields() { let source = "Transform :: []{i32?} -> [str]i32 ! Error\n"; let tree = parse(source); let declaration = tree.root_node().named_child(0).unwrap(); let definition = declaration.child_by_field_name("value").unwrap(); let function = definition.named_child(0).unwrap(); assert_eq!(function.kind(), "function_type"); assert_eq!( function .child_by_field_name("effect") .unwrap() .utf8_text(source.as_bytes()) .unwrap(), "->" ); assert_eq!( function.child_by_field_name("input").unwrap().kind(), "array_type" ); let result = function.child_by_field_name("result").unwrap(); assert_eq!(result.kind(), "result_type"); assert_eq!( result.child_by_field_name("value").unwrap().kind(), "map_type" ); assert_eq!( result.child_by_field_name("error").unwrap().kind(), "identifier" ); } #[test] fn exposes_typed_function_headers() { let source = "named :: @ value: i32 => i32 { value }\nadd :: @ (a: i32, b: i32) { a + b }\n"; let tree = parse(source); assert!( !tree.root_node().has_error(), "{}", tree.root_node().to_sexp() ); let named = tree.root_node().named_child(0).unwrap(); let function = named.child_by_field_name("value").unwrap(); assert_eq!(function.kind(), "function_literal"); let parameter = function.child_by_field_name("parameter").unwrap(); assert_eq!(parameter.kind(), "typed_parameter"); assert_eq!( parameter .child_by_field_name("pattern") .unwrap() .utf8_text(source.as_bytes()) .unwrap(), "value" ); assert_eq!( function .child_by_field_name("effect") .unwrap() .utf8_text(source.as_bytes()) .unwrap(), "=>" ); assert_eq!( function.child_by_field_name("result").unwrap().kind(), "primitive_type" ); assert_eq!( function.child_by_field_name("body").unwrap().kind(), "block" ); let tuple_declaration = tree.root_node().named_child(1).unwrap(); let tuple_function = tuple_declaration.child_by_field_name("value").unwrap(); let tuple_input = tuple_function.child_by_field_name("parameter").unwrap(); assert_eq!(tuple_input.kind(), "typed_tuple_pattern"); assert!(tuple_function.child_by_field_name("parameters").is_none()); assert_eq!(tuple_input.named_child_count(), 2); for index in 0..2 { assert_eq!( tuple_input.named_child(index).unwrap().kind(), "typed_tuple_component" ); } } #[test] fn distinguishes_whole_argument_annotation_from_tuple_component_annotations() { let source = "whole :: @ (left, right): (i32, i32) { left + right }\n\ components :: @ (left: i32, right: i32) { left + right }\n\ product :: @ {value}: &{value: i32:} { value }\n"; let tree = parse(source); assert!( !tree.root_node().has_error(), "{}", tree.root_node().to_sexp() ); let whole = tree.root_node().named_child(0).unwrap(); let whole_function = whole.child_by_field_name("value").unwrap(); let whole_input = whole_function.child_by_field_name("parameter").unwrap(); assert_eq!(whole_input.kind(), "typed_parameter"); assert_eq!( whole_input.child_by_field_name("pattern").unwrap().kind(), "tuple_pattern" ); assert_eq!( whole_input.child_by_field_name("type").unwrap().kind(), "tuple_type" ); let components = tree.root_node().named_child(1).unwrap(); let components_function = components.child_by_field_name("value").unwrap(); let component_input = components_function .child_by_field_name("parameter") .unwrap(); assert_eq!(component_input.kind(), "typed_tuple_pattern"); assert_eq!(component_input.named_child_count(), 2); let product = tree.root_node().named_child(2).unwrap(); let product_function = product.child_by_field_name("value").unwrap(); let product_input = product_function.child_by_field_name("parameter").unwrap(); assert_eq!(product_input.kind(), "typed_parameter"); assert_eq!( product_input.child_by_field_name("pattern").unwrap().kind(), "product_pattern" ); } #[test] 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"; let tree = parse(source); assert!( !tree.root_node().has_error(), "{}", tree.root_node().to_sexp() ); let present = tree.root_node().named_child(0).unwrap(); let wrapped = present.child_by_field_name("value").unwrap(); assert_eq!(wrapped.kind(), "pipeline_expression"); assert_eq!( 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(), "some" ); let double = tree.root_node().named_child(1).unwrap(); let input = double .child_by_field_name("value") .unwrap() .child_by_field_name("parameter") .unwrap(); assert_eq!(input.kind(), "typed_parameter"); assert!(input.child_by_field_name("type").is_none()); let unwrap = tree.root_node().named_child(2).unwrap(); let function = unwrap.child_by_field_name("value").unwrap(); 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 variant = pattern.named_child(0).unwrap(); assert_eq!(variant.kind(), "sum_pattern"); assert_eq!( 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 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, T) -> T: @ (first, second): (T, T) { first }\n\ Card :: &{ label: Output: }\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 [ "present: i32?: some 1\n", "old :: @ value: i32 -> i32 ! str { value! }\n", "old :: @ value: str -> i32 ! str { !value }\n", "context := 1\n", ] { assert!(parse(source).root_node().has_error(), "accepted {source}"); } } #[test] fn rejects_removed_function_literal_forms() { for source in [ "old: i32 -> i32: value@ value\n", "old: i32 -> i32:\n@ value: i32 { value }\n", "old :: @ (value:i32) { value }\n", "old :: @ (value:i32,) { value }\n", "old :: @ (\nvalue:i32\n) -> i32 { value }\n", ] { assert!(parse(source).root_node().has_error(), "accepted {source}"); } } #[test] fn distinguishes_compact_ranges_floats_and_word_prefixes() { let source = "value := (item.value, 0..slice\n\ trailing_dot := 1.\n\ anchor := value\n\ android := value\n\ order: []Item = []\n\ ready := first\n\ and second\n\ or fallback\n"; let tree = parse(source); assert!( !tree.root_node().has_error(), "{}", tree.root_node().to_sexp() ); } }