1/**************************************************************************/
2/* gdscript_parser.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "gdscript_parser.h"
32
33#include "gdscript.h"
34
35#ifdef DEBUG_ENABLED
36#include "gdscript_warning.h"
37#endif
38
39#include "core/config/project_settings.h"
40#include "core/io/file_access.h"
41#include "core/io/resource_loader.h"
42#include "core/math/math_defs.h"
43#include "scene/main/multiplayer_api.h"
44
45#ifdef DEBUG_ENABLED
46#include "core/os/os.h"
47#include "core/string/string_builder.h"
48#include "servers/text_server.h"
49#endif
50
51#ifdef TOOLS_ENABLED
52#include "editor/editor_settings.h"
53#endif
54
55static HashMap<StringName, Variant::Type> builtin_types;
56Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
57 if (builtin_types.is_empty()) {
58 for (int i = 1; i < Variant::VARIANT_MAX; i++) {
59 builtin_types[Variant::get_type_name((Variant::Type)i)] = (Variant::Type)i;
60 }
61 }
62
63 if (builtin_types.has(p_type)) {
64 return builtin_types[p_type];
65 }
66 return Variant::VARIANT_MAX;
67}
68
69#ifdef TOOLS_ENABLED
70HashMap<String, String> GDScriptParser::theme_color_names;
71#endif
72
73void GDScriptParser::cleanup() {
74 builtin_types.clear();
75}
76
77void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const {
78 for (const KeyValue<StringName, AnnotationInfo> &E : valid_annotations) {
79 r_annotations->push_back(E.value.info);
80 }
81}
82
83bool GDScriptParser::annotation_exists(const String &p_annotation_name) const {
84 return valid_annotations.has(p_annotation_name);
85}
86
87GDScriptParser::GDScriptParser() {
88 // Register valid annotations.
89 // TODO: Should this be static?
90 register_annotation(MethodInfo("@tool"), AnnotationInfo::SCRIPT, &GDScriptParser::tool_annotation);
91 register_annotation(MethodInfo("@icon", PropertyInfo(Variant::STRING, "icon_path")), AnnotationInfo::SCRIPT, &GDScriptParser::icon_annotation);
92 register_annotation(MethodInfo("@static_unload"), AnnotationInfo::SCRIPT, &GDScriptParser::static_unload_annotation);
93
94 register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation);
95 // Export annotations.
96 register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
97 register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::NIL>, varray(), true);
98 register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, varray(""), true);
99 register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>);
100 register_annotation(MethodInfo("@export_global_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_FILE, Variant::STRING>, varray(""), true);
101 register_annotation(MethodInfo("@export_global_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_DIR, Variant::STRING>);
102 register_annotation(MethodInfo("@export_multiline"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_MULTILINE_TEXT, Variant::STRING>);
103 register_annotation(MethodInfo("@export_placeholder", PropertyInfo(Variant::STRING, "placeholder")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_PLACEHOLDER_TEXT, Variant::STRING>);
104 register_annotation(MethodInfo("@export_range", PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"), PropertyInfo(Variant::FLOAT, "step"), PropertyInfo(Variant::STRING, "extra_hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_RANGE, Variant::FLOAT>, varray(1.0, ""), true);
105 register_annotation(MethodInfo("@export_exp_easing", PropertyInfo(Variant::STRING, "hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_EASING, Variant::FLOAT>, varray(""), true);
106 register_annotation(MethodInfo("@export_color_no_alpha"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_COLOR_NO_ALPHA, Variant::COLOR>);
107 register_annotation(MethodInfo("@export_node_path", PropertyInfo(Variant::STRING, "type")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NODE_PATH_VALID_TYPES, Variant::NODE_PATH>, varray(""), true);
108 register_annotation(MethodInfo("@export_flags", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FLAGS, Variant::INT>, varray(), true);
109 register_annotation(MethodInfo("@export_flags_2d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_RENDER, Variant::INT>);
110 register_annotation(MethodInfo("@export_flags_2d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_PHYSICS, Variant::INT>);
111 register_annotation(MethodInfo("@export_flags_2d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_NAVIGATION, Variant::INT>);
112 register_annotation(MethodInfo("@export_flags_3d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_RENDER, Variant::INT>);
113 register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
114 register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
115 register_annotation(MethodInfo("@export_flags_avoidance"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_AVOIDANCE, Variant::INT>);
116 // Export grouping annotations.
117 register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>);
118 register_annotation(MethodInfo("@export_group", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_GROUP>, varray(""));
119 register_annotation(MethodInfo("@export_subgroup", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_SUBGROUP>, varray(""));
120 // Warning annotations.
121 register_annotation(MethodInfo("@warning_ignore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, varray(), true);
122 // Networking.
123 register_annotation(MethodInfo("@rpc", PropertyInfo(Variant::STRING, "mode"), PropertyInfo(Variant::STRING, "sync"), PropertyInfo(Variant::STRING, "transfer_mode"), PropertyInfo(Variant::INT, "transfer_channel")), AnnotationInfo::FUNCTION, &GDScriptParser::rpc_annotation, varray("authority", "call_remote", "unreliable", 0));
124
125#ifdef DEBUG_ENABLED
126 is_ignoring_warnings = !(bool)GLOBAL_GET("debug/gdscript/warnings/enable");
127#endif
128
129#ifdef TOOLS_ENABLED
130 if (theme_color_names.is_empty()) {
131 theme_color_names.insert("x", "axis_x_color");
132 theme_color_names.insert("y", "axis_y_color");
133 theme_color_names.insert("z", "axis_z_color");
134 theme_color_names.insert("w", "axis_w_color");
135 }
136#endif
137}
138
139GDScriptParser::~GDScriptParser() {
140 clear();
141}
142
143void GDScriptParser::clear() {
144 while (list != nullptr) {
145 Node *element = list;
146 list = list->next;
147 memdelete(element);
148 }
149
150 head = nullptr;
151 list = nullptr;
152 _is_tool = false;
153 for_completion = false;
154 errors.clear();
155 multiline_stack.clear();
156 nodes_in_progress.clear();
157}
158
159void GDScriptParser::push_error(const String &p_message, const Node *p_origin) {
160 // TODO: Improve error reporting by pointing at source code.
161 // TODO: Errors might point at more than one place at once (e.g. show previous declaration).
162 panic_mode = true;
163 // TODO: Improve positional information.
164 if (p_origin == nullptr) {
165 errors.push_back({ p_message, current.start_line, current.start_column });
166 } else {
167 errors.push_back({ p_message, p_origin->start_line, p_origin->leftmost_column });
168 }
169}
170
171#ifdef DEBUG_ENABLED
172void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Vector<String> &p_symbols) {
173 ERR_FAIL_COND(p_source == nullptr);
174 if (is_ignoring_warnings) {
175 return;
176 }
177 if (GLOBAL_GET("debug/gdscript/warnings/exclude_addons").booleanize() && script_path.begins_with("res://addons/")) {
178 return;
179 }
180
181 if (ignored_warnings.has(p_code)) {
182 return;
183 }
184
185 int warn_level = (int)GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(p_code));
186 if (!warn_level) {
187 return;
188 }
189
190 GDScriptWarning warning;
191 warning.code = p_code;
192 warning.symbols = p_symbols;
193 warning.start_line = p_source->start_line;
194 warning.end_line = p_source->end_line;
195 warning.leftmost_column = p_source->leftmost_column;
196 warning.rightmost_column = p_source->rightmost_column;
197
198 if (warn_level == GDScriptWarning::WarnLevel::ERROR) {
199 push_error(warning.get_message() + String(" (Warning treated as error.)"), p_source);
200 return;
201 }
202
203 List<GDScriptWarning>::Element *before = nullptr;
204 for (List<GDScriptWarning>::Element *E = warnings.front(); E; E = E->next()) {
205 if (E->get().start_line > warning.start_line) {
206 break;
207 }
208 before = E;
209 }
210 if (before) {
211 warnings.insert_after(before, warning);
212 } else {
213 warnings.push_front(warning);
214 }
215}
216#endif
217
218void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node, int p_argument, bool p_force) {
219 if (!for_completion || (!p_force && completion_context.type != COMPLETION_NONE)) {
220 return;
221 }
222 if (previous.cursor_place != GDScriptTokenizer::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizer::CURSOR_END && current.cursor_place == GDScriptTokenizer::CURSOR_NONE) {
223 return;
224 }
225 CompletionContext context;
226 context.type = p_type;
227 context.current_class = current_class;
228 context.current_function = current_function;
229 context.current_suite = current_suite;
230 context.current_line = tokenizer.get_cursor_line();
231 context.current_argument = p_argument;
232 context.node = p_node;
233 completion_context = context;
234}
235
236void GDScriptParser::make_completion_context(CompletionType p_type, Variant::Type p_builtin_type, bool p_force) {
237 if (!for_completion || (!p_force && completion_context.type != COMPLETION_NONE)) {
238 return;
239 }
240 if (previous.cursor_place != GDScriptTokenizer::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizer::CURSOR_END && current.cursor_place == GDScriptTokenizer::CURSOR_NONE) {
241 return;
242 }
243 CompletionContext context;
244 context.type = p_type;
245 context.current_class = current_class;
246 context.current_function = current_function;
247 context.current_suite = current_suite;
248 context.current_line = tokenizer.get_cursor_line();
249 context.builtin_type = p_builtin_type;
250 completion_context = context;
251}
252
253void GDScriptParser::push_completion_call(Node *p_call) {
254 if (!for_completion) {
255 return;
256 }
257 CompletionCall call;
258 call.call = p_call;
259 call.argument = 0;
260 completion_call_stack.push_back(call);
261 if (previous.cursor_place == GDScriptTokenizer::CURSOR_MIDDLE || previous.cursor_place == GDScriptTokenizer::CURSOR_END || current.cursor_place == GDScriptTokenizer::CURSOR_BEGINNING) {
262 completion_call = call;
263 }
264}
265
266void GDScriptParser::pop_completion_call() {
267 if (!for_completion) {
268 return;
269 }
270 ERR_FAIL_COND_MSG(completion_call_stack.is_empty(), "Trying to pop empty completion call stack");
271 completion_call_stack.pop_back();
272}
273
274void GDScriptParser::set_last_completion_call_arg(int p_argument) {
275 if (!for_completion || passed_cursor) {
276 return;
277 }
278 ERR_FAIL_COND_MSG(completion_call_stack.is_empty(), "Trying to set argument on empty completion call stack");
279 completion_call_stack.back()->get().argument = p_argument;
280}
281
282Error GDScriptParser::parse(const String &p_source_code, const String &p_script_path, bool p_for_completion) {
283 clear();
284
285 String source = p_source_code;
286 int cursor_line = -1;
287 int cursor_column = -1;
288 for_completion = p_for_completion;
289
290 int tab_size = 4;
291#ifdef TOOLS_ENABLED
292 if (EditorSettings::get_singleton()) {
293 tab_size = EditorSettings::get_singleton()->get_setting("text_editor/behavior/indent/size");
294 }
295#endif // TOOLS_ENABLED
296
297 if (p_for_completion) {
298 // Remove cursor sentinel char.
299 const Vector<String> lines = p_source_code.split("\n");
300 cursor_line = 1;
301 cursor_column = 1;
302 for (int i = 0; i < lines.size(); i++) {
303 bool found = false;
304 const String &line = lines[i];
305 for (int j = 0; j < line.size(); j++) {
306 if (line[j] == char32_t(0xFFFF)) {
307 found = true;
308 break;
309 } else if (line[j] == '\t') {
310 cursor_column += tab_size - 1;
311 }
312 cursor_column++;
313 }
314 if (found) {
315 break;
316 }
317 cursor_line++;
318 cursor_column = 1;
319 }
320
321 source = source.replace_first(String::chr(0xFFFF), String());
322 }
323
324 tokenizer.set_source_code(source);
325 tokenizer.set_cursor_position(cursor_line, cursor_column);
326 script_path = p_script_path;
327 current = tokenizer.scan();
328 // Avoid error or newline as the first token.
329 // The latter can mess with the parser when opening files filled exclusively with comments and newlines.
330 while (current.type == GDScriptTokenizer::Token::ERROR || current.type == GDScriptTokenizer::Token::NEWLINE) {
331 if (current.type == GDScriptTokenizer::Token::ERROR) {
332 push_error(current.literal);
333 }
334 current = tokenizer.scan();
335 }
336
337#ifdef DEBUG_ENABLED
338 // Warn about parsing an empty script file:
339 if (current.type == GDScriptTokenizer::Token::TK_EOF) {
340 // Create a dummy Node for the warning, pointing to the very beginning of the file
341 Node *nd = alloc_node<PassNode>();
342 nd->start_line = 1;
343 nd->start_column = 0;
344 nd->end_line = 1;
345 nd->leftmost_column = 0;
346 nd->rightmost_column = 0;
347 push_warning(nd, GDScriptWarning::EMPTY_FILE);
348 }
349#endif
350
351 push_multiline(false); // Keep one for the whole parsing.
352 parse_program();
353 pop_multiline();
354
355#ifdef DEBUG_ENABLED
356 if (multiline_stack.size() > 0) {
357 ERR_PRINT("Parser bug: Imbalanced multiline stack.");
358 }
359#endif
360
361 if (errors.is_empty()) {
362 return OK;
363 } else {
364 return ERR_PARSE_ERROR;
365 }
366}
367
368GDScriptTokenizer::Token GDScriptParser::advance() {
369 lambda_ended = false; // Empty marker since we're past the end in any case.
370
371 if (current.type == GDScriptTokenizer::Token::TK_EOF) {
372 ERR_FAIL_COND_V_MSG(current.type == GDScriptTokenizer::Token::TK_EOF, current, "GDScript parser bug: Trying to advance past the end of stream.");
373 }
374 if (for_completion && !completion_call_stack.is_empty()) {
375 if (completion_call.call == nullptr && tokenizer.is_past_cursor()) {
376 completion_call = completion_call_stack.back()->get();
377 passed_cursor = true;
378 }
379 }
380 previous = current;
381 current = tokenizer.scan();
382 while (current.type == GDScriptTokenizer::Token::ERROR) {
383 push_error(current.literal);
384 current = tokenizer.scan();
385 }
386 for (Node *n : nodes_in_progress) {
387 update_extents(n);
388 }
389 return previous;
390}
391
392bool GDScriptParser::match(GDScriptTokenizer::Token::Type p_token_type) {
393 if (!check(p_token_type)) {
394 return false;
395 }
396 advance();
397 return true;
398}
399
400bool GDScriptParser::check(GDScriptTokenizer::Token::Type p_token_type) const {
401 if (p_token_type == GDScriptTokenizer::Token::IDENTIFIER) {
402 return current.is_identifier();
403 }
404 return current.type == p_token_type;
405}
406
407bool GDScriptParser::consume(GDScriptTokenizer::Token::Type p_token_type, const String &p_error_message) {
408 if (match(p_token_type)) {
409 return true;
410 }
411 push_error(p_error_message);
412 return false;
413}
414
415bool GDScriptParser::is_at_end() const {
416 return check(GDScriptTokenizer::Token::TK_EOF);
417}
418
419void GDScriptParser::synchronize() {
420 panic_mode = false;
421 while (!is_at_end()) {
422 if (previous.type == GDScriptTokenizer::Token::NEWLINE || previous.type == GDScriptTokenizer::Token::SEMICOLON) {
423 return;
424 }
425
426 switch (current.type) {
427 case GDScriptTokenizer::Token::CLASS:
428 case GDScriptTokenizer::Token::FUNC:
429 case GDScriptTokenizer::Token::STATIC:
430 case GDScriptTokenizer::Token::VAR:
431 case GDScriptTokenizer::Token::CONST:
432 case GDScriptTokenizer::Token::SIGNAL:
433 //case GDScriptTokenizer::Token::IF: // Can also be inside expressions.
434 case GDScriptTokenizer::Token::FOR:
435 case GDScriptTokenizer::Token::WHILE:
436 case GDScriptTokenizer::Token::MATCH:
437 case GDScriptTokenizer::Token::RETURN:
438 case GDScriptTokenizer::Token::ANNOTATION:
439 return;
440 default:
441 // Do nothing.
442 break;
443 }
444
445 advance();
446 }
447}
448
449void GDScriptParser::push_multiline(bool p_state) {
450 multiline_stack.push_back(p_state);
451 tokenizer.set_multiline_mode(p_state);
452 if (p_state) {
453 // Consume potential whitespace tokens already waiting in line.
454 while (current.type == GDScriptTokenizer::Token::NEWLINE || current.type == GDScriptTokenizer::Token::INDENT || current.type == GDScriptTokenizer::Token::DEDENT) {
455 current = tokenizer.scan(); // Don't call advance() here, as we don't want to change the previous token.
456 }
457 }
458}
459
460void GDScriptParser::pop_multiline() {
461 ERR_FAIL_COND_MSG(multiline_stack.size() == 0, "Parser bug: trying to pop from multiline stack without available value.");
462 multiline_stack.pop_back();
463 tokenizer.set_multiline_mode(multiline_stack.size() > 0 ? multiline_stack.back()->get() : false);
464}
465
466bool GDScriptParser::is_statement_end_token() const {
467 return check(GDScriptTokenizer::Token::NEWLINE) || check(GDScriptTokenizer::Token::SEMICOLON) || check(GDScriptTokenizer::Token::TK_EOF);
468}
469
470bool GDScriptParser::is_statement_end() const {
471 return lambda_ended || in_lambda || is_statement_end_token();
472}
473
474void GDScriptParser::end_statement(const String &p_context) {
475 bool found = false;
476 while (is_statement_end() && !is_at_end()) {
477 // Remove sequential newlines/semicolons.
478 if (is_statement_end_token()) {
479 // Only consume if this is an actual token.
480 advance();
481 } else if (lambda_ended) {
482 lambda_ended = false; // Consume this "token".
483 found = true;
484 break;
485 } else {
486 if (!found) {
487 lambda_ended = true; // Mark the lambda as done since we found something else to end the statement.
488 found = true;
489 }
490 break;
491 }
492
493 found = true;
494 }
495 if (!found && !is_at_end()) {
496 push_error(vformat(R"(Expected end of statement after %s, found "%s" instead.)", p_context, current.get_name()));
497 }
498}
499
500void GDScriptParser::parse_program() {
501 head = alloc_node<ClassNode>();
502 head->fqcn = script_path;
503 current_class = head;
504 bool can_have_class_or_extends = true;
505
506 while (!check(GDScriptTokenizer::Token::TK_EOF)) {
507 if (match(GDScriptTokenizer::Token::ANNOTATION)) {
508 AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
509 if (annotation != nullptr) {
510 if (annotation->applies_to(AnnotationInfo::SCRIPT)) {
511 // `@icon` needs to be applied in the parser. See GH-72444.
512 if (annotation->name == SNAME("@icon")) {
513 annotation->apply(this, head);
514 } else {
515 head->annotations.push_back(annotation);
516 }
517 } else {
518 annotation_stack.push_back(annotation);
519 // This annotation must appear after script-level annotations
520 // and class_name/extends (ex: could be @onready or @export),
521 // so we stop looking for script-level stuff.
522 can_have_class_or_extends = false;
523 break;
524 }
525 }
526 } else if (check(GDScriptTokenizer::Token::LITERAL) && current.literal.get_type() == Variant::STRING) {
527 // Allow strings in class body as multiline comments.
528 advance();
529 if (!match(GDScriptTokenizer::Token::NEWLINE)) {
530 push_error("Expected newline after comment string.");
531 }
532 } else {
533 break;
534 }
535 }
536
537 while (can_have_class_or_extends) {
538 // Order here doesn't matter, but there should be only one of each at most.
539 switch (current.type) {
540 case GDScriptTokenizer::Token::CLASS_NAME:
541 advance();
542 if (head->identifier != nullptr) {
543 push_error(R"("class_name" can only be used once.)");
544 } else {
545 parse_class_name();
546 }
547 break;
548 case GDScriptTokenizer::Token::EXTENDS:
549 advance();
550 if (head->extends_used) {
551 push_error(R"("extends" can only be used once.)");
552 } else {
553 parse_extends();
554 end_statement("superclass");
555 }
556 break;
557 case GDScriptTokenizer::Token::LITERAL:
558 if (current.literal.get_type() == Variant::STRING) {
559 // Allow strings in class body as multiline comments.
560 advance();
561 if (!match(GDScriptTokenizer::Token::NEWLINE)) {
562 push_error("Expected newline after comment string.");
563 }
564 break;
565 }
566 [[fallthrough]];
567 default:
568 // No tokens are allowed between script annotations and class/extends.
569 can_have_class_or_extends = false;
570 break;
571 }
572
573 if (panic_mode) {
574 synchronize();
575 }
576 }
577
578 parse_class_body(true);
579 complete_extents(head);
580
581#ifdef TOOLS_ENABLED
582 for (const KeyValue<int, GDScriptTokenizer::CommentData> &E : tokenizer.get_comments()) {
583 if (E.value.new_line && E.value.comment.begins_with("##")) {
584 class_doc_line = MIN(class_doc_line, E.key);
585 }
586 }
587 if (has_comment(class_doc_line, true)) {
588 head->doc_data = parse_class_doc_comment(class_doc_line, false);
589 }
590#endif // TOOLS_ENABLED
591
592 if (!check(GDScriptTokenizer::Token::TK_EOF)) {
593 push_error("Expected end of file.");
594 }
595
596 clear_unused_annotations();
597}
598
599GDScriptParser::ClassNode *GDScriptParser::find_class(const String &p_qualified_name) const {
600 String first = p_qualified_name.get_slice("::", 0);
601
602 Vector<String> class_names;
603 GDScriptParser::ClassNode *result = nullptr;
604 // Empty initial name means start at the head.
605 if (first.is_empty() || (head->identifier && first == head->identifier->name)) {
606 class_names = p_qualified_name.split("::");
607 result = head;
608 } else if (p_qualified_name.begins_with(script_path)) {
609 // Script path could have a class path separator("::") in it.
610 class_names = p_qualified_name.trim_prefix(script_path).split("::");
611 result = head;
612 } else if (head->has_member(first)) {
613 class_names = p_qualified_name.split("::");
614 GDScriptParser::ClassNode::Member member = head->get_member(first);
615 if (member.type == GDScriptParser::ClassNode::Member::CLASS) {
616 result = member.m_class;
617 }
618 }
619
620 // Starts at index 1 because index 0 was handled above.
621 for (int i = 1; result != nullptr && i < class_names.size(); i++) {
622 String current_name = class_names[i];
623 GDScriptParser::ClassNode *next = nullptr;
624 if (result->has_member(current_name)) {
625 GDScriptParser::ClassNode::Member member = result->get_member(current_name);
626 if (member.type == GDScriptParser::ClassNode::Member::CLASS) {
627 next = member.m_class;
628 }
629 }
630 result = next;
631 }
632
633 return result;
634}
635
636bool GDScriptParser::has_class(const GDScriptParser::ClassNode *p_class) const {
637 if (head->fqcn.is_empty() && p_class->fqcn.get_slice("::", 0).is_empty()) {
638 return p_class == head;
639 } else if (p_class->fqcn.begins_with(head->fqcn)) {
640 return find_class(p_class->fqcn.trim_prefix(head->fqcn)) == p_class;
641 }
642
643 return false;
644}
645
646GDScriptParser::ClassNode *GDScriptParser::parse_class(bool p_is_static) {
647 ClassNode *n_class = alloc_node<ClassNode>();
648
649 ClassNode *previous_class = current_class;
650 current_class = n_class;
651 n_class->outer = previous_class;
652
653 if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for the class name after "class".)")) {
654 n_class->identifier = parse_identifier();
655 if (n_class->outer) {
656 String fqcn = n_class->outer->fqcn;
657 if (fqcn.is_empty()) {
658 fqcn = script_path;
659 }
660 n_class->fqcn = fqcn + "::" + n_class->identifier->name;
661 } else {
662 n_class->fqcn = n_class->identifier->name;
663 }
664 }
665
666 if (match(GDScriptTokenizer::Token::EXTENDS)) {
667 parse_extends();
668 }
669
670 consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after class declaration.)");
671
672 bool multiline = match(GDScriptTokenizer::Token::NEWLINE);
673
674 if (multiline && !consume(GDScriptTokenizer::Token::INDENT, R"(Expected indented block after class declaration.)")) {
675 current_class = previous_class;
676 complete_extents(n_class);
677 return n_class;
678 }
679
680 if (match(GDScriptTokenizer::Token::EXTENDS)) {
681 if (n_class->extends_used) {
682 push_error(R"(Cannot use "extends" more than once in the same class.)");
683 }
684 parse_extends();
685 end_statement("superclass");
686 }
687
688 parse_class_body(multiline);
689 complete_extents(n_class);
690
691 if (multiline) {
692 consume(GDScriptTokenizer::Token::DEDENT, R"(Missing unindent at the end of the class body.)");
693 }
694
695 current_class = previous_class;
696 return n_class;
697}
698
699void GDScriptParser::parse_class_name() {
700 if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for the global class name after "class_name".)")) {
701 current_class->identifier = parse_identifier();
702 current_class->fqcn = String(current_class->identifier->name);
703 }
704
705 if (match(GDScriptTokenizer::Token::EXTENDS)) {
706 // Allow extends on the same line.
707 parse_extends();
708 end_statement("superclass");
709 } else {
710 end_statement("class_name statement");
711 }
712}
713
714void GDScriptParser::parse_extends() {
715 current_class->extends_used = true;
716
717 int chain_index = 0;
718
719 if (match(GDScriptTokenizer::Token::LITERAL)) {
720 if (previous.literal.get_type() != Variant::STRING) {
721 push_error(vformat(R"(Only strings or identifiers can be used after "extends", found "%s" instead.)", Variant::get_type_name(previous.literal.get_type())));
722 }
723 current_class->extends_path = previous.literal;
724
725 if (!match(GDScriptTokenizer::Token::PERIOD)) {
726 return;
727 }
728 }
729
730 make_completion_context(COMPLETION_INHERIT_TYPE, current_class, chain_index++);
731
732 if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected superclass name after "extends".)")) {
733 return;
734 }
735 current_class->extends.push_back(parse_identifier());
736
737 while (match(GDScriptTokenizer::Token::PERIOD)) {
738 make_completion_context(COMPLETION_INHERIT_TYPE, current_class, chain_index++);
739 if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected superclass name after ".".)")) {
740 return;
741 }
742 current_class->extends.push_back(parse_identifier());
743 }
744}
745
746template <class T>
747void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)(bool), AnnotationInfo::TargetKind p_target, const String &p_member_kind, bool p_is_static) {
748 advance();
749
750#ifdef TOOLS_ENABLED
751 int doc_comment_line = previous.start_line - 1;
752#endif // TOOLS_ENABLED
753
754 // Consume annotations.
755 List<AnnotationNode *> annotations;
756 while (!annotation_stack.is_empty()) {
757 AnnotationNode *last_annotation = annotation_stack.back()->get();
758 if (last_annotation->applies_to(p_target)) {
759 annotations.push_front(last_annotation);
760 annotation_stack.pop_back();
761 } else {
762 push_error(vformat(R"(Annotation "%s" cannot be applied to a %s.)", last_annotation->name, p_member_kind));
763 clear_unused_annotations();
764 }
765#ifdef TOOLS_ENABLED
766 if (last_annotation->start_line == doc_comment_line) {
767 doc_comment_line--;
768 }
769#endif // TOOLS_ENABLED
770 }
771
772 T *member = (this->*p_parse_function)(p_is_static);
773 if (member == nullptr) {
774 return;
775 }
776
777 for (AnnotationNode *&annotation : annotations) {
778 member->annotations.push_back(annotation);
779 }
780
781#ifdef TOOLS_ENABLED
782 // Consume doc comments.
783 class_doc_line = MIN(class_doc_line, doc_comment_line - 1);
784
785 // Check whether current line has a doc comment
786 if (has_comment(previous.start_line, true)) {
787 if constexpr (std::is_same_v<T, ClassNode>) {
788 member->doc_data = parse_class_doc_comment(previous.start_line, true, true);
789 } else {
790 member->doc_data = parse_doc_comment(previous.start_line, true);
791 }
792 } else if (has_comment(doc_comment_line, true)) {
793 if constexpr (std::is_same_v<T, ClassNode>) {
794 member->doc_data = parse_class_doc_comment(doc_comment_line, true);
795 } else {
796 member->doc_data = parse_doc_comment(doc_comment_line);
797 }
798 }
799#endif // TOOLS_ENABLED
800
801 if (member->identifier != nullptr) {
802 if (!((String)member->identifier->name).is_empty()) { // Enums may be unnamed.
803 if (current_class->members_indices.has(member->identifier->name)) {
804 push_error(vformat(R"(%s "%s" has the same name as a previously declared %s.)", p_member_kind.capitalize(), member->identifier->name, current_class->get_member(member->identifier->name).get_type_name()), member->identifier);
805 } else {
806 current_class->add_member(member);
807 }
808 } else {
809 current_class->add_member(member);
810 }
811 }
812}
813
814void GDScriptParser::parse_class_body(bool p_is_multiline) {
815 bool class_end = false;
816 bool next_is_static = false;
817 while (!class_end && !is_at_end()) {
818 GDScriptTokenizer::Token token = current;
819 switch (token.type) {
820 case GDScriptTokenizer::Token::VAR:
821 parse_class_member(&GDScriptParser::parse_variable, AnnotationInfo::VARIABLE, "variable", next_is_static);
822 if (next_is_static) {
823 current_class->has_static_data = true;
824 }
825 break;
826 case GDScriptTokenizer::Token::CONST:
827 parse_class_member(&GDScriptParser::parse_constant, AnnotationInfo::CONSTANT, "constant");
828 break;
829 case GDScriptTokenizer::Token::SIGNAL:
830 parse_class_member(&GDScriptParser::parse_signal, AnnotationInfo::SIGNAL, "signal");
831 break;
832 case GDScriptTokenizer::Token::FUNC:
833 parse_class_member(&GDScriptParser::parse_function, AnnotationInfo::FUNCTION, "function", next_is_static);
834 break;
835 case GDScriptTokenizer::Token::CLASS:
836 parse_class_member(&GDScriptParser::parse_class, AnnotationInfo::CLASS, "class");
837 break;
838 case GDScriptTokenizer::Token::ENUM:
839 parse_class_member(&GDScriptParser::parse_enum, AnnotationInfo::NONE, "enum");
840 break;
841 case GDScriptTokenizer::Token::STATIC: {
842 advance();
843 next_is_static = true;
844 if (!check(GDScriptTokenizer::Token::FUNC) && !check(GDScriptTokenizer::Token::VAR)) {
845 push_error(R"(Expected "func" or "var" after "static".)");
846 }
847 } break;
848 case GDScriptTokenizer::Token::ANNOTATION: {
849 advance();
850
851 // Check for standalone and class-level annotations.
852 AnnotationNode *annotation = parse_annotation(AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
853 if (annotation != nullptr) {
854 if (annotation->applies_to(AnnotationInfo::STANDALONE)) {
855 if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
856 push_error(R"(Expected newline after a standalone annotation.)");
857 }
858 if (annotation->name == SNAME("@export_category") || annotation->name == SNAME("@export_group") || annotation->name == SNAME("@export_subgroup")) {
859 current_class->add_member_group(annotation);
860 } else {
861 // For potential non-group standalone annotations.
862 push_error(R"(Unexpected standalone annotation in class body.)");
863 }
864 } else {
865 annotation_stack.push_back(annotation);
866 }
867 }
868 break;
869 }
870 case GDScriptTokenizer::Token::PASS:
871 advance();
872 end_statement(R"("pass")");
873 break;
874 case GDScriptTokenizer::Token::DEDENT:
875 class_end = true;
876 break;
877 case GDScriptTokenizer::Token::LITERAL:
878 if (current.literal.get_type() == Variant::STRING) {
879 // Allow strings in class body as multiline comments.
880 advance();
881 if (!match(GDScriptTokenizer::Token::NEWLINE)) {
882 push_error("Expected newline after comment string.");
883 }
884 break;
885 }
886 [[fallthrough]];
887 default:
888 // Display a completion with identifiers.
889 make_completion_context(COMPLETION_IDENTIFIER, nullptr);
890 push_error(vformat(R"(Unexpected "%s" in class body.)", current.get_name()));
891 advance();
892 break;
893 }
894 if (token.type != GDScriptTokenizer::Token::STATIC) {
895 next_is_static = false;
896 }
897 if (panic_mode) {
898 synchronize();
899 }
900 if (!p_is_multiline) {
901 class_end = true;
902 }
903 }
904}
905
906GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_is_static) {
907 return parse_variable(p_is_static, true);
908}
909
910GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_is_static, bool p_allow_property) {
911 VariableNode *variable = alloc_node<VariableNode>();
912
913 if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected variable name after "var".)")) {
914 complete_extents(variable);
915 return nullptr;
916 }
917
918 variable->identifier = parse_identifier();
919 variable->export_info.name = variable->identifier->name;
920 variable->is_static = p_is_static;
921
922 if (match(GDScriptTokenizer::Token::COLON)) {
923 if (check(GDScriptTokenizer::Token::NEWLINE)) {
924 if (p_allow_property) {
925 advance();
926 return parse_property(variable, true);
927 } else {
928 push_error(R"(Expected type after ":")");
929 complete_extents(variable);
930 return nullptr;
931 }
932 } else if (check((GDScriptTokenizer::Token::EQUAL))) {
933 // Infer type.
934 variable->infer_datatype = true;
935 } else {
936 if (p_allow_property) {
937 make_completion_context(COMPLETION_PROPERTY_DECLARATION_OR_TYPE, variable);
938 if (check(GDScriptTokenizer::Token::IDENTIFIER)) {
939 // Check if get or set.
940 if (current.get_identifier() == "get" || current.get_identifier() == "set") {
941 return parse_property(variable, false);
942 }
943 }
944 }
945
946 // Parse type.
947 variable->datatype_specifier = parse_type();
948 }
949 }
950
951 if (match(GDScriptTokenizer::Token::EQUAL)) {
952 // Initializer.
953 variable->initializer = parse_expression(false);
954 if (variable->initializer == nullptr) {
955 push_error(R"(Expected expression for variable initial value after "=".)");
956 }
957 variable->assignments++;
958 }
959
960 if (p_allow_property && match(GDScriptTokenizer::Token::COLON)) {
961 if (match(GDScriptTokenizer::Token::NEWLINE)) {
962 return parse_property(variable, true);
963 } else {
964 return parse_property(variable, false);
965 }
966 }
967
968 complete_extents(variable);
969 end_statement("variable declaration");
970
971 return variable;
972}
973
974GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_variable, bool p_need_indent) {
975 if (p_need_indent) {
976 if (!consume(GDScriptTokenizer::Token::INDENT, R"(Expected indented block for property after ":".)")) {
977 complete_extents(p_variable);
978 return nullptr;
979 }
980 }
981
982 VariableNode *property = p_variable;
983
984 make_completion_context(COMPLETION_PROPERTY_DECLARATION, property);
985
986 if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected "get" or "set" for property declaration.)")) {
987 complete_extents(p_variable);
988 return nullptr;
989 }
990
991 IdentifierNode *function = parse_identifier();
992
993 if (check(GDScriptTokenizer::Token::EQUAL)) {
994 p_variable->property = VariableNode::PROP_SETGET;
995 } else {
996 p_variable->property = VariableNode::PROP_INLINE;
997 if (!p_need_indent) {
998 push_error("Property with inline code must go to an indented block.");
999 }
1000 }
1001
1002 bool getter_used = false;
1003 bool setter_used = false;
1004
1005 // Run with a loop because order doesn't matter.
1006 for (int i = 0; i < 2; i++) {
1007 if (function->name == SNAME("set")) {
1008 if (setter_used) {
1009 push_error(R"(Properties can only have one setter.)");
1010 } else {
1011 parse_property_setter(property);
1012 setter_used = true;
1013 }
1014 } else if (function->name == SNAME("get")) {
1015 if (getter_used) {
1016 push_error(R"(Properties can only have one getter.)");
1017 } else {
1018 parse_property_getter(property);
1019 getter_used = true;
1020 }
1021 } else {
1022 // TODO: Update message to only have the missing one if it's the case.
1023 push_error(R"(Expected "get" or "set" for property declaration.)");
1024 }
1025
1026 if (i == 0 && p_variable->property == VariableNode::PROP_SETGET) {
1027 if (match(GDScriptTokenizer::Token::COMMA)) {
1028 // Consume potential newline.
1029 if (match(GDScriptTokenizer::Token::NEWLINE)) {
1030 if (!p_need_indent) {
1031 push_error(R"(Inline setter/getter setting cannot span across multiple lines (use "\\"" if needed).)");
1032 }
1033 }
1034 } else {
1035 break;
1036 }
1037 }
1038
1039 if (!match(GDScriptTokenizer::Token::IDENTIFIER)) {
1040 break;
1041 }
1042 function = parse_identifier();
1043 }
1044 complete_extents(p_variable);
1045
1046 if (p_variable->property == VariableNode::PROP_SETGET) {
1047 end_statement("property declaration");
1048 }
1049
1050 if (p_need_indent) {
1051 consume(GDScriptTokenizer::Token::DEDENT, R"(Expected end of indented block for property.)");
1052 }
1053 return property;
1054}
1055
1056void GDScriptParser::parse_property_setter(VariableNode *p_variable) {
1057 switch (p_variable->property) {
1058 case VariableNode::PROP_INLINE: {
1059 FunctionNode *function = alloc_node<FunctionNode>();
1060 IdentifierNode *identifier = alloc_node<IdentifierNode>();
1061 complete_extents(identifier);
1062 identifier->name = "@" + p_variable->identifier->name + "_setter";
1063 function->identifier = identifier;
1064 function->is_static = p_variable->is_static;
1065
1066 consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "set".)");
1067
1068 ParameterNode *parameter = alloc_node<ParameterNode>();
1069 if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected parameter name after "(".)")) {
1070 reset_extents(parameter, previous);
1071 p_variable->setter_parameter = parse_identifier();
1072 parameter->identifier = p_variable->setter_parameter;
1073 function->parameters_indices[parameter->identifier->name] = 0;
1074 function->parameters.push_back(parameter);
1075 }
1076 complete_extents(parameter);
1077
1078 consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after parameter name.)*");
1079 consume(GDScriptTokenizer::Token::COLON, R"*(Expected ":" after ")".)*");
1080
1081 FunctionNode *previous_function = current_function;
1082 current_function = function;
1083 if (p_variable->setter_parameter != nullptr) {
1084 SuiteNode *body = alloc_node<SuiteNode>();
1085 body->add_local(parameter, function);
1086 function->body = parse_suite("setter declaration", body);
1087 p_variable->setter = function;
1088 }
1089 current_function = previous_function;
1090 complete_extents(function);
1091 break;
1092 }
1093 case VariableNode::PROP_SETGET:
1094 consume(GDScriptTokenizer::Token::EQUAL, R"(Expected "=" after "set")");
1095 make_completion_context(COMPLETION_PROPERTY_METHOD, p_variable);
1096 if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected setter function name after "=".)")) {
1097 p_variable->setter_pointer = parse_identifier();
1098 }
1099 break;
1100 case VariableNode::PROP_NONE:
1101 break; // Unreachable.
1102 }
1103}
1104
1105void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
1106 switch (p_variable->property) {
1107 case VariableNode::PROP_INLINE: {
1108 FunctionNode *function = alloc_node<FunctionNode>();
1109
1110 consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "get".)");
1111
1112 IdentifierNode *identifier = alloc_node<IdentifierNode>();
1113 complete_extents(identifier);
1114 identifier->name = "@" + p_variable->identifier->name + "_getter";
1115 function->identifier = identifier;
1116 function->is_static = p_variable->is_static;
1117
1118 FunctionNode *previous_function = current_function;
1119 current_function = function;
1120
1121 SuiteNode *body = alloc_node<SuiteNode>();
1122 function->body = parse_suite("getter declaration", body);
1123 p_variable->getter = function;
1124
1125 current_function = previous_function;
1126 complete_extents(function);
1127 break;
1128 }
1129 case VariableNode::PROP_SETGET:
1130 consume(GDScriptTokenizer::Token::EQUAL, R"(Expected "=" after "get")");
1131 make_completion_context(COMPLETION_PROPERTY_METHOD, p_variable);
1132 if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected getter function name after "=".)")) {
1133 p_variable->getter_pointer = parse_identifier();
1134 }
1135 break;
1136 case VariableNode::PROP_NONE:
1137 break; // Unreachable.
1138 }
1139}
1140
1141GDScriptParser::ConstantNode *GDScriptParser::parse_constant(bool p_is_static) {
1142 ConstantNode *constant = alloc_node<ConstantNode>();
1143
1144 if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected constant name after "const".)")) {
1145 complete_extents(constant);
1146 return nullptr;
1147 }
1148
1149 constant->identifier = parse_identifier();
1150
1151 if (match(GDScriptTokenizer::Token::COLON)) {
1152 if (check((GDScriptTokenizer::Token::EQUAL))) {
1153 // Infer type.
1154 constant->infer_datatype = true;
1155 } else {
1156 // Parse type.
1157 constant->datatype_specifier = parse_type();
1158 }
1159 }
1160
1161 if (consume(GDScriptTokenizer::Token::EQUAL, R"(Expected initializer after constant name.)")) {
1162 // Initializer.
1163 constant->initializer = parse_expression(false);
1164
1165 if (constant->initializer == nullptr) {
1166 push_error(R"(Expected initializer expression for constant.)");
1167 complete_extents(constant);
1168 return nullptr;
1169 }
1170 } else {
1171 complete_extents(constant);
1172 return nullptr;
1173 }
1174
1175 complete_extents(constant);
1176 end_statement("constant declaration");
1177
1178 return constant;
1179}
1180
1181GDScriptParser::ParameterNode *GDScriptParser::parse_parameter() {
1182 if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected parameter name.)")) {
1183 return nullptr;
1184 }
1185
1186 ParameterNode *parameter = alloc_node<ParameterNode>();
1187 parameter->identifier = parse_identifier();
1188
1189 if (match(GDScriptTokenizer::Token::COLON)) {
1190 if (check((GDScriptTokenizer::Token::EQUAL))) {
1191 // Infer type.
1192 parameter->infer_datatype = true;
1193 } else {
1194 // Parse type.
1195 make_completion_context(COMPLETION_TYPE_NAME, parameter);
1196 parameter->datatype_specifier = parse_type();
1197 }
1198 }
1199
1200 if (match(GDScriptTokenizer::Token::EQUAL)) {
1201 // Default value.
1202 parameter->initializer = parse_expression(false);
1203 }
1204
1205 complete_extents(parameter);
1206 return parameter;
1207}
1208
1209GDScriptParser::SignalNode *GDScriptParser::parse_signal(bool p_is_static) {
1210 SignalNode *signal = alloc_node<SignalNode>();
1211
1212 if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected signal name after "signal".)")) {
1213 complete_extents(signal);
1214 return nullptr;
1215 }
1216
1217 signal->identifier = parse_identifier();
1218
1219 if (check(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) {
1220 push_multiline(true);
1221 advance();
1222 do {
1223 if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
1224 // Allow for trailing comma.
1225 break;
1226 }
1227
1228 ParameterNode *parameter = parse_parameter();
1229 if (parameter == nullptr) {
1230 push_error("Expected signal parameter name.");
1231 break;
1232 }
1233 if (parameter->initializer != nullptr) {
1234 push_error(R"(Signal parameters cannot have a default value.)");
1235 }
1236 if (signal->parameters_indices.has(parameter->identifier->name)) {
1237 push_error(vformat(R"(Parameter with name "%s" was already declared for this signal.)", parameter->identifier->name));
1238 } else {
1239 signal->parameters_indices[parameter->identifier->name] = signal->parameters.size();
1240 signal->parameters.push_back(parameter);
1241 }
1242 } while (match(GDScriptTokenizer::Token::COMMA) && !is_at_end());
1243
1244 pop_multiline();
1245 consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after signal parameters.)*");
1246 }
1247
1248 complete_extents(signal);
1249 end_statement("signal declaration");
1250
1251 return signal;
1252}
1253
1254GDScriptParser::EnumNode *GDScriptParser::parse_enum(bool p_is_static) {
1255 EnumNode *enum_node = alloc_node<EnumNode>();
1256 bool named = false;
1257
1258 if (check(GDScriptTokenizer::Token::IDENTIFIER)) {
1259 advance();
1260 enum_node->identifier = parse_identifier();
1261 named = true;
1262 }
1263
1264 push_multiline(true);
1265 consume(GDScriptTokenizer::Token::BRACE_OPEN, vformat(R"(Expected "{" after %s.)", named ? "enum name" : R"("enum")"));
1266
1267 HashMap<StringName, int> elements;
1268
1269#ifdef DEBUG_ENABLED
1270 List<MethodInfo> gdscript_funcs;
1271 GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
1272#endif
1273
1274 do {
1275 if (check(GDScriptTokenizer::Token::BRACE_CLOSE)) {
1276 break; // Allow trailing comma.
1277 }
1278 if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for enum key.)")) {
1279 EnumNode::Value item;
1280 GDScriptParser::IdentifierNode *identifier = parse_identifier();
1281#ifdef DEBUG_ENABLED
1282 if (!named) { // Named enum identifiers do not shadow anything since you can only access them with NamedEnum.ENUM_VALUE
1283 for (MethodInfo &info : gdscript_funcs) {
1284 if (info.name == identifier->name) {
1285 push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "built-in function");
1286 }
1287 }
1288 if (Variant::has_utility_function(identifier->name)) {
1289 push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "built-in function");
1290 } else if (ClassDB::class_exists(identifier->name)) {
1291 push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "global class");
1292 }
1293 }
1294#endif
1295 item.identifier = identifier;
1296 item.parent_enum = enum_node;
1297 item.line = previous.start_line;
1298 item.leftmost_column = previous.leftmost_column;
1299 item.rightmost_column = previous.rightmost_column;
1300
1301 if (elements.has(item.identifier->name)) {
1302 push_error(vformat(R"(Name "%s" was already in this enum (at line %d).)", item.identifier->name, elements[item.identifier->name]), item.identifier);
1303 } else if (!named) {
1304 if (current_class->members_indices.has(item.identifier->name)) {
1305 push_error(vformat(R"(Name "%s" is already used as a class %s.)", item.identifier->name, current_class->get_member(item.identifier->name).get_type_name()));
1306 }
1307 }
1308
1309 elements[item.identifier->name] = item.line;
1310
1311 if (match(GDScriptTokenizer::Token::EQUAL)) {
1312 ExpressionNode *value = parse_expression(false);
1313 if (value == nullptr) {
1314 push_error(R"(Expected expression value after "=".)");
1315 }
1316 item.custom_value = value;
1317 }
1318
1319 item.index = enum_node->values.size();
1320 enum_node->values.push_back(item);
1321 if (!named) {
1322 // Add as member of current class.
1323 current_class->add_member(item);
1324 }
1325 }
1326 } while (match(GDScriptTokenizer::Token::COMMA));
1327
1328 pop_multiline();
1329 consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected closing "}" for enum.)");
1330
1331#ifdef TOOLS_ENABLED
1332 // Enum values documentation.
1333 for (int i = 0; i < enum_node->values.size(); i++) {
1334 int doc_comment_line = enum_node->values[i].line;
1335 bool single_line = false;
1336
1337 if (has_comment(doc_comment_line, true)) {
1338 single_line = true;
1339 } else if (has_comment(doc_comment_line - 1, true)) {
1340 doc_comment_line--;
1341 } else {
1342 continue;
1343 }
1344
1345 if (i == enum_node->values.size() - 1) {
1346 // If close bracket is same line as last value.
1347 if (doc_comment_line == previous.start_line) {
1348 break;
1349 }
1350 } else {
1351 // If two values are same line.
1352 if (doc_comment_line == enum_node->values[i + 1].line) {
1353 continue;
1354 }
1355 }
1356
1357 if (named) {
1358 enum_node->values.write[i].doc_data = parse_doc_comment(doc_comment_line, single_line);
1359 } else {
1360 current_class->set_enum_value_doc_data(enum_node->values[i].identifier->name, parse_doc_comment(doc_comment_line, single_line));
1361 }
1362 }
1363#endif // TOOLS_ENABLED
1364
1365 complete_extents(enum_node);
1366 end_statement("enum");
1367
1368 return enum_node;
1369}
1370
1371void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNode *p_body, const String &p_type) {
1372 if (!check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE) && !is_at_end()) {
1373 bool default_used = false;
1374 do {
1375 if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
1376 // Allow for trailing comma.
1377 break;
1378 }
1379 ParameterNode *parameter = parse_parameter();
1380 if (parameter == nullptr) {
1381 break;
1382 }
1383 if (parameter->initializer != nullptr) {
1384 default_used = true;
1385 } else {
1386 if (default_used) {
1387 push_error("Cannot have mandatory parameters after optional parameters.");
1388 continue;
1389 }
1390 }
1391 if (p_function->parameters_indices.has(parameter->identifier->name)) {
1392 push_error(vformat(R"(Parameter with name "%s" was already declared for this %s.)", parameter->identifier->name, p_type));
1393 } else {
1394 p_function->parameters_indices[parameter->identifier->name] = p_function->parameters.size();
1395 p_function->parameters.push_back(parameter);
1396 p_body->add_local(parameter, current_function);
1397 }
1398 } while (match(GDScriptTokenizer::Token::COMMA));
1399 }
1400
1401 pop_multiline();
1402 consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, vformat(R"*(Expected closing ")" after %s parameters.)*", p_type));
1403
1404 if (match(GDScriptTokenizer::Token::FORWARD_ARROW)) {
1405 make_completion_context(COMPLETION_TYPE_NAME_OR_VOID, p_function);
1406 p_function->return_type = parse_type(true);
1407 if (p_function->return_type == nullptr) {
1408 push_error(R"(Expected return type or "void" after "->".)");
1409 }
1410 }
1411
1412 if (!p_function->source_lambda && p_function->identifier && p_function->identifier->name == GDScriptLanguage::get_singleton()->strings._static_init) {
1413 if (!p_function->is_static) {
1414 push_error(R"(Static constructor must be declared static.)");
1415 }
1416 if (p_function->parameters.size() != 0) {
1417 push_error(R"(Static constructor cannot have parameters.)");
1418 }
1419 current_class->has_static_data = true;
1420 }
1421
1422 // TODO: Improve token consumption so it synchronizes to a statement boundary. This way we can get into the function body with unrecognized tokens.
1423 consume(GDScriptTokenizer::Token::COLON, vformat(R"(Expected ":" after %s declaration.)", p_type));
1424}
1425
1426GDScriptParser::FunctionNode *GDScriptParser::parse_function(bool p_is_static) {
1427 FunctionNode *function = alloc_node<FunctionNode>();
1428
1429 make_completion_context(COMPLETION_OVERRIDE_METHOD, function);
1430
1431 if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after "func".)")) {
1432 complete_extents(function);
1433 return nullptr;
1434 }
1435
1436 FunctionNode *previous_function = current_function;
1437 current_function = function;
1438
1439 function->identifier = parse_identifier();
1440 function->is_static = p_is_static;
1441
1442 SuiteNode *body = alloc_node<SuiteNode>();
1443 SuiteNode *previous_suite = current_suite;
1444 current_suite = body;
1445
1446 push_multiline(true);
1447 consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected opening "(" after function name.)");
1448 parse_function_signature(function, body, "function");
1449
1450 current_suite = previous_suite;
1451 function->body = parse_suite("function declaration", body);
1452
1453 current_function = previous_function;
1454 complete_extents(function);
1455 return function;
1456}
1457
1458GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_valid_targets) {
1459 AnnotationNode *annotation = alloc_node<AnnotationNode>();
1460
1461 annotation->name = previous.literal;
1462
1463 make_completion_context(COMPLETION_ANNOTATION, annotation);
1464
1465 bool valid = true;
1466
1467 if (!valid_annotations.has(annotation->name)) {
1468 push_error(vformat(R"(Unrecognized annotation: "%s".)", annotation->name));
1469 valid = false;
1470 }
1471
1472 annotation->info = &valid_annotations[annotation->name];
1473
1474 if (!annotation->applies_to(p_valid_targets)) {
1475 if (annotation->applies_to(AnnotationInfo::SCRIPT)) {
1476 push_error(vformat(R"(Annotation "%s" must be at the top of the script, before "extends" and "class_name".)", annotation->name));
1477 } else {
1478 push_error(vformat(R"(Annotation "%s" is not allowed in this level.)", annotation->name));
1479 }
1480 valid = false;
1481 }
1482
1483 if (check(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) {
1484 push_multiline(true);
1485 advance();
1486 // Arguments.
1487 push_completion_call(annotation);
1488 make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, 0, true);
1489 int argument_index = 0;
1490 do {
1491 if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
1492 // Allow for trailing comma.
1493 break;
1494 }
1495
1496 make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, argument_index, true);
1497 set_last_completion_call_arg(argument_index++);
1498 ExpressionNode *argument = parse_expression(false);
1499 if (argument == nullptr) {
1500 push_error("Expected expression as the annotation argument.");
1501 valid = false;
1502 continue;
1503 }
1504 annotation->arguments.push_back(argument);
1505 } while (match(GDScriptTokenizer::Token::COMMA) && !is_at_end());
1506
1507 pop_multiline();
1508 consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after annotation arguments.)*");
1509 pop_completion_call();
1510 }
1511 complete_extents(annotation);
1512
1513 match(GDScriptTokenizer::Token::NEWLINE); // Newline after annotation is optional.
1514
1515 if (valid) {
1516 valid = validate_annotation_arguments(annotation);
1517 }
1518
1519 return valid ? annotation : nullptr;
1520}
1521
1522void GDScriptParser::clear_unused_annotations() {
1523 for (const AnnotationNode *annotation : annotation_stack) {
1524 push_error(vformat(R"(Annotation "%s" does not precede a valid target, so it will have no effect.)", annotation->name), annotation);
1525 }
1526
1527 annotation_stack.clear();
1528}
1529
1530bool GDScriptParser::register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments, bool p_is_vararg) {
1531 ERR_FAIL_COND_V_MSG(valid_annotations.has(p_info.name), false, vformat(R"(Annotation "%s" already registered.)", p_info.name));
1532
1533 AnnotationInfo new_annotation;
1534 new_annotation.info = p_info;
1535 new_annotation.info.default_arguments = p_default_arguments;
1536 if (p_is_vararg) {
1537 new_annotation.info.flags |= METHOD_FLAG_VARARG;
1538 }
1539 new_annotation.apply = p_apply;
1540 new_annotation.target_kind = p_target_kinds;
1541
1542 valid_annotations[p_info.name] = new_annotation;
1543 return true;
1544}
1545
1546GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context, SuiteNode *p_suite, bool p_for_lambda) {
1547 SuiteNode *suite = p_suite != nullptr ? p_suite : alloc_node<SuiteNode>();
1548 suite->parent_block = current_suite;
1549 suite->parent_function = current_function;
1550 current_suite = suite;
1551
1552 if (!p_for_lambda && suite->parent_block != nullptr && suite->parent_block->is_in_loop) {
1553 // Do not reset to false if true is set before calling parse_suite().
1554 suite->is_in_loop = true;
1555 }
1556
1557 bool multiline = false;
1558
1559 if (match(GDScriptTokenizer::Token::NEWLINE)) {
1560 multiline = true;
1561 }
1562
1563 if (multiline) {
1564 if (!consume(GDScriptTokenizer::Token::INDENT, vformat(R"(Expected indented block after %s.)", p_context))) {
1565 current_suite = suite->parent_block;
1566 complete_extents(suite);
1567 return suite;
1568 }
1569 }
1570 reset_extents(suite, current);
1571
1572 int error_count = 0;
1573
1574 do {
1575 if (is_at_end() || (!multiline && previous.type == GDScriptTokenizer::Token::SEMICOLON && check(GDScriptTokenizer::Token::NEWLINE))) {
1576 break;
1577 }
1578 Node *statement = parse_statement();
1579 if (statement == nullptr) {
1580 if (error_count++ > 100) {
1581 push_error("Too many statement errors.", suite);
1582 break;
1583 }
1584 continue;
1585 }
1586 suite->statements.push_back(statement);
1587
1588 // Register locals.
1589 switch (statement->type) {
1590 case Node::VARIABLE: {
1591 VariableNode *variable = static_cast<VariableNode *>(statement);
1592 const SuiteNode::Local &local = current_suite->get_local(variable->identifier->name);
1593 if (local.type != SuiteNode::Local::UNDEFINED) {
1594 push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", local.get_name(), variable->identifier->name), variable->identifier);
1595 }
1596 current_suite->add_local(variable, current_function);
1597 break;
1598 }
1599 case Node::CONSTANT: {
1600 ConstantNode *constant = static_cast<ConstantNode *>(statement);
1601 const SuiteNode::Local &local = current_suite->get_local(constant->identifier->name);
1602 if (local.type != SuiteNode::Local::UNDEFINED) {
1603 String name;
1604 if (local.type == SuiteNode::Local::CONSTANT) {
1605 name = "constant";
1606 } else {
1607 name = "variable";
1608 }
1609 push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", name, constant->identifier->name), constant->identifier);
1610 }
1611 current_suite->add_local(constant, current_function);
1612 break;
1613 }
1614 default:
1615 break;
1616 }
1617
1618 } while ((multiline || previous.type == GDScriptTokenizer::Token::SEMICOLON) && !check(GDScriptTokenizer::Token::DEDENT) && !lambda_ended && !is_at_end());
1619
1620 complete_extents(suite);
1621
1622 if (multiline) {
1623 if (!lambda_ended) {
1624 consume(GDScriptTokenizer::Token::DEDENT, vformat(R"(Missing unindent at the end of %s.)", p_context));
1625
1626 } else {
1627 match(GDScriptTokenizer::Token::DEDENT);
1628 }
1629 } else if (previous.type == GDScriptTokenizer::Token::SEMICOLON) {
1630 consume(GDScriptTokenizer::Token::NEWLINE, vformat(R"(Expected newline after ";" at the end of %s.)", p_context));
1631 }
1632
1633 if (p_for_lambda) {
1634 lambda_ended = true;
1635 }
1636 current_suite = suite->parent_block;
1637 return suite;
1638}
1639
1640GDScriptParser::Node *GDScriptParser::parse_statement() {
1641 Node *result = nullptr;
1642#ifdef DEBUG_ENABLED
1643 bool unreachable = current_suite->has_return && !current_suite->has_unreachable_code;
1644#endif
1645
1646 bool is_annotation = false;
1647
1648 switch (current.type) {
1649 case GDScriptTokenizer::Token::PASS:
1650 advance();
1651 result = alloc_node<PassNode>();
1652 complete_extents(result);
1653 end_statement(R"("pass")");
1654 break;
1655 case GDScriptTokenizer::Token::VAR:
1656 advance();
1657 result = parse_variable(false, false);
1658 break;
1659 case GDScriptTokenizer::Token::CONST:
1660 advance();
1661 result = parse_constant(false);
1662 break;
1663 case GDScriptTokenizer::Token::IF:
1664 advance();
1665 result = parse_if();
1666 break;
1667 case GDScriptTokenizer::Token::FOR:
1668 advance();
1669 result = parse_for();
1670 break;
1671 case GDScriptTokenizer::Token::WHILE:
1672 advance();
1673 result = parse_while();
1674 break;
1675 case GDScriptTokenizer::Token::MATCH:
1676 advance();
1677 result = parse_match();
1678 break;
1679 case GDScriptTokenizer::Token::BREAK:
1680 advance();
1681 result = parse_break();
1682 break;
1683 case GDScriptTokenizer::Token::CONTINUE:
1684 advance();
1685 result = parse_continue();
1686 break;
1687 case GDScriptTokenizer::Token::RETURN: {
1688 advance();
1689 ReturnNode *n_return = alloc_node<ReturnNode>();
1690 if (!is_statement_end()) {
1691 if (current_function && (current_function->identifier->name == GDScriptLanguage::get_singleton()->strings._init || current_function->identifier->name == GDScriptLanguage::get_singleton()->strings._static_init)) {
1692 push_error(R"(Constructor cannot return a value.)");
1693 }
1694 n_return->return_value = parse_expression(false);
1695 } else if (in_lambda && !is_statement_end_token()) {
1696 // Try to parse it anyway as this might not be the statement end in a lambda.
1697 // If this fails the expression will be nullptr, but that's the same as no return, so it's fine.
1698 n_return->return_value = parse_expression(false);
1699 }
1700 complete_extents(n_return);
1701 result = n_return;
1702
1703 current_suite->has_return = true;
1704
1705 end_statement("return statement");
1706 break;
1707 }
1708 case GDScriptTokenizer::Token::BREAKPOINT:
1709 advance();
1710 result = alloc_node<BreakpointNode>();
1711 complete_extents(result);
1712 end_statement(R"("breakpoint")");
1713 break;
1714 case GDScriptTokenizer::Token::ASSERT:
1715 advance();
1716 result = parse_assert();
1717 break;
1718 case GDScriptTokenizer::Token::ANNOTATION: {
1719 advance();
1720 is_annotation = true;
1721 AnnotationNode *annotation = parse_annotation(AnnotationInfo::STATEMENT);
1722 if (annotation != nullptr) {
1723 annotation_stack.push_back(annotation);
1724 }
1725 break;
1726 }
1727 default: {
1728 // Expression statement.
1729 ExpressionNode *expression = parse_expression(true); // Allow assignment here.
1730 bool has_ended_lambda = false;
1731 if (expression == nullptr) {
1732 if (in_lambda) {
1733 // If it's not a valid expression beginning, it might be the continuation of the outer expression where this lambda is.
1734 lambda_ended = true;
1735 has_ended_lambda = true;
1736 } else {
1737 advance();
1738 push_error(vformat(R"(Expected statement, found "%s" instead.)", previous.get_name()));
1739 }
1740 } else {
1741 end_statement("expression");
1742 }
1743 lambda_ended = lambda_ended || has_ended_lambda;
1744 result = expression;
1745
1746#ifdef DEBUG_ENABLED
1747 if (expression != nullptr) {
1748 switch (expression->type) {
1749 case Node::CALL:
1750 case Node::ASSIGNMENT:
1751 case Node::AWAIT:
1752 case Node::TERNARY_OPERATOR:
1753 // Fine.
1754 break;
1755 case Node::LAMBDA:
1756 // Standalone lambdas can't be used, so make this an error.
1757 push_error("Standalone lambdas cannot be accessed. Consider assigning it to a variable.", expression);
1758 break;
1759 case Node::LITERAL:
1760 if (static_cast<GDScriptParser::LiteralNode *>(expression)->value.get_type() == Variant::STRING) {
1761 // Allow strings as multiline comments.
1762 break;
1763 }
1764 [[fallthrough]];
1765 default:
1766 push_warning(expression, GDScriptWarning::STANDALONE_EXPRESSION);
1767 }
1768 }
1769#endif
1770 break;
1771 }
1772 }
1773
1774 while (!is_annotation && result != nullptr && !annotation_stack.is_empty()) {
1775 AnnotationNode *last_annotation = annotation_stack.back()->get();
1776 if (last_annotation->applies_to(AnnotationInfo::STATEMENT)) {
1777 result->annotations.push_front(last_annotation);
1778 annotation_stack.pop_back();
1779 } else {
1780 push_error(vformat(R"(Annotation "%s" cannot be applied to a statement.)", last_annotation->name));
1781 clear_unused_annotations();
1782 }
1783 }
1784
1785#ifdef DEBUG_ENABLED
1786 if (unreachable && result != nullptr) {
1787 current_suite->has_unreachable_code = true;
1788 if (current_function) {
1789 push_warning(result, GDScriptWarning::UNREACHABLE_CODE, current_function->identifier ? current_function->identifier->name : "<anonymous lambda>");
1790 } else {
1791 // TODO: Properties setters and getters with unreachable code are not being warned
1792 }
1793 }
1794#endif
1795
1796 if (panic_mode) {
1797 synchronize();
1798 }
1799
1800 return result;
1801}
1802
1803GDScriptParser::AssertNode *GDScriptParser::parse_assert() {
1804 // TODO: Add assert message.
1805 AssertNode *assert = alloc_node<AssertNode>();
1806
1807 push_multiline(true);
1808 consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "assert".)");
1809
1810 assert->condition = parse_expression(false);
1811 if (assert->condition == nullptr) {
1812 push_error("Expected expression to assert.");
1813 pop_multiline();
1814 complete_extents(assert);
1815 return nullptr;
1816 }
1817
1818 if (match(GDScriptTokenizer::Token::COMMA) && !check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
1819 assert->message = parse_expression(false);
1820 if (assert->message == nullptr) {
1821 push_error(R"(Expected error message for assert after ",".)");
1822 pop_multiline();
1823 complete_extents(assert);
1824 return nullptr;
1825 }
1826 match(GDScriptTokenizer::Token::COMMA);
1827 }
1828
1829 pop_multiline();
1830 consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after assert expression.)*");
1831
1832 complete_extents(assert);
1833 end_statement(R"("assert")");
1834
1835 return assert;
1836}
1837
1838GDScriptParser::BreakNode *GDScriptParser::parse_break() {
1839 if (!can_break) {
1840 push_error(R"(Cannot use "break" outside of a loop.)");
1841 }
1842 BreakNode *break_node = alloc_node<BreakNode>();
1843 complete_extents(break_node);
1844 end_statement(R"("break")");
1845 return break_node;
1846}
1847
1848GDScriptParser::ContinueNode *GDScriptParser::parse_continue() {
1849 if (!can_continue) {
1850 push_error(R"(Cannot use "continue" outside of a loop.)");
1851 }
1852 current_suite->has_continue = true;
1853 ContinueNode *cont = alloc_node<ContinueNode>();
1854 complete_extents(cont);
1855 end_statement(R"("continue")");
1856 return cont;
1857}
1858
1859GDScriptParser::ForNode *GDScriptParser::parse_for() {
1860 ForNode *n_for = alloc_node<ForNode>();
1861
1862 if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected loop variable name after "for".)")) {
1863 n_for->variable = parse_identifier();
1864 }
1865
1866 if (match(GDScriptTokenizer::Token::COLON)) {
1867 n_for->datatype_specifier = parse_type();
1868 if (n_for->datatype_specifier == nullptr) {
1869 push_error(R"(Expected type specifier after ":".)");
1870 }
1871 }
1872
1873 if (n_for->datatype_specifier == nullptr) {
1874 consume(GDScriptTokenizer::Token::IN, R"(Expected "in" or ":" after "for" variable name.)");
1875 } else {
1876 consume(GDScriptTokenizer::Token::IN, R"(Expected "in" after "for" variable type specifier.)");
1877 }
1878
1879 n_for->list = parse_expression(false);
1880
1881 if (!n_for->list) {
1882 push_error(R"(Expected iterable after "in".)");
1883 }
1884
1885 consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "for" condition.)");
1886
1887 // Save break/continue state.
1888 bool could_break = can_break;
1889 bool could_continue = can_continue;
1890
1891 // Allow break/continue.
1892 can_break = true;
1893 can_continue = true;
1894
1895 SuiteNode *suite = alloc_node<SuiteNode>();
1896 if (n_for->variable) {
1897 const SuiteNode::Local &local = current_suite->get_local(n_for->variable->name);
1898 if (local.type != SuiteNode::Local::UNDEFINED) {
1899 push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", local.get_name(), n_for->variable->name), n_for->variable);
1900 }
1901 suite->add_local(SuiteNode::Local(n_for->variable, current_function));
1902 }
1903 suite->is_in_loop = true;
1904 n_for->loop = parse_suite(R"("for" block)", suite);
1905 complete_extents(n_for);
1906
1907 // Reset break/continue state.
1908 can_break = could_break;
1909 can_continue = could_continue;
1910
1911 return n_for;
1912}
1913
1914GDScriptParser::IfNode *GDScriptParser::parse_if(const String &p_token) {
1915 IfNode *n_if = alloc_node<IfNode>();
1916
1917 n_if->condition = parse_expression(false);
1918 if (n_if->condition == nullptr) {
1919 push_error(vformat(R"(Expected conditional expression after "%s".)", p_token));
1920 }
1921
1922 consume(GDScriptTokenizer::Token::COLON, vformat(R"(Expected ":" after "%s" condition.)", p_token));
1923
1924 n_if->true_block = parse_suite(vformat(R"("%s" block)", p_token));
1925 n_if->true_block->parent_if = n_if;
1926
1927 if (n_if->true_block->has_continue) {
1928 current_suite->has_continue = true;
1929 }
1930
1931 if (match(GDScriptTokenizer::Token::ELIF)) {
1932 SuiteNode *else_block = alloc_node<SuiteNode>();
1933 else_block->parent_function = current_function;
1934 else_block->parent_block = current_suite;
1935
1936 SuiteNode *previous_suite = current_suite;
1937 current_suite = else_block;
1938
1939 IfNode *elif = parse_if("elif");
1940 else_block->statements.push_back(elif);
1941 complete_extents(else_block);
1942 n_if->false_block = else_block;
1943
1944 current_suite = previous_suite;
1945 } else if (match(GDScriptTokenizer::Token::ELSE)) {
1946 consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "else".)");
1947 n_if->false_block = parse_suite(R"("else" block)");
1948 }
1949 complete_extents(n_if);
1950
1951 if (n_if->false_block != nullptr && n_if->false_block->has_return && n_if->true_block->has_return) {
1952 current_suite->has_return = true;
1953 }
1954 if (n_if->false_block != nullptr && n_if->false_block->has_continue) {
1955 current_suite->has_continue = true;
1956 }
1957
1958 return n_if;
1959}
1960
1961GDScriptParser::MatchNode *GDScriptParser::parse_match() {
1962 MatchNode *match = alloc_node<MatchNode>();
1963
1964 match->test = parse_expression(false);
1965 if (match->test == nullptr) {
1966 push_error(R"(Expected expression to test after "match".)");
1967 }
1968
1969 consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "match" expression.)");
1970 consume(GDScriptTokenizer::Token::NEWLINE, R"(Expected a newline after "match" statement.)");
1971
1972 if (!consume(GDScriptTokenizer::Token::INDENT, R"(Expected an indented block after "match" statement.)")) {
1973 complete_extents(match);
1974 return match;
1975 }
1976
1977 bool all_have_return = true;
1978 bool have_wildcard = false;
1979
1980 while (!check(GDScriptTokenizer::Token::DEDENT) && !is_at_end()) {
1981 MatchBranchNode *branch = parse_match_branch();
1982 if (branch == nullptr) {
1983 advance();
1984 continue;
1985 }
1986
1987#ifdef DEBUG_ENABLED
1988 if (have_wildcard && !branch->patterns.is_empty()) {
1989 push_warning(branch->patterns[0], GDScriptWarning::UNREACHABLE_PATTERN);
1990 }
1991#endif
1992
1993 have_wildcard = have_wildcard || branch->has_wildcard;
1994 all_have_return = all_have_return && branch->block->has_return;
1995 match->branches.push_back(branch);
1996 }
1997 complete_extents(match);
1998
1999 consume(GDScriptTokenizer::Token::DEDENT, R"(Expected an indented block after "match" statement.)");
2000
2001 if (all_have_return && have_wildcard) {
2002 current_suite->has_return = true;
2003 }
2004
2005 return match;
2006}
2007
2008GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
2009 MatchBranchNode *branch = alloc_node<MatchBranchNode>();
2010 reset_extents(branch, current);
2011
2012 bool has_bind = false;
2013
2014 do {
2015 PatternNode *pattern = parse_match_pattern();
2016 if (pattern == nullptr) {
2017 continue;
2018 }
2019 if (pattern->binds.size() > 0) {
2020 has_bind = true;
2021 }
2022 if (branch->patterns.size() > 0 && has_bind) {
2023 push_error(R"(Cannot use a variable bind with multiple patterns.)");
2024 }
2025 if (pattern->pattern_type == PatternNode::PT_REST) {
2026 push_error(R"(Rest pattern can only be used inside array and dictionary patterns.)");
2027 } else if (pattern->pattern_type == PatternNode::PT_BIND || pattern->pattern_type == PatternNode::PT_WILDCARD) {
2028 branch->has_wildcard = true;
2029 }
2030 branch->patterns.push_back(pattern);
2031 } while (match(GDScriptTokenizer::Token::COMMA));
2032
2033 if (branch->patterns.is_empty()) {
2034 push_error(R"(No pattern found for "match" branch.)");
2035 }
2036
2037 if (!consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "match" patterns.)")) {
2038 complete_extents(branch);
2039 return nullptr;
2040 }
2041
2042 SuiteNode *suite = alloc_node<SuiteNode>();
2043 if (branch->patterns.size() > 0) {
2044 for (const KeyValue<StringName, IdentifierNode *> &E : branch->patterns[0]->binds) {
2045 SuiteNode::Local local(E.value, current_function);
2046 local.type = SuiteNode::Local::PATTERN_BIND;
2047 suite->add_local(local);
2048 }
2049 }
2050
2051 branch->block = parse_suite("match pattern block", suite);
2052 complete_extents(branch);
2053
2054 return branch;
2055}
2056
2057GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_root_pattern) {
2058 PatternNode *pattern = alloc_node<PatternNode>();
2059 reset_extents(pattern, current);
2060
2061 switch (current.type) {
2062 case GDScriptTokenizer::Token::VAR: {
2063 // Bind.
2064 advance();
2065 if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected bind name after "var".)")) {
2066 complete_extents(pattern);
2067 return nullptr;
2068 }
2069 pattern->pattern_type = PatternNode::PT_BIND;
2070 pattern->bind = parse_identifier();
2071
2072 PatternNode *root_pattern = p_root_pattern == nullptr ? pattern : p_root_pattern;
2073
2074 if (p_root_pattern != nullptr) {
2075 if (p_root_pattern->has_bind(pattern->bind->name)) {
2076 push_error(vformat(R"(Bind variable name "%s" was already used in this pattern.)", pattern->bind->name));
2077 complete_extents(pattern);
2078 return nullptr;
2079 }
2080 }
2081
2082 if (current_suite->has_local(pattern->bind->name)) {
2083 push_error(vformat(R"(There's already a %s named "%s" in this scope.)", current_suite->get_local(pattern->bind->name).get_name(), pattern->bind->name));
2084 complete_extents(pattern);
2085 return nullptr;
2086 }
2087
2088 root_pattern->binds[pattern->bind->name] = pattern->bind;
2089
2090 } break;
2091 case GDScriptTokenizer::Token::UNDERSCORE:
2092 // Wildcard.
2093 advance();
2094 pattern->pattern_type = PatternNode::PT_WILDCARD;
2095 break;
2096 case GDScriptTokenizer::Token::PERIOD_PERIOD:
2097 // Rest.
2098 advance();
2099 pattern->pattern_type = PatternNode::PT_REST;
2100 break;
2101 case GDScriptTokenizer::Token::BRACKET_OPEN: {
2102 // Array.
2103 advance();
2104 pattern->pattern_type = PatternNode::PT_ARRAY;
2105
2106 if (!check(GDScriptTokenizer::Token::BRACKET_CLOSE)) {
2107 do {
2108 PatternNode *sub_pattern = parse_match_pattern(p_root_pattern != nullptr ? p_root_pattern : pattern);
2109 if (sub_pattern == nullptr) {
2110 continue;
2111 }
2112 if (pattern->rest_used) {
2113 push_error(R"(The ".." pattern must be the last element in the pattern array.)");
2114 } else if (sub_pattern->pattern_type == PatternNode::PT_REST) {
2115 pattern->rest_used = true;
2116 }
2117 pattern->array.push_back(sub_pattern);
2118 } while (match(GDScriptTokenizer::Token::COMMA));
2119 }
2120 consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected "]" to close the array pattern.)");
2121 break;
2122 }
2123 case GDScriptTokenizer::Token::BRACE_OPEN: {
2124 // Dictionary.
2125 advance();
2126 pattern->pattern_type = PatternNode::PT_DICTIONARY;
2127 do {
2128 if (check(GDScriptTokenizer::Token::BRACE_CLOSE) || is_at_end()) {
2129 break;
2130 }
2131 if (match(GDScriptTokenizer::Token::PERIOD_PERIOD)) {
2132 // Rest.
2133 if (pattern->rest_used) {
2134 push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)");
2135 } else {
2136 PatternNode *sub_pattern = alloc_node<PatternNode>();
2137 complete_extents(sub_pattern);
2138 sub_pattern->pattern_type = PatternNode::PT_REST;
2139 pattern->dictionary.push_back({ nullptr, sub_pattern });
2140 pattern->rest_used = true;
2141 }
2142 } else {
2143 ExpressionNode *key = parse_expression(false);
2144 if (key == nullptr) {
2145 push_error(R"(Expected expression as key for dictionary pattern.)");
2146 }
2147 if (match(GDScriptTokenizer::Token::COLON)) {
2148 // Value pattern.
2149 PatternNode *sub_pattern = parse_match_pattern(p_root_pattern != nullptr ? p_root_pattern : pattern);
2150 if (sub_pattern == nullptr) {
2151 continue;
2152 }
2153 if (pattern->rest_used) {
2154 push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)");
2155 } else if (sub_pattern->pattern_type == PatternNode::PT_REST) {
2156 push_error(R"(The ".." pattern cannot be used as a value.)");
2157 } else {
2158 pattern->dictionary.push_back({ key, sub_pattern });
2159 }
2160 } else {
2161 // Key match only.
2162 pattern->dictionary.push_back({ key, nullptr });
2163 }
2164 }
2165 } while (match(GDScriptTokenizer::Token::COMMA));
2166 consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected "}" to close the dictionary pattern.)");
2167 break;
2168 }
2169 default: {
2170 // Expression.
2171 ExpressionNode *expression = parse_expression(false);
2172 if (expression == nullptr) {
2173 push_error(R"(Expected expression for match pattern.)");
2174 complete_extents(pattern);
2175 return nullptr;
2176 } else {
2177 if (expression->type == GDScriptParser::Node::LITERAL) {
2178 pattern->pattern_type = PatternNode::PT_LITERAL;
2179 } else {
2180 pattern->pattern_type = PatternNode::PT_EXPRESSION;
2181 }
2182 pattern->expression = expression;
2183 }
2184 break;
2185 }
2186 }
2187 complete_extents(pattern);
2188
2189 return pattern;
2190}
2191
2192bool GDScriptParser::PatternNode::has_bind(const StringName &p_name) {
2193 return binds.has(p_name);
2194}
2195
2196GDScriptParser::IdentifierNode *GDScriptParser::PatternNode::get_bind(const StringName &p_name) {
2197 return binds[p_name];
2198}
2199
2200GDScriptParser::WhileNode *GDScriptParser::parse_while() {
2201 WhileNode *n_while = alloc_node<WhileNode>();
2202
2203 n_while->condition = parse_expression(false);
2204 if (n_while->condition == nullptr) {
2205 push_error(R"(Expected conditional expression after "while".)");
2206 }
2207
2208 consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "while" condition.)");
2209
2210 // Save break/continue state.
2211 bool could_break = can_break;
2212 bool could_continue = can_continue;
2213
2214 // Allow break/continue.
2215 can_break = true;
2216 can_continue = true;
2217
2218 SuiteNode *suite = alloc_node<SuiteNode>();
2219 suite->is_in_loop = true;
2220 n_while->loop = parse_suite(R"("while" block)", suite);
2221 complete_extents(n_while);
2222
2223 // Reset break/continue state.
2224 can_break = could_break;
2225 can_continue = could_continue;
2226
2227 return n_while;
2228}
2229
2230GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_precedence, bool p_can_assign, bool p_stop_on_assign) {
2231 // Switch multiline mode on for grouping tokens.
2232 // Do this early to avoid the tokenizer generating whitespace tokens.
2233 switch (current.type) {
2234 case GDScriptTokenizer::Token::PARENTHESIS_OPEN:
2235 case GDScriptTokenizer::Token::BRACE_OPEN:
2236 case GDScriptTokenizer::Token::BRACKET_OPEN:
2237 push_multiline(true);
2238 break;
2239 default:
2240 break; // Nothing to do.
2241 }
2242
2243 // Completion can appear whenever an expression is expected.
2244 make_completion_context(COMPLETION_IDENTIFIER, nullptr);
2245
2246 GDScriptTokenizer::Token token = current;
2247 GDScriptTokenizer::Token::Type token_type = token.type;
2248 if (token.is_identifier()) {
2249 // Allow keywords that can be treated as identifiers.
2250 token_type = GDScriptTokenizer::Token::IDENTIFIER;
2251 }
2252 ParseFunction prefix_rule = get_rule(token_type)->prefix;
2253
2254 if (prefix_rule == nullptr) {
2255 // Expected expression. Let the caller give the proper error message.
2256 return nullptr;
2257 }
2258
2259 advance(); // Only consume the token if there's a valid rule.
2260
2261 ExpressionNode *previous_operand = (this->*prefix_rule)(nullptr, p_can_assign);
2262
2263 while (p_precedence <= get_rule(current.type)->precedence) {
2264 if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL) || lambda_ended) {
2265 return previous_operand;
2266 }
2267 // Also switch multiline mode on here for infix operators.
2268 switch (current.type) {
2269 // case GDScriptTokenizer::Token::BRACE_OPEN: // Not an infix operator.
2270 case GDScriptTokenizer::Token::PARENTHESIS_OPEN:
2271 case GDScriptTokenizer::Token::BRACKET_OPEN:
2272 push_multiline(true);
2273 break;
2274 default:
2275 break; // Nothing to do.
2276 }
2277 token = advance();
2278 ParseFunction infix_rule = get_rule(token.type)->infix;
2279 previous_operand = (this->*infix_rule)(previous_operand, p_can_assign);
2280 }
2281
2282 return previous_operand;
2283}
2284
2285GDScriptParser::ExpressionNode *GDScriptParser::parse_expression(bool p_can_assign, bool p_stop_on_assign) {
2286 return parse_precedence(PREC_ASSIGNMENT, p_can_assign, p_stop_on_assign);
2287}
2288
2289GDScriptParser::IdentifierNode *GDScriptParser::parse_identifier() {
2290 IdentifierNode *identifier = static_cast<IdentifierNode *>(parse_identifier(nullptr, false));
2291#ifdef DEBUG_ENABLED
2292 // Check for spoofing here (if available in TextServer) since this isn't called inside expressions. This is only relevant for declarations.
2293 if (identifier && TS->has_feature(TextServer::FEATURE_UNICODE_SECURITY) && TS->spoof_check(identifier->name.operator String())) {
2294 push_warning(identifier, GDScriptWarning::CONFUSABLE_IDENTIFIER, identifier->name.operator String());
2295 }
2296#endif
2297 return identifier;
2298}
2299
2300GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode *p_previous_operand, bool p_can_assign) {
2301 if (!previous.is_identifier()) {
2302 ERR_FAIL_V_MSG(nullptr, "Parser bug: parsing identifier node without identifier token.");
2303 }
2304 IdentifierNode *identifier = alloc_node<IdentifierNode>();
2305 complete_extents(identifier);
2306 identifier->name = previous.get_identifier();
2307 identifier->suite = current_suite;
2308
2309 if (current_suite != nullptr && current_suite->has_local(identifier->name)) {
2310 const SuiteNode::Local &declaration = current_suite->get_local(identifier->name);
2311
2312 identifier->source_function = declaration.source_function;
2313 switch (declaration.type) {
2314 case SuiteNode::Local::CONSTANT:
2315 identifier->source = IdentifierNode::LOCAL_CONSTANT;
2316 identifier->constant_source = declaration.constant;
2317 declaration.constant->usages++;
2318 break;
2319 case SuiteNode::Local::VARIABLE:
2320 identifier->source = IdentifierNode::LOCAL_VARIABLE;
2321 identifier->variable_source = declaration.variable;
2322 declaration.variable->usages++;
2323 break;
2324 case SuiteNode::Local::PARAMETER:
2325 identifier->source = IdentifierNode::FUNCTION_PARAMETER;
2326 identifier->parameter_source = declaration.parameter;
2327 declaration.parameter->usages++;
2328 break;
2329 case SuiteNode::Local::FOR_VARIABLE:
2330 identifier->source = IdentifierNode::LOCAL_ITERATOR;
2331 identifier->bind_source = declaration.bind;
2332 declaration.bind->usages++;
2333 break;
2334 case SuiteNode::Local::PATTERN_BIND:
2335 identifier->source = IdentifierNode::LOCAL_BIND;
2336 identifier->bind_source = declaration.bind;
2337 declaration.bind->usages++;
2338 break;
2339 case SuiteNode::Local::UNDEFINED:
2340 ERR_FAIL_V_MSG(nullptr, "Undefined local found.");
2341 }
2342 }
2343
2344 return identifier;
2345}
2346
2347GDScriptParser::LiteralNode *GDScriptParser::parse_literal() {
2348 return static_cast<LiteralNode *>(parse_literal(nullptr, false));
2349}
2350
2351GDScriptParser::ExpressionNode *GDScriptParser::parse_literal(ExpressionNode *p_previous_operand, bool p_can_assign) {
2352 if (previous.type != GDScriptTokenizer::Token::LITERAL) {
2353 push_error("Parser bug: parsing literal node without literal token.");
2354 ERR_FAIL_V_MSG(nullptr, "Parser bug: parsing literal node without literal token.");
2355 }
2356
2357 LiteralNode *literal = alloc_node<LiteralNode>();
2358 complete_extents(literal);
2359 literal->value = previous.literal;
2360 return literal;
2361}
2362
2363GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_previous_operand, bool p_can_assign) {
2364 if (current_function && current_function->is_static) {
2365 push_error(R"(Cannot use "self" inside a static function.)");
2366 }
2367 SelfNode *self = alloc_node<SelfNode>();
2368 complete_extents(self);
2369 self->current_class = current_class;
2370 return self;
2371}
2372
2373GDScriptParser::ExpressionNode *GDScriptParser::parse_builtin_constant(ExpressionNode *p_previous_operand, bool p_can_assign) {
2374 GDScriptTokenizer::Token::Type op_type = previous.type;
2375 LiteralNode *constant = alloc_node<LiteralNode>();
2376 complete_extents(constant);
2377
2378 switch (op_type) {
2379 case GDScriptTokenizer::Token::CONST_PI:
2380 constant->value = Math_PI;
2381 break;
2382 case GDScriptTokenizer::Token::CONST_TAU:
2383 constant->value = Math_TAU;
2384 break;
2385 case GDScriptTokenizer::Token::CONST_INF:
2386 constant->value = INFINITY;
2387 break;
2388 case GDScriptTokenizer::Token::CONST_NAN:
2389 constant->value = NAN;
2390 break;
2391 default:
2392 return nullptr; // Unreachable.
2393 }
2394
2395 return constant;
2396}
2397
2398GDScriptParser::ExpressionNode *GDScriptParser::parse_unary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
2399 GDScriptTokenizer::Token::Type op_type = previous.type;
2400 UnaryOpNode *operation = alloc_node<UnaryOpNode>();
2401
2402 switch (op_type) {
2403 case GDScriptTokenizer::Token::MINUS:
2404 operation->operation = UnaryOpNode::OP_NEGATIVE;
2405 operation->variant_op = Variant::OP_NEGATE;
2406 operation->operand = parse_precedence(PREC_SIGN, false);
2407 if (operation->operand == nullptr) {
2408 push_error(R"(Expected expression after "-" operator.)");
2409 }
2410 break;
2411 case GDScriptTokenizer::Token::PLUS:
2412 operation->operation = UnaryOpNode::OP_POSITIVE;
2413 operation->variant_op = Variant::OP_POSITIVE;
2414 operation->operand = parse_precedence(PREC_SIGN, false);
2415 if (operation->operand == nullptr) {
2416 push_error(R"(Expected expression after "+" operator.)");
2417 }
2418 break;
2419 case GDScriptTokenizer::Token::TILDE:
2420 operation->operation = UnaryOpNode::OP_COMPLEMENT;
2421 operation->variant_op = Variant::OP_BIT_NEGATE;
2422 operation->operand = parse_precedence(PREC_BIT_NOT, false);
2423 if (operation->operand == nullptr) {
2424 push_error(R"(Expected expression after "~" operator.)");
2425 }
2426 break;
2427 case GDScriptTokenizer::Token::NOT:
2428 case GDScriptTokenizer::Token::BANG:
2429 operation->operation = UnaryOpNode::OP_LOGIC_NOT;
2430 operation->variant_op = Variant::OP_NOT;
2431 operation->operand = parse_precedence(PREC_LOGIC_NOT, false);
2432 if (operation->operand == nullptr) {
2433 push_error(vformat(R"(Expected expression after "%s" operator.)", op_type == GDScriptTokenizer::Token::NOT ? "not" : "!"));
2434 }
2435 break;
2436 default:
2437 complete_extents(operation);
2438 return nullptr; // Unreachable.
2439 }
2440 complete_extents(operation);
2441
2442 return operation;
2443}
2444
2445GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_not_in_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
2446 // check that NOT is followed by IN by consuming it before calling parse_binary_operator which will only receive a plain IN
2447 UnaryOpNode *operation = alloc_node<UnaryOpNode>();
2448 reset_extents(operation, p_previous_operand);
2449 update_extents(operation);
2450 consume(GDScriptTokenizer::Token::IN, R"(Expected "in" after "not" in content-test operator.)");
2451 ExpressionNode *in_operation = parse_binary_operator(p_previous_operand, p_can_assign);
2452 operation->operation = UnaryOpNode::OP_LOGIC_NOT;
2453 operation->variant_op = Variant::OP_NOT;
2454 operation->operand = in_operation;
2455 complete_extents(operation);
2456 return operation;
2457}
2458
2459GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
2460 GDScriptTokenizer::Token op = previous;
2461 BinaryOpNode *operation = alloc_node<BinaryOpNode>();
2462 reset_extents(operation, p_previous_operand);
2463 update_extents(operation);
2464
2465 Precedence precedence = (Precedence)(get_rule(op.type)->precedence + 1);
2466 operation->left_operand = p_previous_operand;
2467 operation->right_operand = parse_precedence(precedence, false);
2468 complete_extents(operation);
2469
2470 if (operation->right_operand == nullptr) {
2471 push_error(vformat(R"(Expected expression after "%s" operator.)", op.get_name()));
2472 }
2473
2474 // TODO: Also for unary, ternary, and assignment.
2475 switch (op.type) {
2476 case GDScriptTokenizer::Token::PLUS:
2477 operation->operation = BinaryOpNode::OP_ADDITION;
2478 operation->variant_op = Variant::OP_ADD;
2479 break;
2480 case GDScriptTokenizer::Token::MINUS:
2481 operation->operation = BinaryOpNode::OP_SUBTRACTION;
2482 operation->variant_op = Variant::OP_SUBTRACT;
2483 break;
2484 case GDScriptTokenizer::Token::STAR:
2485 operation->operation = BinaryOpNode::OP_MULTIPLICATION;
2486 operation->variant_op = Variant::OP_MULTIPLY;
2487 break;
2488 case GDScriptTokenizer::Token::SLASH:
2489 operation->operation = BinaryOpNode::OP_DIVISION;
2490 operation->variant_op = Variant::OP_DIVIDE;
2491 break;
2492 case GDScriptTokenizer::Token::PERCENT:
2493 operation->operation = BinaryOpNode::OP_MODULO;
2494 operation->variant_op = Variant::OP_MODULE;
2495 break;
2496 case GDScriptTokenizer::Token::STAR_STAR:
2497 operation->operation = BinaryOpNode::OP_POWER;
2498 operation->variant_op = Variant::OP_POWER;
2499 break;
2500 case GDScriptTokenizer::Token::LESS_LESS:
2501 operation->operation = BinaryOpNode::OP_BIT_LEFT_SHIFT;
2502 operation->variant_op = Variant::OP_SHIFT_LEFT;
2503 break;
2504 case GDScriptTokenizer::Token::GREATER_GREATER:
2505 operation->operation = BinaryOpNode::OP_BIT_RIGHT_SHIFT;
2506 operation->variant_op = Variant::OP_SHIFT_RIGHT;
2507 break;
2508 case GDScriptTokenizer::Token::AMPERSAND:
2509 operation->operation = BinaryOpNode::OP_BIT_AND;
2510 operation->variant_op = Variant::OP_BIT_AND;
2511 break;
2512 case GDScriptTokenizer::Token::PIPE:
2513 operation->operation = BinaryOpNode::OP_BIT_OR;
2514 operation->variant_op = Variant::OP_BIT_OR;
2515 break;
2516 case GDScriptTokenizer::Token::CARET:
2517 operation->operation = BinaryOpNode::OP_BIT_XOR;
2518 operation->variant_op = Variant::OP_BIT_XOR;
2519 break;
2520 case GDScriptTokenizer::Token::AND:
2521 case GDScriptTokenizer::Token::AMPERSAND_AMPERSAND:
2522 operation->operation = BinaryOpNode::OP_LOGIC_AND;
2523 operation->variant_op = Variant::OP_AND;
2524 break;
2525 case GDScriptTokenizer::Token::OR:
2526 case GDScriptTokenizer::Token::PIPE_PIPE:
2527 operation->operation = BinaryOpNode::OP_LOGIC_OR;
2528 operation->variant_op = Variant::OP_OR;
2529 break;
2530 case GDScriptTokenizer::Token::IN:
2531 operation->operation = BinaryOpNode::OP_CONTENT_TEST;
2532 operation->variant_op = Variant::OP_IN;
2533 break;
2534 case GDScriptTokenizer::Token::EQUAL_EQUAL:
2535 operation->operation = BinaryOpNode::OP_COMP_EQUAL;
2536 operation->variant_op = Variant::OP_EQUAL;
2537 break;
2538 case GDScriptTokenizer::Token::BANG_EQUAL:
2539 operation->operation = BinaryOpNode::OP_COMP_NOT_EQUAL;
2540 operation->variant_op = Variant::OP_NOT_EQUAL;
2541 break;
2542 case GDScriptTokenizer::Token::LESS:
2543 operation->operation = BinaryOpNode::OP_COMP_LESS;
2544 operation->variant_op = Variant::OP_LESS;
2545 break;
2546 case GDScriptTokenizer::Token::LESS_EQUAL:
2547 operation->operation = BinaryOpNode::OP_COMP_LESS_EQUAL;
2548 operation->variant_op = Variant::OP_LESS_EQUAL;
2549 break;
2550 case GDScriptTokenizer::Token::GREATER:
2551 operation->operation = BinaryOpNode::OP_COMP_GREATER;
2552 operation->variant_op = Variant::OP_GREATER;
2553 break;
2554 case GDScriptTokenizer::Token::GREATER_EQUAL:
2555 operation->operation = BinaryOpNode::OP_COMP_GREATER_EQUAL;
2556 operation->variant_op = Variant::OP_GREATER_EQUAL;
2557 break;
2558 default:
2559 return nullptr; // Unreachable.
2560 }
2561
2562 return operation;
2563}
2564
2565GDScriptParser::ExpressionNode *GDScriptParser::parse_ternary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
2566 // Only one ternary operation exists, so no abstraction here.
2567 TernaryOpNode *operation = alloc_node<TernaryOpNode>();
2568 reset_extents(operation, p_previous_operand);
2569 update_extents(operation);
2570
2571 operation->true_expr = p_previous_operand;
2572 operation->condition = parse_precedence(PREC_TERNARY, false);
2573
2574 if (operation->condition == nullptr) {
2575 push_error(R"(Expected expression as ternary condition after "if".)");
2576 }
2577
2578 consume(GDScriptTokenizer::Token::ELSE, R"(Expected "else" after ternary operator condition.)");
2579
2580 operation->false_expr = parse_precedence(PREC_TERNARY, false);
2581
2582 if (operation->false_expr == nullptr) {
2583 push_error(R"(Expected expression after "else".)");
2584 }
2585
2586 complete_extents(operation);
2587 return operation;
2588}
2589
2590GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode *p_previous_operand, bool p_can_assign) {
2591 if (!p_can_assign) {
2592 push_error("Assignment is not allowed inside an expression.");
2593 return parse_expression(false); // Return the following expression.
2594 }
2595 if (p_previous_operand == nullptr) {
2596 return parse_expression(false); // Return the following expression.
2597 }
2598
2599#ifdef DEBUG_ENABLED
2600 VariableNode *source_variable = nullptr;
2601#endif
2602
2603 switch (p_previous_operand->type) {
2604 case Node::IDENTIFIER: {
2605#ifdef DEBUG_ENABLED
2606 // Get source to store assignment count.
2607 // Also remove one usage since assignment isn't usage.
2608 IdentifierNode *id = static_cast<IdentifierNode *>(p_previous_operand);
2609 switch (id->source) {
2610 case IdentifierNode::LOCAL_VARIABLE:
2611
2612 source_variable = id->variable_source;
2613 id->variable_source->usages--;
2614 break;
2615 case IdentifierNode::LOCAL_CONSTANT:
2616 id->constant_source->usages--;
2617 break;
2618 case IdentifierNode::FUNCTION_PARAMETER:
2619 id->parameter_source->usages--;
2620 break;
2621 case IdentifierNode::LOCAL_ITERATOR:
2622 case IdentifierNode::LOCAL_BIND:
2623 id->bind_source->usages--;
2624 break;
2625 default:
2626 break;
2627 }
2628#endif
2629 } break;
2630 case Node::SUBSCRIPT:
2631 // Okay.
2632 break;
2633 default:
2634 push_error(R"(Only identifier, attribute access, and subscription access can be used as assignment target.)");
2635 return parse_expression(false); // Return the following expression.
2636 }
2637
2638 AssignmentNode *assignment = alloc_node<AssignmentNode>();
2639 reset_extents(assignment, p_previous_operand);
2640 update_extents(assignment);
2641
2642 make_completion_context(COMPLETION_ASSIGN, assignment);
2643#ifdef DEBUG_ENABLED
2644 bool has_operator = true;
2645#endif
2646 switch (previous.type) {
2647 case GDScriptTokenizer::Token::EQUAL:
2648 assignment->operation = AssignmentNode::OP_NONE;
2649 assignment->variant_op = Variant::OP_MAX;
2650#ifdef DEBUG_ENABLED
2651 has_operator = false;
2652#endif
2653 break;
2654 case GDScriptTokenizer::Token::PLUS_EQUAL:
2655 assignment->operation = AssignmentNode::OP_ADDITION;
2656 assignment->variant_op = Variant::OP_ADD;
2657 break;
2658 case GDScriptTokenizer::Token::MINUS_EQUAL:
2659 assignment->operation = AssignmentNode::OP_SUBTRACTION;
2660 assignment->variant_op = Variant::OP_SUBTRACT;
2661 break;
2662 case GDScriptTokenizer::Token::STAR_EQUAL:
2663 assignment->operation = AssignmentNode::OP_MULTIPLICATION;
2664 assignment->variant_op = Variant::OP_MULTIPLY;
2665 break;
2666 case GDScriptTokenizer::Token::STAR_STAR_EQUAL:
2667 assignment->operation = AssignmentNode::OP_POWER;
2668 assignment->variant_op = Variant::OP_POWER;
2669 break;
2670 case GDScriptTokenizer::Token::SLASH_EQUAL:
2671 assignment->operation = AssignmentNode::OP_DIVISION;
2672 assignment->variant_op = Variant::OP_DIVIDE;
2673 break;
2674 case GDScriptTokenizer::Token::PERCENT_EQUAL:
2675 assignment->operation = AssignmentNode::OP_MODULO;
2676 assignment->variant_op = Variant::OP_MODULE;
2677 break;
2678 case GDScriptTokenizer::Token::LESS_LESS_EQUAL:
2679 assignment->operation = AssignmentNode::OP_BIT_SHIFT_LEFT;
2680 assignment->variant_op = Variant::OP_SHIFT_LEFT;
2681 break;
2682 case GDScriptTokenizer::Token::GREATER_GREATER_EQUAL:
2683 assignment->operation = AssignmentNode::OP_BIT_SHIFT_RIGHT;
2684 assignment->variant_op = Variant::OP_SHIFT_RIGHT;
2685 break;
2686 case GDScriptTokenizer::Token::AMPERSAND_EQUAL:
2687 assignment->operation = AssignmentNode::OP_BIT_AND;
2688 assignment->variant_op = Variant::OP_BIT_AND;
2689 break;
2690 case GDScriptTokenizer::Token::PIPE_EQUAL:
2691 assignment->operation = AssignmentNode::OP_BIT_OR;
2692 assignment->variant_op = Variant::OP_BIT_OR;
2693 break;
2694 case GDScriptTokenizer::Token::CARET_EQUAL:
2695 assignment->operation = AssignmentNode::OP_BIT_XOR;
2696 assignment->variant_op = Variant::OP_BIT_XOR;
2697 break;
2698 default:
2699 break; // Unreachable.
2700 }
2701 assignment->assignee = p_previous_operand;
2702 assignment->assigned_value = parse_expression(false);
2703 if (assignment->assigned_value == nullptr) {
2704 push_error(R"(Expected an expression after "=".)");
2705 }
2706 complete_extents(assignment);
2707
2708#ifdef DEBUG_ENABLED
2709 if (source_variable != nullptr) {
2710 if (has_operator && source_variable->assignments == 0) {
2711 push_warning(assignment, GDScriptWarning::UNASSIGNED_VARIABLE_OP_ASSIGN, source_variable->identifier->name);
2712 }
2713
2714 source_variable->assignments += 1;
2715 }
2716#endif
2717
2718 return assignment;
2719}
2720
2721GDScriptParser::ExpressionNode *GDScriptParser::parse_await(ExpressionNode *p_previous_operand, bool p_can_assign) {
2722 AwaitNode *await = alloc_node<AwaitNode>();
2723 ExpressionNode *element = parse_precedence(PREC_AWAIT, false);
2724 if (element == nullptr) {
2725 push_error(R"(Expected signal or coroutine after "await".)");
2726 }
2727 await->to_await = element;
2728 complete_extents(await);
2729
2730 if (current_function) { // Might be null in a getter or setter.
2731 current_function->is_coroutine = true;
2732 }
2733
2734 return await;
2735}
2736
2737GDScriptParser::ExpressionNode *GDScriptParser::parse_array(ExpressionNode *p_previous_operand, bool p_can_assign) {
2738 ArrayNode *array = alloc_node<ArrayNode>();
2739
2740 if (!check(GDScriptTokenizer::Token::BRACKET_CLOSE)) {
2741 do {
2742 if (check(GDScriptTokenizer::Token::BRACKET_CLOSE)) {
2743 // Allow for trailing comma.
2744 break;
2745 }
2746
2747 ExpressionNode *element = parse_expression(false);
2748 if (element == nullptr) {
2749 push_error(R"(Expected expression as array element.)");
2750 } else {
2751 array->elements.push_back(element);
2752 }
2753 } while (match(GDScriptTokenizer::Token::COMMA) && !is_at_end());
2754 }
2755 pop_multiline();
2756 consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after array elements.)");
2757 complete_extents(array);
2758
2759 return array;
2760}
2761
2762GDScriptParser::ExpressionNode *GDScriptParser::parse_dictionary(ExpressionNode *p_previous_operand, bool p_can_assign) {
2763 DictionaryNode *dictionary = alloc_node<DictionaryNode>();
2764
2765 bool decided_style = false;
2766 if (!check(GDScriptTokenizer::Token::BRACE_CLOSE)) {
2767 do {
2768 if (check(GDScriptTokenizer::Token::BRACE_CLOSE)) {
2769 // Allow for trailing comma.
2770 break;
2771 }
2772
2773 // Key.
2774 ExpressionNode *key = parse_expression(false, true); // Stop on "=" so we can check for Lua table style.
2775
2776 if (key == nullptr) {
2777 push_error(R"(Expected expression as dictionary key.)");
2778 }
2779
2780 if (!decided_style) {
2781 switch (current.type) {
2782 case GDScriptTokenizer::Token::COLON:
2783 dictionary->style = DictionaryNode::PYTHON_DICT;
2784 break;
2785 case GDScriptTokenizer::Token::EQUAL:
2786 dictionary->style = DictionaryNode::LUA_TABLE;
2787 break;
2788 default:
2789 push_error(R"(Expected ":" or "=" after dictionary key.)");
2790 break;
2791 }
2792 decided_style = true;
2793 }
2794
2795 switch (dictionary->style) {
2796 case DictionaryNode::LUA_TABLE:
2797 if (key != nullptr && key->type != Node::IDENTIFIER && key->type != Node::LITERAL) {
2798 push_error(R"(Expected identifier or string as Lua-style dictionary key (e.g "{ key = value }").)");
2799 advance();
2800 break;
2801 }
2802 if (key != nullptr && key->type == Node::LITERAL && static_cast<LiteralNode *>(key)->value.get_type() != Variant::STRING) {
2803 push_error(R"(Expected identifier or string as Lua-style dictionary key (e.g "{ key = value }").)");
2804 advance();
2805 break;
2806 }
2807 if (!match(GDScriptTokenizer::Token::EQUAL)) {
2808 if (match(GDScriptTokenizer::Token::COLON)) {
2809 push_error(R"(Expected "=" after dictionary key. Mixing dictionary styles is not allowed.)");
2810 advance(); // Consume wrong separator anyway.
2811 } else {
2812 push_error(R"(Expected "=" after dictionary key.)");
2813 }
2814 }
2815 if (key != nullptr) {
2816 key->is_constant = true;
2817 if (key->type == Node::IDENTIFIER) {
2818 key->reduced_value = static_cast<IdentifierNode *>(key)->name;
2819 } else if (key->type == Node::LITERAL) {
2820 key->reduced_value = StringName(static_cast<LiteralNode *>(key)->value.operator String());
2821 }
2822 }
2823 break;
2824 case DictionaryNode::PYTHON_DICT:
2825 if (!match(GDScriptTokenizer::Token::COLON)) {
2826 if (match(GDScriptTokenizer::Token::EQUAL)) {
2827 push_error(R"(Expected ":" after dictionary key. Mixing dictionary styles is not allowed.)");
2828 advance(); // Consume wrong separator anyway.
2829 } else {
2830 push_error(R"(Expected ":" after dictionary key.)");
2831 }
2832 }
2833 break;
2834 }
2835
2836 // Value.
2837 ExpressionNode *value = parse_expression(false);
2838 if (value == nullptr) {
2839 push_error(R"(Expected expression as dictionary value.)");
2840 }
2841
2842 if (key != nullptr && value != nullptr) {
2843 dictionary->elements.push_back({ key, value });
2844 }
2845 } while (match(GDScriptTokenizer::Token::COMMA) && !is_at_end());
2846 }
2847 pop_multiline();
2848 consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected closing "}" after dictionary elements.)");
2849 complete_extents(dictionary);
2850
2851 return dictionary;
2852}
2853
2854GDScriptParser::ExpressionNode *GDScriptParser::parse_grouping(ExpressionNode *p_previous_operand, bool p_can_assign) {
2855 ExpressionNode *grouped = parse_expression(false);
2856 pop_multiline();
2857 if (grouped == nullptr) {
2858 push_error(R"(Expected grouping expression.)");
2859 } else {
2860 consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after grouping expression.)*");
2861 }
2862 return grouped;
2863}
2864
2865GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode *p_previous_operand, bool p_can_assign) {
2866 SubscriptNode *attribute = alloc_node<SubscriptNode>();
2867 reset_extents(attribute, p_previous_operand);
2868 update_extents(attribute);
2869
2870 if (for_completion) {
2871 bool is_builtin = false;
2872 if (p_previous_operand && p_previous_operand->type == Node::IDENTIFIER) {
2873 const IdentifierNode *id = static_cast<const IdentifierNode *>(p_previous_operand);
2874 Variant::Type builtin_type = get_builtin_type(id->name);
2875 if (builtin_type < Variant::VARIANT_MAX) {
2876 make_completion_context(COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD, builtin_type, true);
2877 is_builtin = true;
2878 }
2879 }
2880 if (!is_builtin) {
2881 make_completion_context(COMPLETION_ATTRIBUTE, attribute, -1, true);
2882 }
2883 }
2884
2885 attribute->base = p_previous_operand;
2886
2887 if (current.is_node_name()) {
2888 current.type = GDScriptTokenizer::Token::IDENTIFIER;
2889 }
2890 if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier after "." for attribute access.)")) {
2891 complete_extents(attribute);
2892 return attribute;
2893 }
2894
2895 attribute->is_attribute = true;
2896 attribute->attribute = parse_identifier();
2897
2898 complete_extents(attribute);
2899 return attribute;
2900}
2901
2902GDScriptParser::ExpressionNode *GDScriptParser::parse_subscript(ExpressionNode *p_previous_operand, bool p_can_assign) {
2903 SubscriptNode *subscript = alloc_node<SubscriptNode>();
2904 reset_extents(subscript, p_previous_operand);
2905 update_extents(subscript);
2906
2907 make_completion_context(COMPLETION_SUBSCRIPT, subscript);
2908
2909 subscript->base = p_previous_operand;
2910 subscript->index = parse_expression(false);
2911
2912 if (subscript->index == nullptr) {
2913 push_error(R"(Expected expression after "[".)");
2914 }
2915
2916 pop_multiline();
2917 consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected "]" after subscription index.)");
2918 complete_extents(subscript);
2919
2920 return subscript;
2921}
2922
2923GDScriptParser::ExpressionNode *GDScriptParser::parse_cast(ExpressionNode *p_previous_operand, bool p_can_assign) {
2924 CastNode *cast = alloc_node<CastNode>();
2925 reset_extents(cast, p_previous_operand);
2926 update_extents(cast);
2927
2928 cast->operand = p_previous_operand;
2929 cast->cast_type = parse_type();
2930 complete_extents(cast);
2931
2932 if (cast->cast_type == nullptr) {
2933 push_error(R"(Expected type specifier after "as".)");
2934 return p_previous_operand;
2935 }
2936
2937 return cast;
2938}
2939
2940GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_previous_operand, bool p_can_assign) {
2941 CallNode *call = alloc_node<CallNode>();
2942 reset_extents(call, p_previous_operand);
2943
2944 if (previous.type == GDScriptTokenizer::Token::SUPER) {
2945 // Super call.
2946 call->is_super = true;
2947 push_multiline(true);
2948 if (match(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) {
2949 // Implicit call to the parent method of the same name.
2950 if (current_function == nullptr) {
2951 push_error(R"(Cannot use implicit "super" call outside of a function.)");
2952 pop_multiline();
2953 complete_extents(call);
2954 return nullptr;
2955 }
2956 if (current_function->identifier) {
2957 call->function_name = current_function->identifier->name;
2958 } else {
2959 call->function_name = SNAME("<anonymous>");
2960 }
2961 } else {
2962 consume(GDScriptTokenizer::Token::PERIOD, R"(Expected "." or "(" after "super".)");
2963 make_completion_context(COMPLETION_SUPER_METHOD, call, true);
2964 if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after ".".)")) {
2965 pop_multiline();
2966 complete_extents(call);
2967 return nullptr;
2968 }
2969 IdentifierNode *identifier = parse_identifier();
2970 call->callee = identifier;
2971 call->function_name = identifier->name;
2972 consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after function name.)");
2973 }
2974 } else {
2975 call->callee = p_previous_operand;
2976
2977 if (call->callee == nullptr) {
2978 push_error(R"*(Cannot call on an expression. Use ".call()" if it's a Callable.)*");
2979 } else if (call->callee->type == Node::IDENTIFIER) {
2980 call->function_name = static_cast<IdentifierNode *>(call->callee)->name;
2981 make_completion_context(COMPLETION_METHOD, call->callee);
2982 } else if (call->callee->type == Node::SUBSCRIPT) {
2983 SubscriptNode *attribute = static_cast<SubscriptNode *>(call->callee);
2984 if (attribute->is_attribute) {
2985 if (attribute->attribute) {
2986 call->function_name = attribute->attribute->name;
2987 }
2988 make_completion_context(COMPLETION_ATTRIBUTE_METHOD, call->callee);
2989 } else {
2990 // TODO: The analyzer can see if this is actually a Callable and give better error message.
2991 push_error(R"*(Cannot call on an expression. Use ".call()" if it's a Callable.)*");
2992 }
2993 } else {
2994 push_error(R"*(Cannot call on an expression. Use ".call()" if it's a Callable.)*");
2995 }
2996 }
2997
2998 // Arguments.
2999 CompletionType ct = COMPLETION_CALL_ARGUMENTS;
3000 if (call->function_name == SNAME("load")) {
3001 ct = COMPLETION_RESOURCE_PATH;
3002 }
3003 push_completion_call(call);
3004 int argument_index = 0;
3005 do {
3006 make_completion_context(ct, call, argument_index++, true);
3007 if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
3008 // Allow for trailing comma.
3009 break;
3010 }
3011 bool use_identifier_completion = current.cursor_place == GDScriptTokenizer::CURSOR_END || current.cursor_place == GDScriptTokenizer::CURSOR_MIDDLE;
3012 ExpressionNode *argument = parse_expression(false);
3013 if (argument == nullptr) {
3014 push_error(R"(Expected expression as the function argument.)");
3015 } else {
3016 call->arguments.push_back(argument);
3017
3018 if (argument->type == Node::IDENTIFIER && use_identifier_completion) {
3019 completion_context.type = COMPLETION_IDENTIFIER;
3020 }
3021 }
3022 ct = COMPLETION_CALL_ARGUMENTS;
3023 } while (match(GDScriptTokenizer::Token::COMMA));
3024 pop_completion_call();
3025
3026 pop_multiline();
3027 consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after call arguments.)*");
3028 complete_extents(call);
3029
3030 return call;
3031}
3032
3033GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p_previous_operand, bool p_can_assign) {
3034 if (!current.is_node_name() && !check(GDScriptTokenizer::Token::LITERAL) && !check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) {
3035 push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name()));
3036 return nullptr;
3037 }
3038
3039 if (check(GDScriptTokenizer::Token::LITERAL)) {
3040 if (current.literal.get_type() != Variant::STRING) {
3041 push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name()));
3042 return nullptr;
3043 }
3044 }
3045
3046 GetNodeNode *get_node = alloc_node<GetNodeNode>();
3047
3048 // Store the last item in the path so the parser knows what to expect.
3049 // Allow allows more specific error messages.
3050 enum PathState {
3051 PATH_STATE_START,
3052 PATH_STATE_SLASH,
3053 PATH_STATE_PERCENT,
3054 PATH_STATE_NODE_NAME,
3055 } path_state = PATH_STATE_START;
3056
3057 if (previous.type == GDScriptTokenizer::Token::DOLLAR) {
3058 // Detect initial slash, which will be handled in the loop if it matches.
3059 match(GDScriptTokenizer::Token::SLASH);
3060 } else {
3061 get_node->use_dollar = false;
3062 }
3063
3064 int context_argument = 0;
3065
3066 do {
3067 if (previous.type == GDScriptTokenizer::Token::PERCENT) {
3068 if (path_state != PATH_STATE_START && path_state != PATH_STATE_SLASH) {
3069 push_error(R"("%" is only valid in the beginning of a node name (either after "$" or after "/"))");
3070 complete_extents(get_node);
3071 return nullptr;
3072 }
3073 get_node->full_path += "%";
3074
3075 path_state = PATH_STATE_PERCENT;
3076 } else if (previous.type == GDScriptTokenizer::Token::SLASH) {
3077 if (path_state != PATH_STATE_START && path_state != PATH_STATE_NODE_NAME) {
3078 push_error(R"("/" is only valid at the beginning of the path or after a node name.)");
3079 complete_extents(get_node);
3080 return nullptr;
3081 }
3082
3083 get_node->full_path += "/";
3084
3085 path_state = PATH_STATE_SLASH;
3086 }
3087
3088 make_completion_context(COMPLETION_GET_NODE, get_node, context_argument++);
3089
3090 if (match(GDScriptTokenizer::Token::LITERAL)) {
3091 if (previous.literal.get_type() != Variant::STRING) {
3092 String previous_token;
3093 switch (path_state) {
3094 case PATH_STATE_START:
3095 previous_token = "$";
3096 break;
3097 case PATH_STATE_PERCENT:
3098 previous_token = "%";
3099 break;
3100 case PATH_STATE_SLASH:
3101 previous_token = "/";
3102 break;
3103 default:
3104 break;
3105 }
3106 push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous_token));
3107 complete_extents(get_node);
3108 return nullptr;
3109 }
3110
3111 get_node->full_path += previous.literal.operator String();
3112
3113 path_state = PATH_STATE_NODE_NAME;
3114 } else if (current.is_node_name()) {
3115 advance();
3116 String identifier = previous.get_identifier();
3117#ifdef DEBUG_ENABLED
3118 // Check spoofing.
3119 if (TS->has_feature(TextServer::FEATURE_UNICODE_SECURITY) && TS->spoof_check(identifier)) {
3120 push_warning(get_node, GDScriptWarning::CONFUSABLE_IDENTIFIER, identifier);
3121 }
3122#endif
3123 get_node->full_path += identifier;
3124
3125 path_state = PATH_STATE_NODE_NAME;
3126 } else if (!check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) {
3127 push_error(vformat(R"(Unexpected "%s" in node path.)", current.get_name()));
3128 complete_extents(get_node);
3129 return nullptr;
3130 }
3131 } while (match(GDScriptTokenizer::Token::SLASH) || match(GDScriptTokenizer::Token::PERCENT));
3132
3133 complete_extents(get_node);
3134 return get_node;
3135}
3136
3137GDScriptParser::ExpressionNode *GDScriptParser::parse_preload(ExpressionNode *p_previous_operand, bool p_can_assign) {
3138 PreloadNode *preload = alloc_node<PreloadNode>();
3139 preload->resolved_path = "<missing path>";
3140
3141 push_multiline(true);
3142 consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "preload".)");
3143
3144 make_completion_context(COMPLETION_RESOURCE_PATH, preload);
3145 push_completion_call(preload);
3146
3147 preload->path = parse_expression(false);
3148
3149 if (preload->path == nullptr) {
3150 push_error(R"(Expected resource path after "(".)");
3151 }
3152
3153 pop_completion_call();
3154
3155 pop_multiline();
3156 consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after preload path.)*");
3157 complete_extents(preload);
3158
3159 return preload;
3160}
3161
3162GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_previous_operand, bool p_can_assign) {
3163 LambdaNode *lambda = alloc_node<LambdaNode>();
3164 lambda->parent_function = current_function;
3165 lambda->parent_lambda = current_lambda;
3166
3167 FunctionNode *function = alloc_node<FunctionNode>();
3168 function->source_lambda = lambda;
3169
3170 function->is_static = current_function != nullptr ? current_function->is_static : false;
3171
3172 if (match(GDScriptTokenizer::Token::IDENTIFIER)) {
3173 function->identifier = parse_identifier();
3174 }
3175
3176 bool multiline_context = multiline_stack.back()->get();
3177
3178 // Reset the multiline stack since we don't want the multiline mode one in the lambda body.
3179 push_multiline(false);
3180 if (multiline_context) {
3181 tokenizer.push_expression_indented_block();
3182 }
3183
3184 push_multiline(true); // For the parameters.
3185 if (function->identifier) {
3186 consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected opening "(" after lambda name.)");
3187 } else {
3188 consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected opening "(" after "func".)");
3189 }
3190
3191 FunctionNode *previous_function = current_function;
3192 current_function = function;
3193
3194 LambdaNode *previous_lambda = current_lambda;
3195 current_lambda = lambda;
3196
3197 SuiteNode *body = alloc_node<SuiteNode>();
3198 body->parent_function = current_function;
3199 body->parent_block = current_suite;
3200
3201 SuiteNode *previous_suite = current_suite;
3202 current_suite = body;
3203
3204 parse_function_signature(function, body, "lambda");
3205
3206 current_suite = previous_suite;
3207
3208 bool previous_in_lambda = in_lambda;
3209 in_lambda = true;
3210
3211 // Save break/continue state.
3212 bool could_break = can_break;
3213 bool could_continue = can_continue;
3214
3215 // Disallow break/continue.
3216 can_break = false;
3217 can_continue = false;
3218
3219 function->body = parse_suite("lambda declaration", body, true);
3220 complete_extents(function);
3221 complete_extents(lambda);
3222
3223 pop_multiline();
3224
3225 if (multiline_context) {
3226 // If we're in multiline mode, we want to skip the spurious DEDENT and NEWLINE tokens.
3227 while (check(GDScriptTokenizer::Token::DEDENT) || check(GDScriptTokenizer::Token::INDENT) || check(GDScriptTokenizer::Token::NEWLINE)) {
3228 current = tokenizer.scan(); // Not advance() since we don't want to change the previous token.
3229 }
3230 tokenizer.pop_expression_indented_block();
3231 }
3232
3233 current_function = previous_function;
3234 current_lambda = previous_lambda;
3235 in_lambda = previous_in_lambda;
3236 lambda->function = function;
3237
3238 // Reset break/continue state.
3239 can_break = could_break;
3240 can_continue = could_continue;
3241
3242 return lambda;
3243}
3244
3245GDScriptParser::ExpressionNode *GDScriptParser::parse_type_test(ExpressionNode *p_previous_operand, bool p_can_assign) {
3246 TypeTestNode *type_test = alloc_node<TypeTestNode>();
3247 reset_extents(type_test, p_previous_operand);
3248 update_extents(type_test);
3249
3250 type_test->operand = p_previous_operand;
3251 type_test->test_type = parse_type();
3252 complete_extents(type_test);
3253
3254 if (type_test->test_type == nullptr) {
3255 push_error(R"(Expected type specifier after "is".)");
3256 }
3257
3258 return type_test;
3259}
3260
3261GDScriptParser::ExpressionNode *GDScriptParser::parse_yield(ExpressionNode *p_previous_operand, bool p_can_assign) {
3262 push_error(R"("yield" was removed in Godot 4. Use "await" instead.)");
3263 return nullptr;
3264}
3265
3266GDScriptParser::ExpressionNode *GDScriptParser::parse_invalid_token(ExpressionNode *p_previous_operand, bool p_can_assign) {
3267 // Just for better error messages.
3268 GDScriptTokenizer::Token::Type invalid = previous.type;
3269
3270 switch (invalid) {
3271 case GDScriptTokenizer::Token::QUESTION_MARK:
3272 push_error(R"(Unexpected "?" in source. If you want a ternary operator, use "truthy_value if true_condition else falsy_value".)");
3273 break;
3274 default:
3275 return nullptr; // Unreachable.
3276 }
3277
3278 // Return the previous expression.
3279 return p_previous_operand;
3280}
3281
3282GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
3283 TypeNode *type = alloc_node<TypeNode>();
3284 make_completion_context(p_allow_void ? COMPLETION_TYPE_NAME_OR_VOID : COMPLETION_TYPE_NAME, type);
3285 if (!match(GDScriptTokenizer::Token::IDENTIFIER)) {
3286 if (match(GDScriptTokenizer::Token::VOID)) {
3287 if (p_allow_void) {
3288 complete_extents(type);
3289 TypeNode *void_type = type;
3290 return void_type;
3291 } else {
3292 push_error(R"("void" is only allowed for a function return type.)");
3293 }
3294 }
3295 // Leave error message to the caller who knows the context.
3296 complete_extents(type);
3297 return nullptr;
3298 }
3299
3300 IdentifierNode *type_element = parse_identifier();
3301
3302 type->type_chain.push_back(type_element);
3303
3304 if (match(GDScriptTokenizer::Token::BRACKET_OPEN)) {
3305 // Typed collection (like Array[int]).
3306 type->container_type = parse_type(false); // Don't allow void for array element type.
3307 if (type->container_type == nullptr) {
3308 push_error(R"(Expected type for collection after "[".)");
3309 complete_extents(type);
3310 type = nullptr;
3311 } else if (type->container_type->container_type != nullptr) {
3312 push_error("Nested typed collections are not supported.");
3313 }
3314 consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after collection type.)");
3315 if (type != nullptr) {
3316 complete_extents(type);
3317 }
3318 return type;
3319 }
3320
3321 int chain_index = 1;
3322 while (match(GDScriptTokenizer::Token::PERIOD)) {
3323 make_completion_context(COMPLETION_TYPE_ATTRIBUTE, type, chain_index++);
3324 if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected inner type name after ".".)")) {
3325 type_element = parse_identifier();
3326 type->type_chain.push_back(type_element);
3327 }
3328 }
3329
3330 complete_extents(type);
3331 return type;
3332}
3333
3334#ifdef TOOLS_ENABLED
3335enum DocLineState {
3336 DOC_LINE_NORMAL,
3337 DOC_LINE_IN_CODE,
3338 DOC_LINE_IN_CODEBLOCK,
3339};
3340
3341static String _process_doc_line(const String &p_line, const String &p_text, const String &p_space_prefix, DocLineState &r_state) {
3342 String line = p_line;
3343 if (r_state == DOC_LINE_NORMAL) {
3344 line = line.strip_edges(true, false);
3345 } else {
3346 line = line.trim_prefix(p_space_prefix);
3347 }
3348
3349 String line_join;
3350 if (!p_text.is_empty()) {
3351 if (r_state == DOC_LINE_NORMAL) {
3352 if (p_text.ends_with("[/codeblock]")) {
3353 line_join = "\n";
3354 } else if (!p_text.ends_with("[br]")) {
3355 line_join = " ";
3356 }
3357 } else {
3358 line_join = "\n";
3359 }
3360 }
3361
3362 String result;
3363 int from = 0;
3364 int buffer_start = 0;
3365 const int len = line.length();
3366 bool process = true;
3367 while (process) {
3368 switch (r_state) {
3369 case DOC_LINE_NORMAL: {
3370 int lb_pos = line.find_char('[', from);
3371 if (lb_pos < 0) {
3372 process = false;
3373 break;
3374 }
3375 int rb_pos = line.find_char(']', lb_pos + 1);
3376 if (rb_pos < 0) {
3377 process = false;
3378 break;
3379 }
3380
3381 from = rb_pos + 1;
3382
3383 String tag = line.substr(lb_pos + 1, rb_pos - lb_pos - 1);
3384 if (tag == "code") {
3385 r_state = DOC_LINE_IN_CODE;
3386 } else if (tag == "codeblock") {
3387 if (lb_pos == 0) {
3388 line_join = "\n";
3389 } else {
3390 result += line.substr(buffer_start, lb_pos - buffer_start) + '\n';
3391 }
3392 result += "[codeblock]";
3393 if (from < len) {
3394 result += '\n';
3395 }
3396
3397 r_state = DOC_LINE_IN_CODEBLOCK;
3398 buffer_start = from;
3399 }
3400 } break;
3401 case DOC_LINE_IN_CODE: {
3402 int pos = line.find("[/code]", from);
3403 if (pos < 0) {
3404 process = false;
3405 break;
3406 }
3407
3408 from = pos + 7;
3409
3410 r_state = DOC_LINE_NORMAL;
3411 } break;
3412 case DOC_LINE_IN_CODEBLOCK: {
3413 int pos = line.find("[/codeblock]", from);
3414 if (pos < 0) {
3415 process = false;
3416 break;
3417 }
3418
3419 from = pos + 12;
3420
3421 if (pos == 0) {
3422 line_join = "\n";
3423 } else {
3424 result += line.substr(buffer_start, pos - buffer_start) + '\n';
3425 }
3426 result += "[/codeblock]";
3427 if (from < len) {
3428 result += '\n';
3429 }
3430
3431 r_state = DOC_LINE_NORMAL;
3432 buffer_start = from;
3433 } break;
3434 }
3435 }
3436
3437 result += line.substr(buffer_start);
3438 if (r_state == DOC_LINE_NORMAL) {
3439 result = result.strip_edges(false, true);
3440 }
3441
3442 return line_join + result;
3443}
3444
3445bool GDScriptParser::has_comment(int p_line, bool p_must_be_doc) {
3446 bool has_comment = tokenizer.get_comments().has(p_line);
3447 // If there are no comments or if we don't care whether the comment
3448 // is a docstring, we have our result.
3449 if (!p_must_be_doc || !has_comment) {
3450 return has_comment;
3451 }
3452
3453 return tokenizer.get_comments()[p_line].comment.begins_with("##");
3454}
3455
3456GDScriptParser::MemberDocData GDScriptParser::parse_doc_comment(int p_line, bool p_single_line) {
3457 MemberDocData result;
3458
3459 const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
3460 ERR_FAIL_COND_V(!comments.has(p_line), result);
3461
3462 if (p_single_line) {
3463 if (comments[p_line].comment.begins_with("##")) {
3464 result.description = comments[p_line].comment.trim_prefix("##").strip_edges();
3465 return result;
3466 }
3467 return result;
3468 }
3469
3470 int line = p_line;
3471 DocLineState state = DOC_LINE_NORMAL;
3472
3473 while (comments.has(line - 1)) {
3474 if (!comments[line - 1].new_line || !comments[line - 1].comment.begins_with("##")) {
3475 break;
3476 }
3477 line--;
3478 }
3479
3480 String space_prefix;
3481 if (comments.has(line) && comments[line].comment.begins_with("##")) {
3482 int i = 2;
3483 for (; i < comments[line].comment.length(); i++) {
3484 if (comments[line].comment[i] != ' ') {
3485 break;
3486 }
3487 }
3488 space_prefix = String(" ").repeat(i - 2);
3489 }
3490
3491 while (comments.has(line)) {
3492 if (!comments[line].new_line || !comments[line].comment.begins_with("##")) {
3493 break;
3494 }
3495
3496 String doc_line = comments[line].comment.trim_prefix("##");
3497 line++;
3498
3499 if (state == DOC_LINE_NORMAL) {
3500 String stripped_line = doc_line.strip_edges();
3501 if (stripped_line.begins_with("@deprecated")) {
3502 result.is_deprecated = true;
3503 continue;
3504 } else if (stripped_line.begins_with("@experimental")) {
3505 result.is_experimental = true;
3506 continue;
3507 }
3508 }
3509
3510 result.description += _process_doc_line(doc_line, result.description, space_prefix, state);
3511 }
3512
3513 return result;
3514}
3515
3516GDScriptParser::ClassDocData GDScriptParser::parse_class_doc_comment(int p_line, bool p_inner_class, bool p_single_line) {
3517 ClassDocData result;
3518
3519 const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
3520 ERR_FAIL_COND_V(!comments.has(p_line), result);
3521
3522 if (p_single_line) {
3523 if (comments[p_line].comment.begins_with("##")) {
3524 result.brief = comments[p_line].comment.trim_prefix("##").strip_edges();
3525 return result;
3526 }
3527 return result;
3528 }
3529
3530 int line = p_line;
3531 DocLineState state = DOC_LINE_NORMAL;
3532 bool is_in_brief = true;
3533
3534 if (p_inner_class) {
3535 while (comments.has(line - 1)) {
3536 if (!comments[line - 1].new_line || !comments[line - 1].comment.begins_with("##")) {
3537 break;
3538 }
3539 line--;
3540 }
3541 }
3542
3543 String space_prefix;
3544 if (comments.has(line) && comments[line].comment.begins_with("##")) {
3545 int i = 2;
3546 for (; i < comments[line].comment.length(); i++) {
3547 if (comments[line].comment[i] != ' ') {
3548 break;
3549 }
3550 }
3551 space_prefix = String(" ").repeat(i - 2);
3552 }
3553
3554 while (comments.has(line)) {
3555 if (!comments[line].new_line || !comments[line].comment.begins_with("##")) {
3556 break;
3557 }
3558
3559 String doc_line = comments[line].comment.trim_prefix("##");
3560 line++;
3561
3562 if (state == DOC_LINE_NORMAL) {
3563 String stripped_line = doc_line.strip_edges();
3564
3565 // A blank line separates the description from the brief.
3566 if (is_in_brief && !result.brief.is_empty() && stripped_line.is_empty()) {
3567 is_in_brief = false;
3568 continue;
3569 }
3570
3571 if (stripped_line.begins_with("@tutorial")) {
3572 String title, link;
3573
3574 int begin_scan = String("@tutorial").length();
3575 if (begin_scan >= stripped_line.length()) {
3576 continue; // Invalid syntax.
3577 }
3578
3579 if (stripped_line[begin_scan] == ':') { // No title.
3580 // Syntax: ## @tutorial: https://godotengine.org/ // The title argument is optional.
3581 title = "";
3582 link = stripped_line.trim_prefix("@tutorial:").strip_edges();
3583 } else {
3584 /* Syntax:
3585 * @tutorial ( The Title Here ) : https://the.url/
3586 * ^ open ^ close ^ colon ^ url
3587 */
3588 int open_bracket_pos = begin_scan, close_bracket_pos = 0;
3589 while (open_bracket_pos < stripped_line.length() && (stripped_line[open_bracket_pos] == ' ' || stripped_line[open_bracket_pos] == '\t')) {
3590 open_bracket_pos++;
3591 }
3592 if (open_bracket_pos == stripped_line.length() || stripped_line[open_bracket_pos++] != '(') {
3593 continue; // Invalid syntax.
3594 }
3595 close_bracket_pos = open_bracket_pos;
3596 while (close_bracket_pos < stripped_line.length() && stripped_line[close_bracket_pos] != ')') {
3597 close_bracket_pos++;
3598 }
3599 if (close_bracket_pos == stripped_line.length()) {
3600 continue; // Invalid syntax.
3601 }
3602
3603 int colon_pos = close_bracket_pos + 1;
3604 while (colon_pos < stripped_line.length() && (stripped_line[colon_pos] == ' ' || stripped_line[colon_pos] == '\t')) {
3605 colon_pos++;
3606 }
3607 if (colon_pos == stripped_line.length() || stripped_line[colon_pos++] != ':') {
3608 continue; // Invalid syntax.
3609 }
3610
3611 title = stripped_line.substr(open_bracket_pos, close_bracket_pos - open_bracket_pos).strip_edges();
3612 link = stripped_line.substr(colon_pos).strip_edges();
3613 }
3614
3615 result.tutorials.append(Pair<String, String>(title, link));
3616 continue;
3617 } else if (stripped_line.begins_with("@deprecated")) {
3618 result.is_deprecated = true;
3619 continue;
3620 } else if (stripped_line.begins_with("@experimental")) {
3621 result.is_experimental = true;
3622 continue;
3623 }
3624 }
3625
3626 if (is_in_brief) {
3627 result.brief += _process_doc_line(doc_line, result.brief, space_prefix, state);
3628 } else {
3629 result.description += _process_doc_line(doc_line, result.description, space_prefix, state);
3630 }
3631 }
3632
3633 if (current_class->members.size() > 0) {
3634 const ClassNode::Member &m = current_class->members[0];
3635 int first_member_line = m.get_line();
3636 if (first_member_line == line) {
3637 result = ClassDocData(); // Clear result.
3638 }
3639 }
3640
3641 return result;
3642}
3643#endif // TOOLS_ENABLED
3644
3645GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Type p_token_type) {
3646 // Function table for expression parsing.
3647 // clang-format destroys the alignment here, so turn off for the table.
3648 /* clang-format off */
3649 static ParseRule rules[] = {
3650 // PREFIX INFIX PRECEDENCE (for infix)
3651 { nullptr, nullptr, PREC_NONE }, // EMPTY,
3652 // Basic
3653 { nullptr, nullptr, PREC_NONE }, // ANNOTATION,
3654 { &GDScriptParser::parse_identifier, nullptr, PREC_NONE }, // IDENTIFIER,
3655 { &GDScriptParser::parse_literal, nullptr, PREC_NONE }, // LITERAL,
3656 // Comparison
3657 { nullptr, &GDScriptParser::parse_binary_operator, PREC_COMPARISON }, // LESS,
3658 { nullptr, &GDScriptParser::parse_binary_operator, PREC_COMPARISON }, // LESS_EQUAL,
3659 { nullptr, &GDScriptParser::parse_binary_operator, PREC_COMPARISON }, // GREATER,
3660 { nullptr, &GDScriptParser::parse_binary_operator, PREC_COMPARISON }, // GREATER_EQUAL,
3661 { nullptr, &GDScriptParser::parse_binary_operator, PREC_COMPARISON }, // EQUAL_EQUAL,
3662 { nullptr, &GDScriptParser::parse_binary_operator, PREC_COMPARISON }, // BANG_EQUAL,
3663 // Logical
3664 { nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_AND }, // AND,
3665 { nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_OR }, // OR,
3666 { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_not_in_operator, PREC_CONTENT_TEST }, // NOT,
3667 { nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_AND }, // AMPERSAND_AMPERSAND,
3668 { nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_OR }, // PIPE_PIPE,
3669 { &GDScriptParser::parse_unary_operator, nullptr, PREC_NONE }, // BANG,
3670 // Bitwise
3671 { nullptr, &GDScriptParser::parse_binary_operator, PREC_BIT_AND }, // AMPERSAND,
3672 { nullptr, &GDScriptParser::parse_binary_operator, PREC_BIT_OR }, // PIPE,
3673 { &GDScriptParser::parse_unary_operator, nullptr, PREC_NONE }, // TILDE,
3674 { nullptr, &GDScriptParser::parse_binary_operator, PREC_BIT_XOR }, // CARET,
3675 { nullptr, &GDScriptParser::parse_binary_operator, PREC_BIT_SHIFT }, // LESS_LESS,
3676 { nullptr, &GDScriptParser::parse_binary_operator, PREC_BIT_SHIFT }, // GREATER_GREATER,
3677 // Math
3678 { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // PLUS,
3679 { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // MINUS,
3680 { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // STAR,
3681 { nullptr, &GDScriptParser::parse_binary_operator, PREC_POWER }, // STAR_STAR,
3682 { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // SLASH,
3683 { &GDScriptParser::parse_get_node, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT,
3684 // Assignment
3685 { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // EQUAL,
3686 { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PLUS_EQUAL,
3687 { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // MINUS_EQUAL,
3688 { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // STAR_EQUAL,
3689 { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // STAR_STAR_EQUAL,
3690 { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // SLASH_EQUAL,
3691 { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PERCENT_EQUAL,
3692 { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // LESS_LESS_EQUAL,
3693 { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // GREATER_GREATER_EQUAL,
3694 { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // AMPERSAND_EQUAL,
3695 { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PIPE_EQUAL,
3696 { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // CARET_EQUAL,
3697 // Control flow
3698 { nullptr, &GDScriptParser::parse_ternary_operator, PREC_TERNARY }, // IF,
3699 { nullptr, nullptr, PREC_NONE }, // ELIF,
3700 { nullptr, nullptr, PREC_NONE }, // ELSE,
3701 { nullptr, nullptr, PREC_NONE }, // FOR,
3702 { nullptr, nullptr, PREC_NONE }, // WHILE,
3703 { nullptr, nullptr, PREC_NONE }, // BREAK,
3704 { nullptr, nullptr, PREC_NONE }, // CONTINUE,
3705 { nullptr, nullptr, PREC_NONE }, // PASS,
3706 { nullptr, nullptr, PREC_NONE }, // RETURN,
3707 { nullptr, nullptr, PREC_NONE }, // MATCH,
3708 // Keywords
3709 { nullptr, &GDScriptParser::parse_cast, PREC_CAST }, // AS,
3710 { nullptr, nullptr, PREC_NONE }, // ASSERT,
3711 { &GDScriptParser::parse_await, nullptr, PREC_NONE }, // AWAIT,
3712 { nullptr, nullptr, PREC_NONE }, // BREAKPOINT,
3713 { nullptr, nullptr, PREC_NONE }, // CLASS,
3714 { nullptr, nullptr, PREC_NONE }, // CLASS_NAME,
3715 { nullptr, nullptr, PREC_NONE }, // CONST,
3716 { nullptr, nullptr, PREC_NONE }, // ENUM,
3717 { nullptr, nullptr, PREC_NONE }, // EXTENDS,
3718 { &GDScriptParser::parse_lambda, nullptr, PREC_NONE }, // FUNC,
3719 { nullptr, &GDScriptParser::parse_binary_operator, PREC_CONTENT_TEST }, // IN,
3720 { nullptr, &GDScriptParser::parse_type_test, PREC_TYPE_TEST }, // IS,
3721 { nullptr, nullptr, PREC_NONE }, // NAMESPACE,
3722 { &GDScriptParser::parse_preload, nullptr, PREC_NONE }, // PRELOAD,
3723 { &GDScriptParser::parse_self, nullptr, PREC_NONE }, // SELF,
3724 { nullptr, nullptr, PREC_NONE }, // SIGNAL,
3725 { nullptr, nullptr, PREC_NONE }, // STATIC,
3726 { &GDScriptParser::parse_call, nullptr, PREC_NONE }, // SUPER,
3727 { nullptr, nullptr, PREC_NONE }, // TRAIT,
3728 { nullptr, nullptr, PREC_NONE }, // VAR,
3729 { nullptr, nullptr, PREC_NONE }, // VOID,
3730 { &GDScriptParser::parse_yield, nullptr, PREC_NONE }, // YIELD,
3731 // Punctuation
3732 { &GDScriptParser::parse_array, &GDScriptParser::parse_subscript, PREC_SUBSCRIPT }, // BRACKET_OPEN,
3733 { nullptr, nullptr, PREC_NONE }, // BRACKET_CLOSE,
3734 { &GDScriptParser::parse_dictionary, nullptr, PREC_NONE }, // BRACE_OPEN,
3735 { nullptr, nullptr, PREC_NONE }, // BRACE_CLOSE,
3736 { &GDScriptParser::parse_grouping, &GDScriptParser::parse_call, PREC_CALL }, // PARENTHESIS_OPEN,
3737 { nullptr, nullptr, PREC_NONE }, // PARENTHESIS_CLOSE,
3738 { nullptr, nullptr, PREC_NONE }, // COMMA,
3739 { nullptr, nullptr, PREC_NONE }, // SEMICOLON,
3740 { nullptr, &GDScriptParser::parse_attribute, PREC_ATTRIBUTE }, // PERIOD,
3741 { nullptr, nullptr, PREC_NONE }, // PERIOD_PERIOD,
3742 { nullptr, nullptr, PREC_NONE }, // COLON,
3743 { &GDScriptParser::parse_get_node, nullptr, PREC_NONE }, // DOLLAR,
3744 { nullptr, nullptr, PREC_NONE }, // FORWARD_ARROW,
3745 { nullptr, nullptr, PREC_NONE }, // UNDERSCORE,
3746 // Whitespace
3747 { nullptr, nullptr, PREC_NONE }, // NEWLINE,
3748 { nullptr, nullptr, PREC_NONE }, // INDENT,
3749 { nullptr, nullptr, PREC_NONE }, // DEDENT,
3750 // Constants
3751 { &GDScriptParser::parse_builtin_constant, nullptr, PREC_NONE }, // CONST_PI,
3752 { &GDScriptParser::parse_builtin_constant, nullptr, PREC_NONE }, // CONST_TAU,
3753 { &GDScriptParser::parse_builtin_constant, nullptr, PREC_NONE }, // CONST_INF,
3754 { &GDScriptParser::parse_builtin_constant, nullptr, PREC_NONE }, // CONST_NAN,
3755 // Error message improvement
3756 { nullptr, nullptr, PREC_NONE }, // VCS_CONFLICT_MARKER,
3757 { nullptr, nullptr, PREC_NONE }, // BACKTICK,
3758 { nullptr, &GDScriptParser::parse_invalid_token, PREC_CAST }, // QUESTION_MARK,
3759 // Special
3760 { nullptr, nullptr, PREC_NONE }, // ERROR,
3761 { nullptr, nullptr, PREC_NONE }, // TK_EOF,
3762 };
3763 /* clang-format on */
3764 // Avoid desync.
3765 static_assert(sizeof(rules) / sizeof(rules[0]) == GDScriptTokenizer::Token::TK_MAX, "Amount of parse rules don't match the amount of token types.");
3766
3767 // Let's assume this is never invalid, since nothing generates a TK_MAX.
3768 return &rules[p_token_type];
3769}
3770
3771bool GDScriptParser::SuiteNode::has_local(const StringName &p_name) const {
3772 if (locals_indices.has(p_name)) {
3773 return true;
3774 }
3775 if (parent_block != nullptr) {
3776 return parent_block->has_local(p_name);
3777 }
3778 return false;
3779}
3780
3781const GDScriptParser::SuiteNode::Local &GDScriptParser::SuiteNode::get_local(const StringName &p_name) const {
3782 if (locals_indices.has(p_name)) {
3783 return locals[locals_indices[p_name]];
3784 }
3785 if (parent_block != nullptr) {
3786 return parent_block->get_local(p_name);
3787 }
3788 return empty;
3789}
3790
3791bool GDScriptParser::AnnotationNode::apply(GDScriptParser *p_this, Node *p_target) {
3792 if (is_applied) {
3793 return true;
3794 }
3795 is_applied = true;
3796 return (p_this->*(p_this->valid_annotations[name].apply))(this, p_target);
3797}
3798
3799bool GDScriptParser::AnnotationNode::applies_to(uint32_t p_target_kinds) const {
3800 return (info->target_kind & p_target_kinds) > 0;
3801}
3802
3803bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation) {
3804 ERR_FAIL_COND_V_MSG(!valid_annotations.has(p_annotation->name), false, vformat(R"(Annotation "%s" not found to validate.)", p_annotation->name));
3805
3806 const MethodInfo &info = valid_annotations[p_annotation->name].info;
3807
3808 if (((info.flags & METHOD_FLAG_VARARG) == 0) && p_annotation->arguments.size() > info.arguments.size()) {
3809 push_error(vformat(R"(Annotation "%s" requires at most %d arguments, but %d were given.)", p_annotation->name, info.arguments.size(), p_annotation->arguments.size()));
3810 return false;
3811 }
3812
3813 if (p_annotation->arguments.size() < info.arguments.size() - info.default_arguments.size()) {
3814 push_error(vformat(R"(Annotation "%s" requires at least %d arguments, but %d were given.)", p_annotation->name, info.arguments.size() - info.default_arguments.size(), p_annotation->arguments.size()));
3815 return false;
3816 }
3817
3818 // `@icon`'s argument needs to be resolved in the parser. See GH-72444.
3819 if (p_annotation->name == SNAME("@icon")) {
3820 ExpressionNode *argument = p_annotation->arguments[0];
3821
3822 if (argument->type != Node::LITERAL) {
3823 push_error(R"(Argument 1 of annotation "@icon" must be a string literal.)", argument);
3824 return false;
3825 }
3826
3827 Variant value = static_cast<LiteralNode *>(argument)->value;
3828
3829 if (value.get_type() != Variant::STRING) {
3830 push_error(R"(Argument 1 of annotation "@icon" must be a string literal.)", argument);
3831 return false;
3832 }
3833
3834 p_annotation->resolved_arguments.push_back(value);
3835 }
3836
3837 // For other annotations, see `GDScriptAnalyzer::resolve_annotation()`.
3838
3839 return true;
3840}
3841
3842bool GDScriptParser::tool_annotation(const AnnotationNode *p_annotation, Node *p_node) {
3843#ifdef DEBUG_ENABLED
3844 if (this->_is_tool) {
3845 push_error(R"("@tool" annotation can only be used once.)", p_annotation);
3846 return false;
3847 }
3848#endif // DEBUG_ENABLED
3849 this->_is_tool = true;
3850 return true;
3851}
3852
3853bool GDScriptParser::icon_annotation(const AnnotationNode *p_annotation, Node *p_node) {
3854 ERR_FAIL_COND_V_MSG(p_node->type != Node::CLASS, false, R"("@icon" annotation can only be applied to classes.)");
3855 ERR_FAIL_COND_V(p_annotation->resolved_arguments.is_empty(), false);
3856
3857 ClassNode *p_class = static_cast<ClassNode *>(p_node);
3858 String path = p_annotation->resolved_arguments[0];
3859
3860#ifdef DEBUG_ENABLED
3861 if (!p_class->icon_path.is_empty()) {
3862 push_error(R"("@icon" annotation can only be used once.)", p_annotation);
3863 return false;
3864 }
3865 if (path.is_empty()) {
3866 push_error(R"("@icon" annotation argument must contain the path to the icon.)", p_annotation->arguments[0]);
3867 return false;
3868 }
3869#endif // DEBUG_ENABLED
3870
3871 p_class->icon_path = path;
3872
3873 if (path.is_empty() || path.is_absolute_path()) {
3874 p_class->simplified_icon_path = path.simplify_path();
3875 } else if (path.is_relative_path()) {
3876 p_class->simplified_icon_path = script_path.get_base_dir().path_join(path).simplify_path();
3877 } else {
3878 p_class->simplified_icon_path = path;
3879 }
3880
3881 return true;
3882}
3883
3884bool GDScriptParser::onready_annotation(const AnnotationNode *p_annotation, Node *p_node) {
3885 ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, R"("@onready" annotation can only be applied to class variables.)");
3886
3887 if (current_class && !ClassDB::is_parent_class(current_class->get_datatype().native_type, SNAME("Node"))) {
3888 push_error(R"("@onready" can only be used in classes that inherit "Node".)", p_annotation);
3889 }
3890
3891 VariableNode *variable = static_cast<VariableNode *>(p_node);
3892 if (variable->is_static) {
3893 push_error(R"("@onready" annotation cannot be applied to a static variable.)", p_annotation);
3894 return false;
3895 }
3896 if (variable->onready) {
3897 push_error(R"("@onready" annotation can only be used once per variable.)", p_annotation);
3898 return false;
3899 }
3900 variable->onready = true;
3901 current_class->onready_used = true;
3902 return true;
3903}
3904
3905template <PropertyHint t_hint, Variant::Type t_type>
3906bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node *p_node) {
3907 ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
3908
3909 VariableNode *variable = static_cast<VariableNode *>(p_node);
3910 if (variable->is_static) {
3911 push_error(vformat(R"(Annotation "%s" cannot be applied to a static variable.)", p_annotation->name), p_annotation);
3912 return false;
3913 }
3914 if (variable->exported) {
3915 push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation);
3916 return false;
3917 }
3918
3919 variable->exported = true;
3920
3921 variable->export_info.type = t_type;
3922 variable->export_info.hint = t_hint;
3923
3924 String hint_string;
3925 for (int i = 0; i < p_annotation->resolved_arguments.size(); i++) {
3926 String arg_string = String(p_annotation->resolved_arguments[i]);
3927
3928 if (p_annotation->name != SNAME("@export_placeholder")) {
3929 if (arg_string.is_empty()) {
3930 push_error(vformat(R"(Argument %d of annotation "%s" is empty.)", i + 1, p_annotation->name), p_annotation->arguments[i]);
3931 return false;
3932 }
3933 if (arg_string.contains(",")) {
3934 push_error(vformat(R"(Argument %d of annotation "%s" contains a comma. Use separate arguments instead.)", i + 1, p_annotation->name), p_annotation->arguments[i]);
3935 return false;
3936 }
3937 }
3938
3939 // WARNING: Do not merge with the previous `if` because there `!=`, not `==`!
3940 if (p_annotation->name == SNAME("@export_flags")) {
3941 const int64_t max_flags = 32;
3942 Vector<String> t = arg_string.split(":", true, 1);
3943 if (t[0].is_empty()) {
3944 push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": Expected flag name.)", i + 1), p_annotation->arguments[i]);
3945 return false;
3946 }
3947 if (t.size() == 2) {
3948 if (t[1].is_empty()) {
3949 push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": Expected flag value.)", i + 1), p_annotation->arguments[i]);
3950 return false;
3951 }
3952 if (!t[1].is_valid_int()) {
3953 push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": The flag value must be a valid integer.)", i + 1), p_annotation->arguments[i]);
3954 return false;
3955 }
3956 int64_t value = t[1].to_int();
3957 if (value < 1 || value >= (1LL << max_flags)) {
3958 push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": The flag value must be at least 1 and at most 2 ** %d - 1.)", i + 1, max_flags), p_annotation->arguments[i]);
3959 return false;
3960 }
3961 } else if (i >= max_flags) {
3962 push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": Starting from argument %d, the flag value must be specified explicitly.)", i + 1, max_flags + 1), p_annotation->arguments[i]);
3963 return false;
3964 }
3965 } else if (p_annotation->name == SNAME("@export_node_path")) {
3966 String native_class = arg_string;
3967 if (ScriptServer::is_global_class(arg_string)) {
3968 native_class = ScriptServer::get_global_class_native_base(arg_string);
3969 }
3970 if (!ClassDB::class_exists(native_class)) {
3971 push_error(vformat(R"(Invalid argument %d of annotation "@export_node_path": The class "%s" was not found in the global scope.)", i + 1, arg_string), p_annotation->arguments[i]);
3972 return false;
3973 } else if (!ClassDB::is_parent_class(native_class, SNAME("Node"))) {
3974 push_error(vformat(R"(Invalid argument %d of annotation "@export_node_path": The class "%s" does not inherit "Node".)", i + 1, arg_string), p_annotation->arguments[i]);
3975 return false;
3976 }
3977 }
3978
3979 if (i > 0) {
3980 hint_string += ",";
3981 }
3982 hint_string += arg_string;
3983 }
3984
3985 variable->export_info.hint_string = hint_string;
3986
3987 // This is called after the analyzer is done finding the type, so this should be set here.
3988 DataType export_type = variable->get_datatype();
3989
3990 if (p_annotation->name == SNAME("@export_range")) {
3991 if (export_type.builtin_type == Variant::INT) {
3992 variable->export_info.type = Variant::INT;
3993 }
3994 } else if (p_annotation->name == SNAME("@export_multiline")) {
3995 if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type()) {
3996 DataType inner_type = export_type.get_container_element_type();
3997 if (inner_type.builtin_type != Variant::STRING) {
3998 push_error(vformat(R"("%s" annotation on arrays requires a string type but type "%s" was given instead.)", p_annotation->name.operator String(), inner_type.to_string()), variable);
3999 return false;
4000 }
4001
4002 String hint_prefix = itos(inner_type.builtin_type) + "/" + itos(variable->export_info.hint);
4003 variable->export_info.hint = PROPERTY_HINT_TYPE_STRING;
4004 variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
4005 variable->export_info.type = Variant::ARRAY;
4006
4007 return true;
4008 } else if (export_type.builtin_type == Variant::DICTIONARY) {
4009 variable->export_info.type = Variant::DICTIONARY;
4010
4011 return true;
4012 } else if (export_type.builtin_type == Variant::PACKED_STRING_ARRAY) {
4013 String hint_prefix = itos(Variant::STRING) + "/" + itos(variable->export_info.hint);
4014 variable->export_info.hint = PROPERTY_HINT_TYPE_STRING;
4015 variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
4016 variable->export_info.type = Variant::PACKED_STRING_ARRAY;
4017
4018 return true;
4019 }
4020 }
4021
4022 // WARNING: Do not merge with the previous `else if`! Otherwise `else` (default variable type check)
4023 // will not work for the above annotations. `@export` and `@export_enum` validate the type separately.
4024 if (p_annotation->name == SNAME("@export")) {
4025 if (variable->datatype_specifier == nullptr && variable->initializer == nullptr) {
4026 push_error(R"(Cannot use simple "@export" annotation with variable without type or initializer, since type can't be inferred.)", p_annotation);
4027 return false;
4028 }
4029
4030 bool is_array = false;
4031
4032 if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type()) {
4033 export_type = export_type.get_container_element_type(); // Use inner type for.
4034 is_array = true;
4035 }
4036
4037 if (export_type.is_variant() || export_type.has_no_type()) {
4038 push_error(R"(Cannot use simple "@export" annotation because the type of the initialized value can't be inferred.)", p_annotation);
4039 return false;
4040 }
4041
4042 switch (export_type.kind) {
4043 case GDScriptParser::DataType::BUILTIN:
4044 variable->export_info.type = export_type.builtin_type;
4045 variable->export_info.hint = PROPERTY_HINT_NONE;
4046 variable->export_info.hint_string = Variant::get_type_name(export_type.builtin_type);
4047 break;
4048 case GDScriptParser::DataType::NATIVE:
4049 if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) {
4050 variable->export_info.type = Variant::OBJECT;
4051 variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
4052 variable->export_info.hint_string = export_type.native_type;
4053 } else if (ClassDB::is_parent_class(export_type.native_type, SNAME("Node"))) {
4054 variable->export_info.type = Variant::OBJECT;
4055 variable->export_info.hint = PROPERTY_HINT_NODE_TYPE;
4056 variable->export_info.hint_string = export_type.native_type;
4057 } else {
4058 push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", variable);
4059 return false;
4060 }
4061 break;
4062 case GDScriptParser::DataType::CLASS:
4063 if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) {
4064 variable->export_info.type = Variant::OBJECT;
4065 variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
4066 variable->export_info.hint_string = export_type.to_string();
4067 } else if (ClassDB::is_parent_class(export_type.native_type, SNAME("Node"))) {
4068 variable->export_info.type = Variant::OBJECT;
4069 variable->export_info.hint = PROPERTY_HINT_NODE_TYPE;
4070 variable->export_info.hint_string = export_type.to_string();
4071 } else {
4072 push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", variable);
4073 return false;
4074 }
4075
4076 break;
4077 case GDScriptParser::DataType::SCRIPT: {
4078 StringName class_name;
4079 StringName native_base;
4080 if (export_type.script_type.is_valid()) {
4081 class_name = export_type.script_type->get_language()->get_global_class_name(export_type.script_type->get_path());
4082 native_base = export_type.script_type->get_instance_base_type();
4083 }
4084 if (class_name == StringName()) {
4085 Ref<Script> script = ResourceLoader::load(export_type.script_path, SNAME("Script"));
4086 if (script.is_valid()) {
4087 class_name = script->get_language()->get_global_class_name(export_type.script_path);
4088 native_base = script->get_instance_base_type();
4089 }
4090 }
4091 if (class_name != StringName() && native_base != StringName() && ClassDB::is_parent_class(native_base, SNAME("Resource"))) {
4092 variable->export_info.type = Variant::OBJECT;
4093 variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
4094 variable->export_info.hint_string = class_name;
4095 }
4096 } break;
4097 case GDScriptParser::DataType::ENUM: {
4098 variable->export_info.type = Variant::INT;
4099 variable->export_info.hint = PROPERTY_HINT_ENUM;
4100
4101 String enum_hint_string;
4102 bool first = true;
4103 for (const KeyValue<StringName, int64_t> &E : export_type.enum_values) {
4104 if (!first) {
4105 enum_hint_string += ",";
4106 } else {
4107 first = false;
4108 }
4109 enum_hint_string += E.key.operator String().capitalize().xml_escape();
4110 enum_hint_string += ":";
4111 enum_hint_string += String::num_int64(E.value).xml_escape();
4112 }
4113
4114 variable->export_info.hint_string = enum_hint_string;
4115 variable->export_info.usage |= PROPERTY_USAGE_CLASS_IS_ENUM;
4116 variable->export_info.class_name = String(export_type.native_type).replace("::", ".");
4117 } break;
4118 default:
4119 push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", variable);
4120 break;
4121 }
4122
4123 if (is_array) {
4124 String hint_prefix = itos(variable->export_info.type);
4125 if (variable->export_info.hint) {
4126 hint_prefix += "/" + itos(variable->export_info.hint);
4127 }
4128 variable->export_info.hint = PROPERTY_HINT_TYPE_STRING;
4129 variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
4130 variable->export_info.type = Variant::ARRAY;
4131 }
4132 } else if (p_annotation->name == SNAME("@export_enum")) {
4133 Variant::Type enum_type = Variant::INT;
4134
4135 if (export_type.kind == DataType::BUILTIN && export_type.builtin_type == Variant::STRING) {
4136 enum_type = Variant::STRING;
4137 } else if (export_type.is_variant() && variable->initializer != nullptr) {
4138 DataType initializer_type = variable->initializer->get_datatype();
4139 if (initializer_type.kind == DataType::BUILTIN && initializer_type.builtin_type == Variant::STRING) {
4140 enum_type = Variant::STRING;
4141 }
4142 }
4143
4144 variable->export_info.type = enum_type;
4145
4146 if (!export_type.is_variant() && (export_type.kind != DataType::BUILTIN || export_type.builtin_type != enum_type)) {
4147 push_error(vformat(R"("@export_enum" annotation requires a variable of type "int" or "String" but type "%s" was given instead.)", export_type.to_string()), variable);
4148 return false;
4149 }
4150 } else {
4151 // Validate variable type with export.
4152 if (!export_type.is_variant() && (export_type.kind != DataType::BUILTIN || export_type.builtin_type != t_type)) {
4153 // Allow float/int conversion.
4154 if ((t_type != Variant::FLOAT || export_type.builtin_type != Variant::INT) && (t_type != Variant::INT || export_type.builtin_type != Variant::FLOAT)) {
4155 push_error(vformat(R"("%s" annotation requires a variable of type "%s" but type "%s" was given instead.)", p_annotation->name.operator String(), Variant::get_type_name(t_type), export_type.to_string()), variable);
4156 return false;
4157 }
4158 }
4159 }
4160
4161 return true;
4162}
4163
4164template <PropertyUsageFlags t_usage>
4165bool GDScriptParser::export_group_annotations(const AnnotationNode *p_annotation, Node *p_node) {
4166 AnnotationNode *annotation = const_cast<AnnotationNode *>(p_annotation);
4167
4168 if (annotation->resolved_arguments.is_empty()) {
4169 return false;
4170 }
4171
4172 annotation->export_info.name = annotation->resolved_arguments[0];
4173
4174 switch (t_usage) {
4175 case PROPERTY_USAGE_CATEGORY: {
4176 annotation->export_info.usage = t_usage;
4177 } break;
4178
4179 case PROPERTY_USAGE_GROUP: {
4180 annotation->export_info.usage = t_usage;
4181 if (annotation->resolved_arguments.size() == 2) {
4182 annotation->export_info.hint_string = annotation->resolved_arguments[1];
4183 }
4184 } break;
4185
4186 case PROPERTY_USAGE_SUBGROUP: {
4187 annotation->export_info.usage = t_usage;
4188 if (annotation->resolved_arguments.size() == 2) {
4189 annotation->export_info.hint_string = annotation->resolved_arguments[1];
4190 }
4191 } break;
4192 }
4193
4194 return true;
4195}
4196
4197bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Node *p_node) {
4198#ifdef DEBUG_ENABLED
4199 bool has_error = false;
4200 for (const Variant &warning_name : p_annotation->resolved_arguments) {
4201 GDScriptWarning::Code warning = GDScriptWarning::get_code_from_name(String(warning_name).to_upper());
4202 if (warning == GDScriptWarning::WARNING_MAX) {
4203 push_error(vformat(R"(Invalid warning name: "%s".)", warning_name), p_annotation);
4204 has_error = true;
4205 } else {
4206 p_node->ignored_warnings.push_back(warning);
4207 }
4208 }
4209
4210 return !has_error;
4211
4212#else // ! DEBUG_ENABLED
4213 // Only available in debug builds.
4214 return true;
4215#endif // DEBUG_ENABLED
4216}
4217
4218bool GDScriptParser::rpc_annotation(const AnnotationNode *p_annotation, Node *p_node) {
4219 ERR_FAIL_COND_V_MSG(p_node->type != Node::FUNCTION, false, vformat(R"("%s" annotation can only be applied to functions.)", p_annotation->name));
4220
4221 FunctionNode *function = static_cast<FunctionNode *>(p_node);
4222 if (function->rpc_config.get_type() != Variant::NIL) {
4223 push_error(R"(RPC annotations can only be used once per function.)", p_annotation);
4224 return false;
4225 }
4226
4227 Dictionary rpc_config;
4228 rpc_config["rpc_mode"] = MultiplayerAPI::RPC_MODE_AUTHORITY;
4229 if (!p_annotation->resolved_arguments.is_empty()) {
4230 unsigned char locality_args = 0;
4231 unsigned char permission_args = 0;
4232 unsigned char transfer_mode_args = 0;
4233
4234 for (int i = 0; i < p_annotation->resolved_arguments.size(); i++) {
4235 if (i == 3) {
4236 rpc_config["channel"] = p_annotation->resolved_arguments[i].operator int();
4237 continue;
4238 }
4239
4240 String arg = p_annotation->resolved_arguments[i].operator String();
4241 if (arg == "call_local") {
4242 locality_args++;
4243 rpc_config["call_local"] = true;
4244 } else if (arg == "call_remote") {
4245 locality_args++;
4246 rpc_config["call_local"] = false;
4247 } else if (arg == "any_peer") {
4248 permission_args++;
4249 rpc_config["rpc_mode"] = MultiplayerAPI::RPC_MODE_ANY_PEER;
4250 } else if (arg == "authority") {
4251 permission_args++;
4252 rpc_config["rpc_mode"] = MultiplayerAPI::RPC_MODE_AUTHORITY;
4253 } else if (arg == "reliable") {
4254 transfer_mode_args++;
4255 rpc_config["transfer_mode"] = MultiplayerPeer::TRANSFER_MODE_RELIABLE;
4256 } else if (arg == "unreliable") {
4257 transfer_mode_args++;
4258 rpc_config["transfer_mode"] = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE;
4259 } else if (arg == "unreliable_ordered") {
4260 transfer_mode_args++;
4261 rpc_config["transfer_mode"] = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE_ORDERED;
4262 } else {
4263 push_error(R"(Invalid RPC argument. Must be one of: "call_local"/"call_remote" (local calls), "any_peer"/"authority" (permission), "reliable"/"unreliable"/"unreliable_ordered" (transfer mode).)", p_annotation);
4264 }
4265 }
4266
4267 if (locality_args > 1) {
4268 push_error(R"(Invalid RPC config. The locality ("call_local"/"call_remote") must be specified no more than once.)", p_annotation);
4269 } else if (permission_args > 1) {
4270 push_error(R"(Invalid RPC config. The permission ("any_peer"/"authority") must be specified no more than once.)", p_annotation);
4271 } else if (transfer_mode_args > 1) {
4272 push_error(R"(Invalid RPC config. The transfer mode ("reliable"/"unreliable"/"unreliable_ordered") must be specified no more than once.)", p_annotation);
4273 }
4274 }
4275 function->rpc_config = rpc_config;
4276 return true;
4277}
4278
4279bool GDScriptParser::static_unload_annotation(const AnnotationNode *p_annotation, Node *p_target) {
4280 ERR_FAIL_COND_V_MSG(p_target->type != Node::CLASS, false, vformat(R"("%s" annotation can only be applied to classes.)", p_annotation->name));
4281 ClassNode *p_class = static_cast<ClassNode *>(p_target);
4282 if (p_class->annotated_static_unload) {
4283 push_error(vformat(R"("%s" annotation can only be used once per script.)", p_annotation->name), p_annotation);
4284 return false;
4285 }
4286 p_class->annotated_static_unload = true;
4287 return true;
4288}
4289
4290GDScriptParser::DataType GDScriptParser::SuiteNode::Local::get_datatype() const {
4291 switch (type) {
4292 case CONSTANT:
4293 return constant->get_datatype();
4294 case VARIABLE:
4295 return variable->get_datatype();
4296 case PARAMETER:
4297 return parameter->get_datatype();
4298 case FOR_VARIABLE:
4299 case PATTERN_BIND:
4300 return bind->get_datatype();
4301 case UNDEFINED:
4302 return DataType();
4303 }
4304 return DataType();
4305}
4306
4307String GDScriptParser::SuiteNode::Local::get_name() const {
4308 switch (type) {
4309 case SuiteNode::Local::PARAMETER:
4310 return "parameter";
4311 case SuiteNode::Local::CONSTANT:
4312 return "constant";
4313 case SuiteNode::Local::VARIABLE:
4314 return "variable";
4315 case SuiteNode::Local::FOR_VARIABLE:
4316 return "for loop iterator";
4317 case SuiteNode::Local::PATTERN_BIND:
4318 return "pattern bind";
4319 case SuiteNode::Local::UNDEFINED:
4320 return "<undefined>";
4321 default:
4322 return String();
4323 }
4324}
4325
4326String GDScriptParser::DataType::to_string() const {
4327 switch (kind) {
4328 case VARIANT:
4329 return "Variant";
4330 case BUILTIN:
4331 if (builtin_type == Variant::NIL) {
4332 return "null";
4333 }
4334 if (builtin_type == Variant::ARRAY && has_container_element_type()) {
4335 return vformat("Array[%s]", container_element_type->to_string());
4336 }
4337 return Variant::get_type_name(builtin_type);
4338 case NATIVE:
4339 if (is_meta_type) {
4340 return GDScriptNativeClass::get_class_static();
4341 }
4342 return native_type.operator String();
4343 case CLASS:
4344 if (class_type->identifier != nullptr) {
4345 return class_type->identifier->name.operator String();
4346 }
4347 return class_type->fqcn;
4348 case SCRIPT: {
4349 if (is_meta_type) {
4350 return script_type != nullptr ? script_type->get_class_name().operator String() : "";
4351 }
4352 String name = script_type != nullptr ? script_type->get_name() : "";
4353 if (!name.is_empty()) {
4354 return name;
4355 }
4356 name = script_path;
4357 if (!name.is_empty()) {
4358 return name;
4359 }
4360 return native_type.operator String();
4361 }
4362 case ENUM: {
4363 // native_type contains either the native class defining the enum
4364 // or the fully qualified class name of the script defining the enum
4365 return String(native_type).get_file(); // Remove path, keep filename
4366 }
4367 case RESOLVING:
4368 case UNRESOLVED:
4369 return "<unresolved type>";
4370 }
4371
4372 ERR_FAIL_V_MSG("<unresolved type>", "Kind set outside the enum range.");
4373}
4374
4375PropertyInfo GDScriptParser::DataType::to_property_info(const String &p_name) const {
4376 PropertyInfo result;
4377 result.name = p_name;
4378 result.usage = PROPERTY_USAGE_NONE;
4379
4380 if (!is_hard_type()) {
4381 result.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
4382 return result;
4383 }
4384
4385 switch (kind) {
4386 case BUILTIN:
4387 result.type = builtin_type;
4388 if (builtin_type == Variant::ARRAY && has_container_element_type()) {
4389 const DataType *elem_type = container_element_type;
4390 switch (elem_type->kind) {
4391 case BUILTIN:
4392 result.hint = PROPERTY_HINT_ARRAY_TYPE;
4393 result.hint_string = Variant::get_type_name(elem_type->builtin_type);
4394 break;
4395 case NATIVE:
4396 result.hint = PROPERTY_HINT_ARRAY_TYPE;
4397 result.hint_string = elem_type->native_type;
4398 break;
4399 case SCRIPT:
4400 result.hint = PROPERTY_HINT_ARRAY_TYPE;
4401 if (elem_type->script_type.is_valid() && elem_type->script_type->get_global_name() != StringName()) {
4402 result.hint_string = elem_type->script_type->get_global_name();
4403 } else {
4404 result.hint_string = elem_type->native_type;
4405 }
4406 break;
4407 case CLASS:
4408 result.hint = PROPERTY_HINT_ARRAY_TYPE;
4409 if (elem_type->class_type != nullptr && elem_type->class_type->get_global_name() != StringName()) {
4410 result.hint_string = elem_type->class_type->get_global_name();
4411 } else {
4412 result.hint_string = elem_type->native_type;
4413 }
4414 break;
4415 case ENUM:
4416 result.hint = PROPERTY_HINT_ARRAY_TYPE;
4417 result.hint_string = String(elem_type->native_type).replace("::", ".");
4418 break;
4419 case VARIANT:
4420 case RESOLVING:
4421 case UNRESOLVED:
4422 break;
4423 }
4424 }
4425 break;
4426 case NATIVE:
4427 result.type = Variant::OBJECT;
4428 if (is_meta_type) {
4429 result.class_name = GDScriptNativeClass::get_class_static();
4430 } else {
4431 result.class_name = native_type;
4432 }
4433 break;
4434 case SCRIPT:
4435 result.type = Variant::OBJECT;
4436 if (is_meta_type) {
4437 result.class_name = script_type.is_valid() ? script_type->get_class() : Script::get_class_static();
4438 } else if (script_type.is_valid() && script_type->get_global_name() != StringName()) {
4439 result.class_name = script_type->get_global_name();
4440 } else {
4441 result.class_name = native_type;
4442 }
4443 break;
4444 case CLASS:
4445 result.type = Variant::OBJECT;
4446 if (is_meta_type) {
4447 result.class_name = GDScript::get_class_static();
4448 } else if (class_type != nullptr && class_type->get_global_name() != StringName()) {
4449 result.class_name = class_type->get_global_name();
4450 } else {
4451 result.class_name = native_type;
4452 }
4453 break;
4454 case ENUM:
4455 if (is_meta_type) {
4456 result.type = Variant::DICTIONARY;
4457 } else {
4458 result.type = Variant::INT;
4459 result.usage |= PROPERTY_USAGE_CLASS_IS_ENUM;
4460 result.class_name = String(native_type).replace("::", ".");
4461 }
4462 break;
4463 case VARIANT:
4464 case RESOLVING:
4465 case UNRESOLVED:
4466 result.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
4467 break;
4468 }
4469
4470 return result;
4471}
4472
4473static Variant::Type _variant_type_to_typed_array_element_type(Variant::Type p_type) {
4474 switch (p_type) {
4475 case Variant::PACKED_BYTE_ARRAY:
4476 case Variant::PACKED_INT32_ARRAY:
4477 case Variant::PACKED_INT64_ARRAY:
4478 return Variant::INT;
4479 case Variant::PACKED_FLOAT32_ARRAY:
4480 case Variant::PACKED_FLOAT64_ARRAY:
4481 return Variant::FLOAT;
4482 case Variant::PACKED_STRING_ARRAY:
4483 return Variant::STRING;
4484 case Variant::PACKED_VECTOR2_ARRAY:
4485 return Variant::VECTOR2;
4486 case Variant::PACKED_VECTOR3_ARRAY:
4487 return Variant::VECTOR3;
4488 case Variant::PACKED_COLOR_ARRAY:
4489 return Variant::COLOR;
4490 default:
4491 return Variant::NIL;
4492 }
4493}
4494
4495bool GDScriptParser::DataType::is_typed_container_type() const {
4496 return kind == GDScriptParser::DataType::BUILTIN && _variant_type_to_typed_array_element_type(builtin_type) != Variant::NIL;
4497}
4498
4499GDScriptParser::DataType GDScriptParser::DataType::get_typed_container_type() const {
4500 GDScriptParser::DataType type;
4501 type.kind = GDScriptParser::DataType::BUILTIN;
4502 type.builtin_type = _variant_type_to_typed_array_element_type(builtin_type);
4503 return type;
4504}
4505
4506void GDScriptParser::complete_extents(Node *p_node) {
4507 while (!nodes_in_progress.is_empty() && nodes_in_progress.back()->get() != p_node) {
4508 ERR_PRINT("Parser bug: Mismatch in extents tracking stack.");
4509 nodes_in_progress.pop_back();
4510 }
4511 if (nodes_in_progress.is_empty()) {
4512 ERR_PRINT("Parser bug: Extents tracking stack is empty.");
4513 } else {
4514 nodes_in_progress.pop_back();
4515 }
4516}
4517
4518void GDScriptParser::update_extents(Node *p_node) {
4519 p_node->end_line = previous.end_line;
4520 p_node->end_column = previous.end_column;
4521 p_node->leftmost_column = MIN(p_node->leftmost_column, previous.leftmost_column);
4522 p_node->rightmost_column = MAX(p_node->rightmost_column, previous.rightmost_column);
4523}
4524
4525void GDScriptParser::reset_extents(Node *p_node, GDScriptTokenizer::Token p_token) {
4526 p_node->start_line = p_token.start_line;
4527 p_node->end_line = p_token.end_line;
4528 p_node->start_column = p_token.start_column;
4529 p_node->end_column = p_token.end_column;
4530 p_node->leftmost_column = p_token.leftmost_column;
4531 p_node->rightmost_column = p_token.rightmost_column;
4532}
4533
4534void GDScriptParser::reset_extents(Node *p_node, Node *p_from) {
4535 if (p_from == nullptr) {
4536 return;
4537 }
4538 p_node->start_line = p_from->start_line;
4539 p_node->end_line = p_from->end_line;
4540 p_node->start_column = p_from->start_column;
4541 p_node->end_column = p_from->end_column;
4542 p_node->leftmost_column = p_from->leftmost_column;
4543 p_node->rightmost_column = p_from->rightmost_column;
4544}
4545
4546/*---------- PRETTY PRINT FOR DEBUG ----------*/
4547
4548#ifdef DEBUG_ENABLED
4549
4550void GDScriptParser::TreePrinter::increase_indent() {
4551 indent_level++;
4552 indent = "";
4553 for (int i = 0; i < indent_level * 4; i++) {
4554 if (i % 4 == 0) {
4555 indent += "|";
4556 } else {
4557 indent += " ";
4558 }
4559 }
4560}
4561
4562void GDScriptParser::TreePrinter::decrease_indent() {
4563 indent_level--;
4564 indent = "";
4565 for (int i = 0; i < indent_level * 4; i++) {
4566 if (i % 4 == 0) {
4567 indent += "|";
4568 } else {
4569 indent += " ";
4570 }
4571 }
4572}
4573
4574void GDScriptParser::TreePrinter::push_line(const String &p_line) {
4575 if (!p_line.is_empty()) {
4576 push_text(p_line);
4577 }
4578 printed += "\n";
4579 pending_indent = true;
4580}
4581
4582void GDScriptParser::TreePrinter::push_text(const String &p_text) {
4583 if (pending_indent) {
4584 printed += indent;
4585 pending_indent = false;
4586 }
4587 printed += p_text;
4588}
4589
4590void GDScriptParser::TreePrinter::print_annotation(const AnnotationNode *p_annotation) {
4591 push_text(p_annotation->name);
4592 push_text(" (");
4593 for (int i = 0; i < p_annotation->arguments.size(); i++) {
4594 if (i > 0) {
4595 push_text(" , ");
4596 }
4597 print_expression(p_annotation->arguments[i]);
4598 }
4599 push_line(")");
4600}
4601
4602void GDScriptParser::TreePrinter::print_array(ArrayNode *p_array) {
4603 push_text("[ ");
4604 for (int i = 0; i < p_array->elements.size(); i++) {
4605 if (i > 0) {
4606 push_text(" , ");
4607 }
4608 print_expression(p_array->elements[i]);
4609 }
4610 push_text(" ]");
4611}
4612
4613void GDScriptParser::TreePrinter::print_assert(AssertNode *p_assert) {
4614 push_text("Assert ( ");
4615 print_expression(p_assert->condition);
4616 push_line(" )");
4617}
4618
4619void GDScriptParser::TreePrinter::print_assignment(AssignmentNode *p_assignment) {
4620 switch (p_assignment->assignee->type) {
4621 case Node::IDENTIFIER:
4622 print_identifier(static_cast<IdentifierNode *>(p_assignment->assignee));
4623 break;
4624 case Node::SUBSCRIPT:
4625 print_subscript(static_cast<SubscriptNode *>(p_assignment->assignee));
4626 break;
4627 default:
4628 break; // Unreachable.
4629 }
4630
4631 push_text(" ");
4632 switch (p_assignment->operation) {
4633 case AssignmentNode::OP_ADDITION:
4634 push_text("+");
4635 break;
4636 case AssignmentNode::OP_SUBTRACTION:
4637 push_text("-");
4638 break;
4639 case AssignmentNode::OP_MULTIPLICATION:
4640 push_text("*");
4641 break;
4642 case AssignmentNode::OP_DIVISION:
4643 push_text("/");
4644 break;
4645 case AssignmentNode::OP_MODULO:
4646 push_text("%");
4647 break;
4648 case AssignmentNode::OP_POWER:
4649 push_text("**");
4650 break;
4651 case AssignmentNode::OP_BIT_SHIFT_LEFT:
4652 push_text("<<");
4653 break;
4654 case AssignmentNode::OP_BIT_SHIFT_RIGHT:
4655 push_text(">>");
4656 break;
4657 case AssignmentNode::OP_BIT_AND:
4658 push_text("&");
4659 break;
4660 case AssignmentNode::OP_BIT_OR:
4661 push_text("|");
4662 break;
4663 case AssignmentNode::OP_BIT_XOR:
4664 push_text("^");
4665 break;
4666 case AssignmentNode::OP_NONE:
4667 break;
4668 }
4669 push_text("= ");
4670 print_expression(p_assignment->assigned_value);
4671 push_line();
4672}
4673
4674void GDScriptParser::TreePrinter::print_await(AwaitNode *p_await) {
4675 push_text("Await ");
4676 print_expression(p_await->to_await);
4677}
4678
4679void GDScriptParser::TreePrinter::print_binary_op(BinaryOpNode *p_binary_op) {
4680 // Surround in parenthesis for disambiguation.
4681 push_text("(");
4682 print_expression(p_binary_op->left_operand);
4683 switch (p_binary_op->operation) {
4684 case BinaryOpNode::OP_ADDITION:
4685 push_text(" + ");
4686 break;
4687 case BinaryOpNode::OP_SUBTRACTION:
4688 push_text(" - ");
4689 break;
4690 case BinaryOpNode::OP_MULTIPLICATION:
4691 push_text(" * ");
4692 break;
4693 case BinaryOpNode::OP_DIVISION:
4694 push_text(" / ");
4695 break;
4696 case BinaryOpNode::OP_MODULO:
4697 push_text(" % ");
4698 break;
4699 case BinaryOpNode::OP_POWER:
4700 push_text(" ** ");
4701 break;
4702 case BinaryOpNode::OP_BIT_LEFT_SHIFT:
4703 push_text(" << ");
4704 break;
4705 case BinaryOpNode::OP_BIT_RIGHT_SHIFT:
4706 push_text(" >> ");
4707 break;
4708 case BinaryOpNode::OP_BIT_AND:
4709 push_text(" & ");
4710 break;
4711 case BinaryOpNode::OP_BIT_OR:
4712 push_text(" | ");
4713 break;
4714 case BinaryOpNode::OP_BIT_XOR:
4715 push_text(" ^ ");
4716 break;
4717 case BinaryOpNode::OP_LOGIC_AND:
4718 push_text(" AND ");
4719 break;
4720 case BinaryOpNode::OP_LOGIC_OR:
4721 push_text(" OR ");
4722 break;
4723 case BinaryOpNode::OP_CONTENT_TEST:
4724 push_text(" IN ");
4725 break;
4726 case BinaryOpNode::OP_COMP_EQUAL:
4727 push_text(" == ");
4728 break;
4729 case BinaryOpNode::OP_COMP_NOT_EQUAL:
4730 push_text(" != ");
4731 break;
4732 case BinaryOpNode::OP_COMP_LESS:
4733 push_text(" < ");
4734 break;
4735 case BinaryOpNode::OP_COMP_LESS_EQUAL:
4736 push_text(" <= ");
4737 break;
4738 case BinaryOpNode::OP_COMP_GREATER:
4739 push_text(" > ");
4740 break;
4741 case BinaryOpNode::OP_COMP_GREATER_EQUAL:
4742 push_text(" >= ");
4743 break;
4744 }
4745 print_expression(p_binary_op->right_operand);
4746 // Surround in parenthesis for disambiguation.
4747 push_text(")");
4748}
4749
4750void GDScriptParser::TreePrinter::print_call(CallNode *p_call) {
4751 if (p_call->is_super) {
4752 push_text("super");
4753 if (p_call->callee != nullptr) {
4754 push_text(".");
4755 print_expression(p_call->callee);
4756 }
4757 } else {
4758 print_expression(p_call->callee);
4759 }
4760 push_text("( ");
4761 for (int i = 0; i < p_call->arguments.size(); i++) {
4762 if (i > 0) {
4763 push_text(" , ");
4764 }
4765 print_expression(p_call->arguments[i]);
4766 }
4767 push_text(" )");
4768}
4769
4770void GDScriptParser::TreePrinter::print_cast(CastNode *p_cast) {
4771 print_expression(p_cast->operand);
4772 push_text(" AS ");
4773 print_type(p_cast->cast_type);
4774}
4775
4776void GDScriptParser::TreePrinter::print_class(ClassNode *p_class) {
4777 push_text("Class ");
4778 if (p_class->identifier == nullptr) {
4779 push_text("<unnamed>");
4780 } else {
4781 print_identifier(p_class->identifier);
4782 }
4783
4784 if (p_class->extends_used) {
4785 bool first = true;
4786 push_text(" Extends ");
4787 if (!p_class->extends_path.is_empty()) {
4788 push_text(vformat(R"("%s")", p_class->extends_path));
4789 first = false;
4790 }
4791 for (int i = 0; i < p_class->extends.size(); i++) {
4792 if (!first) {
4793 push_text(".");
4794 } else {
4795 first = false;
4796 }
4797 push_text(p_class->extends[i]->name);
4798 }
4799 }
4800
4801 push_line(" :");
4802
4803 increase_indent();
4804
4805 for (int i = 0; i < p_class->members.size(); i++) {
4806 const ClassNode::Member &m = p_class->members[i];
4807
4808 switch (m.type) {
4809 case ClassNode::Member::CLASS:
4810 print_class(m.m_class);
4811 break;
4812 case ClassNode::Member::VARIABLE:
4813 print_variable(m.variable);
4814 break;
4815 case ClassNode::Member::CONSTANT:
4816 print_constant(m.constant);
4817 break;
4818 case ClassNode::Member::SIGNAL:
4819 print_signal(m.signal);
4820 break;
4821 case ClassNode::Member::FUNCTION:
4822 print_function(m.function);
4823 break;
4824 case ClassNode::Member::ENUM:
4825 print_enum(m.m_enum);
4826 break;
4827 case ClassNode::Member::ENUM_VALUE:
4828 break; // Nothing. Will be printed by enum.
4829 case ClassNode::Member::GROUP:
4830 break; // Nothing. Groups are only used by inspector.
4831 case ClassNode::Member::UNDEFINED:
4832 push_line("<unknown member>");
4833 break;
4834 }
4835 }
4836
4837 decrease_indent();
4838}
4839
4840void GDScriptParser::TreePrinter::print_constant(ConstantNode *p_constant) {
4841 push_text("Constant ");
4842 print_identifier(p_constant->identifier);
4843
4844 increase_indent();
4845
4846 push_line();
4847 push_text("= ");
4848 if (p_constant->initializer == nullptr) {
4849 push_text("<missing value>");
4850 } else {
4851 print_expression(p_constant->initializer);
4852 }
4853 decrease_indent();
4854 push_line();
4855}
4856
4857void GDScriptParser::TreePrinter::print_dictionary(DictionaryNode *p_dictionary) {
4858 push_line("{");
4859 increase_indent();
4860 for (int i = 0; i < p_dictionary->elements.size(); i++) {
4861 print_expression(p_dictionary->elements[i].key);
4862 if (p_dictionary->style == DictionaryNode::PYTHON_DICT) {
4863 push_text(" : ");
4864 } else {
4865 push_text(" = ");
4866 }
4867 print_expression(p_dictionary->elements[i].value);
4868 push_line(" ,");
4869 }
4870 decrease_indent();
4871 push_text("}");
4872}
4873
4874void GDScriptParser::TreePrinter::print_expression(ExpressionNode *p_expression) {
4875 if (p_expression == nullptr) {
4876 push_text("<invalid expression>");
4877 return;
4878 }
4879 switch (p_expression->type) {
4880 case Node::ARRAY:
4881 print_array(static_cast<ArrayNode *>(p_expression));
4882 break;
4883 case Node::ASSIGNMENT:
4884 print_assignment(static_cast<AssignmentNode *>(p_expression));
4885 break;
4886 case Node::AWAIT:
4887 print_await(static_cast<AwaitNode *>(p_expression));
4888 break;
4889 case Node::BINARY_OPERATOR:
4890 print_binary_op(static_cast<BinaryOpNode *>(p_expression));
4891 break;
4892 case Node::CALL:
4893 print_call(static_cast<CallNode *>(p_expression));
4894 break;
4895 case Node::CAST:
4896 print_cast(static_cast<CastNode *>(p_expression));
4897 break;
4898 case Node::DICTIONARY:
4899 print_dictionary(static_cast<DictionaryNode *>(p_expression));
4900 break;
4901 case Node::GET_NODE:
4902 print_get_node(static_cast<GetNodeNode *>(p_expression));
4903 break;
4904 case Node::IDENTIFIER:
4905 print_identifier(static_cast<IdentifierNode *>(p_expression));
4906 break;
4907 case Node::LAMBDA:
4908 print_lambda(static_cast<LambdaNode *>(p_expression));
4909 break;
4910 case Node::LITERAL:
4911 print_literal(static_cast<LiteralNode *>(p_expression));
4912 break;
4913 case Node::PRELOAD:
4914 print_preload(static_cast<PreloadNode *>(p_expression));
4915 break;
4916 case Node::SELF:
4917 print_self(static_cast<SelfNode *>(p_expression));
4918 break;
4919 case Node::SUBSCRIPT:
4920 print_subscript(static_cast<SubscriptNode *>(p_expression));
4921 break;
4922 case Node::TERNARY_OPERATOR:
4923 print_ternary_op(static_cast<TernaryOpNode *>(p_expression));
4924 break;
4925 case Node::TYPE_TEST:
4926 print_type_test(static_cast<TypeTestNode *>(p_expression));
4927 break;
4928 case Node::UNARY_OPERATOR:
4929 print_unary_op(static_cast<UnaryOpNode *>(p_expression));
4930 break;
4931 default:
4932 push_text(vformat("<unknown expression %d>", p_expression->type));
4933 break;
4934 }
4935}
4936
4937void GDScriptParser::TreePrinter::print_enum(EnumNode *p_enum) {
4938 push_text("Enum ");
4939 if (p_enum->identifier != nullptr) {
4940 print_identifier(p_enum->identifier);
4941 } else {
4942 push_text("<unnamed>");
4943 }
4944
4945 push_line(" {");
4946 increase_indent();
4947 for (int i = 0; i < p_enum->values.size(); i++) {
4948 const EnumNode::Value &item = p_enum->values[i];
4949 print_identifier(item.identifier);
4950 push_text(" = ");
4951 push_text(itos(item.value));
4952 push_line(" ,");
4953 }
4954 decrease_indent();
4955 push_line("}");
4956}
4957
4958void GDScriptParser::TreePrinter::print_for(ForNode *p_for) {
4959 push_text("For ");
4960 print_identifier(p_for->variable);
4961 push_text(" IN ");
4962 print_expression(p_for->list);
4963 push_line(" :");
4964
4965 increase_indent();
4966
4967 print_suite(p_for->loop);
4968
4969 decrease_indent();
4970}
4971
4972void GDScriptParser::TreePrinter::print_function(FunctionNode *p_function, const String &p_context) {
4973 for (const AnnotationNode *E : p_function->annotations) {
4974 print_annotation(E);
4975 }
4976 push_text(p_context);
4977 push_text(" ");
4978 if (p_function->identifier) {
4979 print_identifier(p_function->identifier);
4980 } else {
4981 push_text("<anonymous>");
4982 }
4983 push_text("( ");
4984 for (int i = 0; i < p_function->parameters.size(); i++) {
4985 if (i > 0) {
4986 push_text(" , ");
4987 }
4988 print_parameter(p_function->parameters[i]);
4989 }
4990 push_line(" ) :");
4991 increase_indent();
4992 print_suite(p_function->body);
4993 decrease_indent();
4994}
4995
4996void GDScriptParser::TreePrinter::print_get_node(GetNodeNode *p_get_node) {
4997 if (p_get_node->use_dollar) {
4998 push_text("$");
4999 }
5000 push_text(p_get_node->full_path);
5001}
5002
5003void GDScriptParser::TreePrinter::print_identifier(IdentifierNode *p_identifier) {
5004 if (p_identifier != nullptr) {
5005 push_text(p_identifier->name);
5006 } else {
5007 push_text("<invalid identifier>");
5008 }
5009}
5010
5011void GDScriptParser::TreePrinter::print_if(IfNode *p_if, bool p_is_elif) {
5012 if (p_is_elif) {
5013 push_text("Elif ");
5014 } else {
5015 push_text("If ");
5016 }
5017 print_expression(p_if->condition);
5018 push_line(" :");
5019
5020 increase_indent();
5021 print_suite(p_if->true_block);
5022 decrease_indent();
5023
5024 // FIXME: Properly detect "elif" blocks.
5025 if (p_if->false_block != nullptr) {
5026 push_line("Else :");
5027 increase_indent();
5028 print_suite(p_if->false_block);
5029 decrease_indent();
5030 }
5031}
5032
5033void GDScriptParser::TreePrinter::print_lambda(LambdaNode *p_lambda) {
5034 print_function(p_lambda->function, "Lambda");
5035 push_text("| captures [ ");
5036 for (int i = 0; i < p_lambda->captures.size(); i++) {
5037 if (i > 0) {
5038 push_text(" , ");
5039 }
5040 push_text(p_lambda->captures[i]->name.operator String());
5041 }
5042 push_line(" ]");
5043}
5044
5045void GDScriptParser::TreePrinter::print_literal(LiteralNode *p_literal) {
5046 // Prefix for string types.
5047 switch (p_literal->value.get_type()) {
5048 case Variant::NODE_PATH:
5049 push_text("^\"");
5050 break;
5051 case Variant::STRING:
5052 push_text("\"");
5053 break;
5054 case Variant::STRING_NAME:
5055 push_text("&\"");
5056 break;
5057 default:
5058 break;
5059 }
5060 push_text(p_literal->value);
5061 // Suffix for string types.
5062 switch (p_literal->value.get_type()) {
5063 case Variant::NODE_PATH:
5064 case Variant::STRING:
5065 case Variant::STRING_NAME:
5066 push_text("\"");
5067 break;
5068 default:
5069 break;
5070 }
5071}
5072
5073void GDScriptParser::TreePrinter::print_match(MatchNode *p_match) {
5074 push_text("Match ");
5075 print_expression(p_match->test);
5076 push_line(" :");
5077
5078 increase_indent();
5079 for (int i = 0; i < p_match->branches.size(); i++) {
5080 print_match_branch(p_match->branches[i]);
5081 }
5082 decrease_indent();
5083}
5084
5085void GDScriptParser::TreePrinter::print_match_branch(MatchBranchNode *p_match_branch) {
5086 for (int i = 0; i < p_match_branch->patterns.size(); i++) {
5087 if (i > 0) {
5088 push_text(" , ");
5089 }
5090 print_match_pattern(p_match_branch->patterns[i]);
5091 }
5092
5093 push_line(" :");
5094
5095 increase_indent();
5096 print_suite(p_match_branch->block);
5097 decrease_indent();
5098}
5099
5100void GDScriptParser::TreePrinter::print_match_pattern(PatternNode *p_match_pattern) {
5101 switch (p_match_pattern->pattern_type) {
5102 case PatternNode::PT_LITERAL:
5103 print_literal(p_match_pattern->literal);
5104 break;
5105 case PatternNode::PT_WILDCARD:
5106 push_text("_");
5107 break;
5108 case PatternNode::PT_REST:
5109 push_text("..");
5110 break;
5111 case PatternNode::PT_BIND:
5112 push_text("Var ");
5113 print_identifier(p_match_pattern->bind);
5114 break;
5115 case PatternNode::PT_EXPRESSION:
5116 print_expression(p_match_pattern->expression);
5117 break;
5118 case PatternNode::PT_ARRAY:
5119 push_text("[ ");
5120 for (int i = 0; i < p_match_pattern->array.size(); i++) {
5121 if (i > 0) {
5122 push_text(" , ");
5123 }
5124 print_match_pattern(p_match_pattern->array[i]);
5125 }
5126 push_text(" ]");
5127 break;
5128 case PatternNode::PT_DICTIONARY:
5129 push_text("{ ");
5130 for (int i = 0; i < p_match_pattern->dictionary.size(); i++) {
5131 if (i > 0) {
5132 push_text(" , ");
5133 }
5134 if (p_match_pattern->dictionary[i].key != nullptr) {
5135 // Key can be null for rest pattern.
5136 print_expression(p_match_pattern->dictionary[i].key);
5137 push_text(" : ");
5138 }
5139 print_match_pattern(p_match_pattern->dictionary[i].value_pattern);
5140 }
5141 push_text(" }");
5142 break;
5143 }
5144}
5145
5146void GDScriptParser::TreePrinter::print_parameter(ParameterNode *p_parameter) {
5147 print_identifier(p_parameter->identifier);
5148 if (p_parameter->datatype_specifier != nullptr) {
5149 push_text(" : ");
5150 print_type(p_parameter->datatype_specifier);
5151 }
5152 if (p_parameter->initializer != nullptr) {
5153 push_text(" = ");
5154 print_expression(p_parameter->initializer);
5155 }
5156}
5157
5158void GDScriptParser::TreePrinter::print_preload(PreloadNode *p_preload) {
5159 push_text(R"(Preload ( ")");
5160 push_text(p_preload->resolved_path);
5161 push_text(R"(" )");
5162}
5163
5164void GDScriptParser::TreePrinter::print_return(ReturnNode *p_return) {
5165 push_text("Return");
5166 if (p_return->return_value != nullptr) {
5167 push_text(" ");
5168 print_expression(p_return->return_value);
5169 }
5170 push_line();
5171}
5172
5173void GDScriptParser::TreePrinter::print_self(SelfNode *p_self) {
5174 push_text("Self(");
5175 if (p_self->current_class->identifier != nullptr) {
5176 print_identifier(p_self->current_class->identifier);
5177 } else {
5178 push_text("<main class>");
5179 }
5180 push_text(")");
5181}
5182
5183void GDScriptParser::TreePrinter::print_signal(SignalNode *p_signal) {
5184 push_text("Signal ");
5185 print_identifier(p_signal->identifier);
5186 push_text("( ");
5187 for (int i = 0; i < p_signal->parameters.size(); i++) {
5188 print_parameter(p_signal->parameters[i]);
5189 }
5190 push_line(" )");
5191}
5192
5193void GDScriptParser::TreePrinter::print_subscript(SubscriptNode *p_subscript) {
5194 print_expression(p_subscript->base);
5195 if (p_subscript->is_attribute) {
5196 push_text(".");
5197 print_identifier(p_subscript->attribute);
5198 } else {
5199 push_text("[ ");
5200 print_expression(p_subscript->index);
5201 push_text(" ]");
5202 }
5203}
5204
5205void GDScriptParser::TreePrinter::print_statement(Node *p_statement) {
5206 switch (p_statement->type) {
5207 case Node::ASSERT:
5208 print_assert(static_cast<AssertNode *>(p_statement));
5209 break;
5210 case Node::VARIABLE:
5211 print_variable(static_cast<VariableNode *>(p_statement));
5212 break;
5213 case Node::CONSTANT:
5214 print_constant(static_cast<ConstantNode *>(p_statement));
5215 break;
5216 case Node::IF:
5217 print_if(static_cast<IfNode *>(p_statement));
5218 break;
5219 case Node::FOR:
5220 print_for(static_cast<ForNode *>(p_statement));
5221 break;
5222 case Node::WHILE:
5223 print_while(static_cast<WhileNode *>(p_statement));
5224 break;
5225 case Node::MATCH:
5226 print_match(static_cast<MatchNode *>(p_statement));
5227 break;
5228 case Node::RETURN:
5229 print_return(static_cast<ReturnNode *>(p_statement));
5230 break;
5231 case Node::BREAK:
5232 push_line("Break");
5233 break;
5234 case Node::CONTINUE:
5235 push_line("Continue");
5236 break;
5237 case Node::PASS:
5238 push_line("Pass");
5239 break;
5240 case Node::BREAKPOINT:
5241 push_line("Breakpoint");
5242 break;
5243 case Node::ASSIGNMENT:
5244 print_assignment(static_cast<AssignmentNode *>(p_statement));
5245 break;
5246 default:
5247 if (p_statement->is_expression()) {
5248 print_expression(static_cast<ExpressionNode *>(p_statement));
5249 push_line();
5250 } else {
5251 push_line(vformat("<unknown statement %d>", p_statement->type));
5252 }
5253 break;
5254 }
5255}
5256
5257void GDScriptParser::TreePrinter::print_suite(SuiteNode *p_suite) {
5258 for (int i = 0; i < p_suite->statements.size(); i++) {
5259 print_statement(p_suite->statements[i]);
5260 }
5261}
5262
5263void GDScriptParser::TreePrinter::print_ternary_op(TernaryOpNode *p_ternary_op) {
5264 // Surround in parenthesis for disambiguation.
5265 push_text("(");
5266 print_expression(p_ternary_op->true_expr);
5267 push_text(") IF (");
5268 print_expression(p_ternary_op->condition);
5269 push_text(") ELSE (");
5270 print_expression(p_ternary_op->false_expr);
5271 push_text(")");
5272}
5273
5274void GDScriptParser::TreePrinter::print_type(TypeNode *p_type) {
5275 if (p_type->type_chain.is_empty()) {
5276 push_text("Void");
5277 } else {
5278 for (int i = 0; i < p_type->type_chain.size(); i++) {
5279 if (i > 0) {
5280 push_text(".");
5281 }
5282 print_identifier(p_type->type_chain[i]);
5283 }
5284 }
5285}
5286
5287void GDScriptParser::TreePrinter::print_type_test(TypeTestNode *p_test) {
5288 print_expression(p_test->operand);
5289 push_text(" IS ");
5290 print_type(p_test->test_type);
5291}
5292
5293void GDScriptParser::TreePrinter::print_unary_op(UnaryOpNode *p_unary_op) {
5294 // Surround in parenthesis for disambiguation.
5295 push_text("(");
5296 switch (p_unary_op->operation) {
5297 case UnaryOpNode::OP_POSITIVE:
5298 push_text("+");
5299 break;
5300 case UnaryOpNode::OP_NEGATIVE:
5301 push_text("-");
5302 break;
5303 case UnaryOpNode::OP_LOGIC_NOT:
5304 push_text("NOT");
5305 break;
5306 case UnaryOpNode::OP_COMPLEMENT:
5307 push_text("~");
5308 break;
5309 }
5310 print_expression(p_unary_op->operand);
5311 // Surround in parenthesis for disambiguation.
5312 push_text(")");
5313}
5314
5315void GDScriptParser::TreePrinter::print_variable(VariableNode *p_variable) {
5316 for (const AnnotationNode *E : p_variable->annotations) {
5317 print_annotation(E);
5318 }
5319
5320 push_text("Variable ");
5321 print_identifier(p_variable->identifier);
5322
5323 push_text(" : ");
5324 if (p_variable->datatype_specifier != nullptr) {
5325 print_type(p_variable->datatype_specifier);
5326 } else if (p_variable->infer_datatype) {
5327 push_text("<inferred type>");
5328 } else {
5329 push_text("Variant");
5330 }
5331
5332 increase_indent();
5333
5334 push_line();
5335 push_text("= ");
5336 if (p_variable->initializer == nullptr) {
5337 push_text("<default value>");
5338 } else {
5339 print_expression(p_variable->initializer);
5340 }
5341 push_line();
5342
5343 if (p_variable->property != VariableNode::PROP_NONE) {
5344 if (p_variable->getter != nullptr) {
5345 push_text("Get");
5346 if (p_variable->property == VariableNode::PROP_INLINE) {
5347 push_line(":");
5348 increase_indent();
5349 print_suite(p_variable->getter->body);
5350 decrease_indent();
5351 } else {
5352 push_line(" =");
5353 increase_indent();
5354 print_identifier(p_variable->getter_pointer);
5355 push_line();
5356 decrease_indent();
5357 }
5358 }
5359 if (p_variable->setter != nullptr) {
5360 push_text("Set (");
5361 if (p_variable->property == VariableNode::PROP_INLINE) {
5362 if (p_variable->setter_parameter != nullptr) {
5363 print_identifier(p_variable->setter_parameter);
5364 } else {
5365 push_text("<missing>");
5366 }
5367 push_line("):");
5368 increase_indent();
5369 print_suite(p_variable->setter->body);
5370 decrease_indent();
5371 } else {
5372 push_line(" =");
5373 increase_indent();
5374 print_identifier(p_variable->setter_pointer);
5375 push_line();
5376 decrease_indent();
5377 }
5378 }
5379 }
5380
5381 decrease_indent();
5382 push_line();
5383}
5384
5385void GDScriptParser::TreePrinter::print_while(WhileNode *p_while) {
5386 push_text("While ");
5387 print_expression(p_while->condition);
5388 push_line(" :");
5389
5390 increase_indent();
5391 print_suite(p_while->loop);
5392 decrease_indent();
5393}
5394
5395void GDScriptParser::TreePrinter::print_tree(const GDScriptParser &p_parser) {
5396 ERR_FAIL_COND_MSG(p_parser.get_tree() == nullptr, "Parse the code before printing the parse tree.");
5397
5398 if (p_parser.is_tool()) {
5399 push_line("@tool");
5400 }
5401 if (!p_parser.get_tree()->icon_path.is_empty()) {
5402 push_text(R"(@icon (")");
5403 push_text(p_parser.get_tree()->icon_path);
5404 push_line("\")");
5405 }
5406 print_class(p_parser.get_tree());
5407
5408 print_line(String(printed));
5409}
5410
5411#endif // DEBUG_ENABLED
5412