Skip to main content

tree_sitter_reinhardt_head/
lib.rs

1//! tree-sitter grammar for the Reinhardt `head!` DSL.
2
3#![warn(missing_docs)]
4
5use tree_sitter_language::LanguageFn;
6
7unsafe extern "C" {
8	fn tree_sitter_reinhardt_head() -> *const ();
9}
10
11/// The tree-sitter language function for the Reinhardt `head!` DSL grammar.
12pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_reinhardt_head) };
13
14/// The grammar node type metadata.
15pub const NODE_TYPES: &str = include_str!("node-types.json");
16
17#[cfg(test)]
18mod tests {
19	#[test]
20	fn parser_loads() {
21		let mut parser = tree_sitter::Parser::new();
22		parser
23			.set_language(&super::LANGUAGE.into())
24			.expect("load head DSL grammar");
25	}
26
27	// -- Block comment tests --------------------------------------------------
28
29	#[test]
30	fn block_comment_simple() {
31		// Arrange
32		let mut parser = tree_sitter::Parser::new();
33		parser.set_language(&super::LANGUAGE.into()).unwrap();
34		let source = "/* hello */";
35
36		// Act
37		let tree = parser.parse(source, None).unwrap();
38
39		// Assert
40		assert!(
41			!tree.root_node().has_error(),
42			"parse tree: {}",
43			tree.root_node().to_sexp()
44		);
45	}
46
47	#[test]
48	fn block_comment_empty() {
49		// Arrange
50		let mut parser = tree_sitter::Parser::new();
51		parser.set_language(&super::LANGUAGE.into()).unwrap();
52		let source = "/**/";
53
54		// Act
55		let tree = parser.parse(source, None).unwrap();
56
57		// Assert
58		assert!(
59			!tree.root_node().has_error(),
60			"parse tree: {}",
61			tree.root_node().to_sexp()
62		);
63	}
64
65	#[test]
66	fn block_comment_slash_star_slash_is_not_complete() {
67		// Arrange
68		let mut parser = tree_sitter::Parser::new();
69		parser.set_language(&super::LANGUAGE.into()).unwrap();
70		let source = "/*/ div { \"text\" }";
71
72		// Act
73		let tree = parser.parse(source, None).unwrap();
74
75		// Assert — '/*/' should NOT close the comment, so the rest is consumed
76		// as comment body
77		let sexp = tree.root_node().to_sexp();
78		// The entire input should be treated as an unterminated block comment
79		// or error
80		assert!(
81			tree.root_node().has_error() || !sexp.contains("block_comment"),
82			"/*/ should not be a valid block comment: {sexp}"
83		);
84	}
85
86	#[test]
87	fn block_comment_nested_stars() {
88		// Arrange
89		let mut parser = tree_sitter::Parser::new();
90		parser.set_language(&super::LANGUAGE.into()).unwrap();
91		let source = "/* nested ** stars */";
92
93		// Act
94		let tree = parser.parse(source, None).unwrap();
95
96		// Assert
97		assert!(
98			!tree.root_node().has_error(),
99			"parse tree: {}",
100			tree.root_node().to_sexp()
101		);
102	}
103
104	#[test]
105	fn block_comment_unterminated_is_not_accepted() {
106		// Arrange
107		let mut parser = tree_sitter::Parser::new();
108		parser.set_language(&super::LANGUAGE.into()).unwrap();
109		let source = "/* unterminated comment";
110
111		// Act
112		let tree = parser.parse(source, None).unwrap();
113
114		// Assert — the scanner rejects unterminated block comments, so
115		// the text must NOT appear as a block_comment node. The grammar
116		// may error-recover the input as fragments instead.
117		let sexp = tree.root_node().to_sexp();
118		assert!(
119			!sexp.contains("block_comment"),
120			"unterminated input should not produce a block_comment node: {sexp}"
121		);
122	}
123
124	// -- Line comment tests ---------------------------------------------------
125
126	#[test]
127	fn line_comment_simple() {
128		// Arrange
129		let mut parser = tree_sitter::Parser::new();
130		parser.set_language(&super::LANGUAGE.into()).unwrap();
131		let source = "// this is a comment";
132
133		// Act
134		let tree = parser.parse(source, None).unwrap();
135
136		// Assert
137		assert!(
138			!tree.root_node().has_error(),
139			"parse tree: {}",
140			tree.root_node().to_sexp()
141		);
142	}
143
144	#[test]
145	fn line_comment_only_captures_first_line() {
146		// Arrange
147		let mut parser = tree_sitter::Parser::new();
148		parser.set_language(&super::LANGUAGE.into()).unwrap();
149		let source = "// comment\ntitle { \"Page\" }";
150
151		// Act
152		let tree = parser.parse(source, None).unwrap();
153
154		// Assert — the line comment should not swallow the second line
155		let sexp = tree.root_node().to_sexp();
156		assert!(
157			sexp.contains("line_comment"),
158			"expected a line_comment node: {sexp}"
159		);
160		// The second line should produce additional nodes beyond the comment
161		let root = tree.root_node();
162		assert!(
163			root.child_count() > 1,
164			"second line should produce separate nodes after the comment: {sexp}"
165		);
166	}
167
168	// -- Basic DSL structure tests --------------------------------------------
169
170	#[test]
171	fn head_dsl_basic_structure() {
172		// Arrange
173		let mut parser = tree_sitter::Parser::new();
174		parser.set_language(&super::LANGUAGE.into()).unwrap();
175		let source = r#"|| { title { "Page" } }"#;
176
177		// Act
178		let tree = parser.parse(source, None).unwrap();
179
180		// Assert
181		assert!(
182			!tree.root_node().has_error(),
183			"parse tree: {}",
184			tree.root_node().to_sexp()
185		);
186	}
187
188	// -- DSL with embedded comments -------------------------------------------
189
190	#[test]
191	fn head_dsl_with_block_comment() {
192		// Arrange
193		let mut parser = tree_sitter::Parser::new();
194		parser.set_language(&super::LANGUAGE.into()).unwrap();
195		let source = r#"|| { /* comment */ title { "Page" } }"#;
196
197		// Act
198		let tree = parser.parse(source, None).unwrap();
199
200		// Assert
201		let sexp = tree.root_node().to_sexp();
202		assert!(!tree.root_node().has_error(), "parse tree: {sexp}");
203		assert!(
204			sexp.contains("block_comment"),
205			"expected a block_comment node in the tree: {sexp}"
206		);
207	}
208}