1mod error;
23mod kind;
24mod lexer;
25mod parser;
26mod text;
27mod tree;
28
29pub use error::SyntaxError;
30pub use kind::SyntaxKind;
31pub use lexer::{LexResult, Token, tokenize};
32pub use parser::parse;
33pub use text::{LineCol, LineIndex, TextRange};
34pub use tree::{Checkpoint, Descendants, Element, NodeId, SyntaxNode, SyntaxTree, TreeBuilder};
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39
40 fn assert_round_trips(source: &str) {
42 let tree = parse(source);
43 assert_eq!(tree.text(), source, "tree must reproduce its input exactly");
44 }
45
46 fn assert_parses(source: &str) -> SyntaxTree {
47 let tree = parse(source);
48 assert!(
49 !tree.has_errors(),
50 "expected a clean parse of:\n{source}\ngot: {:?}",
51 tree.errors()
52 );
53 assert_eq!(tree.text(), source);
54 tree
55 }
56
57 fn node_kinds(tree: &SyntaxTree) -> Vec<SyntaxKind> {
59 tree.root().descendants().map(SyntaxNode::kind).collect()
60 }
61
62 #[test]
63 fn parses_an_empty_file() {
64 let tree = assert_parses("");
65 assert_eq!(tree.root().kind(), SyntaxKind::SourceFile);
66 }
67
68 #[test]
69 fn parses_class_level_declarations() {
70 let tree = assert_parses(
71 "@tool\nclass_name Player\nextends CharacterBody2D\n\n## The player.\nsignal died\nsignal hit(damage: int, source: Node)\n\nenum State { IDLE, WALKING = 2, }\n\nconst MAX_SPEED := 300.0\nconst GRAVITY: float = 9.8\n\n@export var health: int = 100\n@export_range(0, 10) var lives := 3\nvar _private_state: State = State.IDLE\n@onready var sprite: Sprite2D = $Sprite2D\n",
72 );
73 let kinds = node_kinds(&tree);
74 assert!(kinds.contains(&SyntaxKind::ClassNameDecl));
75 assert!(kinds.contains(&SyntaxKind::ExtendsDecl));
76 assert!(kinds.contains(&SyntaxKind::SignalDecl));
77 assert!(kinds.contains(&SyntaxKind::EnumDecl));
78 assert!(kinds.contains(&SyntaxKind::ConstDecl));
79 assert!(kinds.contains(&SyntaxKind::VarDecl));
80 assert!(kinds.contains(&SyntaxKind::Annotation));
81 }
82
83 #[test]
84 fn annotations_attach_to_the_declaration_they_modify() {
85 let tree = assert_parses("@export var health := 100\n");
86 let var_decl = tree
87 .root()
88 .descendants()
89 .find(|node| node.kind() == SyntaxKind::VarDecl)
90 .expect("a var declaration");
91 assert!(
94 var_decl
95 .child_nodes()
96 .any(|child| child.kind() == SyntaxKind::Annotation),
97 "annotation should be a child of the declaration"
98 );
99 }
100
101 #[test]
102 fn file_level_annotations_stay_separate() {
103 let tree = assert_parses("@tool\nclass_name Foo\n");
104 let class_name = tree
105 .root()
106 .descendants()
107 .find(|node| node.kind() == SyntaxKind::ClassNameDecl)
108 .expect("a class_name declaration");
109 assert!(
112 !class_name
113 .child_nodes()
114 .any(|child| child.kind() == SyntaxKind::Annotation)
115 );
116 }
117
118 #[test]
119 fn parses_functions_and_control_flow() {
120 assert_parses(
121 "func _physics_process(delta: float) -> void:\n\tif health <= 0:\n\t\tdied.emit()\n\telif health < 20:\n\t\tblink()\n\telse:\n\t\tpass\n\n\tfor i in range(10):\n\t\tprint(i)\n\n\twhile true:\n\t\tbreak\n\n\tmatch state:\n\t\tState.IDLE:\n\t\t\tpass\n\t\tvar other when other > 2:\n\t\t\tpass\n\t\t_:\n\t\t\treturn\n",
122 );
123 }
124
125 #[test]
126 fn parses_static_and_abstract_members() {
127 assert_parses("static var count := 0\n\nstatic func reset() -> void:\n\tcount = 0\n");
128 }
129
130 #[test]
131 fn parses_inner_classes() {
132 let tree = assert_parses(
133 "class Inventory extends RefCounted:\n\tvar items: Array[String] = []\n\n\tfunc add(item: String) -> void:\n\t\titems.append(item)\n",
134 );
135 assert!(node_kinds(&tree).contains(&SyntaxKind::ClassDecl));
136 }
137
138 #[test]
139 fn parses_property_accessors() {
140 let tree = assert_parses(
141 "var health := 100:\n\tset(value):\n\t\thealth = maxi(value, 0)\n\tget:\n\t\treturn health\n",
142 );
143 let kinds = node_kinds(&tree);
144 assert!(kinds.contains(&SyntaxKind::Setter));
145 assert!(kinds.contains(&SyntaxKind::Getter));
146 }
147
148 #[test]
149 fn parses_expressions() {
150 assert_parses(
151 "func f():\n\tvar a = 1 + 2 * 3 - -4\n\tvar b = a > 1 and not a < 0 or false\n\tvar c = [1, 2, {\"key\": \"value\", other = 2}]\n\tvar d = c[0].method(1, 2).field\n\tvar e = \"yes\" if a else \"no\"\n\tvar g = a as float\n\tvar h = a is int\n\tvar i = await something()\n\tvar j = preload(\"res://x.tscn\")\n\tvar k = func(x): return x * 2\n\tvar m = 2 ** 3 ** 2\n",
152 );
153 }
154
155 #[test]
156 fn parses_assignment_operators() {
157 let tree = assert_parses(
158 "func f():\n\tx = 1\n\tx += 1\n\tx **= 2\n\tx >>= 1\n\tx[0] = 2\n\tx.y.z = 3\n",
159 );
160 let assignments = node_kinds(&tree)
161 .iter()
162 .filter(|kind| **kind == SyntaxKind::AssignStmt)
163 .count();
164 assert_eq!(assignments, 6);
165 }
166
167 #[test]
168 fn parses_inline_bodies_and_semicolons() {
169 assert_parses("func f():\n\tif true: pass\n\tvar a = 1; var b = 2\n");
170 }
171
172 #[test]
173 fn parses_wrapped_lines() {
174 assert_parses(
175 "func f():\n\tvar a = [\n\t\t1,\n\t\t2,\n\t]\n\tif a \\\n\t\t\tand true:\n\t\tpass\n\tvar b = (\n\t\t1\n\t\t+ 2\n\t)\n",
176 );
177 }
178
179 #[test]
180 fn round_trips_regardless_of_errors() {
181 for source in [
184 "func f(:\n\tpass\n",
185 "var = 5\n",
186 "class_name\n",
187 "func f():\n\treturn ]\n",
188 "@\n",
189 "if if if\n",
190 "enum { \n",
191 "var s = \"unterminated\n",
192 "\t\tweird_indent()\n",
193 "% ^ &\n",
194 ] {
195 assert_round_trips(source);
196 }
197 }
198
199 #[test]
200 fn reports_errors_without_giving_up() {
201 let tree = parse("func f(:\n\tpass\n\nfunc g():\n\tpass\n");
202 assert!(tree.has_errors());
203 let functions = node_kinds(&tree)
205 .iter()
206 .filter(|kind| **kind == SyntaxKind::FuncDecl)
207 .count();
208 assert_eq!(functions, 2, "parser should recover and see both functions");
209 }
210
211 #[test]
212 fn preserves_comments_in_the_tree() {
213 let source = "# leading\nfunc f(): # trailing\n\t# inner\n\tpass\n";
214 let tree = assert_parses(source);
215 let comments: Vec<_> = tree
216 .root()
217 .descendants()
218 .flat_map(SyntaxNode::child_tokens)
219 .filter(|token| token.kind.is_comment())
220 .map(|token| token.text(source))
221 .collect();
222 assert_eq!(comments, vec!["# leading", "# trailing", "# inner"]);
223 }
224
225 #[test]
226 fn leading_comments_attach_to_what_they_document() {
227 let tree =
228 assert_parses("extends Node\n\n# Sets things up.\nfunc _ready() -> void:\n\tpass\n");
229 let func = tree
230 .root()
231 .descendants()
232 .find(|node| node.kind() == SyntaxKind::FuncDecl)
233 .expect("a function declaration");
234 assert!(
238 func.child_tokens().any(|token| token.kind.is_comment()),
239 "leading comment should belong to the declaration it precedes"
240 );
241 }
242
243 #[test]
244 fn distinguishes_inferred_from_explicit_types() {
245 let tree = assert_parses("var a := 1\nvar b: int = 1\n");
246 let has_type_hint = tree
247 .root()
248 .descendants()
249 .filter(|node| node.kind() == SyntaxKind::VarDecl)
250 .map(|node| {
251 node.child_nodes()
252 .any(|child| child.kind() == SyntaxKind::TypeHint)
253 })
254 .collect::<Vec<_>>();
255 assert_eq!(has_type_hint, vec![false, true]);
258 }
259
260 #[test]
261 fn parses_variadic_parameters() {
262 assert_parses("func foo(a, ...rest: Array) -> void:\n\tpass\n");
263 }
264
265 #[test]
266 fn parses_not_in_as_one_operator() {
267 let tree = assert_parses("func f():\n\tvar a = 1 not in [1] not in [true]\n");
268 let binaries = node_kinds(&tree)
270 .iter()
271 .filter(|kind| **kind == SyntaxKind::BinaryExpr)
272 .count();
273 assert_eq!(binaries, 2);
274 }
275
276 #[test]
277 fn parses_abstract_annotations() {
278 assert_parses("@abstract\nclass_name Shape\n\n@abstract func area() -> float\n");
280 assert_parses("func f():\n\tvar abstract = 1\n");
282 }
283
284 #[test]
285 fn parses_annotations_inside_function_bodies() {
286 assert_parses(
287 "func a():\n\t@warning_ignore(\"unused_variable\")\n\tvar x: Array[int] = [1, 2]\n\nfunc b():\n\t@warning_ignore(\"shadowed\") @warning_ignore(\"unused\") var y = 1\n",
288 );
289 }
290
291 #[test]
292 fn parses_semicolon_separated_declarations() {
293 assert_parses("const x = 1; const y = 2\nconst z = 3;\n");
294 }
295
296 #[test]
297 fn parses_docstrings_at_class_level() {
298 assert_parses("\"\"\"docstring\n\"\"\"\n\n\"another\"\n");
299 }
300
301 #[test]
302 fn parses_absolute_node_paths() {
303 assert_parses(
304 "func f():\n\t$/root.name = \"x\"\n\t$/root/A/B/C.free()\n\t$../Sibling.show()\n",
305 );
306 }
307
308 #[test]
309 fn sigil_literals_are_recognised_at_the_start_of_a_line() {
310 assert_parses(
313 "func f():\n\tvar a = 1\n\t^\"node/path\"\n\t&\"string_name\"\n\t%Unique.show()\n",
314 );
315 }
316
317 #[test]
318 fn parses_inline_property_accessors() {
319 assert_parses(
320 "var p1: set = __set\nvar p2: set = __set, get = __get\nvar p3:\n\tget = __get,\n\tset = __set\n",
321 );
322 }
323
324 #[test]
325 fn parses_spaced_inference_operator() {
326 assert_parses("var is_enabled : = true\n");
327 }
328
329 #[test]
330 fn parses_match_patterns() {
331 assert_parses(
332 "func f(x):\n\tmatch x:\n\t\t[1, 2, [1, {1: 2, 2: var z, ..}]]:\n\t\t\tpass\n\t\t{\"name\", \"age\"}:\n\t\t\tpass\n\t\t{\"key\": \"v\", ..}:\n\t\t\tpass\n\t\t1 if true else 2:\n\t\t\tpass\n\t\tvar other when other > 2:\n\t\t\tpass\n\t\t_:\n\t\t\tpass\n",
333 );
334 }
335
336 #[test]
337 fn parses_multiline_lambdas_inside_brackets() {
338 assert_parses(
341 "func f(source):\n\tstack(func():\n\t\tprint(\"foo\")\n\t\tif source == 1:\n\t\t\tpass)\n",
342 );
343 assert_parses(
345 "func f(button):\n\tbutton.pressed.connect(\n\t\tfunc() -> void:\n\t\t\tvar test := \"\"\n\t\t\tuse(test)\n\t)\n",
346 );
347 assert_parses("func f():\n\tvar fs = [func():\n\t\treturn [1, 2, 3], func():\n\t\tpass]\n");
349 assert_parses("func f():\n\tvar g = [func():\n\t\tpass]\n\tuse(g)\n");
351 }
352
353 #[test]
354 fn nested_lambdas_close_in_the_right_order() {
355 assert_parses(
356 "func f():\n\tvar a = [func():\n\t\tpass\n\t\tvar b = func():\n\t\t\tpass\n\t\t\tvar d = {\"f\": func():\n\t\t\t\tpass\n\t\t\t\treturn [1, 2, 3]}]\n",
357 );
358 }
359
360 #[test]
361 fn brackets_inside_a_lambda_body_still_suppress_indentation() {
362 assert_parses(
365 "func f():\n\tcall(func():\n\t\tvar xs = [\n\t\t\t1,\n\t\t\t2,\n\t\t]\n\t\tuse(xs))\n",
366 );
367 }
368
369 #[test]
370 fn handles_crlf_and_missing_trailing_newline() {
371 assert_parses("var a = 1\r\nvar b = 2\r\n");
372 assert_parses("func f():\n\tpass");
373 }
374
375 #[test]
376 fn node_ranges_are_consistent_with_text() {
377 let source = "func hello() -> void:\n\tprint(\"hi\")\n";
378 let tree = parse(source);
379 for node in tree.root().descendants() {
380 let range = node.range();
381 assert!(
382 range.end() as usize <= source.len(),
383 "{:?} range {range} escapes the source",
384 node.kind()
385 );
386 assert_eq!(node.text(), range.slice(source));
387 }
388 }
389}