@@ -633,7 +633,8 @@ void GDScriptParser::parse_program() {
633
633
PUSH_PENDING_ANNOTATIONS_TO_HEAD;
634
634
if (annotation->name == SNAME (" @tool" ) || annotation->name == SNAME (" @icon" )) {
635
635
// Some annotations need to be resolved and applied in the parser.
636
- annotation->apply (this , head, nullptr ); // `head->outer == nullptr`.
636
+ // The root class is not in any class, so `head->outer == nullptr`.
637
+ annotation->apply (this , head, nullptr );
637
638
} else {
638
639
head->annotations .push_back (annotation);
639
640
}
@@ -677,9 +678,25 @@ void GDScriptParser::parse_program() {
677
678
reset_extents (head, current);
678
679
}
679
680
681
+ bool has_early_abstract = false ;
680
682
while (can_have_class_or_extends) {
681
683
// Order here doesn't matter, but there should be only one of each at most.
682
684
switch (current.type ) {
685
+ case GDScriptTokenizer::Token::ABSTRACT: {
686
+ PUSH_PENDING_ANNOTATIONS_TO_HEAD;
687
+ if (head->start_line == 1 ) {
688
+ reset_extents (head, current);
689
+ }
690
+ advance ();
691
+ if (has_early_abstract) {
692
+ push_error (R"( Expected "class_name", "extends", or "class" after "abstract".)" );
693
+ } else {
694
+ has_early_abstract = true ;
695
+ }
696
+ if (current.type == GDScriptTokenizer::Token::NEWLINE) {
697
+ end_statement (" class_name abstract" );
698
+ }
699
+ } break ;
683
700
case GDScriptTokenizer::Token::CLASS_NAME:
684
701
PUSH_PENDING_ANNOTATIONS_TO_HEAD;
685
702
advance ();
@@ -688,6 +705,10 @@ void GDScriptParser::parse_program() {
688
705
} else {
689
706
parse_class_name ();
690
707
}
708
+ if (has_early_abstract) {
709
+ head->is_abstract = true ;
710
+ has_early_abstract = false ;
711
+ }
691
712
break ;
692
713
case GDScriptTokenizer::Token::EXTENDS:
693
714
PUSH_PENDING_ANNOTATIONS_TO_HEAD;
@@ -698,6 +719,10 @@ void GDScriptParser::parse_program() {
698
719
parse_extends ();
699
720
end_statement (" superclass" );
700
721
}
722
+ if (has_early_abstract) {
723
+ head->is_abstract = true ;
724
+ has_early_abstract = false ;
725
+ }
701
726
break ;
702
727
case GDScriptTokenizer::Token::TK_EOF:
703
728
PUSH_PENDING_ANNOTATIONS_TO_HEAD;
@@ -732,7 +757,7 @@ void GDScriptParser::parse_program() {
732
757
733
758
#undef PUSH_PENDING_ANNOTATIONS_TO_HEAD
734
759
735
- parse_class_body (true );
760
+ parse_class_body (has_early_abstract, true );
736
761
737
762
head->end_line = current.end_line ;
738
763
head->end_column = current.end_column ;
@@ -837,12 +862,13 @@ bool GDScriptParser::has_class(const GDScriptParser::ClassNode *p_class) const {
837
862
return false ;
838
863
}
839
864
840
- GDScriptParser::ClassNode *GDScriptParser::parse_class (bool p_is_static) {
865
+ GDScriptParser::ClassNode *GDScriptParser::parse_class (bool p_is_abstract, bool p_is_static) {
841
866
ClassNode *n_class = alloc_node<ClassNode>();
842
867
843
868
ClassNode *previous_class = current_class;
844
869
current_class = n_class;
845
870
n_class->outer = previous_class;
871
+ n_class->is_abstract = p_is_abstract;
846
872
847
873
if (consume (GDScriptTokenizer::Token::IDENTIFIER, R"( Expected identifier for the class name after "class".)" )) {
848
874
n_class->identifier = parse_identifier ();
@@ -879,7 +905,7 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class(bool p_is_static) {
879
905
end_statement (" superclass" );
880
906
}
881
907
882
- parse_class_body (multiline);
908
+ parse_class_body (false , multiline);
883
909
complete_extents (n_class);
884
910
885
911
if (multiline) {
@@ -938,7 +964,7 @@ void GDScriptParser::parse_extends() {
938
964
}
939
965
940
966
template <typename T>
941
- void GDScriptParser::parse_class_member (T *(GDScriptParser::*p_parse_function)(bool ), AnnotationInfo::TargetKind p_target, const String &p_member_kind, bool p_is_static) {
967
+ void GDScriptParser::parse_class_member (T *(GDScriptParser::*p_parse_function)(bool , bool ), AnnotationInfo::TargetKind p_target, const String &p_member_kind, bool p_is_abstract , bool p_is_static) {
942
968
advance ();
943
969
944
970
// Consume annotations.
@@ -954,7 +980,7 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)(b
954
980
}
955
981
}
956
982
957
- T *member = (this ->*p_parse_function)(p_is_static);
983
+ T *member = (this ->*p_parse_function)(p_is_abstract, p_is_static);
958
984
if (member == nullptr ) {
959
985
return ;
960
986
}
@@ -1008,14 +1034,29 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)(b
1008
1034
}
1009
1035
}
1010
1036
1011
- void GDScriptParser::parse_class_body (bool p_is_multiline) {
1037
+ void GDScriptParser::parse_class_body (bool p_is_abstract, bool p_is_multiline) {
1012
1038
bool class_end = false ;
1039
+ // The header parsing code might have skipped over abstract, so we start by checking the previous token.
1040
+ bool next_is_abstract = p_is_abstract;
1041
+ if (next_is_abstract && (current.type != GDScriptTokenizer::Token::CLASS_NAME && current.type != GDScriptTokenizer::Token::CLASS)) {
1042
+ push_error (R"( Expected "class_name" or "class" after "abstract".)" );
1043
+ }
1013
1044
bool next_is_static = false ;
1014
1045
while (!class_end && !is_at_end ()) {
1015
1046
GDScriptTokenizer::Token token = current;
1016
1047
switch (token.type ) {
1048
+ case GDScriptTokenizer::Token::ABSTRACT: {
1049
+ advance ();
1050
+ next_is_abstract = true ;
1051
+ if (check (GDScriptTokenizer::Token::NEWLINE)) {
1052
+ advance ();
1053
+ }
1054
+ if (!check (GDScriptTokenizer::Token::CLASS_NAME) && !check (GDScriptTokenizer::Token::CLASS)) {
1055
+ push_error (R"( Expected "class_name" or "class" after "abstract".)" );
1056
+ }
1057
+ } break ;
1017
1058
case GDScriptTokenizer::Token::VAR:
1018
- parse_class_member (&GDScriptParser::parse_variable, AnnotationInfo::VARIABLE, " variable" , next_is_static);
1059
+ parse_class_member (&GDScriptParser::parse_variable, AnnotationInfo::VARIABLE, " variable" , false , next_is_static);
1019
1060
if (next_is_static) {
1020
1061
current_class->has_static_data = true ;
1021
1062
}
@@ -1027,11 +1068,12 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) {
1027
1068
parse_class_member (&GDScriptParser::parse_signal, AnnotationInfo::SIGNAL, " signal" );
1028
1069
break ;
1029
1070
case GDScriptTokenizer::Token::FUNC:
1030
- parse_class_member (&GDScriptParser::parse_function, AnnotationInfo::FUNCTION, " function" , next_is_static);
1031
- break ;
1032
- case GDScriptTokenizer::Token::CLASS:
1033
- parse_class_member (&GDScriptParser::parse_class, AnnotationInfo::CLASS, " class" );
1071
+ parse_class_member (&GDScriptParser::parse_function, AnnotationInfo::FUNCTION, " function" , false , next_is_static);
1034
1072
break ;
1073
+ case GDScriptTokenizer::Token::CLASS: {
1074
+ parse_class_member (&GDScriptParser::parse_class, AnnotationInfo::CLASS, " class" , next_is_abstract);
1075
+ next_is_abstract = false ;
1076
+ } break ;
1035
1077
case GDScriptTokenizer::Token::ENUM:
1036
1078
parse_class_member (&GDScriptParser::parse_enum, AnnotationInfo::NONE, " enum" );
1037
1079
break ;
@@ -1102,11 +1144,11 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) {
1102
1144
}
1103
1145
}
1104
1146
1105
- GDScriptParser::VariableNode *GDScriptParser::parse_variable (bool p_is_static) {
1106
- return parse_variable (p_is_static, true );
1147
+ GDScriptParser::VariableNode *GDScriptParser::parse_variable (bool p_is_abstract, bool p_is_static) {
1148
+ return parse_variable (p_is_abstract, p_is_static, true );
1107
1149
}
1108
1150
1109
- GDScriptParser::VariableNode *GDScriptParser::parse_variable (bool p_is_static, bool p_allow_property) {
1151
+ GDScriptParser::VariableNode *GDScriptParser::parse_variable (bool p_is_abstract, bool p_is_static, bool p_allow_property) {
1110
1152
VariableNode *variable = alloc_node<VariableNode>();
1111
1153
1112
1154
if (!consume (GDScriptTokenizer::Token::IDENTIFIER, R"( Expected variable name after "var".)" )) {
@@ -1342,7 +1384,7 @@ void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
1342
1384
}
1343
1385
}
1344
1386
1345
- GDScriptParser::ConstantNode *GDScriptParser::parse_constant (bool p_is_static) {
1387
+ GDScriptParser::ConstantNode *GDScriptParser::parse_constant (bool p_is_abstract, bool p_is_static) {
1346
1388
ConstantNode *constant = alloc_node<ConstantNode>();
1347
1389
1348
1390
if (!consume (GDScriptTokenizer::Token::IDENTIFIER, R"( Expected constant name after "const".)" )) {
@@ -1410,7 +1452,7 @@ GDScriptParser::ParameterNode *GDScriptParser::parse_parameter() {
1410
1452
return parameter;
1411
1453
}
1412
1454
1413
- GDScriptParser::SignalNode *GDScriptParser::parse_signal (bool p_is_static) {
1455
+ GDScriptParser::SignalNode *GDScriptParser::parse_signal (bool p_is_abstract, bool p_is_static) {
1414
1456
SignalNode *signal = alloc_node<SignalNode>();
1415
1457
1416
1458
if (!consume (GDScriptTokenizer::Token::IDENTIFIER, R"( Expected signal name after "signal".)" )) {
@@ -1455,7 +1497,7 @@ GDScriptParser::SignalNode *GDScriptParser::parse_signal(bool p_is_static) {
1455
1497
return signal ;
1456
1498
}
1457
1499
1458
- GDScriptParser::EnumNode *GDScriptParser::parse_enum (bool p_is_static) {
1500
+ GDScriptParser::EnumNode *GDScriptParser::parse_enum (bool p_is_abstract, bool p_is_static) {
1459
1501
EnumNode *enum_node = alloc_node<EnumNode>();
1460
1502
bool named = false ;
1461
1503
@@ -1608,7 +1650,7 @@ void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNod
1608
1650
consume (GDScriptTokenizer::Token::COLON, vformat (R"( Expected ":" after %s declaration.)" , p_type));
1609
1651
}
1610
1652
1611
- GDScriptParser::FunctionNode *GDScriptParser::parse_function (bool p_is_static) {
1653
+ GDScriptParser::FunctionNode *GDScriptParser::parse_function (bool p_is_abstract, bool p_is_static) {
1612
1654
FunctionNode *function = alloc_node<FunctionNode>();
1613
1655
1614
1656
make_completion_context (COMPLETION_OVERRIDE_METHOD, function);
@@ -1873,11 +1915,11 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
1873
1915
break ;
1874
1916
case GDScriptTokenizer::Token::VAR:
1875
1917
advance ();
1876
- result = parse_variable (false , false );
1918
+ result = parse_variable (false , false , false );
1877
1919
break ;
1878
1920
case GDScriptTokenizer::Token::CONST:
1879
1921
advance ();
1880
- result = parse_constant (false );
1922
+ result = parse_constant (false , false );
1881
1923
break ;
1882
1924
case GDScriptTokenizer::Token::IF:
1883
1925
advance ();
@@ -4074,6 +4116,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
4074
4116
{ nullptr , nullptr , PREC_NONE }, // MATCH,
4075
4117
{ nullptr , nullptr , PREC_NONE }, // WHEN,
4076
4118
// Keywords
4119
+ { nullptr , nullptr , PREC_NONE }, // ABSTRACT
4077
4120
{ nullptr , &GDScriptParser::parse_cast, PREC_CAST }, // AS,
4078
4121
{ nullptr , nullptr , PREC_NONE }, // ASSERT,
4079
4122
{ &GDScriptParser::parse_await, nullptr , PREC_NONE }, // AWAIT,
@@ -5639,6 +5682,9 @@ void GDScriptParser::TreePrinter::print_cast(CastNode *p_cast) {
5639
5682
}
5640
5683
5641
5684
void GDScriptParser::TreePrinter::print_class (ClassNode *p_class) {
5685
+ if (p_class->is_abstract ) {
5686
+ push_text (" Abstract " );
5687
+ }
5642
5688
push_text (" Class " );
5643
5689
if (p_class->identifier == nullptr ) {
5644
5690
push_text (" <unnamed>" );
@@ -6264,17 +6310,18 @@ void GDScriptParser::TreePrinter::print_while(WhileNode *p_while) {
6264
6310
}
6265
6311
6266
6312
void GDScriptParser::TreePrinter::print_tree (const GDScriptParser &p_parser) {
6267
- ERR_FAIL_NULL_MSG (p_parser.get_tree (), " Parse the code before printing the parse tree." );
6313
+ ClassNode *class_tree = p_parser.get_tree ();
6314
+ ERR_FAIL_NULL_MSG (class_tree, " Parse the code before printing the parse tree." );
6268
6315
6269
6316
if (p_parser.is_tool ()) {
6270
6317
push_line (" @tool" );
6271
6318
}
6272
- if (!p_parser. get_tree () ->icon_path .is_empty ()) {
6319
+ if (!class_tree ->icon_path .is_empty ()) {
6273
6320
push_text (R"( @icon (")" );
6274
- push_text (p_parser. get_tree () ->icon_path );
6321
+ push_text (class_tree ->icon_path );
6275
6322
push_line (" \" )" );
6276
6323
}
6277
- print_class (p_parser. get_tree () );
6324
+ print_class (class_tree );
6278
6325
6279
6326
print_line (String (printed));
6280
6327
}
0 commit comments