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]
74 fn the_keywords_godot_accepts_as_names_are_names_here_too() {
75 assert_parses("var when: int = 3\n");
76 assert_parses("func f(when: int) -> int:\n\treturn when\n");
77 assert_parses("func f(text: String) -> bool:\n\treturn text.match(\"*.gd\")\n");
78 assert_parses("var when := 1\nvar match := 2\n");
79 assert_parses("signal when\n");
80 assert_parses("enum when { A }\n");
81 assert_parses("func when() -> void:\n\tpass\n");
82 assert_parses("var entry := {}\nvar x = entry.when\n");
83 }
84
85 #[test]
89 fn a_match_guard_still_reads_when_as_a_keyword() {
90 assert_parses(
91 "func f(x: int, when: int) -> String:\n\
92 \tmatch x:\n\
93 \t\t1 when when > 0:\n\
94 \t\t\treturn \"guarded\"\n\
95 \t\tvar bound when bound > 5:\n\
96 \t\t\treturn \"bound\"\n\
97 \t\t_:\n\
98 \t\t\treturn \"other\"\n",
99 );
100 assert_round_trips("match x:\n\t1 when y:\n\t\tpass\n");
101 }
102
103 #[test]
104 fn parses_class_level_declarations() {
105 let tree = assert_parses(
106 "@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",
107 );
108 let kinds = node_kinds(&tree);
109 assert!(kinds.contains(&SyntaxKind::ClassNameDecl));
110 assert!(kinds.contains(&SyntaxKind::ExtendsDecl));
111 assert!(kinds.contains(&SyntaxKind::SignalDecl));
112 assert!(kinds.contains(&SyntaxKind::EnumDecl));
113 assert!(kinds.contains(&SyntaxKind::ConstDecl));
114 assert!(kinds.contains(&SyntaxKind::VarDecl));
115 assert!(kinds.contains(&SyntaxKind::Annotation));
116 }
117
118 #[test]
119 fn annotations_attach_to_the_declaration_they_modify() {
120 let tree = assert_parses("@export var health := 100\n");
121 let var_decl = tree
122 .root()
123 .descendants()
124 .find(|node| node.kind() == SyntaxKind::VarDecl)
125 .expect("a var declaration");
126 assert!(
129 var_decl
130 .child_nodes()
131 .any(|child| child.kind() == SyntaxKind::Annotation),
132 "annotation should be a child of the declaration"
133 );
134 }
135
136 #[test]
137 fn file_level_annotations_stay_separate() {
138 let tree = assert_parses("@tool\nclass_name Foo\n");
139 let class_name = tree
140 .root()
141 .descendants()
142 .find(|node| node.kind() == SyntaxKind::ClassNameDecl)
143 .expect("a class_name declaration");
144 assert!(
147 !class_name
148 .child_nodes()
149 .any(|child| child.kind() == SyntaxKind::Annotation)
150 );
151 }
152
153 #[test]
154 fn parses_functions_and_control_flow() {
155 assert_parses(
156 "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",
157 );
158 }
159
160 #[test]
161 fn parses_static_and_abstract_members() {
162 assert_parses("static var count := 0\n\nstatic func reset() -> void:\n\tcount = 0\n");
163 }
164
165 #[test]
166 fn parses_inner_classes() {
167 let tree = assert_parses(
168 "class Inventory extends RefCounted:\n\tvar items: Array[String] = []\n\n\tfunc add(item: String) -> void:\n\t\titems.append(item)\n",
169 );
170 assert!(node_kinds(&tree).contains(&SyntaxKind::ClassDecl));
171 }
172
173 #[test]
174 fn parses_property_accessors() {
175 let tree = assert_parses(
176 "var health := 100:\n\tset(value):\n\t\thealth = maxi(value, 0)\n\tget:\n\t\treturn health\n",
177 );
178 let kinds = node_kinds(&tree);
179 assert!(kinds.contains(&SyntaxKind::Setter));
180 assert!(kinds.contains(&SyntaxKind::Getter));
181 }
182
183 #[test]
184 fn parses_expressions() {
185 assert_parses(
186 "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",
187 );
188 }
189
190 #[test]
191 fn parses_assignment_operators() {
192 let tree = assert_parses(
193 "func f():\n\tx = 1\n\tx += 1\n\tx **= 2\n\tx >>= 1\n\tx[0] = 2\n\tx.y.z = 3\n",
194 );
195 let assignments = node_kinds(&tree)
196 .iter()
197 .filter(|kind| **kind == SyntaxKind::AssignStmt)
198 .count();
199 assert_eq!(assignments, 6);
200 }
201
202 #[test]
203 fn parses_inline_bodies_and_semicolons() {
204 assert_parses("func f():\n\tif true: pass\n\tvar a = 1; var b = 2\n");
205 }
206
207 #[test]
208 fn parses_wrapped_lines() {
209 assert_parses(
210 "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",
211 );
212 }
213
214 #[test]
215 fn round_trips_regardless_of_errors() {
216 for source in [
219 "func f(:\n\tpass\n",
220 "var = 5\n",
221 "class_name\n",
222 "func f():\n\treturn ]\n",
223 "@\n",
224 "if if if\n",
225 "enum { \n",
226 "var s = \"unterminated\n",
227 "\t\tweird_indent()\n",
228 "% ^ &\n",
229 ] {
230 assert_round_trips(source);
231 }
232 }
233
234 #[test]
235 fn reports_errors_without_giving_up() {
236 let tree = parse("func f(:\n\tpass\n\nfunc g():\n\tpass\n");
237 assert!(tree.has_errors());
238 let functions = node_kinds(&tree)
240 .iter()
241 .filter(|kind| **kind == SyntaxKind::FuncDecl)
242 .count();
243 assert_eq!(functions, 2, "parser should recover and see both functions");
244 }
245
246 #[test]
247 fn preserves_comments_in_the_tree() {
248 let source = "# leading\nfunc f(): # trailing\n\t# inner\n\tpass\n";
249 let tree = assert_parses(source);
250 let comments: Vec<_> = tree
251 .root()
252 .descendants()
253 .flat_map(SyntaxNode::child_tokens)
254 .filter(|token| token.kind.is_comment())
255 .map(|token| token.text(source))
256 .collect();
257 assert_eq!(comments, vec!["# leading", "# trailing", "# inner"]);
258 }
259
260 #[test]
261 fn leading_comments_attach_to_what_they_document() {
262 let tree =
263 assert_parses("extends Node\n\n# Sets things up.\nfunc _ready() -> void:\n\tpass\n");
264 let func = tree
265 .root()
266 .descendants()
267 .find(|node| node.kind() == SyntaxKind::FuncDecl)
268 .expect("a function declaration");
269 assert!(
273 func.child_tokens().any(|token| token.kind.is_comment()),
274 "leading comment should belong to the declaration it precedes"
275 );
276 }
277
278 #[test]
279 fn distinguishes_inferred_from_explicit_types() {
280 let tree = assert_parses("var a := 1\nvar b: int = 1\n");
281 let has_type_hint = tree
282 .root()
283 .descendants()
284 .filter(|node| node.kind() == SyntaxKind::VarDecl)
285 .map(|node| {
286 node.child_nodes()
287 .any(|child| child.kind() == SyntaxKind::TypeHint)
288 })
289 .collect::<Vec<_>>();
290 assert_eq!(has_type_hint, vec![false, true]);
293 }
294
295 #[test]
296 fn parses_variadic_parameters() {
297 assert_parses("func foo(a, ...rest: Array) -> void:\n\tpass\n");
298 }
299
300 #[test]
301 fn parses_not_in_as_one_operator() {
302 let tree = assert_parses("func f():\n\tvar a = 1 not in [1] not in [true]\n");
303 let binaries = node_kinds(&tree)
305 .iter()
306 .filter(|kind| **kind == SyntaxKind::BinaryExpr)
307 .count();
308 assert_eq!(binaries, 2);
309 }
310
311 #[test]
312 fn parses_abstract_annotations() {
313 assert_parses("@abstract\nclass_name Shape\n\n@abstract func area() -> float\n");
315 assert_parses("func f():\n\tvar abstract = 1\n");
317 }
318
319 #[test]
320 fn parses_annotations_inside_function_bodies() {
321 assert_parses(
322 "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",
323 );
324 }
325
326 #[test]
327 fn parses_semicolon_separated_declarations() {
328 assert_parses("const x = 1; const y = 2\nconst z = 3;\n");
329 }
330
331 #[test]
332 fn parses_docstrings_at_class_level() {
333 assert_parses("\"\"\"docstring\n\"\"\"\n\n\"another\"\n");
334 }
335
336 #[test]
337 fn parses_absolute_node_paths() {
338 assert_parses(
339 "func f():\n\t$/root.name = \"x\"\n\t$/root/A/B/C.free()\n\t$../Sibling.show()\n",
340 );
341 }
342
343 #[test]
344 fn sigil_literals_are_recognised_at_the_start_of_a_line() {
345 assert_parses(
348 "func f():\n\tvar a = 1\n\t^\"node/path\"\n\t&\"string_name\"\n\t%Unique.show()\n",
349 );
350 }
351
352 #[test]
353 fn parses_inline_property_accessors() {
354 assert_parses(
355 "var p1: set = __set\nvar p2: set = __set, get = __get\nvar p3:\n\tget = __get,\n\tset = __set\n",
356 );
357 }
358
359 #[test]
360 fn parses_spaced_inference_operator() {
361 assert_parses("var is_enabled : = true\n");
362 }
363
364 #[test]
365 fn parses_match_patterns() {
366 assert_parses(
367 "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",
368 );
369 }
370
371 #[test]
372 fn parses_multiline_lambdas_inside_brackets() {
373 assert_parses(
376 "func f(source):\n\tstack(func():\n\t\tprint(\"foo\")\n\t\tif source == 1:\n\t\t\tpass)\n",
377 );
378 assert_parses(
380 "func f(button):\n\tbutton.pressed.connect(\n\t\tfunc() -> void:\n\t\t\tvar test := \"\"\n\t\t\tuse(test)\n\t)\n",
381 );
382 assert_parses("func f():\n\tvar fs = [func():\n\t\treturn [1, 2, 3], func():\n\t\tpass]\n");
384 assert_parses("func f():\n\tvar g = [func():\n\t\tpass]\n\tuse(g)\n");
386 }
387
388 #[test]
389 fn nested_lambdas_close_in_the_right_order() {
390 assert_parses(
391 "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",
392 );
393 }
394
395 #[test]
396 fn brackets_inside_a_lambda_body_still_suppress_indentation() {
397 assert_parses(
400 "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",
401 );
402 }
403
404 #[test]
405 fn handles_crlf_and_missing_trailing_newline() {
406 assert_parses("var a = 1\r\nvar b = 2\r\n");
407 assert_parses("func f():\n\tpass");
408 }
409
410 #[test]
411 fn node_ranges_are_consistent_with_text() {
412 let source = "func hello() -> void:\n\tprint(\"hi\")\n";
413 let tree = parse(source);
414 for node in tree.root().descendants() {
415 let range = node.range();
416 assert!(
417 range.end() as usize <= source.len(),
418 "{:?} range {range} escapes the source",
419 node.kind()
420 );
421 assert_eq!(node.text(), range.slice(source));
422 }
423 }
424}