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