2014-02-10 02:10:30 +01:00
/*************************************************************************/
2017-11-16 18:38:18 +01:00
/* gdscript_parser.cpp */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
2022-01-03 21:27:34 +01:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 02:10:30 +01:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2017-11-16 18:38:18 +01:00
# include "gdscript_parser.h"
2020-11-07 23:33:38 +01:00
# include "core/config/project_settings.h"
2021-06-11 14:51:48 +02:00
# include "core/io/file_access.h"
2018-09-11 18:13:45 +02:00
# include "core/io/resource_loader.h"
2020-05-02 00:14:56 +02:00
# include "core/math/math_defs.h"
2020-06-10 23:18:10 +02:00
# include "gdscript.h"
2022-07-12 23:12:42 +02:00
# include "scene/main/multiplayer_api.h"
2020-05-02 00:14:56 +02:00
# ifdef DEBUG_ENABLED
# include "core/os/os.h"
2020-11-07 23:33:38 +01:00
# include "core/string/string_builder.h"
2022-01-04 13:32:43 +01:00
# include "gdscript_warning.h"
2020-05-02 00:14:56 +02:00
# endif // DEBUG_ENABLED
2020-07-06 17:24:24 +02:00
# ifdef TOOLS_ENABLED
# include "editor/editor_settings.h"
# endif // TOOLS_ENABLED
2020-05-02 00:14:56 +02:00
static HashMap < StringName , Variant : : Type > builtin_types ;
Variant : : Type GDScriptParser : : get_builtin_type ( const StringName & p_type ) {
2020-12-15 13:04:21 +01:00
if ( builtin_types . is_empty ( ) ) {
2020-05-02 00:14:56 +02:00
builtin_types [ " bool " ] = Variant : : BOOL ;
builtin_types [ " int " ] = Variant : : INT ;
builtin_types [ " float " ] = Variant : : FLOAT ;
builtin_types [ " String " ] = Variant : : STRING ;
builtin_types [ " Vector2 " ] = Variant : : VECTOR2 ;
builtin_types [ " Vector2i " ] = Variant : : VECTOR2I ;
builtin_types [ " Rect2 " ] = Variant : : RECT2 ;
builtin_types [ " Rect2i " ] = Variant : : RECT2I ;
builtin_types [ " Transform2D " ] = Variant : : TRANSFORM2D ;
builtin_types [ " Vector3 " ] = Variant : : VECTOR3 ;
builtin_types [ " Vector3i " ] = Variant : : VECTOR3I ;
2022-07-26 01:49:51 +02:00
builtin_types [ " Vector4 " ] = Variant : : VECTOR4 ;
builtin_types [ " Vector4i " ] = Variant : : VECTOR4I ;
2020-05-02 00:14:56 +02:00
builtin_types [ " AABB " ] = Variant : : AABB ;
builtin_types [ " Plane " ] = Variant : : PLANE ;
2021-01-20 08:02:02 +01:00
builtin_types [ " Quaternion " ] = Variant : : QUATERNION ;
2020-05-02 00:14:56 +02:00
builtin_types [ " Basis " ] = Variant : : BASIS ;
2021-06-15 15:04:50 +02:00
builtin_types [ " Transform3D " ] = Variant : : TRANSFORM3D ;
Implement Vector4, Vector4i, Projection
Implement built-in classes Vector4, Vector4i and Projection.
* Two versions of Vector4 (float and integer).
* A Projection class, which is a 4x4 matrix specialized in projection types.
These types have been requested for a long time, but given they were very corner case they were not added before.
Because in Godot 4, reimplementing parts of the rendering engine is now possible, access to these types (heavily used by the rendering code) becomes a necessity.
**Q**: Why Projection and not Matrix4?
**A**: Godot does not use Matrix2, Matrix3, Matrix4x3, etc. naming convention because, within the engine, these types always have a *purpose*. As such, Godot names them: Transform2D, Transform3D or Basis. In this case, this 4x4 matrix is _always_ used as a _Projection_, hence the naming.
2022-07-20 01:11:13 +02:00
builtin_types [ " Projection " ] = Variant : : PROJECTION ;
2020-05-02 00:14:56 +02:00
builtin_types [ " Color " ] = Variant : : COLOR ;
2020-11-09 14:53:05 +01:00
builtin_types [ " RID " ] = Variant : : RID ;
2020-05-02 00:14:56 +02:00
builtin_types [ " Object " ] = Variant : : OBJECT ;
builtin_types [ " StringName " ] = Variant : : STRING_NAME ;
builtin_types [ " NodePath " ] = Variant : : NODE_PATH ;
builtin_types [ " Dictionary " ] = Variant : : DICTIONARY ;
builtin_types [ " Callable " ] = Variant : : CALLABLE ;
builtin_types [ " Signal " ] = Variant : : SIGNAL ;
builtin_types [ " Array " ] = Variant : : ARRAY ;
builtin_types [ " PackedByteArray " ] = Variant : : PACKED_BYTE_ARRAY ;
builtin_types [ " PackedInt32Array " ] = Variant : : PACKED_INT32_ARRAY ;
builtin_types [ " PackedInt64Array " ] = Variant : : PACKED_INT64_ARRAY ;
builtin_types [ " PackedFloat32Array " ] = Variant : : PACKED_FLOAT32_ARRAY ;
builtin_types [ " PackedFloat64Array " ] = Variant : : PACKED_FLOAT64_ARRAY ;
builtin_types [ " PackedStringArray " ] = Variant : : PACKED_STRING_ARRAY ;
builtin_types [ " PackedVector2Array " ] = Variant : : PACKED_VECTOR2_ARRAY ;
builtin_types [ " PackedVector3Array " ] = Variant : : PACKED_VECTOR3_ARRAY ;
builtin_types [ " PackedColorArray " ] = Variant : : PACKED_COLOR_ARRAY ;
// NIL is not here, hence the -1.
if ( builtin_types . size ( ) ! = Variant : : VARIANT_MAX - 1 ) {
ERR_PRINT ( " Outdated parser: amount of built-in types don't match the amount of types in Variant. " ) ;
}
}
if ( builtin_types . has ( p_type ) ) {
return builtin_types [ p_type ] ;
}
return Variant : : VARIANT_MAX ;
}
2020-08-01 21:00:26 +02:00
void GDScriptParser : : cleanup ( ) {
builtin_types . clear ( ) ;
}
2020-07-06 17:24:24 +02:00
void GDScriptParser : : get_annotation_list ( List < MethodInfo > * r_annotations ) const {
2022-05-08 10:09:19 +02:00
for ( const KeyValue < StringName , AnnotationInfo > & E : valid_annotations ) {
r_annotations - > push_back ( E . value . info ) ;
2020-07-06 17:24:24 +02:00
}
}
2022-07-04 17:56:34 +02:00
bool GDScriptParser : : annotation_exists ( const String & p_annotation_name ) const {
return valid_annotations . has ( p_annotation_name ) ;
}
2020-05-02 00:14:56 +02:00
GDScriptParser : : GDScriptParser ( ) {
// Register valid annotations.
// TODO: Should this be static?
register_annotation ( MethodInfo ( " @tool " ) , AnnotationInfo : : SCRIPT , & GDScriptParser : : tool_annotation ) ;
2022-06-23 12:21:07 +02:00
register_annotation ( MethodInfo ( " @icon " , PropertyInfo ( Variant : : STRING , " icon_path " ) ) , AnnotationInfo : : SCRIPT , & GDScriptParser : : icon_annotation ) ;
2020-05-02 00:14:56 +02:00
register_annotation ( MethodInfo ( " @onready " ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : onready_annotation ) ;
// Export annotations.
2021-03-17 14:57:30 +01:00
register_annotation ( MethodInfo ( " @export " ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : export_annotations < PROPERTY_HINT_NONE , Variant : : NIL > ) ;
2022-07-11 22:02:55 +02:00
register_annotation ( MethodInfo ( " @export_enum " , PropertyInfo ( Variant : : STRING , " names " ) ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : export_annotations < PROPERTY_HINT_ENUM , Variant : : INT > , varray ( ) , true ) ;
register_annotation ( MethodInfo ( " @export_file " , PropertyInfo ( Variant : : STRING , " filter " ) ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : export_annotations < PROPERTY_HINT_FILE , Variant : : STRING > , varray ( " " ) , true ) ;
2020-05-02 00:14:56 +02:00
register_annotation ( MethodInfo ( " @export_dir " ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : export_annotations < PROPERTY_HINT_DIR , Variant : : STRING > ) ;
2022-07-11 22:02:55 +02:00
register_annotation ( MethodInfo ( " @export_global_file " , PropertyInfo ( Variant : : STRING , " filter " ) ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : export_annotations < PROPERTY_HINT_GLOBAL_FILE , Variant : : STRING > , varray ( " " ) , true ) ;
2020-05-02 00:14:56 +02:00
register_annotation ( MethodInfo ( " @export_global_dir " ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : export_annotations < PROPERTY_HINT_GLOBAL_DIR , Variant : : STRING > ) ;
register_annotation ( MethodInfo ( " @export_multiline " ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : export_annotations < PROPERTY_HINT_MULTILINE_TEXT , Variant : : STRING > ) ;
2022-08-09 19:41:54 +02:00
register_annotation ( MethodInfo ( " @export_placeholder " , PropertyInfo ( Variant : : STRING , " placeholder " ) ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : export_annotations < PROPERTY_HINT_PLACEHOLDER_TEXT , Variant : : STRING > ) ;
2022-07-11 22:02:55 +02:00
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 ) ;
register_annotation ( MethodInfo ( " @export_exp_easing " , PropertyInfo ( Variant : : STRING , " hints " ) ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : export_annotations < PROPERTY_HINT_EXP_EASING , Variant : : FLOAT > , varray ( " " ) , true ) ;
2020-05-02 00:14:56 +02:00
register_annotation ( MethodInfo ( " @export_color_no_alpha " ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : export_annotations < PROPERTY_HINT_COLOR_NO_ALPHA , Variant : : COLOR > ) ;
2022-07-11 22:02:55 +02:00
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 ) ;
register_annotation ( MethodInfo ( " @export_flags " , PropertyInfo ( Variant : : STRING , " names " ) ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : export_annotations < PROPERTY_HINT_FLAGS , Variant : : INT > , varray ( ) , true ) ;
2020-05-02 00:14:56 +02:00
register_annotation ( MethodInfo ( " @export_flags_2d_render " ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : export_annotations < PROPERTY_HINT_LAYERS_2D_RENDER , Variant : : INT > ) ;
register_annotation ( MethodInfo ( " @export_flags_2d_physics " ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : export_annotations < PROPERTY_HINT_LAYERS_2D_PHYSICS , Variant : : INT > ) ;
2021-03-08 20:56:33 +01:00
register_annotation ( MethodInfo ( " @export_flags_2d_navigation " ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : export_annotations < PROPERTY_HINT_LAYERS_2D_NAVIGATION , Variant : : INT > ) ;
2020-05-02 00:14:56 +02:00
register_annotation ( MethodInfo ( " @export_flags_3d_render " ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : export_annotations < PROPERTY_HINT_LAYERS_3D_RENDER , Variant : : INT > ) ;
register_annotation ( MethodInfo ( " @export_flags_3d_physics " ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : export_annotations < PROPERTY_HINT_LAYERS_3D_PHYSICS , Variant : : INT > ) ;
2021-03-08 20:56:33 +01:00
register_annotation ( MethodInfo ( " @export_flags_3d_navigation " ) , AnnotationInfo : : VARIABLE , & GDScriptParser : : export_annotations < PROPERTY_HINT_LAYERS_3D_NAVIGATION , Variant : : INT > ) ;
2022-07-03 21:30:08 +02:00
// Export grouping annotations.
register_annotation ( MethodInfo ( " @export_category " , PropertyInfo ( Variant : : STRING , " name " ) ) , AnnotationInfo : : STANDALONE , & GDScriptParser : : export_group_annotations < PROPERTY_USAGE_CATEGORY > ) ;
2022-07-11 22:02:55 +02:00
register_annotation ( MethodInfo ( " @export_group " , PropertyInfo ( Variant : : STRING , " name " ) , PropertyInfo ( Variant : : STRING , " prefix " ) ) , AnnotationInfo : : STANDALONE , & GDScriptParser : : export_group_annotations < PROPERTY_USAGE_GROUP > , varray ( " " ) ) ;
register_annotation ( MethodInfo ( " @export_subgroup " , PropertyInfo ( Variant : : STRING , " name " ) , PropertyInfo ( Variant : : STRING , " prefix " ) ) , AnnotationInfo : : STANDALONE , & GDScriptParser : : export_group_annotations < PROPERTY_USAGE_SUBGROUP > , varray ( " " ) ) ;
2022-07-03 21:30:08 +02:00
// Warning annotations.
2022-07-11 22:02:55 +02:00
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 ) ;
2020-05-02 00:14:56 +02:00
// Networking.
2022-07-12 23:12:42 +02:00
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 ( " " , " " , " " , 0 ) , true ) ;
2022-11-20 18:53:56 +01:00
is_ignoring_warnings = ! ( bool ) GLOBAL_GET ( " debug/gdscript/warnings/enable " ) ;
2020-05-02 00:14:56 +02:00
}
GDScriptParser : : ~ GDScriptParser ( ) {
clear ( ) ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
void GDScriptParser : : clear ( ) {
while ( list ! = nullptr ) {
Node * element = list ;
list = list - > next ;
memdelete ( element ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
head = nullptr ;
list = nullptr ;
_is_tool = false ;
for_completion = false ;
errors . clear ( ) ;
multiline_stack . clear ( ) ;
2022-07-11 20:31:15 +02:00
nodes_in_progress . clear ( ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : push_error ( const String & p_message , const Node * p_origin ) {
// TODO: Improve error reporting by pointing at source code.
// TODO: Errors might point at more than one place at once (e.g. show previous declaration).
panic_mode = true ;
// TODO: Improve positional information.
if ( p_origin = = nullptr ) {
errors . push_back ( { p_message , current . start_line , current . start_column } ) ;
} else {
errors . push_back ( { p_message , p_origin - > start_line , p_origin - > leftmost_column } ) ;
}
}
2020-07-16 03:02:44 +02:00
# ifdef DEBUG_ENABLED
2020-06-12 00:31:28 +02:00
void GDScriptParser : : push_warning ( const Node * p_source , GDScriptWarning : : Code p_code , const String & p_symbol1 , const String & p_symbol2 , const String & p_symbol3 , const String & p_symbol4 ) {
2020-11-13 15:09:52 +01:00
ERR_FAIL_COND ( p_source = = nullptr ) ;
2020-06-12 00:31:28 +02:00
Vector < String > symbols ;
2020-12-15 13:04:21 +01:00
if ( ! p_symbol1 . is_empty ( ) ) {
2020-06-12 00:31:28 +02:00
symbols . push_back ( p_symbol1 ) ;
}
2020-12-15 13:04:21 +01:00
if ( ! p_symbol2 . is_empty ( ) ) {
2020-06-12 00:31:28 +02:00
symbols . push_back ( p_symbol2 ) ;
}
2020-12-15 13:04:21 +01:00
if ( ! p_symbol3 . is_empty ( ) ) {
2020-06-12 00:31:28 +02:00
symbols . push_back ( p_symbol3 ) ;
}
2020-12-15 13:04:21 +01:00
if ( ! p_symbol4 . is_empty ( ) ) {
2020-06-12 00:31:28 +02:00
symbols . push_back ( p_symbol4 ) ;
}
push_warning ( p_source , p_code , symbols ) ;
}
void GDScriptParser : : push_warning ( const Node * p_source , GDScriptWarning : : Code p_code , const Vector < String > & p_symbols ) {
2020-11-13 15:09:52 +01:00
ERR_FAIL_COND ( p_source = = nullptr ) ;
2020-06-12 00:31:28 +02:00
if ( is_ignoring_warnings ) {
return ;
}
if ( GLOBAL_GET ( " debug/gdscript/warnings/exclude_addons " ) . booleanize ( ) & & script_path . begins_with ( " res://addons/ " ) ) {
return ;
}
2022-01-04 13:32:43 +01:00
if ( ignored_warning_codes . has ( p_code ) ) {
return ;
}
2020-06-12 00:31:28 +02:00
String warn_name = GDScriptWarning : : get_name_from_code ( ( GDScriptWarning : : Code ) p_code ) . to_lower ( ) ;
if ( ignored_warnings . has ( warn_name ) ) {
return ;
}
2022-03-23 03:44:30 +01:00
int warn_level = ( int ) GLOBAL_GET ( GDScriptWarning : : get_settings_path_from_code ( p_code ) ) ;
if ( ! warn_level ) {
2020-06-12 00:31:28 +02:00
return ;
}
GDScriptWarning warning ;
warning . code = p_code ;
warning . symbols = p_symbols ;
warning . start_line = p_source - > start_line ;
warning . end_line = p_source - > end_line ;
warning . leftmost_column = p_source - > leftmost_column ;
warning . rightmost_column = p_source - > rightmost_column ;
2022-09-05 18:42:46 +02:00
if ( warn_level = = GDScriptWarning : : WarnLevel : : ERROR | | bool ( GLOBAL_GET ( " debug/gdscript/warnings/treat_warnings_as_errors " ) ) ) {
2022-03-23 03:44:30 +01:00
push_error ( warning . get_message ( ) , p_source ) ;
return ;
}
2020-06-12 00:31:28 +02:00
List < GDScriptWarning > : : Element * before = nullptr ;
2021-07-16 05:45:57 +02:00
for ( List < GDScriptWarning > : : Element * E = warnings . front ( ) ; E ; E = E - > next ( ) ) {
2020-06-12 00:31:28 +02:00
if ( E - > get ( ) . start_line > warning . start_line ) {
break ;
}
before = E ;
}
if ( before ) {
warnings . insert_after ( before , warning ) ;
} else {
warnings . push_front ( warning ) ;
}
}
2020-07-16 03:02:44 +02:00
# endif
2020-06-12 00:31:28 +02:00
2020-07-06 17:24:24 +02:00
void GDScriptParser : : make_completion_context ( CompletionType p_type , Node * p_node , int p_argument , bool p_force ) {
if ( ! for_completion | | ( ! p_force & & completion_context . type ! = COMPLETION_NONE ) ) {
return ;
}
if ( previous . cursor_place ! = GDScriptTokenizer : : CURSOR_MIDDLE & & previous . cursor_place ! = GDScriptTokenizer : : CURSOR_END & & current . cursor_place = = GDScriptTokenizer : : CURSOR_NONE ) {
return ;
}
CompletionContext context ;
context . type = p_type ;
context . current_class = current_class ;
context . current_function = current_function ;
context . current_suite = current_suite ;
context . current_line = tokenizer . get_cursor_line ( ) ;
context . current_argument = p_argument ;
context . node = p_node ;
completion_context = context ;
}
void GDScriptParser : : make_completion_context ( CompletionType p_type , Variant : : Type p_builtin_type , bool p_force ) {
if ( ! for_completion | | ( ! p_force & & completion_context . type ! = COMPLETION_NONE ) ) {
return ;
}
if ( previous . cursor_place ! = GDScriptTokenizer : : CURSOR_MIDDLE & & previous . cursor_place ! = GDScriptTokenizer : : CURSOR_END & & current . cursor_place = = GDScriptTokenizer : : CURSOR_NONE ) {
return ;
}
CompletionContext context ;
context . type = p_type ;
context . current_class = current_class ;
context . current_function = current_function ;
context . current_suite = current_suite ;
context . current_line = tokenizer . get_cursor_line ( ) ;
context . builtin_type = p_builtin_type ;
completion_context = context ;
}
void GDScriptParser : : push_completion_call ( Node * p_call ) {
if ( ! for_completion ) {
return ;
}
CompletionCall call ;
call . call = p_call ;
call . argument = 0 ;
completion_call_stack . push_back ( call ) ;
if ( previous . cursor_place = = GDScriptTokenizer : : CURSOR_MIDDLE | | previous . cursor_place = = GDScriptTokenizer : : CURSOR_END | | current . cursor_place = = GDScriptTokenizer : : CURSOR_BEGINNING ) {
completion_call = call ;
}
}
void GDScriptParser : : pop_completion_call ( ) {
if ( ! for_completion ) {
return ;
}
2020-12-15 13:04:21 +01:00
ERR_FAIL_COND_MSG ( completion_call_stack . is_empty ( ) , " Trying to pop empty completion call stack " ) ;
2020-07-06 17:24:24 +02:00
completion_call_stack . pop_back ( ) ;
}
void GDScriptParser : : set_last_completion_call_arg ( int p_argument ) {
if ( ! for_completion | | passed_cursor ) {
return ;
}
2020-12-15 13:04:21 +01:00
ERR_FAIL_COND_MSG ( completion_call_stack . is_empty ( ) , " Trying to set argument on empty completion call stack " ) ;
2020-07-06 17:24:24 +02:00
completion_call_stack . back ( ) - > get ( ) . argument = p_argument ;
}
2020-05-02 00:14:56 +02:00
Error GDScriptParser : : parse ( const String & p_source_code , const String & p_script_path , bool p_for_completion ) {
clear ( ) ;
2020-07-06 17:24:24 +02:00
String source = p_source_code ;
int cursor_line = - 1 ;
int cursor_column = - 1 ;
for_completion = p_for_completion ;
int tab_size = 4 ;
# ifdef TOOLS_ENABLED
if ( EditorSettings : : get_singleton ( ) ) {
2021-08-15 19:14:46 +02:00
tab_size = EditorSettings : : get_singleton ( ) - > get_setting ( " text_editor/behavior/indent/size " ) ;
2020-07-06 17:24:24 +02:00
}
# endif // TOOLS_ENABLED
if ( p_for_completion ) {
// Remove cursor sentinel char.
const Vector < String > lines = p_source_code . split ( " \n " ) ;
cursor_line = 1 ;
cursor_column = 1 ;
for ( int i = 0 ; i < lines . size ( ) ; i + + ) {
bool found = false ;
const String & line = lines [ i ] ;
for ( int j = 0 ; j < line . size ( ) ; j + + ) {
2020-07-27 12:43:20 +02:00
if ( line [ j ] = = char32_t ( 0xFFFF ) ) {
2020-07-06 17:24:24 +02:00
found = true ;
break ;
} else if ( line [ j ] = = ' \t ' ) {
cursor_column + = tab_size - 1 ;
}
cursor_column + + ;
}
if ( found ) {
break ;
}
cursor_line + + ;
cursor_column = 1 ;
}
source = source . replace_first ( String : : chr ( 0xFFFF ) , String ( ) ) ;
}
tokenizer . set_source_code ( source ) ;
tokenizer . set_cursor_position ( cursor_line , cursor_column ) ;
2020-06-12 02:49:58 +02:00
script_path = p_script_path ;
2020-05-02 00:14:56 +02:00
current = tokenizer . scan ( ) ;
2021-09-11 20:38:15 +02:00
// Avoid error or newline as the first token.
// The latter can mess with the parser when opening files filled exclusively with comments and newlines.
while ( current . type = = GDScriptTokenizer : : Token : : ERROR | | current . type = = GDScriptTokenizer : : Token : : NEWLINE ) {
if ( current . type = = GDScriptTokenizer : : Token : : ERROR ) {
push_error ( current . literal ) ;
}
2020-06-01 21:41:05 +02:00
current = tokenizer . scan ( ) ;
}
2020-05-02 00:14:56 +02:00
2021-09-11 20:38:15 +02:00
# ifdef DEBUG_ENABLED
// Warn about parsing an empty script file:
if ( current . type = = GDScriptTokenizer : : Token : : TK_EOF ) {
// Create a dummy Node for the warning, pointing to the very beginning of the file
Node * nd = alloc_node < PassNode > ( ) ;
nd - > start_line = 1 ;
nd - > start_column = 0 ;
nd - > end_line = 1 ;
nd - > leftmost_column = 0 ;
nd - > rightmost_column = 0 ;
push_warning ( nd , GDScriptWarning : : EMPTY_FILE ) ;
}
# endif
2020-05-02 00:14:56 +02:00
push_multiline ( false ) ; // Keep one for the whole parsing.
parse_program ( ) ;
pop_multiline ( ) ;
2018-07-01 18:17:40 +02:00
2020-05-02 00:14:56 +02:00
# ifdef DEBUG_ENABLED
if ( multiline_stack . size ( ) > 0 ) {
ERR_PRINT ( " Parser bug: Imbalanced multiline stack. " ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
# endif
2014-02-10 02:10:30 +01:00
2020-12-15 13:04:21 +01:00
if ( errors . is_empty ( ) ) {
2020-05-02 00:14:56 +02:00
return OK ;
} else {
return ERR_PARSE_ERROR ;
}
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
GDScriptTokenizer : : Token GDScriptParser : : advance ( ) {
2021-03-25 14:36:29 +01:00
lambda_ended = false ; // Empty marker since we're past the end in any case.
2020-05-02 00:14:56 +02:00
if ( current . type = = GDScriptTokenizer : : Token : : TK_EOF ) {
ERR_FAIL_COND_V_MSG ( current . type = = GDScriptTokenizer : : Token : : TK_EOF , current , " GDScript parser bug: Trying to advance past the end of stream. " ) ;
2020-03-06 20:40:47 +01:00
}
2020-12-15 13:04:21 +01:00
if ( for_completion & & ! completion_call_stack . is_empty ( ) ) {
2020-07-06 17:24:24 +02:00
if ( completion_call . call = = nullptr & & tokenizer . is_past_cursor ( ) ) {
completion_call = completion_call_stack . back ( ) - > get ( ) ;
passed_cursor = true ;
}
}
2020-05-02 00:14:56 +02:00
previous = current ;
current = tokenizer . scan ( ) ;
while ( current . type = = GDScriptTokenizer : : Token : : ERROR ) {
push_error ( current . literal ) ;
current = tokenizer . scan ( ) ;
}
2022-07-11 20:31:15 +02:00
for ( Node * n : nodes_in_progress ) {
update_extents ( n ) ;
}
2020-05-02 00:14:56 +02:00
return previous ;
2020-03-06 20:40:47 +01:00
}
2020-05-02 00:14:56 +02:00
bool GDScriptParser : : match ( GDScriptTokenizer : : Token : : Type p_token_type ) {
if ( ! check ( p_token_type ) ) {
2014-02-10 02:10:30 +01:00
return false ;
}
2020-05-02 00:14:56 +02:00
advance ( ) ;
return true ;
}
2014-02-10 02:10:30 +01:00
2021-03-25 14:36:29 +01:00
bool GDScriptParser : : check ( GDScriptTokenizer : : Token : : Type p_token_type ) const {
2020-05-02 00:14:56 +02:00
if ( p_token_type = = GDScriptTokenizer : : Token : : IDENTIFIER ) {
return current . is_identifier ( ) ;
2018-09-16 23:29:17 +02:00
}
2020-05-02 00:14:56 +02:00
return current . type = = p_token_type ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
bool GDScriptParser : : consume ( GDScriptTokenizer : : Token : : Type p_token_type , const String & p_error_message ) {
if ( match ( p_token_type ) ) {
2015-12-29 15:41:37 +01:00
return true ;
2020-05-02 00:14:56 +02:00
}
push_error ( p_error_message ) ;
return false ;
}
2014-02-10 02:10:30 +01:00
2021-03-25 14:36:29 +01:00
bool GDScriptParser : : is_at_end ( ) const {
2020-05-02 00:14:56 +02:00
return check ( GDScriptTokenizer : : Token : : TK_EOF ) ;
}
2019-10-13 21:48:18 +02:00
2020-05-02 00:14:56 +02:00
void GDScriptParser : : synchronize ( ) {
panic_mode = false ;
while ( ! is_at_end ( ) ) {
if ( previous . type = = GDScriptTokenizer : : Token : : NEWLINE | | previous . type = = GDScriptTokenizer : : Token : : SEMICOLON ) {
return ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
switch ( current . type ) {
case GDScriptTokenizer : : Token : : CLASS :
case GDScriptTokenizer : : Token : : FUNC :
case GDScriptTokenizer : : Token : : STATIC :
case GDScriptTokenizer : : Token : : VAR :
case GDScriptTokenizer : : Token : : CONST :
case GDScriptTokenizer : : Token : : SIGNAL :
//case GDScriptTokenizer::Token::IF: // Can also be inside expressions.
case GDScriptTokenizer : : Token : : FOR :
case GDScriptTokenizer : : Token : : WHILE :
case GDScriptTokenizer : : Token : : MATCH :
case GDScriptTokenizer : : Token : : RETURN :
case GDScriptTokenizer : : Token : : ANNOTATION :
return ;
default :
// Do nothing.
break ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
advance ( ) ;
2014-02-10 02:10:30 +01:00
}
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : push_multiline ( bool p_state ) {
multiline_stack . push_back ( p_state ) ;
tokenizer . set_multiline_mode ( p_state ) ;
if ( p_state ) {
// Consume potential whitespace tokens already waiting in line.
while ( current . type = = GDScriptTokenizer : : Token : : NEWLINE | | current . type = = GDScriptTokenizer : : Token : : INDENT | | current . type = = GDScriptTokenizer : : Token : : DEDENT ) {
current = tokenizer . scan ( ) ; // Don't call advance() here, as we don't want to change the previous token.
}
}
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
void GDScriptParser : : pop_multiline ( ) {
ERR_FAIL_COND_MSG ( multiline_stack . size ( ) = = 0 , " Parser bug: trying to pop from multiline stack without available value. " ) ;
multiline_stack . pop_back ( ) ;
tokenizer . set_multiline_mode ( multiline_stack . size ( ) > 0 ? multiline_stack . back ( ) - > get ( ) : false ) ;
}
2021-03-25 14:36:29 +01:00
bool GDScriptParser : : is_statement_end_token ( ) const {
2020-08-18 02:30:39 +02:00
return check ( GDScriptTokenizer : : Token : : NEWLINE ) | | check ( GDScriptTokenizer : : Token : : SEMICOLON ) | | check ( GDScriptTokenizer : : Token : : TK_EOF ) ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2021-03-25 14:36:29 +01:00
bool GDScriptParser : : is_statement_end ( ) const {
return lambda_ended | | in_lambda | | is_statement_end_token ( ) ;
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : end_statement ( const String & p_context ) {
bool found = false ;
2020-08-18 02:30:39 +02:00
while ( is_statement_end ( ) & & ! is_at_end ( ) ) {
2020-05-02 00:14:56 +02:00
// Remove sequential newlines/semicolons.
2021-03-25 14:36:29 +01:00
if ( is_statement_end_token ( ) ) {
// Only consume if this is an actual token.
advance ( ) ;
} else if ( lambda_ended ) {
lambda_ended = false ; // Consume this "token".
found = true ;
break ;
} else {
if ( ! found ) {
lambda_ended = true ; // Mark the lambda as done since we found something else to end the statement.
found = true ;
}
break ;
}
2020-05-02 00:14:56 +02:00
found = true ;
}
2020-08-18 02:30:39 +02:00
if ( ! found & & ! is_at_end ( ) ) {
2020-05-02 00:14:56 +02:00
push_error ( vformat ( R " (Expected end of statement after %s, found " % s " instead.) " , p_context , current . get_name ( ) ) ) ;
}
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
void GDScriptParser : : parse_program ( ) {
head = alloc_node < ClassNode > ( ) ;
2022-11-08 12:51:20 +01:00
head - > fqcn = script_path ;
2020-05-02 00:14:56 +02:00
current_class = head ;
2014-02-10 02:10:30 +01:00
2022-07-03 21:30:08 +02:00
// If we happen to parse an annotation before extends or class_name keywords, track it.
// @tool is allowed, but others should fail.
AnnotationNode * premature_annotation = nullptr ;
2020-05-02 00:14:56 +02:00
if ( match ( GDScriptTokenizer : : Token : : ANNOTATION ) ) {
2022-07-03 21:30:08 +02:00
// Check for @tool, script-level, or standalone annotation.
AnnotationNode * annotation = parse_annotation ( AnnotationInfo : : SCRIPT | AnnotationInfo : : STANDALONE | AnnotationInfo : : CLASS_LEVEL ) ;
2020-08-18 02:02:49 +02:00
if ( annotation ! = nullptr ) {
2022-02-06 14:12:19 +01:00
if ( annotation - > name = = SNAME ( " @tool " ) ) {
2020-08-18 02:02:49 +02:00
// TODO: don't allow @tool anywhere else. (Should all script annotations be the first thing?).
_is_tool = true ;
if ( previous . type ! = GDScriptTokenizer : : Token : : NEWLINE ) {
push_error ( R " (Expected newline after " @ tool " annotation.) " ) ;
}
// @tool annotation has no specific target.
annotation - > apply ( this , nullptr ) ;
2022-07-03 21:30:08 +02:00
} else if ( annotation - > applies_to ( AnnotationInfo : : SCRIPT | AnnotationInfo : : STANDALONE ) ) {
premature_annotation = annotation ;
if ( previous . type ! = GDScriptTokenizer : : Token : : NEWLINE ) {
push_error ( R " (Expected newline after a standalone annotation.) " ) ;
}
annotation - > apply ( this , head ) ;
2020-08-18 02:02:49 +02:00
} else {
2022-07-03 21:30:08 +02:00
premature_annotation = annotation ;
2020-08-18 02:02:49 +02:00
annotation_stack . push_back ( annotation ) ;
2014-02-10 02:10:30 +01:00
}
}
}
2020-05-02 00:14:56 +02:00
for ( bool should_break = false ; ! should_break ; ) {
// Order here doesn't matter, but there should be only one of each at most.
switch ( current . type ) {
case GDScriptTokenizer : : Token : : CLASS_NAME :
2022-07-03 21:30:08 +02:00
if ( premature_annotation ! = nullptr ) {
push_error ( R " ( " class_name " should be used before annotations (except @tool).) " ) ;
2020-05-02 00:14:56 +02:00
}
advance ( ) ;
if ( head - > identifier ! = nullptr ) {
push_error ( R " ( " class_name " can only be used once.) " ) ;
} else {
parse_class_name ( ) ;
}
break ;
case GDScriptTokenizer : : Token : : EXTENDS :
2022-07-03 21:30:08 +02:00
if ( premature_annotation ! = nullptr ) {
push_error ( R " ( " extends " should be used before annotations (except @tool).) " ) ;
2020-05-02 00:14:56 +02:00
}
advance ( ) ;
if ( head - > extends_used ) {
push_error ( R " ( " extends " can only be used once.) " ) ;
} else {
parse_extends ( ) ;
2020-07-16 03:02:44 +02:00
end_statement ( " superclass " ) ;
2020-05-02 00:14:56 +02:00
}
break ;
default :
should_break = true ;
break ;
}
2014-12-17 02:31:57 +01:00
2020-05-02 00:14:56 +02:00
if ( panic_mode ) {
synchronize ( ) ;
2014-12-17 02:31:57 +01:00
}
2020-05-02 00:14:56 +02:00
}
2014-12-17 02:31:57 +01:00
2020-05-02 00:14:56 +02:00
if ( match ( GDScriptTokenizer : : Token : : ANNOTATION ) ) {
2022-07-03 21:30:08 +02:00
// Check for a script-level, or standalone annotation.
AnnotationNode * annotation = parse_annotation ( AnnotationInfo : : SCRIPT | AnnotationInfo : : STANDALONE | AnnotationInfo : : CLASS_LEVEL ) ;
2020-05-02 00:14:56 +02:00
if ( annotation ! = nullptr ) {
2022-07-03 21:30:08 +02:00
if ( annotation - > applies_to ( AnnotationInfo : : SCRIPT | AnnotationInfo : : STANDALONE ) ) {
2020-05-02 00:14:56 +02:00
if ( previous . type ! = GDScriptTokenizer : : Token : : NEWLINE ) {
2022-07-03 21:30:08 +02:00
push_error ( R " (Expected newline after a standalone annotation.) " ) ;
2020-05-02 00:14:56 +02:00
}
annotation - > apply ( this , head ) ;
} else {
annotation_stack . push_back ( annotation ) ;
}
2016-09-12 15:52:29 +02:00
}
2014-12-17 02:31:57 +01:00
}
2021-09-21 19:13:23 +02:00
parse_class_body ( true ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( head ) ;
2020-05-02 00:14:56 +02:00
2020-11-29 03:37:57 +01:00
# ifdef TOOLS_ENABLED
2021-08-09 22:13:42 +02:00
for ( const KeyValue < int , GDScriptTokenizer : : CommentData > & E : tokenizer . get_comments ( ) ) {
if ( E . value . new_line & & E . value . comment . begins_with ( " ## " ) ) {
class_doc_line = MIN ( class_doc_line , E . key ) ;
2020-11-29 03:37:57 +01:00
}
}
if ( has_comment ( class_doc_line ) ) {
get_class_doc_comment ( class_doc_line , head - > doc_brief_description , head - > doc_description , head - > doc_tutorials , false ) ;
}
# endif // TOOLS_ENABLED
2020-05-02 00:14:56 +02:00
if ( ! check ( GDScriptTokenizer : : Token : : TK_EOF ) ) {
push_error ( " Expected end of file. " ) ;
}
clear_unused_annotations ( ) ;
2014-12-17 02:31:57 +01:00
}
2020-05-02 00:14:56 +02:00
GDScriptParser : : ClassNode * GDScriptParser : : parse_class ( ) {
ClassNode * n_class = alloc_node < ClassNode > ( ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
ClassNode * previous_class = current_class ;
current_class = n_class ;
n_class - > outer = previous_class ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected identifier for the class name after " class " . ) " ) ) {
n_class - > identifier = parse_identifier ( ) ;
2022-11-08 12:51:20 +01:00
if ( n_class - > outer ) {
2022-10-09 18:41:28 +02:00
String fqcn = n_class - > outer - > fqcn ;
if ( fqcn . is_empty ( ) ) {
fqcn = script_path ;
}
n_class - > fqcn = fqcn + " :: " + n_class - > identifier - > name ;
} else {
n_class - > fqcn = n_class - > identifier - > name ;
2022-11-08 12:51:20 +01:00
}
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-16 03:02:44 +02:00
if ( match ( GDScriptTokenizer : : Token : : EXTENDS ) ) {
2020-05-02 00:14:56 +02:00
parse_extends ( ) ;
}
2016-07-22 14:22:34 +02:00
2020-05-02 00:14:56 +02:00
consume ( GDScriptTokenizer : : Token : : COLON , R " (Expected " : " after class declaration.) " ) ;
2014-02-10 02:10:30 +01:00
2021-09-21 19:13:23 +02:00
bool multiline = match ( GDScriptTokenizer : : Token : : NEWLINE ) ;
if ( multiline & & ! consume ( GDScriptTokenizer : : Token : : INDENT , R " (Expected indented block after class declaration.) " ) ) {
2020-05-02 00:14:56 +02:00
current_class = previous_class ;
2022-07-11 20:31:15 +02:00
complete_extents ( n_class ) ;
2020-05-02 00:14:56 +02:00
return n_class ;
}
2014-04-05 23:50:09 +02:00
2020-08-31 15:27:11 +02:00
if ( match ( GDScriptTokenizer : : Token : : EXTENDS ) ) {
if ( n_class - > extends_used ) {
push_error ( R " (Cannot use " extends " more than once in the same class.) " ) ;
}
parse_extends ( ) ;
end_statement ( " superclass " ) ;
}
2021-09-21 19:13:23 +02:00
parse_class_body ( multiline ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( n_class ) ;
2019-09-02 13:46:38 +02:00
2021-09-21 19:13:23 +02:00
if ( multiline ) {
consume ( GDScriptTokenizer : : Token : : DEDENT , R " (Missing unindent at the end of the class body.) " ) ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
current_class = previous_class ;
return n_class ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
void GDScriptParser : : parse_class_name ( ) {
if ( consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected identifier for the global class name after " class_name " .) " ) ) {
current_class - > identifier = parse_identifier ( ) ;
2022-11-08 12:51:20 +01:00
current_class - > fqcn = String ( current_class - > identifier - > name ) ;
2020-05-02 00:14:56 +02:00
}
2017-01-08 06:04:53 +01:00
2020-05-02 00:14:56 +02:00
if ( match ( GDScriptTokenizer : : Token : : EXTENDS ) ) {
// Allow extends on the same line.
parse_extends ( ) ;
2020-07-16 03:02:44 +02:00
end_statement ( " superclass " ) ;
2020-05-02 00:14:56 +02:00
} else {
end_statement ( " class_name statement " ) ;
}
}
2017-01-08 06:04:53 +01:00
2020-05-02 00:14:56 +02:00
void GDScriptParser : : parse_extends ( ) {
current_class - > extends_used = true ;
2017-01-08 06:04:53 +01:00
2020-07-06 17:24:24 +02:00
int chain_index = 0 ;
2020-05-02 00:14:56 +02:00
if ( match ( GDScriptTokenizer : : Token : : LITERAL ) ) {
if ( previous . literal . get_type ( ) ! = Variant : : STRING ) {
push_error ( vformat ( R " (Only strings or identifiers can be used after " extends " , found " % s " instead.) " , Variant : : get_type_name ( previous . literal . get_type ( ) ) ) ) ;
}
current_class - > extends_path = previous . literal ;
2017-03-31 19:28:34 +02:00
2020-05-02 00:14:56 +02:00
if ( ! match ( GDScriptTokenizer : : Token : : PERIOD ) ) {
return ;
}
}
2017-01-08 06:04:53 +01:00
2020-07-06 17:24:24 +02:00
make_completion_context ( COMPLETION_INHERIT_TYPE , current_class , chain_index + + ) ;
2020-05-02 00:14:56 +02:00
if ( ! consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected superclass name after " extends " .) " ) ) {
return ;
}
current_class - > extends . push_back ( previous . literal ) ;
2017-01-08 06:04:53 +01:00
2020-05-02 00:14:56 +02:00
while ( match ( GDScriptTokenizer : : Token : : PERIOD ) ) {
2020-07-06 17:24:24 +02:00
make_completion_context ( COMPLETION_INHERIT_TYPE , current_class , chain_index + + ) ;
2020-05-02 00:14:56 +02:00
if ( ! consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected superclass name after " . " .) " ) ) {
return ;
}
current_class - > extends . push_back ( previous . literal ) ;
}
}
2017-02-16 14:29:18 +01:00
2020-05-02 00:14:56 +02:00
template < class T >
void GDScriptParser : : parse_class_member ( T * ( GDScriptParser : : * p_parse_function ) ( ) , AnnotationInfo : : TargetKind p_target , const String & p_member_kind ) {
advance ( ) ;
2022-01-04 13:32:43 +01:00
2020-11-29 03:37:57 +01:00
# ifdef TOOLS_ENABLED
2022-01-04 13:32:43 +01:00
int doc_comment_line = previous . start_line - 1 ;
2020-11-29 03:37:57 +01:00
# endif // TOOLS_ENABLED
2020-05-02 00:14:56 +02:00
// Consume annotations.
2022-01-04 13:32:43 +01:00
List < AnnotationNode * > annotations ;
2020-12-15 13:04:21 +01:00
while ( ! annotation_stack . is_empty ( ) ) {
2020-05-02 00:14:56 +02:00
AnnotationNode * last_annotation = annotation_stack . back ( ) - > get ( ) ;
if ( last_annotation - > applies_to ( p_target ) ) {
2022-01-04 13:32:43 +01:00
annotations . push_front ( last_annotation ) ;
2020-05-02 00:14:56 +02:00
annotation_stack . pop_back ( ) ;
} else {
push_error ( vformat ( R " (Annotation " % s " cannot be applied to a %s.) " , last_annotation - > name , p_member_kind ) ) ;
clear_unused_annotations ( ) ;
}
2020-11-29 03:37:57 +01:00
# ifdef TOOLS_ENABLED
if ( last_annotation - > start_line = = doc_comment_line ) {
doc_comment_line - - ;
}
# endif // TOOLS_ENABLED
2020-05-02 00:14:56 +02:00
}
2020-11-29 03:37:57 +01:00
2022-01-04 13:32:43 +01:00
T * member = ( this - > * p_parse_function ) ( ) ;
if ( member = = nullptr ) {
return ;
}
// Apply annotations.
for ( AnnotationNode * & annotation : annotations ) {
member - > annotations . push_back ( annotation ) ;
}
2020-11-29 03:37:57 +01:00
# ifdef TOOLS_ENABLED
// Consume doc comments.
2020-11-29 04:42:06 +01:00
class_doc_line = MIN ( class_doc_line , doc_comment_line - 1 ) ;
2020-11-29 03:37:57 +01:00
if ( has_comment ( doc_comment_line ) ) {
if constexpr ( std : : is_same_v < T , ClassNode > ) {
get_class_doc_comment ( doc_comment_line , member - > doc_brief_description , member - > doc_description , member - > doc_tutorials , true ) ;
} else {
member - > doc_description = get_doc_comment ( doc_comment_line ) ;
}
}
# endif // TOOLS_ENABLED
2020-05-02 00:14:56 +02:00
if ( member - > identifier ! = nullptr ) {
2021-11-06 19:12:19 +01:00
if ( ! ( ( String ) member - > identifier - > name ) . is_empty ( ) ) { // Enums may be unnamed.
2021-11-13 16:40:53 +01:00
# ifdef DEBUG_ENABLED
2021-11-06 19:12:19 +01:00
List < MethodInfo > gdscript_funcs ;
GDScriptLanguage : : get_singleton ( ) - > get_public_functions ( & gdscript_funcs ) ;
for ( MethodInfo & info : gdscript_funcs ) {
if ( info . name = = member - > identifier - > name ) {
2021-11-13 16:40:53 +01:00
push_warning ( member - > identifier , GDScriptWarning : : SHADOWED_GLOBAL_IDENTIFIER , p_member_kind , member - > identifier - > name , " built-in function " ) ;
2021-11-06 19:12:19 +01:00
}
}
2021-11-13 16:40:53 +01:00
if ( Variant : : has_utility_function ( member - > identifier - > name ) ) {
push_warning ( member - > identifier , GDScriptWarning : : SHADOWED_GLOBAL_IDENTIFIER , p_member_kind , member - > identifier - > name , " built-in function " ) ;
}
# endif
2021-11-06 19:12:19 +01:00
if ( current_class - > members_indices . has ( member - > identifier - > name ) ) {
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 ) ;
} else {
current_class - > add_member ( member ) ;
}
2020-05-02 00:14:56 +02:00
} else {
current_class - > add_member ( member ) ;
}
}
}
2017-02-16 14:29:18 +01:00
2021-09-21 19:13:23 +02:00
void GDScriptParser : : parse_class_body ( bool p_is_multiline ) {
2020-05-02 00:14:56 +02:00
bool class_end = false ;
while ( ! class_end & & ! is_at_end ( ) ) {
switch ( current . type ) {
case GDScriptTokenizer : : Token : : VAR :
parse_class_member ( & GDScriptParser : : parse_variable , AnnotationInfo : : VARIABLE , " variable " ) ;
break ;
case GDScriptTokenizer : : Token : : CONST :
parse_class_member ( & GDScriptParser : : parse_constant , AnnotationInfo : : CONSTANT , " constant " ) ;
break ;
case GDScriptTokenizer : : Token : : SIGNAL :
parse_class_member ( & GDScriptParser : : parse_signal , AnnotationInfo : : SIGNAL , " signal " ) ;
break ;
case GDScriptTokenizer : : Token : : STATIC :
case GDScriptTokenizer : : Token : : FUNC :
parse_class_member ( & GDScriptParser : : parse_function , AnnotationInfo : : FUNCTION , " function " ) ;
break ;
case GDScriptTokenizer : : Token : : CLASS :
parse_class_member ( & GDScriptParser : : parse_class , AnnotationInfo : : CLASS , " class " ) ;
break ;
case GDScriptTokenizer : : Token : : ENUM :
parse_class_member ( & GDScriptParser : : parse_enum , AnnotationInfo : : NONE , " enum " ) ;
break ;
case GDScriptTokenizer : : Token : : ANNOTATION : {
advance ( ) ;
2022-07-03 21:30:08 +02:00
// Check for class-level annotations.
AnnotationNode * annotation = parse_annotation ( AnnotationInfo : : STANDALONE | AnnotationInfo : : CLASS_LEVEL ) ;
2020-05-02 00:14:56 +02:00
if ( annotation ! = nullptr ) {
2022-07-03 21:30:08 +02:00
if ( annotation - > applies_to ( AnnotationInfo : : STANDALONE ) ) {
if ( previous . type ! = GDScriptTokenizer : : Token : : NEWLINE ) {
push_error ( R " (Expected newline after a standalone annotation.) " ) ;
}
annotation - > apply ( this , head ) ;
} else {
annotation_stack . push_back ( annotation ) ;
}
2017-02-16 14:29:18 +01:00
}
2020-05-02 00:14:56 +02:00
break ;
2016-01-24 23:45:11 +01:00
}
2020-05-02 00:14:56 +02:00
case GDScriptTokenizer : : Token : : PASS :
2020-07-16 03:02:44 +02:00
advance ( ) ;
2020-05-02 00:14:56 +02:00
end_statement ( R " ( " pass " ) " ) ;
break ;
case GDScriptTokenizer : : Token : : DEDENT :
class_end = true ;
break ;
default :
2021-12-30 11:25:02 +01:00
// Display a completion with identifiers.
make_completion_context ( COMPLETION_IDENTIFIER , nullptr ) ;
2020-05-02 00:14:56 +02:00
push_error ( vformat ( R " (Unexpected " % s " in class body.) " , current . get_name ( ) ) ) ;
advance ( ) ;
break ;
}
if ( panic_mode ) {
synchronize ( ) ;
}
2021-09-21 19:13:23 +02:00
if ( ! p_is_multiline ) {
class_end = true ;
}
2020-05-02 00:14:56 +02:00
}
}
2017-02-16 14:29:18 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : VariableNode * GDScriptParser : : parse_variable ( ) {
2020-06-01 21:41:05 +02:00
return parse_variable ( true ) ;
}
GDScriptParser : : VariableNode * GDScriptParser : : parse_variable ( bool p_allow_property ) {
2022-07-11 20:31:15 +02:00
VariableNode * variable = alloc_node < VariableNode > ( ) ;
2020-05-02 00:14:56 +02:00
if ( ! consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected variable name after " var " .) " ) ) {
2022-07-11 20:31:15 +02:00
complete_extents ( variable ) ;
2020-05-02 00:14:56 +02:00
return nullptr ;
}
2017-02-16 14:29:18 +01:00
2021-12-13 09:39:16 +01:00
variable - > identifier = parse_identifier ( ) ;
variable - > export_info . name = variable - > identifier - > name ;
2014-12-07 06:04:20 +01:00
2020-05-02 00:14:56 +02:00
if ( match ( GDScriptTokenizer : : Token : : COLON ) ) {
2020-06-01 21:41:05 +02:00
if ( check ( GDScriptTokenizer : : Token : : NEWLINE ) ) {
if ( p_allow_property ) {
advance ( ) ;
return parse_property ( variable , true ) ;
} else {
push_error ( R " (Expected type after " : " ) " ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( variable ) ;
2020-06-01 21:41:05 +02:00
return nullptr ;
}
} else if ( check ( ( GDScriptTokenizer : : Token : : EQUAL ) ) ) {
2020-05-02 00:14:56 +02:00
// Infer type.
variable - > infer_datatype = true ;
} else {
2020-07-06 17:24:24 +02:00
if ( p_allow_property ) {
make_completion_context ( COMPLETION_PROPERTY_DECLARATION_OR_TYPE , variable ) ;
if ( check ( GDScriptTokenizer : : Token : : IDENTIFIER ) ) {
// Check if get or set.
if ( current . get_identifier ( ) = = " get " | | current . get_identifier ( ) = = " set " ) {
return parse_property ( variable , false ) ;
}
2020-06-01 21:41:05 +02:00
}
}
2020-05-02 00:14:56 +02:00
// Parse type.
variable - > datatype_specifier = parse_type ( ) ;
}
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( match ( GDScriptTokenizer : : Token : : EQUAL ) ) {
// Initializer.
variable - > initializer = parse_expression ( false ) ;
2021-03-17 15:09:57 +01:00
if ( variable - > initializer = = nullptr ) {
push_error ( R " (Expected expression for variable initial value after " = " .) " ) ;
}
2020-06-12 00:31:28 +02:00
variable - > assignments + + ;
2020-05-02 00:14:56 +02:00
}
2018-05-30 04:16:54 +02:00
2020-06-01 21:41:05 +02:00
if ( p_allow_property & & match ( GDScriptTokenizer : : Token : : COLON ) ) {
if ( match ( GDScriptTokenizer : : Token : : NEWLINE ) ) {
return parse_property ( variable , true ) ;
} else {
return parse_property ( variable , false ) ;
}
}
2022-07-11 20:31:15 +02:00
complete_extents ( variable ) ;
2020-05-02 00:14:56 +02:00
end_statement ( " variable declaration " ) ;
2018-05-30 04:16:54 +02:00
2020-05-02 00:14:56 +02:00
return variable ;
}
2014-02-10 02:10:30 +01:00
2020-06-01 21:41:05 +02:00
GDScriptParser : : VariableNode * GDScriptParser : : parse_property ( VariableNode * p_variable , bool p_need_indent ) {
if ( p_need_indent ) {
if ( ! consume ( GDScriptTokenizer : : Token : : INDENT , R " (Expected indented block for property after " : " .) " ) ) {
2022-07-11 20:31:15 +02:00
complete_extents ( p_variable ) ;
2020-06-01 21:41:05 +02:00
return nullptr ;
}
}
2020-07-06 17:24:24 +02:00
VariableNode * property = p_variable ;
make_completion_context ( COMPLETION_PROPERTY_DECLARATION , property ) ;
2020-06-01 21:41:05 +02:00
if ( ! consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected " get " or " set " for property declaration.) " ) ) {
2022-07-11 20:31:15 +02:00
complete_extents ( p_variable ) ;
2020-06-01 21:41:05 +02:00
return nullptr ;
}
IdentifierNode * function = parse_identifier ( ) ;
if ( check ( GDScriptTokenizer : : Token : : EQUAL ) ) {
p_variable - > property = VariableNode : : PROP_SETGET ;
} else {
p_variable - > property = VariableNode : : PROP_INLINE ;
if ( ! p_need_indent ) {
push_error ( " Property with inline code must go to an indented block. " ) ;
}
}
bool getter_used = false ;
bool setter_used = false ;
// Run with a loop because order doesn't matter.
for ( int i = 0 ; i < 2 ; i + + ) {
if ( function - > name = = " set " ) {
if ( setter_used ) {
push_error ( R " (Properties can only have one setter.) " ) ;
} else {
parse_property_setter ( property ) ;
setter_used = true ;
}
} else if ( function - > name = = " get " ) {
if ( getter_used ) {
push_error ( R " (Properties can only have one getter.) " ) ;
} else {
parse_property_getter ( property ) ;
getter_used = true ;
}
} else {
// TODO: Update message to only have the missing one if it's the case.
push_error ( R " (Expected " get " or " set " for property declaration.) " ) ;
}
if ( i = = 0 & & p_variable - > property = = VariableNode : : PROP_SETGET ) {
if ( match ( GDScriptTokenizer : : Token : : COMMA ) ) {
// Consume potential newline.
if ( match ( GDScriptTokenizer : : Token : : NEWLINE ) ) {
if ( ! p_need_indent ) {
push_error ( R " (Inline setter/getter setting cannot span across multiple lines (use " \ \ " " if needed ) . ) " );
}
}
} else {
break ;
}
}
if ( ! match ( GDScriptTokenizer : : Token : : IDENTIFIER ) ) {
break ;
}
function = parse_identifier ( ) ;
}
2022-07-11 20:31:15 +02:00
complete_extents ( p_variable ) ;
2020-06-01 21:41:05 +02:00
if ( p_variable - > property = = VariableNode : : PROP_SETGET ) {
end_statement ( " property declaration " ) ;
}
if ( p_need_indent ) {
consume ( GDScriptTokenizer : : Token : : DEDENT , R " (Expected end of indented block for property.) " ) ;
}
return property ;
}
void GDScriptParser : : parse_property_setter ( VariableNode * p_variable ) {
switch ( p_variable - > property ) {
2021-09-06 07:04:43 +02:00
case VariableNode : : PROP_INLINE : {
2022-07-11 20:31:15 +02:00
FunctionNode * function = alloc_node < FunctionNode > ( ) ;
2021-09-06 07:04:43 +02:00
IdentifierNode * identifier = alloc_node < IdentifierNode > ( ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( identifier ) ;
2021-09-06 07:04:43 +02:00
identifier - > name = " @ " + p_variable - > identifier - > name + " _setter " ;
function - > identifier = identifier ;
2022-07-11 20:31:15 +02:00
consume ( GDScriptTokenizer : : Token : : PARENTHESIS_OPEN , R " (Expected " ( " after " set " .) " ) ;
2021-09-06 07:04:43 +02:00
ParameterNode * parameter = alloc_node < ParameterNode > ( ) ;
2022-07-11 20:31:15 +02:00
if ( consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected parameter name after " ( " .) " ) ) {
reset_extents ( parameter , previous ) ;
p_variable - > setter_parameter = parse_identifier ( ) ;
parameter - > identifier = p_variable - > setter_parameter ;
2021-10-15 20:53:24 +02:00
function - > parameters_indices [ parameter - > identifier - > name ] = 0 ;
function - > parameters . push_back ( parameter ) ;
2022-07-11 20:31:15 +02:00
}
complete_extents ( parameter ) ;
2021-09-06 07:04:43 +02:00
2022-07-11 20:31:15 +02:00
consume ( GDScriptTokenizer : : Token : : PARENTHESIS_CLOSE , R " *(Expected " ) " after parameter name.)* " ) ;
consume ( GDScriptTokenizer : : Token : : COLON , R " *(Expected " : " after " ) " .)* " ) ;
FunctionNode * previous_function = current_function ;
current_function = function ;
if ( p_variable - > setter_parameter ! = nullptr ) {
2021-10-15 20:53:24 +02:00
SuiteNode * body = alloc_node < SuiteNode > ( ) ;
body - > add_local ( parameter , function ) ;
function - > body = parse_suite ( " setter declaration " , body ) ;
p_variable - > setter = function ;
}
2021-09-06 07:04:43 +02:00
current_function = previous_function ;
2022-07-11 20:31:15 +02:00
complete_extents ( function ) ;
2021-09-06 07:04:43 +02:00
break ;
}
2020-06-01 21:41:05 +02:00
case VariableNode : : PROP_SETGET :
consume ( GDScriptTokenizer : : Token : : EQUAL , R " (Expected " = " after " set " ) " ) ;
2020-07-06 17:24:24 +02:00
make_completion_context ( COMPLETION_PROPERTY_METHOD , p_variable ) ;
2020-06-01 21:41:05 +02:00
if ( consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected setter function name after " = " .) " ) ) {
p_variable - > setter_pointer = parse_identifier ( ) ;
}
break ;
case VariableNode : : PROP_NONE :
break ; // Unreachable.
}
}
void GDScriptParser : : parse_property_getter ( VariableNode * p_variable ) {
switch ( p_variable - > property ) {
2021-09-06 07:04:43 +02:00
case VariableNode : : PROP_INLINE : {
2022-07-11 20:31:15 +02:00
FunctionNode * function = alloc_node < FunctionNode > ( ) ;
2020-06-01 21:41:05 +02:00
consume ( GDScriptTokenizer : : Token : : COLON , R " (Expected " : " after " get " .) " ) ;
2021-09-06 07:04:43 +02:00
IdentifierNode * identifier = alloc_node < IdentifierNode > ( ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( identifier ) ;
2021-09-06 07:04:43 +02:00
identifier - > name = " @ " + p_variable - > identifier - > name + " _getter " ;
function - > identifier = identifier ;
FunctionNode * previous_function = current_function ;
current_function = function ;
SuiteNode * body = alloc_node < SuiteNode > ( ) ;
function - > body = parse_suite ( " getter declaration " , body ) ;
p_variable - > getter = function ;
2022-07-11 20:31:15 +02:00
2021-09-06 07:04:43 +02:00
current_function = previous_function ;
2022-07-11 20:31:15 +02:00
complete_extents ( function ) ;
2020-06-01 21:41:05 +02:00
break ;
2021-09-06 07:04:43 +02:00
}
2020-06-01 21:41:05 +02:00
case VariableNode : : PROP_SETGET :
consume ( GDScriptTokenizer : : Token : : EQUAL , R " (Expected " = " after " get " ) " ) ;
2020-07-06 17:24:24 +02:00
make_completion_context ( COMPLETION_PROPERTY_METHOD , p_variable ) ;
2020-06-01 21:41:05 +02:00
if ( consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected getter function name after " = " .) " ) ) {
p_variable - > getter_pointer = parse_identifier ( ) ;
}
break ;
case VariableNode : : PROP_NONE :
break ; // Unreachable.
}
}
2020-05-02 00:14:56 +02:00
GDScriptParser : : ConstantNode * GDScriptParser : : parse_constant ( ) {
2022-07-11 20:31:15 +02:00
ConstantNode * constant = alloc_node < ConstantNode > ( ) ;
2020-05-02 00:14:56 +02:00
if ( ! consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected constant name after " const " .) " ) ) {
return nullptr ;
}
2018-05-30 04:16:54 +02:00
2020-05-02 00:14:56 +02:00
constant - > identifier = parse_identifier ( ) ;
2014-09-15 16:33:30 +02:00
2020-05-02 00:14:56 +02:00
if ( match ( GDScriptTokenizer : : Token : : COLON ) ) {
if ( check ( ( GDScriptTokenizer : : Token : : EQUAL ) ) ) {
// Infer type.
constant - > infer_datatype = true ;
} else {
// Parse type.
constant - > datatype_specifier = parse_type ( ) ;
}
}
2014-09-15 16:33:30 +02:00
2020-05-02 00:14:56 +02:00
if ( consume ( GDScriptTokenizer : : Token : : EQUAL , R " (Expected initializer after constant name.) " ) ) {
// Initializer.
constant - > initializer = parse_expression ( false ) ;
2014-09-15 16:33:30 +02:00
2020-05-02 00:14:56 +02:00
if ( constant - > initializer = = nullptr ) {
push_error ( R " (Expected initializer expression for constant.) " ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( constant ) ;
2020-05-02 00:14:56 +02:00
return nullptr ;
}
2020-12-30 18:57:46 +01:00
} else {
2022-07-11 20:31:15 +02:00
complete_extents ( constant ) ;
2020-12-30 18:57:46 +01:00
return nullptr ;
2020-05-02 00:14:56 +02:00
}
2014-09-15 16:33:30 +02:00
2022-07-11 20:31:15 +02:00
complete_extents ( constant ) ;
2020-05-02 00:14:56 +02:00
end_statement ( " constant declaration " ) ;
2016-10-03 20:18:21 +02:00
2020-05-02 00:14:56 +02:00
return constant ;
}
2016-10-03 20:18:21 +02:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ParameterNode * GDScriptParser : : parse_parameter ( ) {
if ( ! consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected parameter name.) " ) ) {
return nullptr ;
}
2014-09-15 16:33:30 +02:00
2020-05-02 00:14:56 +02:00
ParameterNode * parameter = alloc_node < ParameterNode > ( ) ;
2021-12-13 09:39:16 +01:00
parameter - > identifier = parse_identifier ( ) ;
2014-09-15 16:33:30 +02:00
2020-05-02 00:14:56 +02:00
if ( match ( GDScriptTokenizer : : Token : : COLON ) ) {
if ( check ( ( GDScriptTokenizer : : Token : : EQUAL ) ) ) {
// Infer type.
parameter - > infer_datatype = true ;
} else {
// Parse type.
2020-07-06 17:24:24 +02:00
make_completion_context ( COMPLETION_TYPE_NAME , parameter ) ;
2020-05-02 00:14:56 +02:00
parameter - > datatype_specifier = parse_type ( ) ;
}
}
2016-08-07 03:11:03 +02:00
2020-05-02 00:14:56 +02:00
if ( match ( GDScriptTokenizer : : Token : : EQUAL ) ) {
// Default value.
parameter - > default_value = parse_expression ( false ) ;
}
2014-09-15 16:33:30 +02:00
2022-07-11 20:31:15 +02:00
complete_extents ( parameter ) ;
2020-05-02 00:14:56 +02:00
return parameter ;
}
2014-09-15 16:33:30 +02:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : SignalNode * GDScriptParser : : parse_signal ( ) {
2022-07-11 20:31:15 +02:00
SignalNode * signal = alloc_node < SignalNode > ( ) ;
2020-05-02 00:14:56 +02:00
if ( ! consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected signal name after " signal " .) " ) ) {
2022-07-11 20:31:15 +02:00
complete_extents ( signal ) ;
2020-05-02 00:14:56 +02:00
return nullptr ;
}
2016-10-03 20:18:21 +02:00
2020-05-02 00:14:56 +02:00
signal - > identifier = parse_identifier ( ) ;
2014-09-15 16:33:30 +02:00
2021-09-21 18:40:39 +02:00
if ( check ( GDScriptTokenizer : : Token : : PARENTHESIS_OPEN ) ) {
push_multiline ( true ) ;
advance ( ) ;
2020-08-19 16:32:48 +02:00
do {
if ( check ( GDScriptTokenizer : : Token : : PARENTHESIS_CLOSE ) ) {
// Allow for trailing comma.
break ;
}
2020-05-02 00:14:56 +02:00
ParameterNode * parameter = parse_parameter ( ) ;
if ( parameter = = nullptr ) {
2020-08-19 16:32:48 +02:00
push_error ( " Expected signal parameter name. " ) ;
2020-05-02 00:14:56 +02:00
break ;
2014-09-15 16:33:30 +02:00
}
2020-05-02 00:14:56 +02:00
if ( parameter - > default_value ! = nullptr ) {
push_error ( R " (Signal parameters cannot have a default value.) " ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
if ( signal - > parameters_indices . has ( parameter - > identifier - > name ) ) {
push_error ( vformat ( R " (Parameter with name " % s " was already declared for this signal.) " , parameter - > identifier - > name ) ) ;
} else {
signal - > parameters_indices [ parameter - > identifier - > name ] = signal - > parameters . size ( ) ;
signal - > parameters . push_back ( parameter ) ;
2014-12-17 02:31:57 +01:00
}
2020-08-19 16:32:48 +02:00
} while ( match ( GDScriptTokenizer : : Token : : COMMA ) & & ! is_at_end ( ) ) ;
2021-09-21 18:40:39 +02:00
pop_multiline ( ) ;
2020-05-02 00:14:56 +02:00
consume ( GDScriptTokenizer : : Token : : PARENTHESIS_CLOSE , R " *(Expected closing " ) " after signal parameters.)* " ) ;
}
2014-12-17 02:31:57 +01:00
2022-07-11 20:31:15 +02:00
complete_extents ( signal ) ;
2020-05-02 00:14:56 +02:00
end_statement ( " signal declaration " ) ;
2018-01-18 22:03:34 +01:00
2020-05-02 00:14:56 +02:00
return signal ;
}
2018-01-18 22:03:34 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : EnumNode * GDScriptParser : : parse_enum ( ) {
EnumNode * enum_node = alloc_node < EnumNode > ( ) ;
bool named = false ;
2018-01-18 22:03:34 +01:00
2020-05-02 00:14:56 +02:00
if ( check ( GDScriptTokenizer : : Token : : IDENTIFIER ) ) {
advance ( ) ;
enum_node - > identifier = parse_identifier ( ) ;
named = true ;
}
2018-01-18 22:03:34 +01:00
2020-05-02 00:14:56 +02:00
push_multiline ( true ) ;
consume ( GDScriptTokenizer : : Token : : BRACE_OPEN , vformat ( R " (Expected " { " after %s.) " , named ? " enum name " : R " ( " enum " ) " ) ) ;
2014-02-10 02:10:30 +01:00
2020-08-19 16:14:16 +02:00
HashMap < StringName , int > elements ;
2021-11-13 16:40:53 +01:00
# ifdef DEBUG_ENABLED
2021-11-06 19:12:19 +01:00
List < MethodInfo > gdscript_funcs ;
GDScriptLanguage : : get_singleton ( ) - > get_public_functions ( & gdscript_funcs ) ;
2021-11-13 16:40:53 +01:00
# endif
2021-11-06 19:12:19 +01:00
2020-05-02 00:14:56 +02:00
do {
if ( check ( GDScriptTokenizer : : Token : : BRACE_CLOSE ) ) {
break ; // Allow trailing comma.
}
2020-09-18 13:35:51 +02:00
if ( consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected identifier for enum key.) " ) ) {
2020-05-02 00:14:56 +02:00
EnumNode : : Value item ;
2021-11-06 19:12:19 +01:00
GDScriptParser : : IdentifierNode * identifier = parse_identifier ( ) ;
2021-11-13 16:40:53 +01:00
# ifdef DEBUG_ENABLED
2021-11-06 19:12:19 +01:00
for ( MethodInfo & info : gdscript_funcs ) {
if ( info . name = = identifier - > name ) {
2021-11-13 16:40:53 +01:00
push_warning ( identifier , GDScriptWarning : : SHADOWED_GLOBAL_IDENTIFIER , " enum member " , identifier - > name , " built-in function " ) ;
2021-11-06 19:12:19 +01:00
}
}
if ( Variant : : has_utility_function ( identifier - > name ) ) {
2021-11-13 16:40:53 +01:00
push_warning ( identifier , GDScriptWarning : : SHADOWED_GLOBAL_IDENTIFIER , " enum member " , identifier - > name , " built-in function " ) ;
2021-11-06 19:12:19 +01:00
} else if ( ClassDB : : class_exists ( identifier - > name ) ) {
2021-11-13 16:40:53 +01:00
push_warning ( identifier , GDScriptWarning : : SHADOWED_GLOBAL_IDENTIFIER , " enum member " , identifier - > name , " global class " ) ;
2021-11-06 19:12:19 +01:00
}
2021-11-13 16:40:53 +01:00
# endif
2021-11-06 19:12:19 +01:00
item . identifier = identifier ;
2020-08-18 22:44:20 +02:00
item . parent_enum = enum_node ;
item . line = previous . start_line ;
item . leftmost_column = previous . leftmost_column ;
2020-11-29 03:37:57 +01:00
item . rightmost_column = previous . rightmost_column ;
2020-05-02 00:14:56 +02:00
2020-08-19 16:14:16 +02:00
if ( elements . has ( item . identifier - > name ) ) {
push_error ( vformat ( R " (Name " % s " was already in this enum (at line %d).) " , item . identifier - > name , elements [ item . identifier - > name ] ) , item . identifier ) ;
} else if ( ! named ) {
2020-05-02 00:14:56 +02:00
// TODO: Abstract this recursive member check.
ClassNode * parent = current_class ;
while ( parent ! = nullptr ) {
if ( parent - > members_indices . has ( item . identifier - > name ) ) {
push_error ( vformat ( R " (Name " % s " is already used as a class %s.) " , item . identifier - > name , parent - > get_member ( item . identifier - > name ) . get_type_name ( ) ) ) ;
break ;
2018-09-10 15:31:36 +02:00
}
2020-05-02 00:14:56 +02:00
parent = parent - > outer ;
2018-01-18 22:03:34 +01:00
}
}
2014-12-17 02:31:57 +01:00
2020-08-19 16:14:16 +02:00
elements [ item . identifier - > name ] = item . line ;
2020-05-02 00:14:56 +02:00
if ( match ( GDScriptTokenizer : : Token : : EQUAL ) ) {
2020-08-18 22:44:20 +02:00
ExpressionNode * value = parse_expression ( false ) ;
if ( value = = nullptr ) {
push_error ( R " (Expected expression value after " = " .) " ) ;
2017-11-17 05:42:24 +01:00
}
2020-08-18 22:44:20 +02:00
item . custom_value = value ;
2020-05-02 00:14:56 +02:00
}
2020-08-18 22:44:20 +02:00
item . index = enum_node - > values . size ( ) ;
2020-05-02 00:14:56 +02:00
enum_node - > values . push_back ( item ) ;
2020-06-12 02:49:58 +02:00
if ( ! named ) {
2020-05-02 00:14:56 +02:00
// Add as member of current class.
current_class - > add_member ( item ) ;
}
}
} while ( match ( GDScriptTokenizer : : Token : : COMMA ) ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
pop_multiline ( ) ;
consume ( GDScriptTokenizer : : Token : : BRACE_CLOSE , R " (Expected closing " } " for enum.) " ) ;
2014-02-10 02:10:30 +01:00
2020-11-29 03:37:57 +01:00
# ifdef TOOLS_ENABLED
2021-05-20 12:07:26 +02:00
// Enum values documentation.
2020-11-29 03:37:57 +01:00
for ( int i = 0 ; i < enum_node - > values . size ( ) ; i + + ) {
if ( i = = enum_node - > values . size ( ) - 1 ) {
// If close bracket is same line as last value.
if ( enum_node - > values [ i ] . line ! = previous . start_line & & has_comment ( enum_node - > values [ i ] . line ) ) {
if ( named ) {
enum_node - > values . write [ i ] . doc_description = get_doc_comment ( enum_node - > values [ i ] . line , true ) ;
} else {
current_class - > set_enum_value_doc ( enum_node - > values [ i ] . identifier - > name , get_doc_comment ( enum_node - > values [ i ] . line , true ) ) ;
}
}
} else {
// If two values are same line.
if ( enum_node - > values [ i ] . line ! = enum_node - > values [ i + 1 ] . line & & has_comment ( enum_node - > values [ i ] . line ) ) {
if ( named ) {
enum_node - > values . write [ i ] . doc_description = get_doc_comment ( enum_node - > values [ i ] . line , true ) ;
} else {
current_class - > set_enum_value_doc ( enum_node - > values [ i ] . identifier - > name , get_doc_comment ( enum_node - > values [ i ] . line , true ) ) ;
}
}
}
}
# endif // TOOLS_ENABLED
2022-07-11 20:31:15 +02:00
complete_extents ( enum_node ) ;
2020-05-02 00:14:56 +02:00
end_statement ( " enum " ) ;
2014-12-17 02:31:57 +01:00
2020-05-02 00:14:56 +02:00
return enum_node ;
}
2014-02-10 02:10:30 +01:00
2021-03-25 14:36:29 +01:00
void GDScriptParser : : parse_function_signature ( FunctionNode * p_function , SuiteNode * p_body , const String & p_type ) {
2020-05-02 00:14:56 +02:00
if ( ! check ( GDScriptTokenizer : : Token : : PARENTHESIS_CLOSE ) & & ! is_at_end ( ) ) {
bool default_used = false ;
do {
if ( check ( GDScriptTokenizer : : Token : : PARENTHESIS_CLOSE ) ) {
// Allow for trailing comma.
break ;
}
ParameterNode * parameter = parse_parameter ( ) ;
if ( parameter = = nullptr ) {
break ;
2014-12-17 02:31:57 +01:00
}
2020-05-02 00:14:56 +02:00
if ( parameter - > default_value ! = nullptr ) {
default_used = true ;
} else {
if ( default_used ) {
push_error ( " Cannot have a mandatory parameters after optional parameters. " ) ;
continue ;
2020-05-14 16:41:43 +02:00
}
2017-11-17 05:42:24 +01:00
}
2021-03-25 14:36:29 +01:00
if ( p_function - > parameters_indices . has ( parameter - > identifier - > name ) ) {
push_error ( vformat ( R " (Parameter with name " % s " was already declared for this %s.) " , parameter - > identifier - > name , p_type ) ) ;
2020-05-02 00:14:56 +02:00
} else {
2021-03-25 14:36:29 +01:00
p_function - > parameters_indices [ parameter - > identifier - > name ] = p_function - > parameters . size ( ) ;
p_function - > parameters . push_back ( parameter ) ;
2021-03-26 13:03:16 +01:00
p_body - > add_local ( parameter , current_function ) ;
2014-12-17 02:31:57 +01:00
}
2020-05-02 00:14:56 +02:00
} while ( match ( GDScriptTokenizer : : Token : : COMMA ) ) ;
}
2014-12-17 02:31:57 +01:00
2020-05-02 00:14:56 +02:00
pop_multiline ( ) ;
2021-03-25 14:36:29 +01:00
consume ( GDScriptTokenizer : : Token : : PARENTHESIS_CLOSE , vformat ( R " *(Expected closing " ) " after %s parameters.)* " , p_type ) ) ;
2014-12-17 02:31:57 +01:00
2020-05-02 00:14:56 +02:00
if ( match ( GDScriptTokenizer : : Token : : FORWARD_ARROW ) ) {
2021-03-25 14:36:29 +01:00
make_completion_context ( COMPLETION_TYPE_NAME_OR_VOID , p_function ) ;
p_function - > return_type = parse_type ( true ) ;
if ( p_function - > return_type = = nullptr ) {
2020-08-19 19:08:52 +02:00
push_error ( R " (Expected return type or " void " after " - > " .) " ) ;
}
2020-05-02 00:14:56 +02:00
}
2014-12-17 02:31:57 +01:00
2020-05-02 00:14:56 +02:00
// TODO: Improve token consumption so it synchronizes to a statement boundary. This way we can get into the function body with unrecognized tokens.
2021-03-25 14:36:29 +01:00
consume ( GDScriptTokenizer : : Token : : COLON , vformat ( R " (Expected " : " after %s declaration.) " , p_type ) ) ;
}
GDScriptParser : : FunctionNode * GDScriptParser : : parse_function ( ) {
2022-07-11 20:31:15 +02:00
FunctionNode * function = alloc_node < FunctionNode > ( ) ;
2021-03-25 14:36:29 +01:00
bool _static = false ;
if ( previous . type = = GDScriptTokenizer : : Token : : STATIC ) {
// TODO: Improve message if user uses "static" with "var" or "const"
if ( ! consume ( GDScriptTokenizer : : Token : : FUNC , R " (Expected " func " after " static " .) " ) ) {
2022-07-11 20:31:15 +02:00
complete_extents ( function ) ;
2021-03-25 14:36:29 +01:00
return nullptr ;
}
_static = true ;
}
make_completion_context ( COMPLETION_OVERRIDE_METHOD , function ) ;
if ( ! consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected function name after " func " .) " ) ) {
2022-07-11 20:31:15 +02:00
complete_extents ( function ) ;
2021-03-25 14:36:29 +01:00
return nullptr ;
}
FunctionNode * previous_function = current_function ;
current_function = function ;
function - > identifier = parse_identifier ( ) ;
function - > is_static = _static ;
SuiteNode * body = alloc_node < SuiteNode > ( ) ;
SuiteNode * previous_suite = current_suite ;
current_suite = body ;
push_multiline ( true ) ;
consume ( GDScriptTokenizer : : Token : : PARENTHESIS_OPEN , R " (Expected opening " ( " after function name.) " ) ;
parse_function_signature ( function , body , " function " ) ;
2018-05-28 18:38:35 +02:00
2020-06-10 23:18:10 +02:00
current_suite = previous_suite ;
function - > body = parse_suite ( " function declaration " , body ) ;
2019-01-17 22:17:06 +01:00
2020-05-02 00:14:56 +02:00
current_function = previous_function ;
2022-07-11 20:31:15 +02:00
complete_extents ( function ) ;
2020-05-02 00:14:56 +02:00
return function ;
}
2019-03-05 22:19:02 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : AnnotationNode * GDScriptParser : : parse_annotation ( uint32_t p_valid_targets ) {
AnnotationNode * annotation = alloc_node < AnnotationNode > ( ) ;
2016-06-30 03:17:55 +02:00
2020-05-02 00:14:56 +02:00
annotation - > name = previous . literal ;
2014-02-10 02:10:30 +01:00
2020-07-06 17:24:24 +02:00
make_completion_context ( COMPLETION_ANNOTATION , annotation ) ;
2020-05-02 00:14:56 +02:00
bool valid = true ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( ! valid_annotations . has ( annotation - > name ) ) {
push_error ( vformat ( R " (Unrecognized annotation: " % s " .) " , annotation - > name ) ) ;
valid = false ;
}
annotation - > info = & valid_annotations [ annotation - > name ] ;
if ( ! annotation - > applies_to ( p_valid_targets ) ) {
push_error ( vformat ( R " (Annotation " % s " is not allowed in this level.) " , annotation - > name ) ) ;
valid = false ;
}
if ( match ( GDScriptTokenizer : : Token : : PARENTHESIS_OPEN ) ) {
// Arguments.
2020-07-06 17:24:24 +02:00
push_completion_call ( annotation ) ;
make_completion_context ( COMPLETION_ANNOTATION_ARGUMENTS , annotation , 0 , true ) ;
2020-05-02 00:14:56 +02:00
if ( ! check ( GDScriptTokenizer : : Token : : PARENTHESIS_CLOSE ) & & ! is_at_end ( ) ) {
2022-03-10 08:29:40 +01:00
push_multiline ( true ) ;
2020-07-16 03:02:44 +02:00
int argument_index = 0 ;
2020-05-02 00:14:56 +02:00
do {
2020-07-16 03:02:44 +02:00
make_completion_context ( COMPLETION_ANNOTATION_ARGUMENTS , annotation , argument_index , true ) ;
set_last_completion_call_arg ( argument_index + + ) ;
2020-05-02 00:14:56 +02:00
ExpressionNode * argument = parse_expression ( false ) ;
if ( argument = = nullptr ) {
valid = false ;
continue ;
2019-04-09 17:08:36 +02:00
}
2020-05-02 00:14:56 +02:00
annotation - > arguments . push_back ( argument ) ;
} while ( match ( GDScriptTokenizer : : Token : : COMMA ) ) ;
2022-03-10 08:29:40 +01:00
pop_multiline ( ) ;
2020-05-02 00:14:56 +02:00
consume ( GDScriptTokenizer : : Token : : PARENTHESIS_CLOSE , R " *(Expected " ) " after annotation arguments.)* " ) ;
}
2020-07-06 17:24:24 +02:00
pop_completion_call ( ) ;
2020-05-02 00:14:56 +02:00
}
2022-07-11 20:31:15 +02:00
complete_extents ( annotation ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
match ( GDScriptTokenizer : : Token : : NEWLINE ) ; // Newline after annotation is optional.
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( valid ) {
valid = validate_annotation_arguments ( annotation ) ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
return valid ? annotation : nullptr ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
void GDScriptParser : : clear_unused_annotations ( ) {
2021-07-24 15:46:25 +02:00
for ( const AnnotationNode * annotation : annotation_stack ) {
2020-07-06 17:24:24 +02:00
push_error ( vformat ( R " (Annotation " % s " does not precedes a valid target, so it will have no effect.) " , annotation - > name ) , annotation ) ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
annotation_stack . clear ( ) ;
}
2022-07-11 22:02:55 +02:00
bool 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 ) {
2020-05-02 00:14:56 +02:00
ERR_FAIL_COND_V_MSG ( valid_annotations . has ( p_info . name ) , false , vformat ( R " (Annotation " % s " already registered.) " , p_info . name ) ) ;
2018-08-26 18:31:23 +02:00
2020-05-02 00:14:56 +02:00
AnnotationInfo new_annotation ;
new_annotation . info = p_info ;
2022-07-11 22:02:55 +02:00
new_annotation . info . default_arguments = p_default_arguments ;
2020-05-02 00:14:56 +02:00
if ( p_is_vararg ) {
new_annotation . info . flags | = METHOD_FLAG_VARARG ;
}
new_annotation . apply = p_apply ;
new_annotation . target_kind = p_target_kinds ;
2018-08-26 18:31:23 +02:00
2020-05-02 00:14:56 +02:00
valid_annotations [ p_info . name ] = new_annotation ;
return true ;
}
2018-08-26 18:31:23 +02:00
2021-03-25 14:36:29 +01:00
GDScriptParser : : SuiteNode * GDScriptParser : : parse_suite ( const String & p_context , SuiteNode * p_suite , bool p_for_lambda ) {
2020-06-10 23:18:10 +02:00
SuiteNode * suite = p_suite ! = nullptr ? p_suite : alloc_node < SuiteNode > ( ) ;
2020-05-02 00:14:56 +02:00
suite - > parent_block = current_suite ;
2021-03-26 13:03:16 +01:00
suite - > parent_function = current_function ;
2020-05-02 00:14:56 +02:00
current_suite = suite ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
bool multiline = false ;
2014-02-10 02:10:30 +01:00
2021-03-25 14:36:29 +01:00
if ( match ( GDScriptTokenizer : : Token : : NEWLINE ) ) {
2020-05-02 00:14:56 +02:00
multiline = true ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( multiline ) {
if ( ! consume ( GDScriptTokenizer : : Token : : INDENT , vformat ( R " (Expected indented block after %s.) " , p_context ) ) ) {
current_suite = suite - > parent_block ;
2022-07-11 20:31:15 +02:00
complete_extents ( suite ) ;
2020-05-02 00:14:56 +02:00
return suite ;
}
}
2022-07-11 20:31:15 +02:00
reset_extents ( suite , current ) ;
2020-05-02 00:14:56 +02:00
2021-03-25 14:36:29 +01:00
int error_count = 0 ;
2020-05-02 00:14:56 +02:00
do {
2021-09-21 19:13:23 +02:00
if ( ! multiline & & previous . type = = GDScriptTokenizer : : Token : : SEMICOLON & & check ( GDScriptTokenizer : : Token : : NEWLINE ) ) {
break ;
}
2020-05-02 00:14:56 +02:00
Node * statement = parse_statement ( ) ;
if ( statement = = nullptr ) {
2021-03-25 14:36:29 +01:00
if ( error_count + + > 100 ) {
push_error ( " Too many statement errors. " , suite ) ;
break ;
}
2020-05-02 00:14:56 +02:00
continue ;
}
suite - > statements . push_back ( statement ) ;
// Register locals.
switch ( statement - > type ) {
case Node : : VARIABLE : {
VariableNode * variable = static_cast < VariableNode * > ( statement ) ;
const SuiteNode : : Local & local = current_suite - > get_local ( variable - > identifier - > name ) ;
if ( local . type ! = SuiteNode : : Local : : UNDEFINED ) {
2022-11-18 21:20:26 +01:00
push_error ( vformat ( R " (There is already a %s named " % s " declared in this scope.) " , local . get_name ( ) , variable - > identifier - > name ) , variable - > identifier ) ;
2020-05-02 00:14:56 +02:00
}
2021-03-26 13:03:16 +01:00
current_suite - > add_local ( variable , current_function ) ;
2020-05-02 00:14:56 +02:00
break ;
}
case Node : : CONSTANT : {
ConstantNode * constant = static_cast < ConstantNode * > ( statement ) ;
const SuiteNode : : Local & local = current_suite - > get_local ( constant - > identifier - > name ) ;
if ( local . type ! = SuiteNode : : Local : : UNDEFINED ) {
String name ;
if ( local . type = = SuiteNode : : Local : : CONSTANT ) {
name = " constant " ;
} else {
name = " variable " ;
2020-05-14 16:41:43 +02:00
}
2022-11-18 21:20:26 +01:00
push_error ( vformat ( R " (There is already a %s named " % s " declared in this scope.) " , name , constant - > identifier - > name ) , constant - > identifier ) ;
2014-02-10 02:10:30 +01:00
}
2021-03-26 13:03:16 +01:00
current_suite - > add_local ( constant , current_function ) ;
2020-05-02 00:14:56 +02:00
break ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
default :
break ;
}
2014-02-10 02:10:30 +01:00
2021-09-21 19:13:23 +02:00
} while ( ( multiline | | previous . type = = GDScriptTokenizer : : Token : : SEMICOLON ) & & ! check ( GDScriptTokenizer : : Token : : DEDENT ) & & ! lambda_ended & & ! is_at_end ( ) ) ;
2014-02-10 02:10:30 +01:00
2022-07-11 20:31:15 +02:00
complete_extents ( suite ) ;
2020-05-02 00:14:56 +02:00
if ( multiline ) {
2021-03-25 14:36:29 +01:00
if ( ! lambda_ended ) {
consume ( GDScriptTokenizer : : Token : : DEDENT , vformat ( R " (Missing unindent at the end of %s.) " , p_context ) ) ;
} else {
match ( GDScriptTokenizer : : Token : : DEDENT ) ;
}
} else if ( previous . type = = GDScriptTokenizer : : Token : : SEMICOLON ) {
consume ( GDScriptTokenizer : : Token : : NEWLINE , vformat ( R " (Expected newline after " ; " at the end of %s.) " , p_context ) ) ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2021-03-25 14:36:29 +01:00
if ( p_for_lambda ) {
lambda_ended = true ;
}
2020-05-02 00:14:56 +02:00
current_suite = suite - > parent_block ;
return suite ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : Node * GDScriptParser : : parse_statement ( ) {
Node * result = nullptr ;
2020-07-16 03:02:44 +02:00
# ifdef DEBUG_ENABLED
2020-06-12 00:31:28 +02:00
bool unreachable = current_suite - > has_return & & ! current_suite - > has_unreachable_code ;
2020-07-16 03:02:44 +02:00
# endif
2020-06-12 00:31:28 +02:00
2022-01-04 13:32:43 +01:00
bool is_annotation = false ;
2020-05-02 00:14:56 +02:00
switch ( current . type ) {
case GDScriptTokenizer : : Token : : PASS :
advance ( ) ;
result = alloc_node < PassNode > ( ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( result ) ;
2020-05-02 00:14:56 +02:00
end_statement ( R " ( " pass " ) " ) ;
break ;
case GDScriptTokenizer : : Token : : VAR :
advance ( ) ;
result = parse_variable ( ) ;
break ;
case GDScriptTokenizer : : Token : : CONST :
advance ( ) ;
result = parse_constant ( ) ;
break ;
case GDScriptTokenizer : : Token : : IF :
advance ( ) ;
result = parse_if ( ) ;
break ;
case GDScriptTokenizer : : Token : : FOR :
advance ( ) ;
result = parse_for ( ) ;
break ;
case GDScriptTokenizer : : Token : : WHILE :
advance ( ) ;
result = parse_while ( ) ;
break ;
case GDScriptTokenizer : : Token : : MATCH :
advance ( ) ;
result = parse_match ( ) ;
break ;
case GDScriptTokenizer : : Token : : BREAK :
advance ( ) ;
result = parse_break ( ) ;
break ;
case GDScriptTokenizer : : Token : : CONTINUE :
advance ( ) ;
result = parse_continue ( ) ;
break ;
case GDScriptTokenizer : : Token : : RETURN : {
advance ( ) ;
ReturnNode * n_return = alloc_node < ReturnNode > ( ) ;
if ( ! is_statement_end ( ) ) {
2020-08-26 21:08:19 +02:00
if ( current_function & & current_function - > identifier - > name = = GDScriptLanguage : : get_singleton ( ) - > strings . _init ) {
2020-07-16 03:02:44 +02:00
push_error ( R " (Constructor cannot return a value.) " ) ;
}
2020-05-02 00:14:56 +02:00
n_return - > return_value = parse_expression ( false ) ;
2021-03-25 14:36:29 +01:00
} else if ( in_lambda & & ! is_statement_end_token ( ) ) {
// Try to parse it anyway as this might not be the statement end in a lambda.
// If this fails the expression will be nullptr, but that's the same as no return, so it's fine.
n_return - > return_value = parse_expression ( false ) ;
2020-05-02 00:14:56 +02:00
}
2022-07-11 20:31:15 +02:00
complete_extents ( n_return ) ;
2020-05-02 00:14:56 +02:00
result = n_return ;
2020-06-12 00:31:28 +02:00
current_suite - > has_return = true ;
2020-05-02 00:14:56 +02:00
end_statement ( " return statement " ) ;
break ;
}
case GDScriptTokenizer : : Token : : BREAKPOINT :
advance ( ) ;
result = alloc_node < BreakpointNode > ( ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( result ) ;
2020-05-02 00:14:56 +02:00
end_statement ( R " ( " breakpoint " ) " ) ;
break ;
case GDScriptTokenizer : : Token : : ASSERT :
advance ( ) ;
result = parse_assert ( ) ;
break ;
case GDScriptTokenizer : : Token : : ANNOTATION : {
advance ( ) ;
2022-01-04 13:32:43 +01:00
is_annotation = true ;
2020-05-02 00:14:56 +02:00
AnnotationNode * annotation = parse_annotation ( AnnotationInfo : : STATEMENT ) ;
if ( annotation ! = nullptr ) {
annotation_stack . push_back ( annotation ) ;
}
break ;
}
default : {
// Expression statement.
ExpressionNode * expression = parse_expression ( true ) ; // Allow assignment here.
2021-03-25 14:36:29 +01:00
bool has_ended_lambda = false ;
2020-06-01 21:41:05 +02:00
if ( expression = = nullptr ) {
2021-03-25 14:36:29 +01:00
if ( in_lambda ) {
// If it's not a valid expression beginning, it might be the continuation of the outer expression where this lambda is.
lambda_ended = true ;
has_ended_lambda = true ;
} else {
2022-07-11 01:42:40 +02:00
advance ( ) ;
2021-03-25 14:36:29 +01:00
push_error ( vformat ( R " (Expected statement, found " % s " instead.) " , previous . get_name ( ) ) ) ;
}
2022-07-11 02:32:28 +02:00
} else {
end_statement ( " expression " ) ;
2020-06-01 21:41:05 +02:00
}
2021-03-25 14:36:29 +01:00
lambda_ended = lambda_ended | | has_ended_lambda ;
2020-05-02 00:14:56 +02:00
result = expression ;
2020-06-12 00:31:28 +02:00
2020-07-16 03:02:44 +02:00
# ifdef DEBUG_ENABLED
2020-06-12 00:31:28 +02:00
if ( expression ! = nullptr ) {
switch ( expression - > type ) {
case Node : : CALL :
case Node : : ASSIGNMENT :
case Node : : AWAIT :
// Fine.
break ;
2022-05-23 17:25:03 +02:00
case Node : : LAMBDA :
// Standalone lambdas can't be used, so make this an error.
push_error ( " Standalone lambdas cannot be accessed. Consider assigning it to a variable. " , expression ) ;
break ;
2020-06-12 00:31:28 +02:00
default :
push_warning ( expression , GDScriptWarning : : STANDALONE_EXPRESSION ) ;
}
}
2020-07-16 03:02:44 +02:00
# endif
2020-05-02 00:14:56 +02:00
break ;
}
}
2014-02-10 02:10:30 +01:00
2022-01-04 13:32:43 +01:00
// Apply annotations to statement.
while ( ! is_annotation & & result ! = nullptr & & ! annotation_stack . is_empty ( ) ) {
AnnotationNode * last_annotation = annotation_stack . back ( ) - > get ( ) ;
if ( last_annotation - > applies_to ( AnnotationInfo : : STATEMENT ) ) {
result - > annotations . push_front ( last_annotation ) ;
annotation_stack . pop_back ( ) ;
} else {
push_error ( vformat ( R " (Annotation " % s " cannot be applied to a statement.) " , last_annotation - > name ) ) ;
clear_unused_annotations ( ) ;
}
}
2020-07-16 03:02:44 +02:00
# ifdef DEBUG_ENABLED
2020-11-13 15:09:52 +01:00
if ( unreachable & & result ! = nullptr ) {
2020-06-12 00:31:28 +02:00
current_suite - > has_unreachable_code = true ;
2020-10-31 02:21:50 +01:00
if ( current_function ) {
2021-03-25 14:36:29 +01:00
push_warning ( result , GDScriptWarning : : UNREACHABLE_CODE , current_function - > identifier ? current_function - > identifier - > name : " <anonymous lambda> " ) ;
2020-10-31 02:21:50 +01:00
} else {
// TODO: Properties setters and getters with unreachable code are not being warned
}
2020-06-12 00:31:28 +02:00
}
2020-07-16 03:02:44 +02:00
# endif
2020-06-12 00:31:28 +02:00
2020-05-02 00:14:56 +02:00
if ( panic_mode ) {
synchronize ( ) ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
return result ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : AssertNode * GDScriptParser : : parse_assert ( ) {
// TODO: Add assert message.
AssertNode * assert = alloc_node < AssertNode > ( ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
consume ( GDScriptTokenizer : : Token : : PARENTHESIS_OPEN , R " (Expected " ( " after " assert " .) " ) ;
assert - > condition = parse_expression ( false ) ;
if ( assert - > condition = = nullptr ) {
push_error ( " Expected expression to assert. " ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( assert ) ;
2020-05-02 00:14:56 +02:00
return nullptr ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( match ( GDScriptTokenizer : : Token : : COMMA ) ) {
// Error message.
2020-12-27 06:57:50 +01:00
assert - > message = parse_expression ( false ) ;
if ( assert - > message = = nullptr ) {
push_error ( R " (Expected error message for assert after " , " .) " ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( assert ) ;
2020-05-02 00:14:56 +02:00
return nullptr ;
}
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
consume ( GDScriptTokenizer : : Token : : PARENTHESIS_CLOSE , R " *(Expected " ) " after assert expression.)* " ) ;
2014-02-10 02:10:30 +01:00
2022-07-11 20:31:15 +02:00
complete_extents ( assert ) ;
2020-05-02 00:14:56 +02:00
end_statement ( R " ( " assert " ) " ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
return assert ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : BreakNode * GDScriptParser : : parse_break ( ) {
if ( ! can_break ) {
push_error ( R " (Cannot use " break " outside of a loop.) " ) ;
}
2022-07-11 20:31:15 +02:00
BreakNode * break_node = alloc_node < BreakNode > ( ) ;
complete_extents ( break_node ) ;
2020-05-02 00:14:56 +02:00
end_statement ( R " ( " break " ) " ) ;
2022-07-11 20:31:15 +02:00
return break_node ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ContinueNode * GDScriptParser : : parse_continue ( ) {
if ( ! can_continue ) {
push_error ( R " (Cannot use " continue " outside of a loop or pattern matching block.) " ) ;
}
2020-06-12 00:31:28 +02:00
current_suite - > has_continue = true ;
2020-08-07 19:51:56 +02:00
ContinueNode * cont = alloc_node < ContinueNode > ( ) ;
cont - > is_for_match = is_continue_match ;
2022-07-11 20:31:15 +02:00
complete_extents ( cont ) ;
end_statement ( R " ( " continue " ) " ) ;
2020-08-07 19:51:56 +02:00
return cont ;
2020-05-02 00:14:56 +02:00
}
2016-11-26 13:40:13 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ForNode * GDScriptParser : : parse_for ( ) {
ForNode * n_for = alloc_node < ForNode > ( ) ;
2016-11-26 13:40:13 +01:00
2020-05-02 00:14:56 +02:00
if ( consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected loop variable name after " for " .) " ) ) {
n_for - > variable = parse_identifier ( ) ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
consume ( GDScriptTokenizer : : Token : : IN , R " (Expected " in " after " for " variable name.) " ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
n_for - > list = parse_expression ( false ) ;
2014-02-10 02:10:30 +01:00
2021-10-02 19:53:56 +02:00
if ( ! n_for - > list ) {
push_error ( R " (Expected a list or range after " in " .) " ) ;
}
2020-05-02 00:14:56 +02:00
consume ( GDScriptTokenizer : : Token : : COLON , R " (Expected " : " after " for " condition.) " ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
// Save break/continue state.
bool could_break = can_break ;
bool could_continue = can_continue ;
2020-08-07 19:51:56 +02:00
bool was_continue_match = is_continue_match ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
// Allow break/continue.
can_break = true ;
can_continue = true ;
2020-08-07 19:51:56 +02:00
is_continue_match = false ;
2014-02-10 02:10:30 +01:00
2020-06-10 23:18:10 +02:00
SuiteNode * suite = alloc_node < SuiteNode > ( ) ;
2020-07-16 03:02:44 +02:00
if ( n_for - > variable ) {
2022-07-04 11:30:39 +02:00
const SuiteNode : : Local & local = current_suite - > get_local ( n_for - > variable - > name ) ;
if ( local . type ! = SuiteNode : : Local : : UNDEFINED ) {
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 ) ;
}
2021-03-26 13:03:16 +01:00
suite - > add_local ( SuiteNode : : Local ( n_for - > variable , current_function ) ) ;
2020-07-16 03:02:44 +02:00
}
2020-07-06 17:24:24 +02:00
suite - > parent_for = n_for ;
2020-06-10 23:18:10 +02:00
n_for - > loop = parse_suite ( R " ( " for " block) " , suite ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( n_for ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
// Reset break/continue state.
can_break = could_break ;
can_continue = could_continue ;
2020-08-07 19:51:56 +02:00
is_continue_match = was_continue_match ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
return n_for ;
}
2018-08-26 18:31:23 +02:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : IfNode * GDScriptParser : : parse_if ( const String & p_token ) {
IfNode * n_if = alloc_node < IfNode > ( ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
n_if - > condition = parse_expression ( false ) ;
if ( n_if - > condition = = nullptr ) {
push_error ( vformat ( R " (Expected conditional expression after " % s " .) " , p_token ) ) ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
consume ( GDScriptTokenizer : : Token : : COLON , vformat ( R " (Expected " : " after " % s " condition.) " , p_token ) ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
n_if - > true_block = parse_suite ( vformat ( R " ( " % s " block) " , p_token ) ) ;
2020-07-06 17:24:24 +02:00
n_if - > true_block - > parent_if = n_if ;
2014-02-10 02:10:30 +01:00
2020-06-12 00:31:28 +02:00
if ( n_if - > true_block - > has_continue ) {
current_suite - > has_continue = true ;
}
2020-05-02 00:14:56 +02:00
if ( match ( GDScriptTokenizer : : Token : : ELIF ) ) {
SuiteNode * else_block = alloc_node < SuiteNode > ( ) ;
2022-07-11 20:31:15 +02:00
IfNode * elif = parse_if ( " elif " ) ;
2020-05-02 00:14:56 +02:00
else_block - > statements . push_back ( elif ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( else_block ) ;
2020-05-02 00:14:56 +02:00
n_if - > false_block = else_block ;
} else if ( match ( GDScriptTokenizer : : Token : : ELSE ) ) {
consume ( GDScriptTokenizer : : Token : : COLON , R " (Expected " : " after " else " .) " ) ;
n_if - > false_block = parse_suite ( R " ( " else " block) " ) ;
}
2022-07-11 20:31:15 +02:00
complete_extents ( n_if ) ;
2014-02-10 02:10:30 +01:00
2020-06-12 00:31:28 +02:00
if ( n_if - > false_block ! = nullptr & & n_if - > false_block - > has_return & & n_if - > true_block - > has_return ) {
current_suite - > has_return = true ;
}
if ( n_if - > false_block ! = nullptr & & n_if - > false_block - > has_continue ) {
current_suite - > has_continue = true ;
}
2020-05-02 00:14:56 +02:00
return n_if ;
}
2014-12-17 02:31:57 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : MatchNode * GDScriptParser : : parse_match ( ) {
MatchNode * match = alloc_node < MatchNode > ( ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
match - > test = parse_expression ( false ) ;
if ( match - > test = = nullptr ) {
push_error ( R " (Expected expression to test after " match " .) " ) ;
}
2019-07-03 16:28:50 +02:00
2020-05-02 00:14:56 +02:00
consume ( GDScriptTokenizer : : Token : : COLON , R " (Expected " : " after " match " expression.) " ) ;
consume ( GDScriptTokenizer : : Token : : NEWLINE , R " (Expected a newline after " match " statement.) " ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( ! consume ( GDScriptTokenizer : : Token : : INDENT , R " (Expected an indented block after " match " statement.) " ) ) {
2022-07-11 20:31:15 +02:00
complete_extents ( match ) ;
2020-05-02 00:14:56 +02:00
return match ;
}
2014-12-17 02:31:57 +01:00
2020-07-16 03:02:44 +02:00
# ifdef DEBUG_ENABLED
2020-06-12 00:31:28 +02:00
bool all_have_return = true ;
bool have_wildcard = false ;
bool have_wildcard_without_continue = false ;
2020-07-16 03:02:44 +02:00
# endif
2020-06-12 00:31:28 +02:00
2020-05-02 00:14:56 +02:00
while ( ! check ( GDScriptTokenizer : : Token : : DEDENT ) & & ! is_at_end ( ) ) {
MatchBranchNode * branch = parse_match_branch ( ) ;
if ( branch = = nullptr ) {
2021-08-24 21:27:17 +02:00
advance ( ) ;
2020-05-02 00:14:56 +02:00
continue ;
}
2020-06-12 00:31:28 +02:00
2020-07-16 03:02:44 +02:00
# ifdef DEBUG_ENABLED
2022-07-01 02:05:00 +02:00
if ( have_wildcard_without_continue & & ! branch - > patterns . is_empty ( ) ) {
2020-06-12 00:31:28 +02:00
push_warning ( branch - > patterns [ 0 ] , GDScriptWarning : : UNREACHABLE_PATTERN ) ;
}
if ( branch - > has_wildcard ) {
have_wildcard = true ;
if ( ! branch - > block - > has_continue ) {
have_wildcard_without_continue = true ;
}
}
if ( ! branch - > block - > has_return ) {
all_have_return = false ;
}
2020-07-16 03:02:44 +02:00
# endif
2020-05-02 00:14:56 +02:00
match - > branches . push_back ( branch ) ;
}
2022-07-11 20:31:15 +02:00
complete_extents ( match ) ;
2014-12-17 02:31:57 +01:00
2020-05-02 00:14:56 +02:00
consume ( GDScriptTokenizer : : Token : : DEDENT , R " (Expected an indented block after " match " statement.) " ) ;
2014-12-17 02:31:57 +01:00
2020-07-16 03:02:44 +02:00
# ifdef DEBUG_ENABLED
2021-11-21 15:53:21 +01:00
if ( all_have_return & & have_wildcard ) {
2020-06-12 00:31:28 +02:00
current_suite - > has_return = true ;
}
2020-07-16 03:02:44 +02:00
# endif
2020-06-12 00:31:28 +02:00
2020-05-02 00:14:56 +02:00
return match ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : MatchBranchNode * GDScriptParser : : parse_match_branch ( ) {
MatchBranchNode * branch = alloc_node < MatchBranchNode > ( ) ;
2022-07-11 20:31:15 +02:00
reset_extents ( branch , current ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
bool has_bind = false ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
do {
PatternNode * pattern = parse_match_pattern ( ) ;
if ( pattern = = nullptr ) {
continue ;
}
2022-04-04 09:47:08 +02:00
if ( pattern - > binds . size ( ) > 0 ) {
2020-05-02 00:14:56 +02:00
has_bind = true ;
}
if ( branch - > patterns . size ( ) > 0 & & has_bind ) {
push_error ( R " (Cannot use a variable bind with multiple patterns.) " ) ;
}
if ( pattern - > pattern_type = = PatternNode : : PT_REST ) {
push_error ( R " (Rest pattern can only be used inside array and dictionary patterns.) " ) ;
2020-06-12 00:31:28 +02:00
} else if ( pattern - > pattern_type = = PatternNode : : PT_BIND | | pattern - > pattern_type = = PatternNode : : PT_WILDCARD ) {
branch - > has_wildcard = true ;
2020-05-02 00:14:56 +02:00
}
branch - > patterns . push_back ( pattern ) ;
} while ( match ( GDScriptTokenizer : : Token : : COMMA ) ) ;
2014-02-10 02:10:30 +01:00
2020-12-15 13:04:21 +01:00
if ( branch - > patterns . is_empty ( ) ) {
2020-05-02 00:14:56 +02:00
push_error ( R " (No pattern found for " match " branch.) " ) ;
}
2014-02-10 02:10:30 +01:00
2021-08-25 15:42:48 +02:00
if ( ! consume ( GDScriptTokenizer : : Token : : COLON , R " (Expected " : " after " match " patterns.) " ) ) {
2022-07-11 20:31:15 +02:00
complete_extents ( branch ) ;
2021-08-24 21:27:17 +02:00
return nullptr ;
2021-08-25 15:42:48 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
// Save continue state.
bool could_continue = can_continue ;
2020-08-07 19:51:56 +02:00
bool was_continue_match = is_continue_match ;
2020-05-02 00:14:56 +02:00
// Allow continue for match.
can_continue = true ;
2020-08-07 19:51:56 +02:00
is_continue_match = true ;
2014-02-10 02:10:30 +01:00
2020-06-10 23:18:10 +02:00
SuiteNode * suite = alloc_node < SuiteNode > ( ) ;
if ( branch - > patterns . size ( ) > 0 ) {
2022-05-08 10:09:19 +02:00
for ( const KeyValue < StringName , IdentifierNode * > & E : branch - > patterns [ 0 ] - > binds ) {
SuiteNode : : Local local ( E . value , current_function ) ;
2022-04-04 09:47:08 +02:00
local . type = SuiteNode : : Local : : PATTERN_BIND ;
2020-06-10 23:18:10 +02:00
suite - > add_local ( local ) ;
}
}
branch - > block = parse_suite ( " match pattern block " , suite ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( branch ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
// Restore continue state.
can_continue = could_continue ;
2020-08-07 19:51:56 +02:00
is_continue_match = was_continue_match ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
return branch ;
}
2014-02-10 02:10:30 +01:00
2020-06-10 23:18:10 +02:00
GDScriptParser : : PatternNode * GDScriptParser : : parse_match_pattern ( PatternNode * p_root_pattern ) {
2020-05-02 00:14:56 +02:00
PatternNode * pattern = alloc_node < PatternNode > ( ) ;
2022-07-11 20:31:15 +02:00
reset_extents ( pattern , current ) ;
2018-05-30 04:16:51 +02:00
2020-05-02 00:14:56 +02:00
switch ( current . type ) {
2020-06-10 23:18:10 +02:00
case GDScriptTokenizer : : Token : : VAR : {
2020-05-02 00:14:56 +02:00
// Bind.
advance ( ) ;
if ( ! consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected bind name after " var " .) " ) ) {
2022-07-11 20:31:15 +02:00
complete_extents ( pattern ) ;
2020-04-02 01:20:12 +02:00
return nullptr ;
2018-05-30 04:16:51 +02:00
}
2020-05-02 00:14:56 +02:00
pattern - > pattern_type = PatternNode : : PT_BIND ;
pattern - > bind = parse_identifier ( ) ;
2020-06-10 23:18:10 +02:00
PatternNode * root_pattern = p_root_pattern = = nullptr ? pattern : p_root_pattern ;
if ( p_root_pattern ! = nullptr ) {
if ( p_root_pattern - > has_bind ( pattern - > bind - > name ) ) {
push_error ( vformat ( R " (Bind variable name " % s " was already used in this pattern.) " , pattern - > bind - > name ) ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( pattern ) ;
2020-06-10 23:18:10 +02:00
return nullptr ;
}
}
if ( current_suite - > has_local ( pattern - > bind - > name ) ) {
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 ) ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( pattern ) ;
2020-06-10 23:18:10 +02:00
return nullptr ;
}
root_pattern - > binds [ pattern - > bind - > name ] = pattern - > bind ;
} break ;
2020-05-02 00:14:56 +02:00
case GDScriptTokenizer : : Token : : UNDERSCORE :
// Wildcard.
advance ( ) ;
pattern - > pattern_type = PatternNode : : PT_WILDCARD ;
break ;
case GDScriptTokenizer : : Token : : PERIOD_PERIOD :
// Rest.
advance ( ) ;
pattern - > pattern_type = PatternNode : : PT_REST ;
break ;
case GDScriptTokenizer : : Token : : BRACKET_OPEN : {
// Array.
advance ( ) ;
pattern - > pattern_type = PatternNode : : PT_ARRAY ;
if ( ! check ( GDScriptTokenizer : : Token : : BRACKET_CLOSE ) ) {
do {
2020-06-10 23:18:10 +02:00
PatternNode * sub_pattern = parse_match_pattern ( p_root_pattern ! = nullptr ? p_root_pattern : pattern ) ;
2020-05-02 00:14:56 +02:00
if ( sub_pattern = = nullptr ) {
continue ;
}
if ( pattern - > rest_used ) {
push_error ( R " (The " . . " pattern must be the last element in the pattern array.) " ) ;
} else if ( sub_pattern - > pattern_type = = PatternNode : : PT_REST ) {
pattern - > rest_used = true ;
}
pattern - > array . push_back ( sub_pattern ) ;
} while ( match ( GDScriptTokenizer : : Token : : COMMA ) ) ;
}
consume ( GDScriptTokenizer : : Token : : BRACKET_CLOSE , R " (Expected " ] " to close the array pattern.) " ) ;
break ;
2018-05-30 04:16:51 +02:00
}
2020-05-02 00:14:56 +02:00
case GDScriptTokenizer : : Token : : BRACE_OPEN : {
// Dictionary.
advance ( ) ;
pattern - > pattern_type = PatternNode : : PT_DICTIONARY ;
2021-08-24 21:27:17 +02:00
do {
if ( check ( GDScriptTokenizer : : Token : : BRACE_CLOSE ) | | is_at_end ( ) ) {
break ;
}
if ( match ( GDScriptTokenizer : : Token : : PERIOD_PERIOD ) ) {
// Rest.
if ( pattern - > rest_used ) {
push_error ( R " (The " . . " pattern must be the last element in the pattern dictionary.) " ) ;
} else {
PatternNode * sub_pattern = alloc_node < PatternNode > ( ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( sub_pattern ) ;
2021-08-24 21:27:17 +02:00
sub_pattern - > pattern_type = PatternNode : : PT_REST ;
pattern - > dictionary . push_back ( { nullptr , sub_pattern } ) ;
pattern - > rest_used = true ;
}
} else {
ExpressionNode * key = parse_expression ( false ) ;
if ( key = = nullptr ) {
push_error ( R " (Expected expression as key for dictionary pattern.) " ) ;
}
if ( match ( GDScriptTokenizer : : Token : : COLON ) ) {
// Value pattern.
PatternNode * sub_pattern = parse_match_pattern ( p_root_pattern ! = nullptr ? p_root_pattern : pattern ) ;
if ( sub_pattern = = nullptr ) {
continue ;
}
2020-05-02 00:14:56 +02:00
if ( pattern - > rest_used ) {
push_error ( R " (The " . . " pattern must be the last element in the pattern dictionary.) " ) ;
2021-08-24 21:27:17 +02:00
} else if ( sub_pattern - > pattern_type = = PatternNode : : PT_REST ) {
push_error ( R " (The " . . " pattern cannot be used as a value.) " ) ;
2020-05-02 00:14:56 +02:00
} else {
2021-08-24 21:27:17 +02:00
pattern - > dictionary . push_back ( { key , sub_pattern } ) ;
2020-05-02 00:14:56 +02:00
}
} else {
2021-08-24 21:27:17 +02:00
// Key match only.
pattern - > dictionary . push_back ( { key , nullptr } ) ;
2020-05-02 00:14:56 +02:00
}
2021-08-24 21:27:17 +02:00
}
} while ( match ( GDScriptTokenizer : : Token : : COMMA ) ) ;
2020-05-02 00:14:56 +02:00
consume ( GDScriptTokenizer : : Token : : BRACE_CLOSE , R " (Expected " } " to close the dictionary pattern.) " ) ;
break ;
}
default : {
// Expression.
ExpressionNode * expression = parse_expression ( false ) ;
if ( expression = = nullptr ) {
push_error ( R " (Expected expression for match pattern.) " ) ;
2021-08-24 21:27:17 +02:00
return nullptr ;
2020-05-02 00:14:56 +02:00
} else {
2021-08-24 21:27:17 +02:00
if ( expression - > type = = GDScriptParser : : Node : : LITERAL ) {
pattern - > pattern_type = PatternNode : : PT_LITERAL ;
} else {
pattern - > pattern_type = PatternNode : : PT_EXPRESSION ;
}
2020-05-02 00:14:56 +02:00
pattern - > expression = expression ;
2014-04-05 23:50:09 +02:00
}
2020-05-02 00:14:56 +02:00
break ;
2014-04-05 23:50:09 +02:00
}
2020-05-02 00:14:56 +02:00
}
2022-07-11 20:31:15 +02:00
complete_extents ( pattern ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
return pattern ;
}
2014-02-10 02:10:30 +01:00
2020-06-10 23:18:10 +02:00
bool GDScriptParser : : PatternNode : : has_bind ( const StringName & p_name ) {
return binds . has ( p_name ) ;
}
GDScriptParser : : IdentifierNode * GDScriptParser : : PatternNode : : get_bind ( const StringName & p_name ) {
return binds [ p_name ] ;
}
2020-05-02 00:14:56 +02:00
GDScriptParser : : WhileNode * GDScriptParser : : parse_while ( ) {
WhileNode * n_while = alloc_node < WhileNode > ( ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
n_while - > condition = parse_expression ( false ) ;
if ( n_while - > condition = = nullptr ) {
push_error ( R " (Expected conditional expression after " while " .) " ) ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
consume ( GDScriptTokenizer : : Token : : COLON , R " (Expected " : " after " while " condition.) " ) ;
2017-08-24 05:06:56 +02:00
2020-05-02 00:14:56 +02:00
// Save break/continue state.
bool could_break = can_break ;
bool could_continue = can_continue ;
2020-08-07 19:51:56 +02:00
bool was_continue_match = is_continue_match ;
2017-03-05 16:44:50 +01:00
2020-05-02 00:14:56 +02:00
// Allow break/continue.
can_break = true ;
can_continue = true ;
2020-08-07 19:51:56 +02:00
is_continue_match = false ;
2017-08-24 05:06:56 +02:00
2020-05-02 00:14:56 +02:00
n_while - > loop = parse_suite ( R " ( " while " block) " ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( n_while ) ;
2020-05-02 00:14:56 +02:00
// Reset break/continue state.
can_break = could_break ;
can_continue = could_continue ;
2020-08-07 19:51:56 +02:00
is_continue_match = was_continue_match ;
2020-05-02 00:14:56 +02:00
return n_while ;
}
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_precedence ( Precedence p_precedence , bool p_can_assign , bool p_stop_on_assign ) {
// Switch multiline mode on for grouping tokens.
// Do this early to avoid the tokenizer generating whitespace tokens.
switch ( current . type ) {
case GDScriptTokenizer : : Token : : PARENTHESIS_OPEN :
case GDScriptTokenizer : : Token : : BRACE_OPEN :
case GDScriptTokenizer : : Token : : BRACKET_OPEN :
push_multiline ( true ) ;
break ;
default :
break ; // Nothing to do.
}
2020-07-16 03:02:44 +02:00
// Completion can appear whenever an expression is expected.
make_completion_context ( COMPLETION_IDENTIFIER , nullptr ) ;
2021-03-25 14:36:29 +01:00
GDScriptTokenizer : : Token token = current ;
2020-05-02 00:14:56 +02:00
ParseFunction prefix_rule = get_rule ( token . type ) - > prefix ;
if ( prefix_rule = = nullptr ) {
// Expected expression. Let the caller give the proper error message.
return nullptr ;
}
2021-03-25 14:36:29 +01:00
advance ( ) ; // Only consume the token if there's a valid rule.
2020-05-02 00:14:56 +02:00
ExpressionNode * previous_operand = ( this - > * prefix_rule ) ( nullptr , p_can_assign ) ;
while ( p_precedence < = get_rule ( current . type ) - > precedence ) {
2022-05-24 02:13:25 +02:00
if ( previous_operand = = nullptr | | ( p_stop_on_assign & & current . type = = GDScriptTokenizer : : Token : : EQUAL ) | | ( previous_operand - > type = = Node : : LAMBDA & & lambda_ended ) ) {
2020-05-02 00:14:56 +02:00
return previous_operand ;
}
// Also switch multiline mode on here for infix operators.
switch ( current . type ) {
// case GDScriptTokenizer::Token::BRACE_OPEN: // Not an infix operator.
case GDScriptTokenizer : : Token : : PARENTHESIS_OPEN :
case GDScriptTokenizer : : Token : : BRACKET_OPEN :
push_multiline ( true ) ;
2020-05-10 13:00:47 +02:00
break ;
default :
2020-05-02 00:14:56 +02:00
break ; // Nothing to do.
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
token = advance ( ) ;
ParseFunction infix_rule = get_rule ( token . type ) - > infix ;
previous_operand = ( this - > * infix_rule ) ( previous_operand , p_can_assign ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
return previous_operand ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_expression ( bool p_can_assign , bool p_stop_on_assign ) {
return parse_precedence ( PREC_ASSIGNMENT , p_can_assign , p_stop_on_assign ) ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : IdentifierNode * GDScriptParser : : parse_identifier ( ) {
return static_cast < IdentifierNode * > ( parse_identifier ( nullptr , false ) ) ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_identifier ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
if ( ! previous . is_identifier ( ) ) {
ERR_FAIL_V_MSG ( nullptr , " Parser bug: parsing literal node without literal token. " ) ;
}
IdentifierNode * identifier = alloc_node < IdentifierNode > ( ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( identifier ) ;
2020-06-01 21:41:05 +02:00
identifier - > name = previous . get_identifier ( ) ;
2020-06-10 23:18:10 +02:00
if ( current_suite ! = nullptr & & current_suite - > has_local ( identifier - > name ) ) {
const SuiteNode : : Local & declaration = current_suite - > get_local ( identifier - > name ) ;
2021-03-26 13:03:16 +01:00
identifier - > source_function = declaration . source_function ;
2020-06-10 23:18:10 +02:00
switch ( declaration . type ) {
case SuiteNode : : Local : : CONSTANT :
identifier - > source = IdentifierNode : : LOCAL_CONSTANT ;
identifier - > constant_source = declaration . constant ;
2020-06-12 00:31:28 +02:00
declaration . constant - > usages + + ;
2020-06-10 23:18:10 +02:00
break ;
case SuiteNode : : Local : : VARIABLE :
identifier - > source = IdentifierNode : : LOCAL_VARIABLE ;
identifier - > variable_source = declaration . variable ;
2020-06-12 00:31:28 +02:00
declaration . variable - > usages + + ;
2020-06-10 23:18:10 +02:00
break ;
case SuiteNode : : Local : : PARAMETER :
identifier - > source = IdentifierNode : : FUNCTION_PARAMETER ;
identifier - > parameter_source = declaration . parameter ;
2020-06-12 00:31:28 +02:00
declaration . parameter - > usages + + ;
2020-06-10 23:18:10 +02:00
break ;
case SuiteNode : : Local : : FOR_VARIABLE :
identifier - > source = IdentifierNode : : LOCAL_ITERATOR ;
identifier - > bind_source = declaration . bind ;
2020-06-12 00:31:28 +02:00
declaration . bind - > usages + + ;
2020-06-10 23:18:10 +02:00
break ;
case SuiteNode : : Local : : PATTERN_BIND :
identifier - > source = IdentifierNode : : LOCAL_BIND ;
identifier - > bind_source = declaration . bind ;
2020-06-12 00:31:28 +02:00
declaration . bind - > usages + + ;
2020-06-10 23:18:10 +02:00
break ;
case SuiteNode : : Local : : UNDEFINED :
ERR_FAIL_V_MSG ( nullptr , " Undefined local found. " ) ;
}
}
2020-05-02 00:14:56 +02:00
return identifier ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : LiteralNode * GDScriptParser : : parse_literal ( ) {
return static_cast < LiteralNode * > ( parse_literal ( nullptr , false ) ) ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_literal ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
if ( previous . type ! = GDScriptTokenizer : : Token : : LITERAL ) {
push_error ( " Parser bug: parsing literal node without literal token. " ) ;
ERR_FAIL_V_MSG ( nullptr , " Parser bug: parsing literal node without literal token. " ) ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
LiteralNode * literal = alloc_node < LiteralNode > ( ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( literal ) ;
2020-05-02 00:14:56 +02:00
literal - > value = previous . literal ;
return literal ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_self ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
2020-08-31 15:01:45 +02:00
if ( current_function & & current_function - > is_static ) {
push_error ( R " (Cannot use " self " inside a static function.) " ) ;
2020-07-16 03:02:44 +02:00
}
2020-05-02 00:14:56 +02:00
SelfNode * self = alloc_node < SelfNode > ( ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( self ) ;
2020-05-02 00:14:56 +02:00
self - > current_class = current_class ;
return self ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_builtin_constant ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
GDScriptTokenizer : : Token : : Type op_type = previous . type ;
LiteralNode * constant = alloc_node < LiteralNode > ( ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( constant ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
switch ( op_type ) {
case GDScriptTokenizer : : Token : : CONST_PI :
constant - > value = Math_PI ;
break ;
case GDScriptTokenizer : : Token : : CONST_TAU :
constant - > value = Math_TAU ;
break ;
case GDScriptTokenizer : : Token : : CONST_INF :
2021-07-21 10:40:31 +02:00
constant - > value = INFINITY ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : CONST_NAN :
2021-07-21 10:40:31 +02:00
constant - > value = NAN ;
2020-05-02 00:14:56 +02:00
break ;
default :
return nullptr ; // Unreachable.
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
return constant ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_unary_operator ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
GDScriptTokenizer : : Token : : Type op_type = previous . type ;
UnaryOpNode * operation = alloc_node < UnaryOpNode > ( ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
switch ( op_type ) {
case GDScriptTokenizer : : Token : : MINUS :
operation - > operation = UnaryOpNode : : OP_NEGATIVE ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_NEGATE ;
2020-05-02 00:14:56 +02:00
operation - > operand = parse_precedence ( PREC_SIGN , false ) ;
2021-09-15 16:08:59 +02:00
if ( operation - > operand = = nullptr ) {
push_error ( R " (Expected expression after " - " operator.) " ) ;
}
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : PLUS :
operation - > operation = UnaryOpNode : : OP_POSITIVE ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_POSITIVE ;
2020-05-02 00:14:56 +02:00
operation - > operand = parse_precedence ( PREC_SIGN , false ) ;
2021-09-15 16:08:59 +02:00
if ( operation - > operand = = nullptr ) {
push_error ( R " (Expected expression after " + " operator.) " ) ;
}
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : TILDE :
operation - > operation = UnaryOpNode : : OP_COMPLEMENT ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_BIT_NEGATE ;
2020-05-02 00:14:56 +02:00
operation - > operand = parse_precedence ( PREC_BIT_NOT , false ) ;
2021-09-15 16:08:59 +02:00
if ( operation - > operand = = nullptr ) {
push_error ( R " (Expected expression after " ~ " operator.) " ) ;
}
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : NOT :
case GDScriptTokenizer : : Token : : BANG :
operation - > operation = UnaryOpNode : : OP_LOGIC_NOT ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_NOT ;
2020-05-02 00:14:56 +02:00
operation - > operand = parse_precedence ( PREC_LOGIC_NOT , false ) ;
2021-09-15 16:08:59 +02:00
if ( operation - > operand = = nullptr ) {
push_error ( vformat ( R " (Expected expression after " % s " operator.) " , op_type = = GDScriptTokenizer : : Token : : NOT ? " not " : " ! " ) ) ;
}
2020-05-02 00:14:56 +02:00
break ;
default :
2022-07-11 20:31:15 +02:00
complete_extents ( operation ) ;
2020-05-02 00:14:56 +02:00
return nullptr ; // Unreachable.
}
2022-07-11 20:31:15 +02:00
complete_extents ( operation ) ;
2016-08-25 20:18:35 +02:00
2020-05-02 00:14:56 +02:00
return operation ;
}
2014-02-10 02:10:30 +01:00
2020-09-01 09:39:17 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_binary_not_in_operator ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
// check that NOT is followed by IN by consuming it before calling parse_binary_operator which will only receive a plain IN
2022-07-11 20:31:15 +02:00
UnaryOpNode * operation = alloc_node < UnaryOpNode > ( ) ;
reset_extents ( operation , p_previous_operand ) ;
update_extents ( operation ) ;
2020-09-01 09:39:17 +02:00
consume ( GDScriptTokenizer : : Token : : IN , R " (Expected " in " after " not " in content-test operator.) " ) ;
ExpressionNode * in_operation = parse_binary_operator ( p_previous_operand , p_can_assign ) ;
operation - > operation = UnaryOpNode : : OP_LOGIC_NOT ;
operation - > variant_op = Variant : : OP_NOT ;
operation - > operand = in_operation ;
2022-07-11 20:31:15 +02:00
complete_extents ( operation ) ;
2020-09-01 09:39:17 +02:00
return operation ;
}
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_binary_operator ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
GDScriptTokenizer : : Token op = previous ;
BinaryOpNode * operation = alloc_node < BinaryOpNode > ( ) ;
2022-07-11 20:31:15 +02:00
reset_extents ( operation , p_previous_operand ) ;
update_extents ( operation ) ;
2017-03-05 16:44:50 +01:00
2020-05-02 00:14:56 +02:00
Precedence precedence = ( Precedence ) ( get_rule ( op . type ) - > precedence + 1 ) ;
operation - > left_operand = p_previous_operand ;
operation - > right_operand = parse_precedence ( precedence , false ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( operation ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( operation - > right_operand = = nullptr ) {
push_error ( vformat ( R " (Expected expression after " % s " operator. " ) " , op.get_name()));
}
2014-02-10 02:10:30 +01:00
2020-06-10 23:18:10 +02:00
// TODO: Also for unary, ternary, and assignment.
2020-05-02 00:14:56 +02:00
switch ( op . type ) {
case GDScriptTokenizer : : Token : : PLUS :
operation - > operation = BinaryOpNode : : OP_ADDITION ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_ADD ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : MINUS :
operation - > operation = BinaryOpNode : : OP_SUBTRACTION ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_SUBTRACT ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : STAR :
operation - > operation = BinaryOpNode : : OP_MULTIPLICATION ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_MULTIPLY ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : SLASH :
operation - > operation = BinaryOpNode : : OP_DIVISION ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_DIVIDE ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : PERCENT :
operation - > operation = BinaryOpNode : : OP_MODULO ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_MODULE ;
2020-05-02 00:14:56 +02:00
break ;
2022-03-07 18:25:21 +01:00
case GDScriptTokenizer : : Token : : STAR_STAR :
operation - > operation = BinaryOpNode : : OP_POWER ;
operation - > variant_op = Variant : : OP_POWER ;
break ;
2020-05-02 00:14:56 +02:00
case GDScriptTokenizer : : Token : : LESS_LESS :
operation - > operation = BinaryOpNode : : OP_BIT_LEFT_SHIFT ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_SHIFT_LEFT ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : GREATER_GREATER :
operation - > operation = BinaryOpNode : : OP_BIT_RIGHT_SHIFT ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_SHIFT_RIGHT ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : AMPERSAND :
operation - > operation = BinaryOpNode : : OP_BIT_AND ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_BIT_AND ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : PIPE :
2020-06-10 23:18:10 +02:00
operation - > operation = BinaryOpNode : : OP_BIT_OR ;
operation - > variant_op = Variant : : OP_BIT_OR ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : CARET :
operation - > operation = BinaryOpNode : : OP_BIT_XOR ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_BIT_XOR ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : AND :
case GDScriptTokenizer : : Token : : AMPERSAND_AMPERSAND :
operation - > operation = BinaryOpNode : : OP_LOGIC_AND ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_AND ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : OR :
case GDScriptTokenizer : : Token : : PIPE_PIPE :
operation - > operation = BinaryOpNode : : OP_LOGIC_OR ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_OR ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : IS :
operation - > operation = BinaryOpNode : : OP_TYPE_TEST ;
break ;
case GDScriptTokenizer : : Token : : IN :
operation - > operation = BinaryOpNode : : OP_CONTENT_TEST ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_IN ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : EQUAL_EQUAL :
operation - > operation = BinaryOpNode : : OP_COMP_EQUAL ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_EQUAL ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : BANG_EQUAL :
operation - > operation = BinaryOpNode : : OP_COMP_NOT_EQUAL ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_NOT_EQUAL ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : LESS :
operation - > operation = BinaryOpNode : : OP_COMP_LESS ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_LESS ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : LESS_EQUAL :
operation - > operation = BinaryOpNode : : OP_COMP_LESS_EQUAL ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_LESS_EQUAL ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : GREATER :
operation - > operation = BinaryOpNode : : OP_COMP_GREATER ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_GREATER ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : GREATER_EQUAL :
operation - > operation = BinaryOpNode : : OP_COMP_GREATER_EQUAL ;
2020-06-10 23:18:10 +02:00
operation - > variant_op = Variant : : OP_GREATER_EQUAL ;
2020-05-02 00:14:56 +02:00
break ;
default :
return nullptr ; // Unreachable.
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
return operation ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_ternary_operator ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
// Only one ternary operation exists, so no abstraction here.
TernaryOpNode * operation = alloc_node < TernaryOpNode > ( ) ;
2022-07-11 20:31:15 +02:00
reset_extents ( operation , p_previous_operand ) ;
update_extents ( operation ) ;
2014-02-10 02:10:30 +01:00
2022-07-11 20:31:15 +02:00
operation - > true_expr = p_previous_operand ;
2020-05-02 00:14:56 +02:00
operation - > condition = parse_precedence ( PREC_TERNARY , false ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( operation - > condition = = nullptr ) {
push_error ( R " (Expected expression as ternary condition after " if " .) " ) ;
}
2017-03-05 16:44:50 +01:00
2020-05-02 00:14:56 +02:00
consume ( GDScriptTokenizer : : Token : : ELSE , R " (Expected " else " after ternary operator condition.) " ) ;
2016-08-25 20:18:35 +02:00
2020-05-02 00:14:56 +02:00
operation - > false_expr = parse_precedence ( PREC_TERNARY , false ) ;
2016-08-25 20:18:35 +02:00
2021-09-15 16:43:36 +02:00
if ( operation - > false_expr = = nullptr ) {
push_error ( R " (Expected expression after " else " .) " ) ;
}
2022-07-11 20:31:15 +02:00
complete_extents ( operation ) ;
2020-05-02 00:14:56 +02:00
return operation ;
}
2016-08-25 20:18:35 +02:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_assignment ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
if ( ! p_can_assign ) {
push_error ( " Assignment is not allowed inside an expression. " ) ;
return parse_expression ( false ) ; // Return the following expression.
}
2021-11-21 21:17:35 +01:00
if ( p_previous_operand = = nullptr ) {
return parse_expression ( false ) ; // Return the following expression.
}
2016-08-25 20:18:35 +02:00
2020-07-16 03:02:44 +02:00
# ifdef DEBUG_ENABLED
2020-06-12 00:31:28 +02:00
VariableNode * source_variable = nullptr ;
2020-07-16 03:02:44 +02:00
# endif
2020-06-12 00:31:28 +02:00
2020-05-02 00:14:56 +02:00
switch ( p_previous_operand - > type ) {
2020-06-12 00:31:28 +02:00
case Node : : IDENTIFIER : {
2020-07-16 03:02:44 +02:00
# ifdef DEBUG_ENABLED
2020-06-12 00:31:28 +02:00
// Get source to store assignment count.
// Also remove one usage since assignment isn't usage.
IdentifierNode * id = static_cast < IdentifierNode * > ( p_previous_operand ) ;
switch ( id - > source ) {
case IdentifierNode : : LOCAL_VARIABLE :
2020-07-16 03:02:44 +02:00
2020-06-12 00:31:28 +02:00
source_variable = id - > variable_source ;
id - > variable_source - > usages - - ;
break ;
case IdentifierNode : : LOCAL_CONSTANT :
id - > constant_source - > usages - - ;
break ;
case IdentifierNode : : FUNCTION_PARAMETER :
id - > parameter_source - > usages - - ;
break ;
case IdentifierNode : : LOCAL_ITERATOR :
case IdentifierNode : : LOCAL_BIND :
id - > bind_source - > usages - - ;
break ;
default :
break ;
}
2020-07-16 03:02:44 +02:00
# endif
} break ;
2020-05-02 00:14:56 +02:00
case Node : : SUBSCRIPT :
// Okay.
break ;
default :
push_error ( R " (Only identifier, attribute access, and subscription access can be used as assignment target.) " ) ;
return parse_expression ( false ) ; // Return the following expression.
}
2016-08-25 20:18:35 +02:00
2020-05-02 00:14:56 +02:00
AssignmentNode * assignment = alloc_node < AssignmentNode > ( ) ;
2022-07-11 20:31:15 +02:00
reset_extents ( assignment , p_previous_operand ) ;
update_extents ( assignment ) ;
2020-07-06 17:24:24 +02:00
make_completion_context ( COMPLETION_ASSIGN , assignment ) ;
2020-07-16 03:02:44 +02:00
# ifdef DEBUG_ENABLED
2020-06-12 00:31:28 +02:00
bool has_operator = true ;
2020-07-16 03:02:44 +02:00
# endif
2020-05-02 00:14:56 +02:00
switch ( previous . type ) {
case GDScriptTokenizer : : Token : : EQUAL :
assignment - > operation = AssignmentNode : : OP_NONE ;
2020-08-07 14:51:09 +02:00
assignment - > variant_op = Variant : : OP_MAX ;
2020-07-16 03:02:44 +02:00
# ifdef DEBUG_ENABLED
2020-06-12 00:31:28 +02:00
has_operator = false ;
2020-07-16 03:02:44 +02:00
# endif
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : PLUS_EQUAL :
assignment - > operation = AssignmentNode : : OP_ADDITION ;
2020-08-07 14:51:09 +02:00
assignment - > variant_op = Variant : : OP_ADD ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : MINUS_EQUAL :
assignment - > operation = AssignmentNode : : OP_SUBTRACTION ;
2020-08-07 14:51:09 +02:00
assignment - > variant_op = Variant : : OP_SUBTRACT ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : STAR_EQUAL :
assignment - > operation = AssignmentNode : : OP_MULTIPLICATION ;
2020-08-07 14:51:09 +02:00
assignment - > variant_op = Variant : : OP_MULTIPLY ;
2020-05-02 00:14:56 +02:00
break ;
2022-03-07 18:25:21 +01:00
case GDScriptTokenizer : : Token : : STAR_STAR_EQUAL :
assignment - > operation = AssignmentNode : : OP_POWER ;
assignment - > variant_op = Variant : : OP_POWER ;
break ;
2020-05-02 00:14:56 +02:00
case GDScriptTokenizer : : Token : : SLASH_EQUAL :
assignment - > operation = AssignmentNode : : OP_DIVISION ;
2020-08-07 14:51:09 +02:00
assignment - > variant_op = Variant : : OP_DIVIDE ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : PERCENT_EQUAL :
assignment - > operation = AssignmentNode : : OP_MODULO ;
2020-08-07 14:51:09 +02:00
assignment - > variant_op = Variant : : OP_MODULE ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : LESS_LESS_EQUAL :
assignment - > operation = AssignmentNode : : OP_BIT_SHIFT_LEFT ;
2020-08-07 14:51:09 +02:00
assignment - > variant_op = Variant : : OP_SHIFT_LEFT ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : GREATER_GREATER_EQUAL :
assignment - > operation = AssignmentNode : : OP_BIT_SHIFT_RIGHT ;
2020-08-07 14:51:09 +02:00
assignment - > variant_op = Variant : : OP_SHIFT_RIGHT ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : AMPERSAND_EQUAL :
assignment - > operation = AssignmentNode : : OP_BIT_AND ;
2020-08-07 14:51:09 +02:00
assignment - > variant_op = Variant : : OP_BIT_AND ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : PIPE_EQUAL :
assignment - > operation = AssignmentNode : : OP_BIT_OR ;
2020-08-07 14:51:09 +02:00
assignment - > variant_op = Variant : : OP_BIT_OR ;
2020-05-02 00:14:56 +02:00
break ;
case GDScriptTokenizer : : Token : : CARET_EQUAL :
assignment - > operation = AssignmentNode : : OP_BIT_XOR ;
2020-08-07 14:51:09 +02:00
assignment - > variant_op = Variant : : OP_BIT_XOR ;
2020-05-02 00:14:56 +02:00
break ;
default :
break ; // Unreachable.
}
assignment - > assignee = p_previous_operand ;
assignment - > assigned_value = parse_expression ( false ) ;
2021-08-25 18:32:24 +02:00
if ( assignment - > assigned_value = = nullptr ) {
push_error ( R " (Expected an expression after " = " .) " ) ;
}
2022-07-11 20:31:15 +02:00
complete_extents ( assignment ) ;
2016-08-25 20:18:35 +02:00
2020-07-16 03:02:44 +02:00
# ifdef DEBUG_ENABLED
2021-09-10 22:08:02 +02:00
if ( source_variable ! = nullptr ) {
if ( has_operator & & source_variable - > assignments = = 0 ) {
push_warning ( assignment , GDScriptWarning : : UNASSIGNED_VARIABLE_OP_ASSIGN , source_variable - > identifier - > name ) ;
}
source_variable - > assignments + = 1 ;
2020-06-12 00:31:28 +02:00
}
2020-07-16 03:02:44 +02:00
# endif
2020-06-12 00:31:28 +02:00
2020-05-02 00:14:56 +02:00
return assignment ;
}
2016-08-25 20:18:35 +02:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_await ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
AwaitNode * await = alloc_node < AwaitNode > ( ) ;
2021-08-25 00:33:17 +02:00
ExpressionNode * element = parse_precedence ( PREC_AWAIT , false ) ;
if ( element = = nullptr ) {
push_error ( R " (Expected signal or coroutine after " await " .) " ) ;
}
await - > to_await = element ;
2022-07-11 20:31:15 +02:00
complete_extents ( await ) ;
2016-08-25 20:18:35 +02:00
2021-08-13 00:19:55 +02:00
if ( current_function ) { // Might be null in a getter or setter.
current_function - > is_coroutine = true ;
}
2020-06-11 00:53:25 +02:00
2020-05-02 00:14:56 +02:00
return await ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_array ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
ArrayNode * array = alloc_node < ArrayNode > ( ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( ! check ( GDScriptTokenizer : : Token : : BRACKET_CLOSE ) ) {
do {
if ( check ( GDScriptTokenizer : : Token : : BRACKET_CLOSE ) ) {
// Allow for trailing comma.
break ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
ExpressionNode * element = parse_expression ( false ) ;
if ( element = = nullptr ) {
push_error ( R " (Expected expression as array element.) " ) ;
} else {
array - > elements . push_back ( element ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
} while ( match ( GDScriptTokenizer : : Token : : COMMA ) & & ! is_at_end ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
pop_multiline ( ) ;
consume ( GDScriptTokenizer : : Token : : BRACKET_CLOSE , R " (Expected closing " ] " after array elements.) " ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( array ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
return array ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_dictionary ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
DictionaryNode * dictionary = alloc_node < DictionaryNode > ( ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
bool decided_style = false ;
if ( ! check ( GDScriptTokenizer : : Token : : BRACE_CLOSE ) ) {
do {
if ( check ( GDScriptTokenizer : : Token : : BRACE_CLOSE ) ) {
// Allow for trailing comma.
break ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
// Key.
ExpressionNode * key = parse_expression ( false , true ) ; // Stop on "=" so we can check for Lua table style.
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( key = = nullptr ) {
push_error ( R " (Expected expression as dictionary key.) " ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
if ( ! decided_style ) {
switch ( current . type ) {
case GDScriptTokenizer : : Token : : COLON :
dictionary - > style = DictionaryNode : : PYTHON_DICT ;
break ;
case GDScriptTokenizer : : Token : : EQUAL :
dictionary - > style = DictionaryNode : : LUA_TABLE ;
break ;
default :
push_error ( R " (Expected " : " or " = " after dictionary key.) " ) ;
break ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
decided_style = true ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
switch ( dictionary - > style ) {
case DictionaryNode : : LUA_TABLE :
2021-09-15 14:56:24 +02:00
if ( key ! = nullptr & & key - > type ! = Node : : IDENTIFIER & & key - > type ! = Node : : LITERAL ) {
push_error ( " Expected identifier or string as LUA-style dictionary key. " ) ;
advance ( ) ;
break ;
}
if ( key ! = nullptr & & key - > type = = Node : : LITERAL & & static_cast < LiteralNode * > ( key ) - > value . get_type ( ) ! = Variant : : STRING ) {
push_error ( " Expected identifier or string as LUA-style dictionary key. " ) ;
2021-08-10 15:32:07 +02:00
advance ( ) ;
break ;
2020-05-02 00:14:56 +02:00
}
if ( ! match ( GDScriptTokenizer : : Token : : EQUAL ) ) {
if ( match ( GDScriptTokenizer : : Token : : COLON ) ) {
push_error ( R " (Expected " = " after dictionary key. Mixing dictionary styles is not allowed.) " ) ;
advance ( ) ; // Consume wrong separator anyway.
} else {
push_error ( R " (Expected " = " after dictionary key.) " ) ;
}
}
2021-08-24 13:19:40 +02:00
if ( key ! = nullptr ) {
key - > is_constant = true ;
2021-09-15 14:56:24 +02:00
if ( key - > type = = Node : : IDENTIFIER ) {
key - > reduced_value = static_cast < IdentifierNode * > ( key ) - > name ;
} else if ( key - > type = = Node : : LITERAL ) {
key - > reduced_value = StringName ( static_cast < LiteralNode * > ( key ) - > value . operator String ( ) ) ;
}
2021-08-24 13:19:40 +02:00
}
2020-05-02 00:14:56 +02:00
break ;
case DictionaryNode : : PYTHON_DICT :
if ( ! match ( GDScriptTokenizer : : Token : : COLON ) ) {
if ( match ( GDScriptTokenizer : : Token : : EQUAL ) ) {
push_error ( R " (Expected " : " after dictionary key. Mixing dictionary styles is not allowed.) " ) ;
advance ( ) ; // Consume wrong separator anyway.
} else {
push_error ( R " (Expected " : " after dictionary key.) " ) ;
}
}
break ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
// Value.
ExpressionNode * value = parse_expression ( false ) ;
if ( value = = nullptr ) {
push_error ( R " (Expected expression as dictionary value.) " ) ;
2017-03-05 16:44:50 +01:00
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( key ! = nullptr & & value ! = nullptr ) {
dictionary - > elements . push_back ( { key , value } ) ;
}
} while ( match ( GDScriptTokenizer : : Token : : COMMA ) & & ! is_at_end ( ) ) ;
}
pop_multiline ( ) ;
consume ( GDScriptTokenizer : : Token : : BRACE_CLOSE , R " (Expected closing " } " after dictionary elements.) " ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( dictionary ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
return dictionary ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_grouping ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
ExpressionNode * grouped = parse_expression ( false ) ;
pop_multiline ( ) ;
2020-08-05 21:42:33 +02:00
if ( grouped = = nullptr ) {
push_error ( R " (Expected grouping expression.) " ) ;
} else {
consume ( GDScriptTokenizer : : Token : : PARENTHESIS_CLOSE , R " *(Expected closing " ) " after grouping expression.)* " ) ;
}
2020-05-02 00:14:56 +02:00
return grouped ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_attribute ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
SubscriptNode * attribute = alloc_node < SubscriptNode > ( ) ;
2022-07-11 20:31:15 +02:00
reset_extents ( attribute , p_previous_operand ) ;
update_extents ( attribute ) ;
2014-02-10 02:10:30 +01:00
2020-07-06 17:24:24 +02:00
if ( for_completion ) {
bool is_builtin = false ;
2021-04-05 16:17:59 +02:00
if ( p_previous_operand & & p_previous_operand - > type = = Node : : IDENTIFIER ) {
2020-07-06 17:24:24 +02:00
const IdentifierNode * id = static_cast < const IdentifierNode * > ( p_previous_operand ) ;
Variant : : Type builtin_type = get_builtin_type ( id - > name ) ;
if ( builtin_type < Variant : : VARIANT_MAX ) {
2022-03-10 12:55:54 +01:00
make_completion_context ( COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD , builtin_type , true ) ;
2020-07-06 17:24:24 +02:00
is_builtin = true ;
}
}
if ( ! is_builtin ) {
make_completion_context ( COMPLETION_ATTRIBUTE , attribute , - 1 , true ) ;
}
}
2020-05-02 00:14:56 +02:00
attribute - > base = p_previous_operand ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( ! consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected identifier after " . " for attribute access.) " ) ) {
2022-07-11 20:31:15 +02:00
complete_extents ( attribute ) ;
2020-07-06 17:24:24 +02:00
return attribute ;
2020-05-02 00:14:56 +02:00
}
2022-02-18 13:52:46 +01:00
attribute - > is_attribute = true ;
2020-05-02 00:14:56 +02:00
attribute - > attribute = parse_identifier ( ) ;
2014-02-10 02:10:30 +01:00
2022-07-11 20:31:15 +02:00
complete_extents ( attribute ) ;
2020-05-02 00:14:56 +02:00
return attribute ;
}
2016-07-22 14:22:34 +02:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_subscript ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
SubscriptNode * subscript = alloc_node < SubscriptNode > ( ) ;
2022-07-11 20:31:15 +02:00
reset_extents ( subscript , p_previous_operand ) ;
update_extents ( subscript ) ;
2014-02-10 02:10:30 +01:00
2020-07-06 17:24:24 +02:00
make_completion_context ( COMPLETION_SUBSCRIPT , subscript ) ;
2020-05-02 00:14:56 +02:00
subscript - > base = p_previous_operand ;
subscript - > index = parse_expression ( false ) ;
2014-02-10 02:10:30 +01:00
2021-09-17 19:31:51 +02:00
if ( subscript - > index = = nullptr ) {
push_error ( R " (Expected expression after " [ " .) " ) ;
}
2020-05-02 00:14:56 +02:00
pop_multiline ( ) ;
consume ( GDScriptTokenizer : : Token : : BRACKET_CLOSE , R " (Expected " ] " after subscription index.) " ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( subscript ) ;
2014-09-15 16:33:30 +02:00
2020-05-02 00:14:56 +02:00
return subscript ;
}
2014-09-15 16:33:30 +02:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_cast ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
CastNode * cast = alloc_node < CastNode > ( ) ;
2022-07-11 20:31:15 +02:00
reset_extents ( cast , p_previous_operand ) ;
update_extents ( cast ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
cast - > operand = p_previous_operand ;
cast - > cast_type = parse_type ( ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( cast ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( cast - > cast_type = = nullptr ) {
push_error ( R " (Expected type specifier after " as " .) " ) ;
return p_previous_operand ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
return cast ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_call ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
CallNode * call = alloc_node < CallNode > ( ) ;
2022-07-11 20:31:15 +02:00
reset_extents ( call , p_previous_operand ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( previous . type = = GDScriptTokenizer : : Token : : SUPER ) {
// Super call.
call - > is_super = true ;
push_multiline ( true ) ;
if ( match ( GDScriptTokenizer : : Token : : PARENTHESIS_OPEN ) ) {
// Implicit call to the parent method of the same name.
if ( current_function = = nullptr ) {
push_error ( R " (Cannot use implicit " super " call outside of a function.) " ) ;
pop_multiline ( ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( call ) ;
2020-05-02 00:14:56 +02:00
return nullptr ;
}
2022-03-30 16:53:49 +02:00
if ( current_function - > identifier ) {
call - > function_name = current_function - > identifier - > name ;
} else {
call - > function_name = SNAME ( " <anonymous> " ) ;
}
2020-05-02 00:14:56 +02:00
} else {
consume ( GDScriptTokenizer : : Token : : PERIOD , R " (Expected " . " or " ( " after " super " .) " ) ;
2020-07-16 03:02:44 +02:00
make_completion_context ( COMPLETION_SUPER_METHOD , call , true ) ;
2020-05-02 00:14:56 +02:00
if ( ! consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected function name after " . " .) " ) ) {
pop_multiline ( ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( call ) ;
2020-05-02 00:14:56 +02:00
return nullptr ;
}
2020-06-10 23:18:10 +02:00
IdentifierNode * identifier = parse_identifier ( ) ;
call - > callee = identifier ;
call - > function_name = identifier - > name ;
2020-05-02 00:14:56 +02:00
consume ( GDScriptTokenizer : : Token : : PARENTHESIS_OPEN , R " (Expected " ( " after function name.) " ) ;
}
} else {
call - > callee = p_previous_operand ;
2020-06-10 23:18:10 +02:00
2020-08-05 21:41:46 +02:00
if ( call - > callee = = nullptr ) {
push_error ( R " *(Cannot call on an expression. Use " . call ( ) " if it's a Callable.)* " ) ;
} else if ( call - > callee - > type = = Node : : IDENTIFIER ) {
2020-06-10 23:18:10 +02:00
call - > function_name = static_cast < IdentifierNode * > ( call - > callee ) - > name ;
2020-07-16 03:02:44 +02:00
make_completion_context ( COMPLETION_METHOD , call - > callee ) ;
2020-06-10 23:18:10 +02:00
} else if ( call - > callee - > type = = Node : : SUBSCRIPT ) {
SubscriptNode * attribute = static_cast < SubscriptNode * > ( call - > callee ) ;
if ( attribute - > is_attribute ) {
2020-07-16 03:02:44 +02:00
if ( attribute - > attribute ) {
call - > function_name = attribute - > attribute - > name ;
}
make_completion_context ( COMPLETION_ATTRIBUTE_METHOD , call - > callee ) ;
2020-06-10 23:18:10 +02:00
} else {
// TODO: The analyzer can see if this is actually a Callable and give better error message.
push_error ( R " *(Cannot call on an expression. Use " . call ( ) " if it's a Callable.)* " ) ;
}
} else {
push_error ( R " *(Cannot call on an expression. Use " . call ( ) " if it's a Callable.)* " ) ;
}
2020-05-02 00:14:56 +02:00
}
2014-11-06 01:20:42 +01:00
2020-11-06 09:53:51 +01:00
// Arguments.
CompletionType ct = COMPLETION_CALL_ARGUMENTS ;
2020-11-26 15:56:32 +01:00
if ( call - > function_name = = " load " ) {
2020-11-06 09:53:51 +01:00
ct = COMPLETION_RESOURCE_PATH ;
2020-05-02 00:14:56 +02:00
}
2020-11-06 09:53:51 +01:00
push_completion_call ( call ) ;
int argument_index = 0 ;
do {
make_completion_context ( ct , call , argument_index + + , true ) ;
if ( check ( GDScriptTokenizer : : Token : : PARENTHESIS_CLOSE ) ) {
// Allow for trailing comma.
break ;
}
2022-10-02 11:56:46 +02:00
bool use_identifier_completion = current . cursor_place = = GDScriptTokenizer : : CURSOR_END | | current . cursor_place = = GDScriptTokenizer : : CURSOR_MIDDLE ;
2020-11-06 09:53:51 +01:00
ExpressionNode * argument = parse_expression ( false ) ;
if ( argument = = nullptr ) {
push_error ( R " (Expected expression as the function argument.) " ) ;
} else {
call - > arguments . push_back ( argument ) ;
2022-08-22 10:59:24 +02:00
2022-10-02 11:56:46 +02:00
if ( argument - > type = = Node : : IDENTIFIER & & use_identifier_completion ) {
2022-08-22 10:59:24 +02:00
completion_context . type = COMPLETION_IDENTIFIER ;
}
2020-11-06 09:53:51 +01:00
}
ct = COMPLETION_CALL_ARGUMENTS ;
} while ( match ( GDScriptTokenizer : : Token : : COMMA ) ) ;
pop_completion_call ( ) ;
2014-11-06 01:20:42 +01:00
2020-05-02 00:14:56 +02:00
pop_multiline ( ) ;
consume ( GDScriptTokenizer : : Token : : PARENTHESIS_CLOSE , R " *(Expected closing " ) " after call arguments.)* " ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( call ) ;
2014-11-06 01:20:42 +01:00
2020-05-02 00:14:56 +02:00
return call ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_get_node ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
2022-05-26 17:56:39 +02:00
if ( ! current . is_node_name ( ) & & ! check ( GDScriptTokenizer : : Token : : LITERAL ) & & ! check ( GDScriptTokenizer : : Token : : SLASH ) & & ! check ( GDScriptTokenizer : : Token : : PERCENT ) ) {
push_error ( vformat ( R " (Expected node path as string or identifier after " % s " .) " , previous . get_name ( ) ) ) ;
return nullptr ;
}
if ( check ( GDScriptTokenizer : : Token : : LITERAL ) ) {
if ( current . literal . get_type ( ) ! = Variant : : STRING ) {
push_error ( vformat ( R " (Expected node path as string or identifier after " % s " .) " , previous . get_name ( ) ) ) ;
2020-05-02 00:14:56 +02:00
return nullptr ;
}
2022-05-26 17:56:39 +02:00
}
GetNodeNode * get_node = alloc_node < GetNodeNode > ( ) ;
// Store the last item in the path so the parser knows what to expect.
// Allow allows more specific error messages.
enum PathState {
PATH_STATE_START ,
PATH_STATE_SLASH ,
PATH_STATE_PERCENT ,
PATH_STATE_NODE_NAME ,
} path_state = PATH_STATE_START ;
if ( previous . type = = GDScriptTokenizer : : Token : : DOLLAR ) {
// Detect initial slash, which will be handled in the loop if it matches.
match ( GDScriptTokenizer : : Token : : SLASH ) ;
# ifdef DEBUG_ENABLED
} else {
get_node - > use_dollar = false ;
# endif
}
int context_argument = 0 ;
do {
if ( previous . type = = GDScriptTokenizer : : Token : : PERCENT ) {
if ( path_state ! = PATH_STATE_START & & path_state ! = PATH_STATE_SLASH ) {
push_error ( R " ( " % " is only valid in the beginning of a node name (either after " $ " or after " / " )) " ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( get_node ) ;
2021-10-28 20:16:22 +02:00
return nullptr ;
}
2022-05-26 17:56:39 +02:00
get_node - > full_path + = " % " ;
path_state = PATH_STATE_PERCENT ;
} else if ( previous . type = = GDScriptTokenizer : : Token : : SLASH ) {
if ( path_state ! = PATH_STATE_START & & path_state ! = PATH_STATE_NODE_NAME ) {
push_error ( R " ( " / " is only valid at the beginning of the path or after a node name.) " ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( get_node ) ;
2022-05-26 17:56:39 +02:00
return nullptr ;
}
get_node - > full_path + = " / " ;
path_state = PATH_STATE_SLASH ;
}
make_completion_context ( COMPLETION_GET_NODE , get_node , context_argument + + ) ;
if ( match ( GDScriptTokenizer : : Token : : LITERAL ) ) {
if ( previous . literal . get_type ( ) ! = Variant : : STRING ) {
String previous_token ;
switch ( path_state ) {
case PATH_STATE_START :
previous_token = " $ " ;
break ;
case PATH_STATE_PERCENT :
previous_token = " % " ;
break ;
case PATH_STATE_SLASH :
previous_token = " / " ;
break ;
default :
break ;
}
push_error ( vformat ( R " (Expected node path as string or identifier after " % s " .) " , previous_token ) ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( get_node ) ;
2020-05-02 00:14:56 +02:00
return nullptr ;
2014-02-10 02:10:30 +01:00
}
2022-05-26 17:56:39 +02:00
get_node - > full_path + = previous . literal . operator String ( ) ;
path_state = PATH_STATE_NODE_NAME ;
} else if ( current . is_node_name ( ) ) {
2020-08-19 15:19:05 +02:00
advance ( ) ;
2022-05-26 17:56:39 +02:00
get_node - > full_path + = previous . get_identifier ( ) ;
path_state = PATH_STATE_NODE_NAME ;
} else if ( ! check ( GDScriptTokenizer : : Token : : SLASH ) & & ! check ( GDScriptTokenizer : : Token : : PERCENT ) ) {
push_error ( vformat ( R " (Unexpected " % s " in node path.) " , current . get_name ( ) ) ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( get_node ) ;
2022-05-26 17:56:39 +02:00
return nullptr ;
}
} while ( match ( GDScriptTokenizer : : Token : : SLASH ) | | match ( GDScriptTokenizer : : Token : : PERCENT ) ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( get_node ) ;
2022-05-26 17:56:39 +02:00
return get_node ;
2020-05-02 00:14:56 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_preload ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
PreloadNode * preload = alloc_node < PreloadNode > ( ) ;
preload - > resolved_path = " <missing path> " ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
push_multiline ( true ) ;
consume ( GDScriptTokenizer : : Token : : PARENTHESIS_OPEN , R " (Expected " ( " after " preload " .) " ) ;
2016-12-29 11:31:19 +01:00
2020-07-06 17:24:24 +02:00
make_completion_context ( COMPLETION_RESOURCE_PATH , preload ) ;
push_completion_call ( preload ) ;
2020-05-02 00:14:56 +02:00
preload - > path = parse_expression ( false ) ;
if ( preload - > path = = nullptr ) {
push_error ( R " (Expected resource path after " ( " .) " ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
2020-07-06 17:24:24 +02:00
pop_completion_call ( ) ;
2020-05-02 00:14:56 +02:00
pop_multiline ( ) ;
consume ( GDScriptTokenizer : : Token : : PARENTHESIS_CLOSE , R " *(Expected " ) " after preload path.)* " ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( preload ) ;
2020-05-02 00:14:56 +02:00
return preload ;
2014-02-10 02:10:30 +01:00
}
2021-03-25 14:36:29 +01:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_lambda ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
LambdaNode * lambda = alloc_node < LambdaNode > ( ) ;
2021-03-26 13:03:16 +01:00
lambda - > parent_function = current_function ;
2021-03-25 14:36:29 +01:00
FunctionNode * function = alloc_node < FunctionNode > ( ) ;
2021-03-26 13:03:16 +01:00
function - > source_lambda = lambda ;
function - > is_static = current_function ! = nullptr ? current_function - > is_static : false ;
2021-03-25 14:36:29 +01:00
if ( match ( GDScriptTokenizer : : Token : : IDENTIFIER ) ) {
function - > identifier = parse_identifier ( ) ;
}
bool multiline_context = multiline_stack . back ( ) - > get ( ) ;
// Reset the multiline stack since we don't want the multiline mode one in the lambda body.
push_multiline ( false ) ;
if ( multiline_context ) {
tokenizer . push_expression_indented_block ( ) ;
}
push_multiline ( true ) ; // For the parameters.
if ( function - > identifier ) {
consume ( GDScriptTokenizer : : Token : : PARENTHESIS_OPEN , R " (Expected opening " ( " after lambda name.) " ) ;
} else {
consume ( GDScriptTokenizer : : Token : : PARENTHESIS_OPEN , R " (Expected opening " ( " after " func " .) " ) ;
}
FunctionNode * previous_function = current_function ;
current_function = function ;
SuiteNode * body = alloc_node < SuiteNode > ( ) ;
2022-05-24 02:38:31 +02:00
body - > parent_function = current_function ;
body - > parent_block = current_suite ;
2021-03-25 14:36:29 +01:00
SuiteNode * previous_suite = current_suite ;
current_suite = body ;
parse_function_signature ( function , body , " lambda " ) ;
current_suite = previous_suite ;
bool previous_in_lambda = in_lambda ;
in_lambda = true ;
function - > body = parse_suite ( " lambda declaration " , body , true ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( function ) ;
complete_extents ( lambda ) ;
2021-03-25 14:36:29 +01:00
pop_multiline ( ) ;
if ( multiline_context ) {
// If we're in multiline mode, we want to skip the spurious DEDENT and NEWLINE tokens.
while ( check ( GDScriptTokenizer : : Token : : DEDENT ) | | check ( GDScriptTokenizer : : Token : : INDENT ) | | check ( GDScriptTokenizer : : Token : : NEWLINE ) ) {
current = tokenizer . scan ( ) ; // Not advance() since we don't want to change the previous token.
}
tokenizer . pop_expression_indented_block ( ) ;
}
current_function = previous_function ;
in_lambda = previous_in_lambda ;
lambda - > function = function ;
return lambda ;
}
2021-09-21 19:38:14 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_yield ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
push_error ( R " ( " yield " was removed in Godot 4.0. Use " await " instead.) " ) ;
return nullptr ;
}
2020-05-02 00:14:56 +02:00
GDScriptParser : : ExpressionNode * GDScriptParser : : parse_invalid_token ( ExpressionNode * p_previous_operand , bool p_can_assign ) {
// Just for better error messages.
GDScriptTokenizer : : Token : : Type invalid = previous . type ;
switch ( invalid ) {
case GDScriptTokenizer : : Token : : QUESTION_MARK :
push_error ( R " (Unexpected " ? " in source. If you want a ternary operator, use " truthy_value if true_condition else falsy_value " .) " ) ;
break ;
default :
return nullptr ; // Unreachable.
2020-05-14 16:41:43 +02:00
}
2020-05-02 00:14:56 +02:00
// Return the previous expression.
return p_previous_operand ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
GDScriptParser : : TypeNode * GDScriptParser : : parse_type ( bool p_allow_void ) {
2020-07-06 17:24:24 +02:00
TypeNode * type = alloc_node < TypeNode > ( ) ;
make_completion_context ( p_allow_void ? COMPLETION_TYPE_NAME_OR_VOID : COMPLETION_TYPE_NAME , type ) ;
2020-05-02 00:14:56 +02:00
if ( ! match ( GDScriptTokenizer : : Token : : IDENTIFIER ) ) {
if ( match ( GDScriptTokenizer : : Token : : VOID ) ) {
if ( p_allow_void ) {
2022-07-11 20:31:15 +02:00
complete_extents ( type ) ;
TypeNode * void_type = type ;
2020-07-16 03:02:44 +02:00
return void_type ;
2020-05-02 00:14:56 +02:00
} else {
push_error ( R " ( " void " is only allowed for a function return type.) " ) ;
2020-05-14 16:41:43 +02:00
}
2020-03-09 00:24:12 +01:00
}
2020-05-02 00:14:56 +02:00
// Leave error message to the caller who knows the context.
2022-07-11 20:31:15 +02:00
complete_extents ( type ) ;
2020-05-02 00:14:56 +02:00
return nullptr ;
2020-03-09 00:24:12 +01:00
}
2020-06-10 23:18:10 +02:00
IdentifierNode * type_element = parse_identifier ( ) ;
2020-03-09 00:24:12 +01:00
2020-06-10 23:18:10 +02:00
type - > type_chain . push_back ( type_element ) ;
2021-03-09 16:32:35 +01:00
if ( match ( GDScriptTokenizer : : Token : : BRACKET_OPEN ) ) {
// Typed collection (like Array[int]).
type - > container_type = parse_type ( false ) ; // Don't allow void for array element type.
if ( type - > container_type = = nullptr ) {
push_error ( R " (Expected type for collection after " [ " .) " ) ;
2022-07-11 20:31:15 +02:00
complete_extents ( type ) ;
2021-03-09 16:32:35 +01:00
type = nullptr ;
} else if ( type - > container_type - > container_type ! = nullptr ) {
push_error ( " Nested typed collections are not supported. " ) ;
}
consume ( GDScriptTokenizer : : Token : : BRACKET_CLOSE , R " (Expected closing " ] " after collection type.) " ) ;
2022-07-11 20:31:15 +02:00
if ( type ! = nullptr ) {
complete_extents ( type ) ;
}
2021-03-09 16:32:35 +01:00
return type ;
}
2020-07-06 17:24:24 +02:00
int chain_index = 1 ;
2020-06-10 23:18:10 +02:00
while ( match ( GDScriptTokenizer : : Token : : PERIOD ) ) {
2020-07-06 17:24:24 +02:00
make_completion_context ( COMPLETION_TYPE_ATTRIBUTE , type , chain_index + + ) ;
2020-06-10 23:18:10 +02:00
if ( consume ( GDScriptTokenizer : : Token : : IDENTIFIER , R " (Expected inner type name after " . " .) " ) ) {
type_element = parse_identifier ( ) ;
type - > type_chain . push_back ( type_element ) ;
2020-03-09 00:24:12 +01:00
}
2020-05-02 00:14:56 +02:00
}
2022-07-11 20:31:15 +02:00
complete_extents ( type ) ;
2020-05-02 00:14:56 +02:00
return type ;
}
2020-11-29 03:37:57 +01:00
# ifdef TOOLS_ENABLED
static bool _in_codeblock ( String p_line , bool p_already_in , int * r_block_begins = nullptr ) {
int start_block = p_line . rfind ( " [codeblock] " ) ;
int end_block = p_line . rfind ( " [/codeblock] " ) ;
if ( start_block ! = - 1 & & r_block_begins ) {
* r_block_begins = start_block ;
}
if ( p_already_in ) {
if ( end_block = = - 1 ) {
return true ;
} else if ( start_block = = - 1 ) {
return false ;
} else {
return start_block > end_block ;
}
} else {
if ( start_block = = - 1 ) {
return false ;
} else if ( end_block = = - 1 ) {
return true ;
} else {
return start_block > end_block ;
}
}
}
bool GDScriptParser : : has_comment ( int p_line ) {
return tokenizer . get_comments ( ) . has ( p_line ) ;
}
String GDScriptParser : : get_doc_comment ( int p_line , bool p_single_line ) {
2022-05-13 15:04:37 +02:00
const HashMap < int , GDScriptTokenizer : : CommentData > & comments = tokenizer . get_comments ( ) ;
2020-11-29 03:37:57 +01:00
ERR_FAIL_COND_V ( ! comments . has ( p_line ) , String ( ) ) ;
if ( p_single_line ) {
if ( comments [ p_line ] . comment . begins_with ( " ## " ) ) {
return comments [ p_line ] . comment . trim_prefix ( " ## " ) . strip_edges ( ) ;
}
return " " ;
}
String doc ;
int line = p_line ;
bool in_codeblock = false ;
while ( comments . has ( line - 1 ) ) {
if ( ! comments [ line - 1 ] . new_line | | ! comments [ line - 1 ] . comment . begins_with ( " ## " ) ) {
break ;
}
line - - ;
}
int codeblock_begins = 0 ;
while ( comments . has ( line ) ) {
if ( ! comments [ line ] . new_line | | ! comments [ line ] . comment . begins_with ( " ## " ) ) {
break ;
}
String doc_line = comments [ line ] . comment . trim_prefix ( " ## " ) ;
in_codeblock = _in_codeblock ( doc_line , in_codeblock , & codeblock_begins ) ;
if ( in_codeblock ) {
int i = 0 ;
for ( ; i < codeblock_begins ; i + + ) {
if ( doc_line [ i ] ! = ' ' ) {
break ;
}
}
doc_line = doc_line . substr ( i ) ;
} else {
doc_line = doc_line . strip_edges ( ) ;
}
String line_join = ( in_codeblock ) ? " \n " : " " ;
2020-12-15 13:04:21 +01:00
doc = ( doc . is_empty ( ) ) ? doc_line : doc + line_join + doc_line ;
2020-11-29 03:37:57 +01:00
line + + ;
}
return doc ;
}
void GDScriptParser : : get_class_doc_comment ( int p_line , String & p_brief , String & p_desc , Vector < Pair < String , String > > & p_tutorials , bool p_inner_class ) {
2022-05-13 15:04:37 +02:00
const HashMap < int , GDScriptTokenizer : : CommentData > & comments = tokenizer . get_comments ( ) ;
2020-11-29 03:37:57 +01:00
if ( ! comments . has ( p_line ) ) {
return ;
}
2021-12-09 10:42:46 +01:00
ERR_FAIL_COND ( ! p_brief . is_empty ( ) | | ! p_desc . is_empty ( ) | | p_tutorials . size ( ) ! = 0 ) ;
2020-11-29 03:37:57 +01:00
int line = p_line ;
bool in_codeblock = false ;
enum Mode {
BRIEF ,
DESC ,
TUTORIALS ,
DONE ,
} ;
Mode mode = BRIEF ;
if ( p_inner_class ) {
while ( comments . has ( line - 1 ) ) {
if ( ! comments [ line - 1 ] . new_line | | ! comments [ line - 1 ] . comment . begins_with ( " ## " ) ) {
break ;
}
line - - ;
}
}
int codeblock_begins = 0 ;
while ( comments . has ( line ) ) {
if ( ! comments [ line ] . new_line | | ! comments [ line ] . comment . begins_with ( " ## " ) ) {
break ;
}
String title , link ; // For tutorials.
String doc_line = comments [ line + + ] . comment . trim_prefix ( " ## " ) ;
2021-12-14 20:16:26 +01:00
String stripped_line = doc_line . strip_edges ( ) ;
2020-11-29 03:37:57 +01:00
// Set the read mode.
2021-12-14 20:16:26 +01:00
if ( stripped_line . is_empty ( ) & & mode = = BRIEF & & ! p_brief . is_empty ( ) ) {
2020-11-29 03:37:57 +01:00
mode = DESC ;
2021-12-14 20:16:26 +01:00
continue ;
2020-11-29 03:37:57 +01:00
2021-12-14 20:16:26 +01:00
} else if ( stripped_line . begins_with ( " @tutorial " ) ) {
2020-11-29 03:37:57 +01:00
int begin_scan = String ( " @tutorial " ) . length ( ) ;
2021-12-14 20:16:26 +01:00
if ( begin_scan > = stripped_line . length ( ) ) {
2020-11-29 03:37:57 +01:00
continue ; // invalid syntax.
}
2021-12-14 20:16:26 +01:00
if ( stripped_line [ begin_scan ] = = ' : ' ) { // No title.
2020-11-29 03:37:57 +01:00
// Syntax: ## @tutorial: https://godotengine.org/ // The title argument is optional.
title = " " ;
2021-12-14 20:16:26 +01:00
link = stripped_line . trim_prefix ( " @tutorial: " ) . strip_edges ( ) ;
2020-11-29 03:37:57 +01:00
} else {
/* Syntax:
2021-10-28 15:43:36 +02:00
* @ tutorial ( The Title Here ) : https : //the.url/
* ^ open ^ close ^ colon ^ url
*/
2020-11-29 03:37:57 +01:00
int open_bracket_pos = begin_scan , close_bracket_pos = 0 ;
2021-12-14 20:16:26 +01:00
while ( open_bracket_pos < stripped_line . length ( ) & & ( stripped_line [ open_bracket_pos ] = = ' ' | | stripped_line [ open_bracket_pos ] = = ' \t ' ) ) {
2020-11-29 03:37:57 +01:00
open_bracket_pos + + ;
}
2021-12-14 20:16:26 +01:00
if ( open_bracket_pos = = stripped_line . length ( ) | | stripped_line [ open_bracket_pos + + ] ! = ' ( ' ) {
2020-11-29 03:37:57 +01:00
continue ; // invalid syntax.
}
close_bracket_pos = open_bracket_pos ;
2021-12-14 20:16:26 +01:00
while ( close_bracket_pos < stripped_line . length ( ) & & stripped_line [ close_bracket_pos ] ! = ' ) ' ) {
2020-11-29 03:37:57 +01:00
close_bracket_pos + + ;
}
2021-12-14 20:16:26 +01:00
if ( close_bracket_pos = = stripped_line . length ( ) ) {
2020-11-29 03:37:57 +01:00
continue ; // invalid syntax.
}
int colon_pos = close_bracket_pos + 1 ;
2021-12-14 20:16:26 +01:00
while ( colon_pos < stripped_line . length ( ) & & ( stripped_line [ colon_pos ] = = ' ' | | stripped_line [ colon_pos ] = = ' \t ' ) ) {
2020-11-29 03:37:57 +01:00
colon_pos + + ;
}
2021-12-14 20:16:26 +01:00
if ( colon_pos = = stripped_line . length ( ) | | stripped_line [ colon_pos + + ] ! = ' : ' ) {
2020-11-29 03:37:57 +01:00
continue ; // invalid syntax.
}
2021-12-14 20:16:26 +01:00
title = stripped_line . substr ( open_bracket_pos , close_bracket_pos - open_bracket_pos ) . strip_edges ( ) ;
link = stripped_line . substr ( colon_pos ) . strip_edges ( ) ;
2020-11-29 03:37:57 +01:00
}
mode = TUTORIALS ;
in_codeblock = false ;
2021-12-14 20:16:26 +01:00
} else if ( stripped_line . is_empty ( ) ) {
2020-11-29 03:37:57 +01:00
continue ;
} else {
// Tutorial docs are single line, we need a @tag after it.
if ( mode = = TUTORIALS ) {
mode = DONE ;
}
in_codeblock = _in_codeblock ( doc_line , in_codeblock , & codeblock_begins ) ;
}
if ( in_codeblock ) {
int i = 0 ;
for ( ; i < codeblock_begins ; i + + ) {
if ( doc_line [ i ] ! = ' ' ) {
break ;
}
}
doc_line = doc_line . substr ( i ) ;
} else {
2021-12-14 20:16:26 +01:00
doc_line = stripped_line ;
2020-11-29 03:37:57 +01:00
}
String line_join = ( in_codeblock ) ? " \n " : " " ;
switch ( mode ) {
case BRIEF :
p_brief = ( p_brief . length ( ) = = 0 ) ? doc_line : p_brief + line_join + doc_line ;
break ;
case DESC :
p_desc = ( p_desc . length ( ) = = 0 ) ? doc_line : p_desc + line_join + doc_line ;
break ;
case TUTORIALS :
p_tutorials . append ( Pair < String , String > ( title , link ) ) ;
break ;
case DONE :
2022-07-17 22:04:22 +02:00
break ;
}
}
if ( current_class - > members . size ( ) > 0 ) {
const ClassNode : : Member & m = current_class - > members [ 0 ] ;
int first_member_line = m . get_line ( ) ;
if ( first_member_line = = line ) {
p_brief = " " ;
p_desc = " " ;
p_tutorials . clear ( ) ;
2020-11-29 03:37:57 +01:00
}
}
}
# endif // TOOLS_ENABLED
2020-05-02 00:14:56 +02:00
GDScriptParser : : ParseRule * GDScriptParser : : get_rule ( GDScriptTokenizer : : Token : : Type p_token_type ) {
// Function table for expression parsing.
// clang-format destroys the alignment here, so turn off for the table.
/* clang-format off */
static ParseRule rules [ ] = {
2020-06-10 23:18:10 +02:00
// PREFIX INFIX PRECEDENCE (for infix)
2020-05-02 00:14:56 +02:00
{ nullptr , nullptr , PREC_NONE } , // EMPTY,
// Basic
{ nullptr , nullptr , PREC_NONE } , // ANNOTATION,
{ & GDScriptParser : : parse_identifier , nullptr , PREC_NONE } , // IDENTIFIER,
{ & GDScriptParser : : parse_literal , nullptr , PREC_NONE } , // LITERAL,
// Comparison
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_COMPARISON } , // LESS,
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_COMPARISON } , // LESS_EQUAL,
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_COMPARISON } , // GREATER,
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_COMPARISON } , // GREATER_EQUAL,
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_COMPARISON } , // EQUAL_EQUAL,
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_COMPARISON } , // BANG_EQUAL,
// Logical
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_LOGIC_AND } , // AND,
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_LOGIC_OR } , // OR,
2020-09-01 09:39:17 +02:00
{ & GDScriptParser : : parse_unary_operator , & GDScriptParser : : parse_binary_not_in_operator , PREC_CONTENT_TEST } , // NOT,
2020-05-02 00:14:56 +02:00
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_LOGIC_AND } , // AMPERSAND_AMPERSAND,
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_LOGIC_OR } , // PIPE_PIPE,
{ & GDScriptParser : : parse_unary_operator , nullptr , PREC_NONE } , // BANG,
// Bitwise
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_BIT_AND } , // AMPERSAND,
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_BIT_OR } , // PIPE,
{ & GDScriptParser : : parse_unary_operator , nullptr , PREC_NONE } , // TILDE,
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_BIT_XOR } , // CARET,
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_BIT_SHIFT } , // LESS_LESS,
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_BIT_SHIFT } , // GREATER_GREATER,
// Math
2021-01-10 14:03:05 +01:00
{ & GDScriptParser : : parse_unary_operator , & GDScriptParser : : parse_binary_operator , PREC_ADDITION_SUBTRACTION } , // PLUS,
{ & GDScriptParser : : parse_unary_operator , & GDScriptParser : : parse_binary_operator , PREC_ADDITION_SUBTRACTION } , // MINUS,
2020-05-02 00:14:56 +02:00
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_FACTOR } , // STAR,
2022-05-12 09:02:03 +02:00
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_POWER } , // STAR_STAR,
2020-05-02 00:14:56 +02:00
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_FACTOR } , // SLASH,
2022-05-26 17:56:39 +02:00
{ & GDScriptParser : : parse_get_node , & GDScriptParser : : parse_binary_operator , PREC_FACTOR } , // PERCENT,
2020-05-02 00:14:56 +02:00
// Assignment
{ nullptr , & GDScriptParser : : parse_assignment , PREC_ASSIGNMENT } , // EQUAL,
{ nullptr , & GDScriptParser : : parse_assignment , PREC_ASSIGNMENT } , // PLUS_EQUAL,
{ nullptr , & GDScriptParser : : parse_assignment , PREC_ASSIGNMENT } , // MINUS_EQUAL,
{ nullptr , & GDScriptParser : : parse_assignment , PREC_ASSIGNMENT } , // STAR_EQUAL,
2022-03-07 18:25:21 +01:00
{ nullptr , & GDScriptParser : : parse_assignment , PREC_ASSIGNMENT } , // STAR_STAR_EQUAL,
2020-05-02 00:14:56 +02:00
{ nullptr , & GDScriptParser : : parse_assignment , PREC_ASSIGNMENT } , // SLASH_EQUAL,
{ nullptr , & GDScriptParser : : parse_assignment , PREC_ASSIGNMENT } , // PERCENT_EQUAL,
{ nullptr , & GDScriptParser : : parse_assignment , PREC_ASSIGNMENT } , // LESS_LESS_EQUAL,
{ nullptr , & GDScriptParser : : parse_assignment , PREC_ASSIGNMENT } , // GREATER_GREATER_EQUAL,
{ nullptr , & GDScriptParser : : parse_assignment , PREC_ASSIGNMENT } , // AMPERSAND_EQUAL,
{ nullptr , & GDScriptParser : : parse_assignment , PREC_ASSIGNMENT } , // PIPE_EQUAL,
{ nullptr , & GDScriptParser : : parse_assignment , PREC_ASSIGNMENT } , // CARET_EQUAL,
// Control flow
{ nullptr , & GDScriptParser : : parse_ternary_operator , PREC_TERNARY } , // IF,
{ nullptr , nullptr , PREC_NONE } , // ELIF,
{ nullptr , nullptr , PREC_NONE } , // ELSE,
{ nullptr , nullptr , PREC_NONE } , // FOR,
{ nullptr , nullptr , PREC_NONE } , // WHILE,
{ nullptr , nullptr , PREC_NONE } , // BREAK,
{ nullptr , nullptr , PREC_NONE } , // CONTINUE,
{ nullptr , nullptr , PREC_NONE } , // PASS,
{ nullptr , nullptr , PREC_NONE } , // RETURN,
{ nullptr , nullptr , PREC_NONE } , // MATCH,
// Keywords
{ nullptr , & GDScriptParser : : parse_cast , PREC_CAST } , // AS,
{ nullptr , nullptr , PREC_NONE } , // ASSERT,
{ & GDScriptParser : : parse_await , nullptr , PREC_NONE } , // AWAIT,
{ nullptr , nullptr , PREC_NONE } , // BREAKPOINT,
{ nullptr , nullptr , PREC_NONE } , // CLASS,
{ nullptr , nullptr , PREC_NONE } , // CLASS_NAME,
{ nullptr , nullptr , PREC_NONE } , // CONST,
{ nullptr , nullptr , PREC_NONE } , // ENUM,
{ nullptr , nullptr , PREC_NONE } , // EXTENDS,
2021-03-25 14:36:29 +01:00
{ & GDScriptParser : : parse_lambda , nullptr , PREC_NONE } , // FUNC,
2020-05-02 00:14:56 +02:00
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_CONTENT_TEST } , // IN,
{ nullptr , & GDScriptParser : : parse_binary_operator , PREC_TYPE_TEST } , // IS,
{ nullptr , nullptr , PREC_NONE } , // NAMESPACE,
{ & GDScriptParser : : parse_preload , nullptr , PREC_NONE } , // PRELOAD,
{ & GDScriptParser : : parse_self , nullptr , PREC_NONE } , // SELF,
{ nullptr , nullptr , PREC_NONE } , // SIGNAL,
{ nullptr , nullptr , PREC_NONE } , // STATIC,
{ & GDScriptParser : : parse_call , nullptr , PREC_NONE } , // SUPER,
2020-07-16 03:02:44 +02:00
{ nullptr , nullptr , PREC_NONE } , // TRAIT,
2020-05-02 00:14:56 +02:00
{ nullptr , nullptr , PREC_NONE } , // VAR,
{ nullptr , nullptr , PREC_NONE } , // VOID,
2021-09-21 19:38:14 +02:00
{ & GDScriptParser : : parse_yield , nullptr , PREC_NONE } , // YIELD,
2020-05-02 00:14:56 +02:00
// Punctuation
{ & GDScriptParser : : parse_array , & GDScriptParser : : parse_subscript , PREC_SUBSCRIPT } , // BRACKET_OPEN,
{ nullptr , nullptr , PREC_NONE } , // BRACKET_CLOSE,
{ & GDScriptParser : : parse_dictionary , nullptr , PREC_NONE } , // BRACE_OPEN,
{ nullptr , nullptr , PREC_NONE } , // BRACE_CLOSE,
{ & GDScriptParser : : parse_grouping , & GDScriptParser : : parse_call , PREC_CALL } , // PARENTHESIS_OPEN,
{ nullptr , nullptr , PREC_NONE } , // PARENTHESIS_CLOSE,
{ nullptr , nullptr , PREC_NONE } , // COMMA,
{ nullptr , nullptr , PREC_NONE } , // SEMICOLON,
{ nullptr , & GDScriptParser : : parse_attribute , PREC_ATTRIBUTE } , // PERIOD,
{ nullptr , nullptr , PREC_NONE } , // PERIOD_PERIOD,
{ nullptr , nullptr , PREC_NONE } , // COLON,
{ & GDScriptParser : : parse_get_node , nullptr , PREC_NONE } , // DOLLAR,
{ nullptr , nullptr , PREC_NONE } , // FORWARD_ARROW,
{ nullptr , nullptr , PREC_NONE } , // UNDERSCORE,
// Whitespace
{ nullptr , nullptr , PREC_NONE } , // NEWLINE,
{ nullptr , nullptr , PREC_NONE } , // INDENT,
{ nullptr , nullptr , PREC_NONE } , // DEDENT,
// Constants
{ & GDScriptParser : : parse_builtin_constant , nullptr , PREC_NONE } , // CONST_PI,
{ & GDScriptParser : : parse_builtin_constant , nullptr , PREC_NONE } , // CONST_TAU,
{ & GDScriptParser : : parse_builtin_constant , nullptr , PREC_NONE } , // CONST_INF,
{ & GDScriptParser : : parse_builtin_constant , nullptr , PREC_NONE } , // CONST_NAN,
// Error message improvement
{ nullptr , nullptr , PREC_NONE } , // VCS_CONFLICT_MARKER,
{ nullptr , nullptr , PREC_NONE } , // BACKTICK,
{ nullptr , & GDScriptParser : : parse_invalid_token , PREC_CAST } , // QUESTION_MARK,
// Special
{ nullptr , nullptr , PREC_NONE } , // ERROR,
{ nullptr , nullptr , PREC_NONE } , // TK_EOF,
} ;
/* clang-format on */
// Avoid desync.
static_assert ( sizeof ( rules ) / sizeof ( rules [ 0 ] ) = = GDScriptTokenizer : : Token : : TK_MAX , " Amount of parse rules don't match the amount of token types. " ) ;
2021-03-12 14:35:16 +01:00
// Let's assume this is never invalid, since nothing generates a TK_MAX.
2020-05-02 00:14:56 +02:00
return & rules [ p_token_type ] ;
}
bool GDScriptParser : : SuiteNode : : has_local ( const StringName & p_name ) const {
if ( locals_indices . has ( p_name ) ) {
2020-03-09 00:24:12 +01:00
return true ;
}
2020-05-02 00:14:56 +02:00
if ( parent_block ! = nullptr ) {
return parent_block - > has_local ( p_name ) ;
2020-03-09 00:24:12 +01:00
}
return false ;
}
2020-05-02 00:14:56 +02:00
const GDScriptParser : : SuiteNode : : Local & GDScriptParser : : SuiteNode : : get_local ( const StringName & p_name ) const {
if ( locals_indices . has ( p_name ) ) {
return locals [ locals_indices [ p_name ] ] ;
2015-08-30 16:50:10 +02:00
}
2020-05-02 00:14:56 +02:00
if ( parent_block ! = nullptr ) {
return parent_block - > get_local ( p_name ) ;
2015-09-02 05:56:51 +02:00
}
2020-05-02 00:14:56 +02:00
return empty ;
}
2015-09-02 05:56:51 +02:00
2020-05-02 00:14:56 +02:00
bool GDScriptParser : : AnnotationNode : : apply ( GDScriptParser * p_this , Node * p_target ) const {
return ( p_this - > * ( p_this - > valid_annotations [ name ] . apply ) ) ( this , p_target ) ;
2015-08-30 16:50:10 +02:00
}
2020-05-02 00:14:56 +02:00
bool GDScriptParser : : AnnotationNode : : applies_to ( uint32_t p_target_kinds ) const {
return ( info - > target_kind & p_target_kinds ) > 0 ;
2018-05-30 04:16:54 +02:00
}
2020-05-02 00:14:56 +02:00
bool GDScriptParser : : validate_annotation_arguments ( AnnotationNode * p_annotation ) {
ERR_FAIL_COND_V_MSG ( ! valid_annotations . has ( p_annotation - > name ) , false , vformat ( R " (Annotation " % s " not found to validate.) " , p_annotation - > name ) ) ;
2020-01-10 23:43:33 +01:00
2020-05-02 00:14:56 +02:00
const MethodInfo & info = valid_annotations [ p_annotation - > name ] . info ;
2018-06-05 18:50:21 +02:00
2020-05-02 00:14:56 +02:00
if ( ( ( info . flags & METHOD_FLAG_VARARG ) = = 0 ) & & p_annotation - > arguments . size ( ) > info . arguments . size ( ) ) {
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 ( ) ) ) ;
return false ;
2018-05-30 04:16:54 +02:00
}
2020-05-02 00:14:56 +02:00
if ( p_annotation - > arguments . size ( ) < info . arguments . size ( ) - info . default_arguments . size ( ) ) {
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 ( ) ) ) ;
return false ;
2018-05-30 04:16:54 +02:00
}
2020-05-02 00:14:56 +02:00
const List < PropertyInfo > : : Element * E = info . arguments . front ( ) ;
for ( int i = 0 ; i < p_annotation - > arguments . size ( ) ; i + + ) {
ExpressionNode * argument = p_annotation - > arguments [ i ] ;
const PropertyInfo & parameter = E - > get ( ) ;
if ( E - > next ( ) ! = nullptr ) {
E = E - > next ( ) ;
}
switch ( parameter . type ) {
case Variant : : STRING :
case Variant : : STRING_NAME :
case Variant : : NODE_PATH :
// Allow "quote-less strings", as long as they are recognized as identifiers.
if ( argument - > type = = Node : : IDENTIFIER ) {
IdentifierNode * string = static_cast < IdentifierNode * > ( argument ) ;
Callable : : CallError error ;
Vector < Variant > args = varray ( string - > name ) ;
const Variant * name = args . ptr ( ) ;
2020-11-09 04:19:09 +01:00
Variant r ;
Variant : : construct ( parameter . type , r , & ( name ) , 1 , error ) ;
p_annotation - > resolved_arguments . push_back ( r ) ;
2020-05-02 00:14:56 +02:00
if ( error . error ! = Callable : : CallError : : CALL_OK ) {
2021-12-03 21:37:06 +01:00
push_error ( vformat ( R " (Expected %s as argument %d of annotation " % s " .) " , Variant : : get_type_name ( parameter . type ) , i + 1 , p_annotation - > name ) ) ;
2021-07-04 00:17:03 +02:00
p_annotation - > resolved_arguments . remove_at ( p_annotation - > resolved_arguments . size ( ) - 1 ) ;
2020-05-02 00:14:56 +02:00
return false ;
2019-03-04 12:25:59 +01:00
}
2020-05-02 00:14:56 +02:00
break ;
2018-06-19 07:55:52 +02:00
}
2020-05-02 00:14:56 +02:00
[[fallthrough]] ;
default : {
if ( argument - > type ! = Node : : LITERAL ) {
2021-12-03 21:37:06 +01:00
push_error ( vformat ( R " (Expected %s as argument %d of annotation " % s " .) " , Variant : : get_type_name ( parameter . type ) , i + 1 , p_annotation - > name ) ) ;
2020-05-02 00:14:56 +02:00
return false ;
2020-05-10 07:05:29 +02:00
}
2018-05-30 04:16:54 +02:00
2020-05-02 00:14:56 +02:00
Variant value = static_cast < LiteralNode * > ( argument ) - > value ;
if ( ! Variant : : can_convert_strict ( value . get_type ( ) , parameter . type ) ) {
2021-12-03 21:37:06 +01:00
push_error ( vformat ( R " (Expected %s as argument %d of annotation " % s " .) " , Variant : : get_type_name ( parameter . type ) , i + 1 , p_annotation - > name ) ) ;
2020-05-02 00:14:56 +02:00
return false ;
2018-05-30 04:16:54 +02:00
}
2020-05-02 00:14:56 +02:00
Callable : : CallError error ;
const Variant * args = & value ;
2020-11-09 04:19:09 +01:00
Variant r ;
Variant : : construct ( parameter . type , r , & ( args ) , 1 , error ) ;
p_annotation - > resolved_arguments . push_back ( r ) ;
2020-05-02 00:14:56 +02:00
if ( error . error ! = Callable : : CallError : : CALL_OK ) {
2021-12-03 21:37:06 +01:00
push_error ( vformat ( R " (Expected %s as argument %d of annotation " % s " .) " , Variant : : get_type_name ( parameter . type ) , i + 1 , p_annotation - > name ) ) ;
2021-07-04 00:17:03 +02:00
p_annotation - > resolved_arguments . remove_at ( p_annotation - > resolved_arguments . size ( ) - 1 ) ;
2020-05-02 00:14:56 +02:00
return false ;
2018-05-30 04:16:54 +02:00
}
2020-05-10 12:56:01 +02:00
break ;
2020-05-14 16:41:43 +02:00
}
2018-05-30 04:16:54 +02:00
}
2019-11-18 00:19:06 +01:00
}
2020-05-02 00:14:56 +02:00
return true ;
2018-05-30 04:16:54 +02:00
}
2020-05-02 00:14:56 +02:00
bool GDScriptParser : : tool_annotation ( const AnnotationNode * p_annotation , Node * p_node ) {
this - > _is_tool = true ;
return true ;
}
2018-07-25 17:12:46 +02:00
2020-05-02 00:14:56 +02:00
bool GDScriptParser : : icon_annotation ( const AnnotationNode * p_annotation , Node * p_node ) {
ERR_FAIL_COND_V_MSG ( p_node - > type ! = Node : : CLASS , false , R " ( " @ icon " annotation can only be applied to classes.) " ) ;
ClassNode * p_class = static_cast < ClassNode * > ( p_node ) ;
p_class - > icon_path = p_annotation - > resolved_arguments [ 0 ] ;
return true ;
}
2018-05-30 04:16:54 +02:00
2020-05-02 00:14:56 +02:00
bool GDScriptParser : : onready_annotation ( const AnnotationNode * p_annotation , Node * p_node ) {
ERR_FAIL_COND_V_MSG ( p_node - > type ! = Node : : VARIABLE , false , R " ( " @ onready " annotation can only be applied to class variables.) " ) ;
2018-05-30 04:16:54 +02:00
2020-05-02 00:14:56 +02:00
VariableNode * variable = static_cast < VariableNode * > ( p_node ) ;
if ( variable - > onready ) {
push_error ( R " ( " @ onready " annotation can only be used once per variable.) " ) ;
return false ;
2018-05-30 04:16:54 +02:00
}
2020-05-02 00:14:56 +02:00
variable - > onready = true ;
current_class - > onready_used = true ;
return true ;
2018-05-30 04:16:54 +02:00
}
2020-05-02 00:14:56 +02:00
template < PropertyHint t_hint , Variant : : Type t_type >
bool GDScriptParser : : export_annotations ( const AnnotationNode * p_annotation , Node * p_node ) {
ERR_FAIL_COND_V_MSG ( p_node - > type ! = Node : : VARIABLE , false , vformat ( R " ( " % s " annotation can only be applied to variables.) " , p_annotation - > name ) ) ;
2018-05-30 04:16:54 +02:00
2022-03-16 15:09:29 +01:00
{
const int max_flags = 32 ;
if ( t_hint = = PropertyHint : : PROPERTY_HINT_FLAGS & & p_annotation - > resolved_arguments . size ( ) > max_flags ) {
push_error ( vformat ( R " (The argument count limit for " @ export_flags " is exceeded (%d/%d).) " , p_annotation - > resolved_arguments . size ( ) , max_flags ) , p_annotation ) ;
return false ;
}
}
2020-05-02 00:14:56 +02:00
VariableNode * variable = static_cast < VariableNode * > ( p_node ) ;
if ( variable - > exported ) {
push_error ( vformat ( R " (Annotation " % s " cannot be used with another " @ export " annotation.) " , p_annotation - > name ) , p_annotation ) ;
return false ;
2018-07-01 18:17:40 +02:00
}
2020-05-02 00:14:56 +02:00
variable - > exported = true ;
2021-03-17 14:57:30 +01:00
2020-05-02 00:14:56 +02:00
variable - > export_info . type = t_type ;
variable - > export_info . hint = t_hint ;
2018-05-30 04:16:54 +02:00
2020-05-02 00:14:56 +02:00
String hint_string ;
for ( int i = 0 ; i < p_annotation - > resolved_arguments . size ( ) ; i + + ) {
if ( i > 0 ) {
hint_string + = " , " ;
}
hint_string + = String ( p_annotation - > resolved_arguments [ i ] ) ;
}
2018-06-05 18:50:21 +02:00
2020-05-02 00:14:56 +02:00
variable - > export_info . hint_string = hint_string ;
2018-05-30 04:16:54 +02:00
Fix various typos
Found via `codespell -q 3 -S ./thirdparty,*.po,./DONORS.md -L ackward,ang,ans,ba,beng,cas,childs,childrens,dof,doubleclick,expct,fave,findn,gird,hist,inh,inout,leapyear,lod,nd,numer,ois,ony,paket,ro,seeked,sinc,switchs,te,uint,varn,vew`
2022-01-02 19:47:52 +01:00
// This is called after the analyzer is done finding the type, so this should be set here.
2021-03-18 14:17:42 +01:00
DataType export_type = variable - > get_datatype ( ) ;
2021-03-17 14:57:30 +01:00
2022-02-06 14:12:19 +01:00
if ( p_annotation - > name = = SNAME ( " @export " ) ) {
2021-03-17 14:57:30 +01:00
if ( variable - > datatype_specifier = = nullptr & & variable - > initializer = = nullptr ) {
push_error ( R " (Cannot use simple " @ export " annotation with variable without type or initializer, since type can't be inferred.) " , p_annotation ) ;
return false ;
}
2021-03-18 14:17:42 +01:00
bool is_array = false ;
if ( export_type . builtin_type = = Variant : : ARRAY & & export_type . has_container_element_type ( ) ) {
export_type = export_type . get_container_element_type ( ) ; // Use inner type for.
is_array = true ;
}
2021-03-17 14:57:30 +01:00
if ( export_type . is_variant ( ) | | export_type . has_no_type ( ) ) {
push_error ( R " (Cannot use simple " @ export " annotation because the type of the initialized value can't be inferred.) " , p_annotation ) ;
return false ;
}
switch ( export_type . kind ) {
case GDScriptParser : : DataType : : BUILTIN :
variable - > export_info . type = export_type . builtin_type ;
variable - > export_info . hint = PROPERTY_HINT_NONE ;
variable - > export_info . hint_string = Variant : : get_type_name ( export_type . builtin_type ) ;
break ;
case GDScriptParser : : DataType : : NATIVE :
2022-02-06 14:12:19 +01:00
if ( ClassDB : : is_parent_class ( export_type . native_type , SNAME ( " Resource " ) ) ) {
2021-03-17 14:57:30 +01:00
variable - > export_info . type = Variant : : OBJECT ;
variable - > export_info . hint = PROPERTY_HINT_RESOURCE_TYPE ;
2021-08-17 15:06:54 +02:00
variable - > export_info . hint_string = export_type . native_type ;
2022-06-27 23:41:07 +02:00
} else if ( ClassDB : : is_parent_class ( export_type . native_type , SNAME ( " Node " ) ) ) {
variable - > export_info . type = Variant : : OBJECT ;
variable - > export_info . hint = PROPERTY_HINT_NODE_TYPE ;
variable - > export_info . hint_string = export_type . native_type ;
2021-03-17 14:57:30 +01:00
} else {
2022-06-27 23:41:07 +02:00
push_error ( R " (Export type can only be built-in, a resource, a node, or an enum.) " , variable ) ;
2021-03-18 14:17:42 +01:00
return false ;
2021-03-17 14:57:30 +01:00
}
break ;
2022-04-25 06:33:18 +02:00
case GDScriptParser : : DataType : : CLASS :
// Can assume type is a global GDScript class.
2022-10-08 00:16:54 +02:00
if ( ClassDB : : is_parent_class ( export_type . native_type , SNAME ( " Resource " ) ) ) {
variable - > export_info . type = Variant : : OBJECT ;
variable - > export_info . hint = PROPERTY_HINT_RESOURCE_TYPE ;
variable - > export_info . hint_string = export_type . class_type - > identifier - > name ;
} else if ( ClassDB : : is_parent_class ( export_type . native_type , SNAME ( " Node " ) ) ) {
variable - > export_info . type = Variant : : OBJECT ;
variable - > export_info . hint = PROPERTY_HINT_NODE_TYPE ;
variable - > export_info . hint_string = export_type . class_type - > identifier - > name ;
} else {
push_error ( R " (Export type can only be built-in, a resource, a node or an enum.) " , variable ) ;
2022-04-25 06:33:18 +02:00
return false ;
}
2022-10-08 00:16:54 +02:00
2022-04-25 06:33:18 +02:00
break ;
case GDScriptParser : : DataType : : SCRIPT : {
StringName class_name ;
if ( export_type . script_type ! = nullptr & & export_type . script_type . is_valid ( ) ) {
class_name = export_type . script_type - > get_language ( ) - > get_global_class_name ( export_type . script_type - > get_path ( ) ) ;
}
if ( class_name = = StringName ( ) ) {
Ref < Script > script = ResourceLoader : : load ( export_type . script_path , SNAME ( " Script " ) ) ;
if ( script . is_valid ( ) ) {
class_name = script - > get_language ( ) - > get_global_class_name ( export_type . script_path ) ;
}
}
if ( class_name ! = StringName ( ) & & ClassDB : : is_parent_class ( ScriptServer : : get_global_class_native_base ( class_name ) , SNAME ( " Resource " ) ) ) {
variable - > export_info . type = Variant : : OBJECT ;
variable - > export_info . hint = PROPERTY_HINT_RESOURCE_TYPE ;
variable - > export_info . hint_string = class_name ;
}
} break ;
2021-03-17 18:58:05 +01:00
case GDScriptParser : : DataType : : ENUM : {
variable - > export_info . type = Variant : : INT ;
variable - > export_info . hint = PROPERTY_HINT_ENUM ;
String enum_hint_string ;
2022-05-08 10:09:19 +02:00
bool first = true ;
2022-11-08 09:03:25 +01:00
for ( const KeyValue < StringName , int64_t > & E : export_type . enum_values ) {
2022-05-08 10:09:19 +02:00
if ( ! first ) {
2021-03-17 18:58:05 +01:00
enum_hint_string + = " , " ;
2022-05-13 01:11:33 +02:00
} else {
2022-05-08 10:09:19 +02:00
first = false ;
2021-03-17 18:58:05 +01:00
}
2022-05-08 10:09:19 +02:00
enum_hint_string + = E . key . operator String ( ) . capitalize ( ) . xml_escape ( ) ;
enum_hint_string + = " : " ;
enum_hint_string + = String : : num_int64 ( E . value ) . xml_escape ( ) ;
2021-03-17 18:58:05 +01:00
}
variable - > export_info . hint_string = enum_hint_string ;
} break ;
2021-03-17 14:57:30 +01:00
default :
// TODO: Allow custom user resources.
2021-03-17 18:58:05 +01:00
push_error ( R " (Export type can only be built-in, a resource, or an enum.) " , variable ) ;
2021-03-17 14:57:30 +01:00
break ;
}
2021-03-18 14:17:42 +01:00
if ( is_array ) {
String hint_prefix = itos ( variable - > export_info . type ) ;
if ( variable - > export_info . hint ) {
hint_prefix + = " / " + itos ( variable - > export_info . hint ) ;
}
variable - > export_info . hint = PROPERTY_HINT_TYPE_STRING ;
variable - > export_info . hint_string = hint_prefix + " : " + variable - > export_info . hint_string ;
variable - > export_info . type = Variant : : ARRAY ;
}
2021-03-17 14:57:30 +01:00
} else {
// Validate variable type with export.
if ( ! export_type . is_variant ( ) & & ( export_type . kind ! = DataType : : BUILTIN | | export_type . builtin_type ! = t_type ) ) {
// Allow float/int conversion.
if ( ( t_type ! = Variant : : FLOAT | | export_type . builtin_type ! = Variant : : INT ) & & ( t_type ! = Variant : : INT | | export_type . builtin_type ! = Variant : : FLOAT ) ) {
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 ) ;
return false ;
}
}
}
2020-05-02 00:14:56 +02:00
return true ;
}
2018-05-30 04:16:54 +02:00
2022-07-03 21:30:08 +02:00
template < PropertyUsageFlags t_usage >
bool GDScriptParser : : export_group_annotations ( const AnnotationNode * p_annotation , Node * p_node ) {
AnnotationNode * annotation = const_cast < AnnotationNode * > ( p_annotation ) ;
annotation - > export_info . name = annotation - > resolved_arguments [ 0 ] ;
switch ( t_usage ) {
case PROPERTY_USAGE_CATEGORY : {
annotation - > export_info . usage = t_usage ;
} break ;
case PROPERTY_USAGE_GROUP : {
annotation - > export_info . usage = t_usage ;
if ( annotation - > resolved_arguments . size ( ) = = 2 ) {
annotation - > export_info . hint_string = annotation - > resolved_arguments [ 1 ] ;
}
} break ;
case PROPERTY_USAGE_SUBGROUP : {
annotation - > export_info . usage = t_usage ;
if ( annotation - > resolved_arguments . size ( ) = = 2 ) {
annotation - > export_info . hint_string = annotation - > resolved_arguments [ 1 ] ;
}
} break ;
}
current_class - > add_member_group ( annotation ) ;
return true ;
}
2020-05-02 00:14:56 +02:00
bool GDScriptParser : : warning_annotations ( const AnnotationNode * p_annotation , Node * p_node ) {
2022-01-04 13:32:43 +01:00
# ifdef DEBUG_ENABLED
bool has_error = false ;
for ( const Variant & warning_name : p_annotation - > resolved_arguments ) {
GDScriptWarning : : Code warning = GDScriptWarning : : get_code_from_name ( String ( warning_name ) . to_upper ( ) ) ;
if ( warning = = GDScriptWarning : : WARNING_MAX ) {
push_error ( vformat ( R " (Invalid warning name: " % s " .) " , warning_name ) , p_annotation ) ;
has_error = true ;
} else {
p_node - > ignored_warnings . push_back ( warning ) ;
}
}
return ! has_error ;
# else // ! DEBUG_ENABLED
// Only available in debug builds.
return true ;
# endif // DEBUG_ENABLED
2020-05-02 00:14:56 +02:00
}
2022-07-12 23:12:42 +02:00
bool GDScriptParser : : rpc_annotation ( const AnnotationNode * p_annotation , Node * p_node ) {
ERR_FAIL_COND_V_MSG ( p_node - > type ! = Node : : FUNCTION , false , vformat ( R " ( " % s " annotation can only be applied to functions.) " , p_annotation - > name ) ) ;
2020-05-02 00:14:56 +02:00
2022-07-12 23:12:42 +02:00
FunctionNode * function = static_cast < FunctionNode * > ( p_node ) ;
if ( function - > rpc_config . get_type ( ) ! = Variant : : NIL ) {
push_error ( R " (RPC annotations can only be used once per function.) " , p_annotation ) ;
return false ;
}
Dictionary rpc_config ;
rpc_config [ " rpc_mode " ] = MultiplayerAPI : : RPC_MODE_AUTHORITY ;
2021-08-10 21:45:58 +02:00
if ( p_annotation - > resolved_arguments . size ( ) ) {
int last = p_annotation - > resolved_arguments . size ( ) - 1 ;
if ( p_annotation - > resolved_arguments [ last ] . get_type ( ) = = Variant : : INT ) {
2022-07-12 23:12:42 +02:00
rpc_config [ " channel " ] = p_annotation - > resolved_arguments [ last ] . operator int ( ) ;
2021-08-10 21:45:58 +02:00
last - = 1 ;
}
if ( last > 3 ) {
push_error ( R " (Invalid RPC arguments. At most 4 arguments are allowed, where only the last argument can be an integer to specify the channel.') " , p_annotation ) ;
return false ;
}
for ( int i = last ; i > = 0 ; i - - ) {
2021-06-24 10:28:15 +02:00
String mode = p_annotation - > resolved_arguments [ i ] . operator String ( ) ;
2021-10-01 10:43:22 +02:00
if ( mode = = " any_peer " ) {
2022-07-12 23:12:42 +02:00
rpc_config [ " rpc_mode " ] = MultiplayerAPI : : RPC_MODE_ANY_PEER ;
2021-10-01 10:43:22 +02:00
} else if ( mode = = " authority " ) {
2022-07-12 23:12:42 +02:00
rpc_config [ " rpc_mode " ] = MultiplayerAPI : : RPC_MODE_AUTHORITY ;
2021-10-01 10:43:22 +02:00
} else if ( mode = = " call_local " ) {
2022-07-12 23:12:42 +02:00
rpc_config [ " call_local " ] = true ;
2021-10-01 10:43:22 +02:00
} else if ( mode = = " call_remote " ) {
2022-07-12 23:12:42 +02:00
rpc_config [ " call_local " ] = false ;
2021-08-10 21:45:58 +02:00
} else if ( mode = = " reliable " ) {
2022-07-12 23:12:42 +02:00
rpc_config [ " transfer_mode " ] = MultiplayerPeer : : TRANSFER_MODE_RELIABLE ;
2021-06-24 10:28:15 +02:00
} else if ( mode = = " unreliable " ) {
2022-07-12 23:12:42 +02:00
rpc_config [ " transfer_mode " ] = MultiplayerPeer : : TRANSFER_MODE_UNRELIABLE ;
2021-10-01 10:43:22 +02:00
} else if ( mode = = " unreliable_ordered " ) {
2022-07-12 23:12:42 +02:00
rpc_config [ " transfer_mode " ] = MultiplayerPeer : : TRANSFER_MODE_UNRELIABLE_ORDERED ;
2021-06-24 10:28:15 +02:00
} else {
2021-10-06 23:51:57 +02:00
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 ) ;
2021-06-24 10:28:15 +02:00
}
2020-05-02 00:14:56 +02:00
}
2021-06-24 10:28:15 +02:00
}
2022-07-12 23:12:42 +02:00
function - > rpc_config = rpc_config ;
2020-05-02 00:14:56 +02:00
return true ;
}
2020-06-10 23:18:10 +02:00
GDScriptParser : : DataType GDScriptParser : : SuiteNode : : Local : : get_datatype ( ) const {
switch ( type ) {
case CONSTANT :
return constant - > get_datatype ( ) ;
case VARIABLE :
return variable - > get_datatype ( ) ;
case PARAMETER :
return parameter - > get_datatype ( ) ;
case FOR_VARIABLE :
case PATTERN_BIND :
return bind - > get_datatype ( ) ;
case UNDEFINED :
return DataType ( ) ;
}
return DataType ( ) ;
}
String GDScriptParser : : SuiteNode : : Local : : get_name ( ) const {
switch ( type ) {
case SuiteNode : : Local : : PARAMETER :
2022-09-29 11:53:28 +02:00
return " parameter " ;
2020-06-10 23:18:10 +02:00
case SuiteNode : : Local : : CONSTANT :
2022-09-29 11:53:28 +02:00
return " constant " ;
2020-06-10 23:18:10 +02:00
case SuiteNode : : Local : : VARIABLE :
2022-09-29 11:53:28 +02:00
return " variable " ;
2020-06-10 23:18:10 +02:00
case SuiteNode : : Local : : FOR_VARIABLE :
2022-09-29 11:53:28 +02:00
return " for loop iterator " ;
2020-06-10 23:18:10 +02:00
case SuiteNode : : Local : : PATTERN_BIND :
2022-09-29 11:53:28 +02:00
return " pattern_bind " ;
2020-06-10 23:18:10 +02:00
case SuiteNode : : Local : : UNDEFINED :
2022-09-29 11:53:28 +02:00
return " <undefined> " ;
default :
return String ( ) ;
2020-06-10 23:18:10 +02:00
}
}
String GDScriptParser : : DataType : : to_string ( ) const {
switch ( kind ) {
case VARIANT :
return " Variant " ;
case BUILTIN :
if ( builtin_type = = Variant : : NIL ) {
return " null " ;
}
2021-03-09 16:32:35 +01:00
if ( builtin_type = = Variant : : ARRAY & & has_container_element_type ( ) ) {
return vformat ( " Array[%s] " , container_element_type - > to_string ( ) ) ;
}
2020-06-10 23:18:10 +02:00
return Variant : : get_type_name ( builtin_type ) ;
case NATIVE :
if ( is_meta_type ) {
return GDScriptNativeClass : : get_class_static ( ) ;
}
return native_type . operator String ( ) ;
case CLASS :
if ( is_meta_type ) {
return GDScript : : get_class_static ( ) ;
}
if ( class_type - > identifier ! = nullptr ) {
return class_type - > identifier - > name . operator String ( ) ;
}
2020-06-12 02:49:58 +02:00
return class_type - > fqcn ;
2020-06-10 23:18:10 +02:00
case SCRIPT : {
if ( is_meta_type ) {
return script_type - > get_class_name ( ) . operator String ( ) ;
}
2022-09-23 17:13:57 +02:00
String name = script_type ! = nullptr ? script_type - > get_name ( ) : " " ;
2020-12-15 13:04:21 +01:00
if ( ! name . is_empty ( ) ) {
2020-06-10 23:18:10 +02:00
return name ;
}
2020-07-16 03:02:44 +02:00
name = script_path ;
2020-12-15 13:04:21 +01:00
if ( ! name . is_empty ( ) ) {
2020-06-10 23:18:10 +02:00
return name ;
}
return native_type . operator String ( ) ;
}
2020-06-12 02:49:58 +02:00
case ENUM :
return enum_type . operator String ( ) + " (enum) " ;
2020-06-10 23:18:10 +02:00
case UNRESOLVED :
return " <unresolved type> " ;
}
ERR_FAIL_V_MSG ( " <unresolved type " , " Kind set outside the enum range. " ) ;
}
2021-08-14 05:44:22 +02:00
static Variant : : Type _variant_type_to_typed_array_element_type ( Variant : : Type p_type ) {
switch ( p_type ) {
case Variant : : PACKED_BYTE_ARRAY :
case Variant : : PACKED_INT32_ARRAY :
case Variant : : PACKED_INT64_ARRAY :
return Variant : : INT ;
case Variant : : PACKED_FLOAT32_ARRAY :
case Variant : : PACKED_FLOAT64_ARRAY :
return Variant : : FLOAT ;
case Variant : : PACKED_STRING_ARRAY :
return Variant : : STRING ;
case Variant : : PACKED_VECTOR2_ARRAY :
return Variant : : VECTOR2 ;
case Variant : : PACKED_VECTOR3_ARRAY :
return Variant : : VECTOR3 ;
case Variant : : PACKED_COLOR_ARRAY :
return Variant : : COLOR ;
default :
return Variant : : NIL ;
}
}
bool GDScriptParser : : DataType : : is_typed_container_type ( ) const {
return kind = = GDScriptParser : : DataType : : BUILTIN & & _variant_type_to_typed_array_element_type ( builtin_type ) ! = Variant : : NIL ;
}
GDScriptParser : : DataType GDScriptParser : : DataType : : get_typed_container_type ( ) const {
GDScriptParser : : DataType type ;
type . kind = GDScriptParser : : DataType : : BUILTIN ;
type . builtin_type = _variant_type_to_typed_array_element_type ( builtin_type ) ;
return type ;
}
2022-07-11 20:31:15 +02:00
void GDScriptParser : : complete_extents ( Node * p_node ) {
while ( ! nodes_in_progress . is_empty ( ) & & nodes_in_progress . back ( ) - > get ( ) ! = p_node ) {
ERR_PRINT ( " Parser bug: Mismatch in extents tracking stack. " ) ;
nodes_in_progress . pop_back ( ) ;
}
if ( nodes_in_progress . is_empty ( ) ) {
ERR_PRINT ( " Parser bug: Extents tracking stack is empty. " ) ;
} else {
nodes_in_progress . pop_back ( ) ;
}
}
void GDScriptParser : : update_extents ( Node * p_node ) {
p_node - > end_line = previous . end_line ;
p_node - > end_column = previous . end_column ;
p_node - > leftmost_column = MIN ( p_node - > leftmost_column , previous . leftmost_column ) ;
p_node - > rightmost_column = MAX ( p_node - > rightmost_column , previous . rightmost_column ) ;
}
void GDScriptParser : : reset_extents ( Node * p_node , GDScriptTokenizer : : Token p_token ) {
p_node - > start_line = p_token . start_line ;
p_node - > end_line = p_token . end_line ;
p_node - > start_column = p_token . start_column ;
p_node - > end_column = p_token . end_column ;
p_node - > leftmost_column = p_token . leftmost_column ;
p_node - > rightmost_column = p_token . rightmost_column ;
}
void GDScriptParser : : reset_extents ( Node * p_node , Node * p_from ) {
if ( p_from = = nullptr ) {
return ;
}
p_node - > start_line = p_from - > start_line ;
p_node - > end_line = p_from - > end_line ;
p_node - > start_column = p_from - > start_column ;
p_node - > end_column = p_from - > end_column ;
p_node - > leftmost_column = p_from - > leftmost_column ;
p_node - > rightmost_column = p_from - > rightmost_column ;
}
2020-05-02 00:14:56 +02:00
/*---------- PRETTY PRINT FOR DEBUG ----------*/
2018-07-01 18:17:40 +02:00
# ifdef DEBUG_ENABLED
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : increase_indent ( ) {
indent_level + + ;
indent = " " ;
for ( int i = 0 ; i < indent_level * 4 ; i + + ) {
if ( i % 4 = = 0 ) {
indent + = " | " ;
} else {
indent + = " " ;
2018-07-01 18:17:40 +02:00
}
}
2018-05-30 04:16:54 +02:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : decrease_indent ( ) {
indent_level - - ;
indent = " " ;
for ( int i = 0 ; i < indent_level * 4 ; i + + ) {
if ( i % 4 = = 0 ) {
indent + = " | " ;
} else {
indent + = " " ;
}
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : push_line ( const String & p_line ) {
2020-12-15 13:04:21 +01:00
if ( ! p_line . is_empty ( ) ) {
2020-05-02 00:14:56 +02:00
push_text ( p_line ) ;
2018-07-01 18:17:40 +02:00
}
2020-05-02 00:14:56 +02:00
printed + = " \n " ;
pending_indent = true ;
2018-07-01 18:17:40 +02:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : push_text ( const String & p_text ) {
if ( pending_indent ) {
printed + = indent ;
pending_indent = false ;
2018-07-01 18:17:40 +02:00
}
2020-05-02 00:14:56 +02:00
printed + = p_text ;
}
2021-07-24 15:46:25 +02:00
void GDScriptParser : : TreePrinter : : print_annotation ( const AnnotationNode * p_annotation ) {
2020-05-02 00:14:56 +02:00
push_text ( p_annotation - > name ) ;
push_text ( " ( " ) ;
for ( int i = 0 ; i < p_annotation - > arguments . size ( ) ; i + + ) {
if ( i > 0 ) {
push_text ( " , " ) ;
}
print_expression ( p_annotation - > arguments [ i ] ) ;
2018-07-01 18:17:40 +02:00
}
2020-05-02 00:14:56 +02:00
push_line ( " ) " ) ;
}
void GDScriptParser : : TreePrinter : : print_array ( ArrayNode * p_array ) {
push_text ( " [ " ) ;
for ( int i = 0 ; i < p_array - > elements . size ( ) ; i + + ) {
if ( i > 0 ) {
push_text ( " , " ) ;
}
print_expression ( p_array - > elements [ i ] ) ;
2018-07-01 18:17:40 +02:00
}
2020-05-02 00:14:56 +02:00
push_text ( " ] " ) ;
}
2018-07-01 18:17:40 +02:00
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_assert ( AssertNode * p_assert ) {
push_text ( " Assert ( " ) ;
print_expression ( p_assert - > condition ) ;
push_line ( " ) " ) ;
}
2018-07-01 18:17:40 +02:00
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_assignment ( AssignmentNode * p_assignment ) {
switch ( p_assignment - > assignee - > type ) {
case Node : : IDENTIFIER :
print_identifier ( static_cast < IdentifierNode * > ( p_assignment - > assignee ) ) ;
2018-07-01 18:17:40 +02:00
break ;
2020-05-02 00:14:56 +02:00
case Node : : SUBSCRIPT :
print_subscript ( static_cast < SubscriptNode * > ( p_assignment - > assignee ) ) ;
break ;
default :
break ; // Unreachable.
2018-07-01 18:17:40 +02:00
}
2020-05-02 00:14:56 +02:00
push_text ( " " ) ;
switch ( p_assignment - > operation ) {
case AssignmentNode : : OP_ADDITION :
push_text ( " + " ) ;
break ;
case AssignmentNode : : OP_SUBTRACTION :
push_text ( " - " ) ;
break ;
case AssignmentNode : : OP_MULTIPLICATION :
push_text ( " * " ) ;
break ;
case AssignmentNode : : OP_DIVISION :
push_text ( " / " ) ;
break ;
case AssignmentNode : : OP_MODULO :
push_text ( " % " ) ;
break ;
2022-03-07 18:25:21 +01:00
case AssignmentNode : : OP_POWER :
push_text ( " ** " ) ;
break ;
2020-05-02 00:14:56 +02:00
case AssignmentNode : : OP_BIT_SHIFT_LEFT :
push_text ( " << " ) ;
break ;
case AssignmentNode : : OP_BIT_SHIFT_RIGHT :
push_text ( " >> " ) ;
break ;
case AssignmentNode : : OP_BIT_AND :
push_text ( " & " ) ;
break ;
case AssignmentNode : : OP_BIT_OR :
push_text ( " | " ) ;
break ;
case AssignmentNode : : OP_BIT_XOR :
push_text ( " ^ " ) ;
break ;
case AssignmentNode : : OP_NONE :
break ;
2018-07-01 18:17:40 +02:00
}
2020-05-02 00:14:56 +02:00
push_text ( " = " ) ;
print_expression ( p_assignment - > assigned_value ) ;
push_line ( ) ;
2018-07-01 18:17:40 +02:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_await ( AwaitNode * p_await ) {
push_text ( " Await " ) ;
print_expression ( p_await - > to_await ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_binary_op ( BinaryOpNode * p_binary_op ) {
// Surround in parenthesis for disambiguation.
push_text ( " ( " ) ;
print_expression ( p_binary_op - > left_operand ) ;
switch ( p_binary_op - > operation ) {
case BinaryOpNode : : OP_ADDITION :
push_text ( " + " ) ;
break ;
case BinaryOpNode : : OP_SUBTRACTION :
push_text ( " - " ) ;
break ;
case BinaryOpNode : : OP_MULTIPLICATION :
push_text ( " * " ) ;
break ;
case BinaryOpNode : : OP_DIVISION :
push_text ( " / " ) ;
break ;
case BinaryOpNode : : OP_MODULO :
push_text ( " % " ) ;
break ;
2022-03-07 18:25:21 +01:00
case BinaryOpNode : : OP_POWER :
push_text ( " ** " ) ;
break ;
2020-05-02 00:14:56 +02:00
case BinaryOpNode : : OP_BIT_LEFT_SHIFT :
push_text ( " << " ) ;
break ;
case BinaryOpNode : : OP_BIT_RIGHT_SHIFT :
push_text ( " >> " ) ;
break ;
case BinaryOpNode : : OP_BIT_AND :
push_text ( " & " ) ;
break ;
case BinaryOpNode : : OP_BIT_OR :
push_text ( " | " ) ;
break ;
case BinaryOpNode : : OP_BIT_XOR :
push_text ( " ^ " ) ;
break ;
case BinaryOpNode : : OP_LOGIC_AND :
push_text ( " AND " ) ;
break ;
case BinaryOpNode : : OP_LOGIC_OR :
push_text ( " OR " ) ;
break ;
case BinaryOpNode : : OP_TYPE_TEST :
push_text ( " IS " ) ;
break ;
case BinaryOpNode : : OP_CONTENT_TEST :
push_text ( " IN " ) ;
break ;
case BinaryOpNode : : OP_COMP_EQUAL :
push_text ( " == " ) ;
break ;
case BinaryOpNode : : OP_COMP_NOT_EQUAL :
push_text ( " != " ) ;
break ;
case BinaryOpNode : : OP_COMP_LESS :
push_text ( " < " ) ;
break ;
case BinaryOpNode : : OP_COMP_LESS_EQUAL :
push_text ( " <= " ) ;
break ;
case BinaryOpNode : : OP_COMP_GREATER :
push_text ( " > " ) ;
break ;
case BinaryOpNode : : OP_COMP_GREATER_EQUAL :
push_text ( " >= " ) ;
break ;
}
print_expression ( p_binary_op - > right_operand ) ;
// Surround in parenthesis for disambiguation.
push_text ( " ) " ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_call ( CallNode * p_call ) {
if ( p_call - > is_super ) {
push_text ( " super " ) ;
if ( p_call - > callee ! = nullptr ) {
push_text ( " . " ) ;
print_expression ( p_call - > callee ) ;
}
} else {
print_expression ( p_call - > callee ) ;
}
push_text ( " ( " ) ;
for ( int i = 0 ; i < p_call - > arguments . size ( ) ; i + + ) {
if ( i > 0 ) {
push_text ( " , " ) ;
}
print_expression ( p_call - > arguments [ i ] ) ;
}
push_text ( " ) " ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_cast ( CastNode * p_cast ) {
print_expression ( p_cast - > operand ) ;
push_text ( " AS " ) ;
print_type ( p_cast - > cast_type ) ;
2019-06-14 16:38:54 +02:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_class ( ClassNode * p_class ) {
push_text ( " Class " ) ;
if ( p_class - > identifier = = nullptr ) {
push_text ( " <unnamed> " ) ;
} else {
print_identifier ( p_class - > identifier ) ;
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
if ( p_class - > extends_used ) {
bool first = true ;
push_text ( " Extends " ) ;
2020-12-15 13:04:21 +01:00
if ( ! p_class - > extends_path . is_empty ( ) ) {
2020-05-02 00:14:56 +02:00
push_text ( vformat ( R " ( " % s " ) " , p_class - > extends_path ) ) ;
first = false ;
}
for ( int i = 0 ; i < p_class - > extends . size ( ) ; i + + ) {
if ( ! first ) {
push_text ( " . " ) ;
} else {
first = false ;
}
push_text ( p_class - > extends [ i ] ) ;
}
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
push_line ( " : " ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
increase_indent ( ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
for ( int i = 0 ; i < p_class - > members . size ( ) ; i + + ) {
const ClassNode : : Member & m = p_class - > members [ i ] ;
2020-03-28 09:12:19 +01:00
2020-05-02 00:14:56 +02:00
switch ( m . type ) {
case ClassNode : : Member : : CLASS :
print_class ( m . m_class ) ;
break ;
case ClassNode : : Member : : VARIABLE :
print_variable ( m . variable ) ;
break ;
case ClassNode : : Member : : CONSTANT :
print_constant ( m . constant ) ;
break ;
case ClassNode : : Member : : SIGNAL :
print_signal ( m . signal ) ;
break ;
case ClassNode : : Member : : FUNCTION :
print_function ( m . function ) ;
break ;
case ClassNode : : Member : : ENUM :
print_enum ( m . m_enum ) ;
break ;
case ClassNode : : Member : : ENUM_VALUE :
break ; // Nothing. Will be printed by enum.
2022-07-03 21:30:08 +02:00
case ClassNode : : Member : : GROUP :
break ; // Nothing. Groups are only used by inspector.
2020-05-02 00:14:56 +02:00
case ClassNode : : Member : : UNDEFINED :
push_line ( " <unknown member> " ) ;
break ;
}
2018-05-30 04:16:52 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
decrease_indent ( ) ;
}
2019-03-03 20:36:42 +01:00
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_constant ( ConstantNode * p_constant ) {
push_text ( " Constant " ) ;
print_identifier ( p_constant - > identifier ) ;
2018-05-30 04:16:52 +02:00
2020-05-02 00:14:56 +02:00
increase_indent ( ) ;
push_line ( ) ;
push_text ( " = " ) ;
if ( p_constant - > initializer = = nullptr ) {
push_text ( " <missing value> " ) ;
} else {
print_expression ( p_constant - > initializer ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
decrease_indent ( ) ;
push_line ( ) ;
}
2018-05-30 04:16:52 +02:00
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_dictionary ( DictionaryNode * p_dictionary ) {
push_line ( " { " ) ;
increase_indent ( ) ;
for ( int i = 0 ; i < p_dictionary - > elements . size ( ) ; i + + ) {
print_expression ( p_dictionary - > elements [ i ] . key ) ;
if ( p_dictionary - > style = = DictionaryNode : : PYTHON_DICT ) {
push_text ( " : " ) ;
} else {
push_text ( " = " ) ;
}
print_expression ( p_dictionary - > elements [ i ] . value ) ;
push_line ( " , " ) ;
}
decrease_indent ( ) ;
push_text ( " } " ) ;
}
2020-01-08 16:22:41 +01:00
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_expression ( ExpressionNode * p_expression ) {
2021-03-25 14:36:29 +01:00
if ( p_expression = = nullptr ) {
push_text ( " <invalid expression> " ) ;
return ;
}
2020-05-02 00:14:56 +02:00
switch ( p_expression - > type ) {
case Node : : ARRAY :
print_array ( static_cast < ArrayNode * > ( p_expression ) ) ;
break ;
case Node : : ASSIGNMENT :
print_assignment ( static_cast < AssignmentNode * > ( p_expression ) ) ;
break ;
case Node : : AWAIT :
print_await ( static_cast < AwaitNode * > ( p_expression ) ) ;
break ;
case Node : : BINARY_OPERATOR :
print_binary_op ( static_cast < BinaryOpNode * > ( p_expression ) ) ;
break ;
case Node : : CALL :
print_call ( static_cast < CallNode * > ( p_expression ) ) ;
break ;
case Node : : CAST :
print_cast ( static_cast < CastNode * > ( p_expression ) ) ;
break ;
case Node : : DICTIONARY :
print_dictionary ( static_cast < DictionaryNode * > ( p_expression ) ) ;
break ;
case Node : : GET_NODE :
print_get_node ( static_cast < GetNodeNode * > ( p_expression ) ) ;
break ;
case Node : : IDENTIFIER :
print_identifier ( static_cast < IdentifierNode * > ( p_expression ) ) ;
break ;
2021-03-25 14:36:29 +01:00
case Node : : LAMBDA :
print_lambda ( static_cast < LambdaNode * > ( p_expression ) ) ;
break ;
2020-05-02 00:14:56 +02:00
case Node : : LITERAL :
print_literal ( static_cast < LiteralNode * > ( p_expression ) ) ;
break ;
case Node : : PRELOAD :
print_preload ( static_cast < PreloadNode * > ( p_expression ) ) ;
break ;
case Node : : SELF :
print_self ( static_cast < SelfNode * > ( p_expression ) ) ;
break ;
case Node : : SUBSCRIPT :
print_subscript ( static_cast < SubscriptNode * > ( p_expression ) ) ;
break ;
case Node : : TERNARY_OPERATOR :
print_ternary_op ( static_cast < TernaryOpNode * > ( p_expression ) ) ;
break ;
case Node : : UNARY_OPERATOR :
print_unary_op ( static_cast < UnaryOpNode * > ( p_expression ) ) ;
break ;
default :
push_text ( vformat ( " <unknown expression %d> " , p_expression - > type ) ) ;
break ;
2020-05-14 16:41:43 +02:00
}
2020-05-02 00:14:56 +02:00
}
2018-05-30 04:16:54 +02:00
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_enum ( EnumNode * p_enum ) {
push_text ( " Enum " ) ;
if ( p_enum - > identifier ! = nullptr ) {
print_identifier ( p_enum - > identifier ) ;
} else {
push_text ( " <unnamed> " ) ;
}
2018-05-30 04:16:54 +02:00
2020-05-02 00:14:56 +02:00
push_line ( " { " ) ;
increase_indent ( ) ;
for ( int i = 0 ; i < p_enum - > values . size ( ) ; i + + ) {
const EnumNode : : Value & item = p_enum - > values [ i ] ;
print_identifier ( item . identifier ) ;
push_text ( " = " ) ;
push_text ( itos ( item . value ) ) ;
push_line ( " , " ) ;
2018-05-30 04:16:54 +02:00
}
2020-05-02 00:14:56 +02:00
decrease_indent ( ) ;
push_line ( " } " ) ;
}
2018-05-30 04:16:54 +02:00
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_for ( ForNode * p_for ) {
push_text ( " For " ) ;
print_identifier ( p_for - > variable ) ;
push_text ( " IN " ) ;
print_expression ( p_for - > list ) ;
push_line ( " : " ) ;
2018-05-30 04:16:54 +02:00
2020-05-02 00:14:56 +02:00
increase_indent ( ) ;
2020-03-28 09:12:19 +01:00
2020-05-02 00:14:56 +02:00
print_suite ( p_for - > loop ) ;
2018-05-30 04:16:54 +02:00
2020-05-02 00:14:56 +02:00
decrease_indent ( ) ;
}
2019-01-23 21:45:33 +01:00
2021-03-25 14:36:29 +01:00
void GDScriptParser : : TreePrinter : : print_function ( FunctionNode * p_function , const String & p_context ) {
2021-07-24 15:46:25 +02:00
for ( const AnnotationNode * E : p_function - > annotations ) {
2021-07-16 05:45:57 +02:00
print_annotation ( E ) ;
2020-05-02 00:14:56 +02:00
}
2021-03-25 14:36:29 +01:00
push_text ( p_context ) ;
push_text ( " " ) ;
if ( p_function - > identifier ) {
print_identifier ( p_function - > identifier ) ;
} else {
push_text ( " <anonymous> " ) ;
}
2020-05-02 00:14:56 +02:00
push_text ( " ( " ) ;
for ( int i = 0 ; i < p_function - > parameters . size ( ) ; i + + ) {
if ( i > 0 ) {
push_text ( " , " ) ;
2018-07-01 18:17:40 +02:00
}
2020-05-02 00:14:56 +02:00
print_parameter ( p_function - > parameters [ i ] ) ;
}
push_line ( " ) : " ) ;
increase_indent ( ) ;
print_suite ( p_function - > body ) ;
decrease_indent ( ) ;
}
void GDScriptParser : : TreePrinter : : print_get_node ( GetNodeNode * p_get_node ) {
2022-05-26 17:56:39 +02:00
if ( p_get_node - > use_dollar ) {
push_text ( " $ " ) ;
2018-07-01 18:17:40 +02:00
}
2022-05-26 17:56:39 +02:00
push_text ( p_get_node - > full_path ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_identifier ( IdentifierNode * p_identifier ) {
2022-01-03 01:47:18 +01:00
if ( p_identifier ! = nullptr ) {
push_text ( p_identifier - > name ) ;
} else {
push_text ( " <invalid identifier> " ) ;
}
2014-02-25 13:31:47 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_if ( IfNode * p_if , bool p_is_elif ) {
if ( p_is_elif ) {
push_text ( " Elif " ) ;
} else {
push_text ( " If " ) ;
}
print_expression ( p_if - > condition ) ;
push_line ( " : " ) ;
2014-12-17 02:31:57 +01:00
2020-05-02 00:14:56 +02:00
increase_indent ( ) ;
print_suite ( p_if - > true_block ) ;
decrease_indent ( ) ;
2014-02-25 13:31:47 +01:00
2020-05-02 00:14:56 +02:00
// FIXME: Properly detect "elif" blocks.
if ( p_if - > false_block ! = nullptr ) {
push_line ( " Else : " ) ;
increase_indent ( ) ;
print_suite ( p_if - > false_block ) ;
decrease_indent ( ) ;
}
2014-02-25 13:31:47 +01:00
}
2021-03-25 14:36:29 +01:00
void GDScriptParser : : TreePrinter : : print_lambda ( LambdaNode * p_lambda ) {
print_function ( p_lambda - > function , " Lambda " ) ;
2021-03-26 13:03:16 +01:00
push_text ( " | captures [ " ) ;
2021-03-28 16:03:13 +02:00
for ( int i = 0 ; i < p_lambda - > captures . size ( ) ; i + + ) {
if ( i > 0 ) {
2021-03-26 13:03:16 +01:00
push_text ( " , " ) ;
}
2021-03-28 16:03:13 +02:00
push_text ( p_lambda - > captures [ i ] - > name . operator String ( ) ) ;
2021-03-26 13:03:16 +01:00
}
push_line ( " ] " ) ;
2021-03-25 14:36:29 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_literal ( LiteralNode * p_literal ) {
// Prefix for string types.
switch ( p_literal - > value . get_type ( ) ) {
case Variant : : NODE_PATH :
push_text ( " ^ \" " ) ;
break ;
case Variant : : STRING :
push_text ( " \" " ) ;
break ;
case Variant : : STRING_NAME :
push_text ( " & \" " ) ;
break ;
default :
break ;
}
push_text ( p_literal - > value ) ;
// Suffix for string types.
switch ( p_literal - > value . get_type ( ) ) {
case Variant : : NODE_PATH :
case Variant : : STRING :
case Variant : : STRING_NAME :
push_text ( " \" " ) ;
break ;
default :
break ;
}
2016-01-23 19:36:03 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_match ( MatchNode * p_match ) {
push_text ( " Match " ) ;
print_expression ( p_match - > test ) ;
push_line ( " : " ) ;
increase_indent ( ) ;
for ( int i = 0 ; i < p_match - > branches . size ( ) ; i + + ) {
print_match_branch ( p_match - > branches [ i ] ) ;
}
decrease_indent ( ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_match_branch ( MatchBranchNode * p_match_branch ) {
for ( int i = 0 ; i < p_match_branch - > patterns . size ( ) ; i + + ) {
if ( i > 0 ) {
push_text ( " , " ) ;
}
print_match_pattern ( p_match_branch - > patterns [ i ] ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
push_line ( " : " ) ;
2014-02-10 02:10:30 +01:00
2020-05-02 00:14:56 +02:00
increase_indent ( ) ;
print_suite ( p_match_branch - > block ) ;
decrease_indent ( ) ;
}
2014-12-17 03:46:55 +01:00
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_match_pattern ( PatternNode * p_match_pattern ) {
switch ( p_match_pattern - > pattern_type ) {
case PatternNode : : PT_LITERAL :
print_literal ( p_match_pattern - > literal ) ;
break ;
case PatternNode : : PT_WILDCARD :
push_text ( " _ " ) ;
break ;
case PatternNode : : PT_REST :
push_text ( " .. " ) ;
break ;
case PatternNode : : PT_BIND :
push_text ( " Var " ) ;
print_identifier ( p_match_pattern - > bind ) ;
break ;
case PatternNode : : PT_EXPRESSION :
print_expression ( p_match_pattern - > expression ) ;
break ;
case PatternNode : : PT_ARRAY :
push_text ( " [ " ) ;
for ( int i = 0 ; i < p_match_pattern - > array . size ( ) ; i + + ) {
if ( i > 0 ) {
push_text ( " , " ) ;
}
print_match_pattern ( p_match_pattern - > array [ i ] ) ;
}
push_text ( " ] " ) ;
break ;
case PatternNode : : PT_DICTIONARY :
push_text ( " { " ) ;
for ( int i = 0 ; i < p_match_pattern - > dictionary . size ( ) ; i + + ) {
if ( i > 0 ) {
push_text ( " , " ) ;
}
if ( p_match_pattern - > dictionary [ i ] . key ! = nullptr ) {
// Key can be null for rest pattern.
print_expression ( p_match_pattern - > dictionary [ i ] . key ) ;
push_text ( " : " ) ;
}
print_match_pattern ( p_match_pattern - > dictionary [ i ] . value_pattern ) ;
}
push_text ( " } " ) ;
break ;
}
}
2015-08-30 16:50:10 +02:00
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_parameter ( ParameterNode * p_parameter ) {
print_identifier ( p_parameter - > identifier ) ;
if ( p_parameter - > datatype_specifier ! = nullptr ) {
push_text ( " : " ) ;
print_type ( p_parameter - > datatype_specifier ) ;
}
if ( p_parameter - > default_value ! = nullptr ) {
push_text ( " = " ) ;
print_expression ( p_parameter - > default_value ) ;
}
}
2014-12-17 02:31:57 +01:00
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_preload ( PreloadNode * p_preload ) {
push_text ( R " (Preload ( " ) " );
push_text ( p_preload - > resolved_path ) ;
push_text ( R " ( " ) " );
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_return ( ReturnNode * p_return ) {
push_text ( " Return " ) ;
if ( p_return - > return_value ! = nullptr ) {
push_text ( " " ) ;
print_expression ( p_return - > return_value ) ;
}
push_line ( ) ;
2014-12-17 02:31:57 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_self ( SelfNode * p_self ) {
push_text ( " Self( " ) ;
if ( p_self - > current_class - > identifier ! = nullptr ) {
print_identifier ( p_self - > current_class - > identifier ) ;
} else {
push_text ( " <main class> " ) ;
}
push_text ( " ) " ) ;
2014-12-17 02:31:57 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_signal ( SignalNode * p_signal ) {
push_text ( " Signal " ) ;
print_identifier ( p_signal - > identifier ) ;
push_text ( " ( " ) ;
for ( int i = 0 ; i < p_signal - > parameters . size ( ) ; i + + ) {
print_parameter ( p_signal - > parameters [ i ] ) ;
}
push_line ( " ) " ) ;
2014-12-17 02:31:57 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_subscript ( SubscriptNode * p_subscript ) {
print_expression ( p_subscript - > base ) ;
if ( p_subscript - > is_attribute ) {
push_text ( " . " ) ;
print_identifier ( p_subscript - > attribute ) ;
} else {
push_text ( " [ " ) ;
print_expression ( p_subscript - > index ) ;
push_text ( " ] " ) ;
}
2014-12-17 02:31:57 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_statement ( Node * p_statement ) {
switch ( p_statement - > type ) {
case Node : : ASSERT :
print_assert ( static_cast < AssertNode * > ( p_statement ) ) ;
break ;
case Node : : VARIABLE :
print_variable ( static_cast < VariableNode * > ( p_statement ) ) ;
break ;
case Node : : CONSTANT :
print_constant ( static_cast < ConstantNode * > ( p_statement ) ) ;
break ;
case Node : : IF :
print_if ( static_cast < IfNode * > ( p_statement ) ) ;
break ;
case Node : : FOR :
print_for ( static_cast < ForNode * > ( p_statement ) ) ;
break ;
case Node : : WHILE :
print_while ( static_cast < WhileNode * > ( p_statement ) ) ;
break ;
case Node : : MATCH :
print_match ( static_cast < MatchNode * > ( p_statement ) ) ;
break ;
case Node : : RETURN :
print_return ( static_cast < ReturnNode * > ( p_statement ) ) ;
break ;
case Node : : BREAK :
push_line ( " Break " ) ;
break ;
case Node : : CONTINUE :
push_line ( " Continue " ) ;
break ;
case Node : : PASS :
push_line ( " Pass " ) ;
break ;
case Node : : BREAKPOINT :
push_line ( " Breakpoint " ) ;
break ;
case Node : : ASSIGNMENT :
print_assignment ( static_cast < AssignmentNode * > ( p_statement ) ) ;
break ;
default :
if ( p_statement - > is_expression ( ) ) {
print_expression ( static_cast < ExpressionNode * > ( p_statement ) ) ;
push_line ( ) ;
} else {
push_line ( vformat ( " <unknown statement %d> " , p_statement - > type ) ) ;
}
break ;
}
2014-12-17 02:31:57 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_suite ( SuiteNode * p_suite ) {
for ( int i = 0 ; i < p_suite - > statements . size ( ) ; i + + ) {
print_statement ( p_suite - > statements [ i ] ) ;
}
2014-12-17 02:31:57 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_ternary_op ( TernaryOpNode * p_ternary_op ) {
// Surround in parenthesis for disambiguation.
push_text ( " ( " ) ;
print_expression ( p_ternary_op - > true_expr ) ;
push_text ( " ) IF ( " ) ;
print_expression ( p_ternary_op - > condition ) ;
push_text ( " ) ELSE ( " ) ;
print_expression ( p_ternary_op - > false_expr ) ;
push_text ( " ) " ) ;
2014-12-17 02:31:57 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_type ( TypeNode * p_type ) {
2020-12-15 13:04:21 +01:00
if ( p_type - > type_chain . is_empty ( ) ) {
2020-05-02 00:14:56 +02:00
push_text ( " Void " ) ;
2020-06-10 23:18:10 +02:00
} else {
for ( int i = 0 ; i < p_type - > type_chain . size ( ) ; i + + ) {
if ( i > 0 ) {
push_text ( " . " ) ;
}
print_identifier ( p_type - > type_chain [ i ] ) ;
}
2020-05-02 00:14:56 +02:00
}
2014-12-17 02:31:57 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_unary_op ( UnaryOpNode * p_unary_op ) {
// Surround in parenthesis for disambiguation.
push_text ( " ( " ) ;
switch ( p_unary_op - > operation ) {
case UnaryOpNode : : OP_POSITIVE :
push_text ( " + " ) ;
break ;
case UnaryOpNode : : OP_NEGATIVE :
push_text ( " - " ) ;
break ;
case UnaryOpNode : : OP_LOGIC_NOT :
push_text ( " NOT " ) ;
break ;
case UnaryOpNode : : OP_COMPLEMENT :
push_text ( " ~ " ) ;
break ;
}
print_expression ( p_unary_op - > operand ) ;
// Surround in parenthesis for disambiguation.
push_text ( " ) " ) ;
2014-12-17 02:31:57 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_variable ( VariableNode * p_variable ) {
2021-07-24 15:46:25 +02:00
for ( const AnnotationNode * E : p_variable - > annotations ) {
2021-07-16 05:45:57 +02:00
print_annotation ( E ) ;
2020-05-02 00:14:56 +02:00
}
push_text ( " Variable " ) ;
print_identifier ( p_variable - > identifier ) ;
2020-06-01 21:41:05 +02:00
push_text ( " : " ) ;
if ( p_variable - > datatype_specifier ! = nullptr ) {
print_type ( p_variable - > datatype_specifier ) ;
} else if ( p_variable - > infer_datatype ) {
push_text ( " <inferred type> " ) ;
} else {
push_text ( " Variant " ) ;
}
2020-05-02 00:14:56 +02:00
increase_indent ( ) ;
push_line ( ) ;
push_text ( " = " ) ;
if ( p_variable - > initializer = = nullptr ) {
push_text ( " <default value> " ) ;
} else {
print_expression ( p_variable - > initializer ) ;
}
2020-06-01 21:41:05 +02:00
push_line ( ) ;
if ( p_variable - > property ! = VariableNode : : PROP_NONE ) {
if ( p_variable - > getter ! = nullptr ) {
push_text ( " Get " ) ;
if ( p_variable - > property = = VariableNode : : PROP_INLINE ) {
push_line ( " : " ) ;
increase_indent ( ) ;
2021-09-06 07:04:43 +02:00
print_suite ( p_variable - > getter - > body ) ;
2020-06-01 21:41:05 +02:00
decrease_indent ( ) ;
} else {
push_line ( " = " ) ;
increase_indent ( ) ;
print_identifier ( p_variable - > getter_pointer ) ;
push_line ( ) ;
decrease_indent ( ) ;
}
}
if ( p_variable - > setter ! = nullptr ) {
push_text ( " Set ( " ) ;
if ( p_variable - > property = = VariableNode : : PROP_INLINE ) {
if ( p_variable - > setter_parameter ! = nullptr ) {
print_identifier ( p_variable - > setter_parameter ) ;
} else {
push_text ( " <missing> " ) ;
}
push_line ( " ): " ) ;
increase_indent ( ) ;
2021-09-06 07:04:43 +02:00
print_suite ( p_variable - > setter - > body ) ;
2020-06-01 21:41:05 +02:00
decrease_indent ( ) ;
} else {
push_line ( " = " ) ;
increase_indent ( ) ;
print_identifier ( p_variable - > setter_pointer ) ;
push_line ( ) ;
decrease_indent ( ) ;
}
}
}
2020-05-02 00:14:56 +02:00
decrease_indent ( ) ;
push_line ( ) ;
2016-09-12 15:52:29 +02:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_while ( WhileNode * p_while ) {
push_text ( " While " ) ;
print_expression ( p_while - > condition ) ;
push_line ( " : " ) ;
increase_indent ( ) ;
print_suite ( p_while - > loop ) ;
decrease_indent ( ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
void GDScriptParser : : TreePrinter : : print_tree ( const GDScriptParser & p_parser ) {
ERR_FAIL_COND_MSG ( p_parser . get_tree ( ) = = nullptr , " Parse the code before printing the parse tree. " ) ;
if ( p_parser . is_tool ( ) ) {
push_line ( " @tool " ) ;
}
2020-12-15 13:04:21 +01:00
if ( ! p_parser . get_tree ( ) - > icon_path . is_empty ( ) ) {
2020-05-02 00:14:56 +02:00
push_text ( R " (@icon ( " ) " );
push_text ( p_parser . get_tree ( ) - > icon_path ) ;
push_line ( " \" ) " ) ;
}
print_class ( p_parser . get_tree ( ) ) ;
2021-10-07 15:40:48 +02:00
print_line ( String ( printed ) ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-02 00:14:56 +02:00
# endif // DEBUG_ENABLED