Files
tree-sitter-lscript/bindings/rust/lib.rs
T

292 lines
10 KiB
Rust

//! 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_wrappers_and_untyped_inputs() {
let source = "present: i32?: some 1\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(), "unary_expression");
assert_eq!(
wrapped
.child_by_field_name("operator")
.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 wrapper = pattern.named_child(0).unwrap();
assert_eq!(wrapper.kind(), "wrapper_pattern");
assert_eq!(
wrapper
.child_by_field_name("wrapper")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"ok"
);
}
#[test]
fn rejects_removed_wrapper_forms() {
for source in [
"old: i32?: 1->|.some\n",
"old :: @ value: i32 -> i32 ! str { value! }\n",
"old :: @ value: str -> i32 ! str { !value }\n",
"some := 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..<limit)->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()
);
}
}