From e63b6d494a936bc4c577804b2721d266c9d4009f Mon Sep 17 00:00:00 2001 From: Chronos Ouroboros Date: Fri, 1 Nov 2019 18:33:15 -0300 Subject: [PATCH] Added mixins for classes. --- src/scripting/zscript/zcc-parse.lemon | 56 ++++++++++++++ src/scripting/zscript/zcc_compile.cpp | 107 ++++++++++++++++++++++++++ src/scripting/zscript/zcc_compile.h | 19 +++++ src/scripting/zscript/zcc_parser.cpp | 1 + src/scripting/zscript/zcc_parser.h | 21 +++++ src/utility/sc_man_scanner.re | 1 + src/utility/sc_man_tokens.h | 1 + 7 files changed, 206 insertions(+) diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index 67506975f9c..9bf5932cf68 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -179,6 +179,7 @@ translation_unit(X) ::= translation_unit(X) EOF. translation_unit(X) ::= error. { X = NULL; } %type external_declaration {ZCC_TreeNode *} +external_declaration(X) ::= mixin_definition(A). { X = A; /*X-overwrites-A*/ } external_declaration(X) ::= class_definition(A). { X = A; /*X-overwrites-A*/ } external_declaration(X) ::= struct_def(A). { X = A; /*X-overwrites-A*/ } external_declaration(X) ::= enum_def(A). { X = A; /*X-overwrites-A*/ } @@ -312,6 +313,7 @@ class_body(X) ::= LBRACE class_innards(A) RBRACE. { X = A; /*X-overwrites-A*/ } class_innards(X) ::= . { X = NULL; } class_innards(X) ::= class_innards(X) class_member(B). { SAFE_APPEND(X,B); } +%type mixin_statement{ZCC_MixinStmt *} %type property_def{ZCC_Property *} %type flag_def{ZCC_FlagDef *} %type struct_def{ZCC_Struct *} @@ -320,6 +322,7 @@ class_innards(X) ::= class_innards(X) class_member(B). { SAFE_APPEND(X,B); } %type const_def {ZCC_ConstantDef *} class_member(X) ::= declarator(A). { X = A; /*X-overwrites-A*/ } +class_member(X) ::= mixin_statement(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= enum_def(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= struct_def(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= states_def(A). { X = A; /*X-overwrites-A*/ } @@ -330,6 +333,14 @@ class_member(X) ::= flag_def(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= staticarray_statement(A). { X = A; /*X-overwrites-A*/ } +/*----- Mixin statement -----*/ +mixin_statement(X) ::= MIXIN(T) IDENTIFIER(A) SEMICOLON. +{ + NEW_AST_NODE(MixinStmt,stmt,T); + stmt->MixinName = A.Name(); + X = stmt; +} + /*----- Struct Definition -----*/ /* Structs can define variables and enums. */ @@ -510,6 +521,51 @@ enumerator(X) ::= IDENTIFIER(A) EQ expr(B). /* Expression must be constant. */ X = node; } +/************ Mixin Definition ************/ +/* Can only occur at global scope. */ + +%type mixin_definition{ZCC_MixinDef *} +%type mixin_class_definition{ZCC_MixinDef *} + +mixin_definition(X) ::= mixin_class_definition(A). { X = A; /*X-overwrites-A*/ } + +/*------ Mixin Class Definition ------*/ +%type mixin_class_body{ZCC_TreeNode *} +%type mixin_class_member{ZCC_TreeNode *} + +mixin_class_definition(X) ::= MIXIN(T) CLASS IDENTIFIER(A) LBRACE mixin_class_body(B) RBRACE. +{ + NEW_AST_NODE(MixinDef,def,T); + def->Body = B; + def->NodeName = A.Name(); + def->MixinType = ZCC_Mixin_Class; + def->Symbol = nullptr; + X = def; +} + +/*------ Mixin Class Body ------*/ +// Body is a list of: +// * variable definitions +// * function definitions +// * enum definitions +// * struct definitions +// * state definitions +// * constants +// * defaults + +mixin_class_body(X) ::= . { X = NULL; } +mixin_class_body(X) ::= mixin_class_body(X) mixin_class_member(B). { SAFE_APPEND(X,B); } + +mixin_class_member(X) ::= declarator(A). { X = A; /*X-overwrites-A*/ } +mixin_class_member(X) ::= enum_def(A). { X = A; /*X-overwrites-A*/ } +mixin_class_member(X) ::= struct_def(A). { X = A; /*X-overwrites-A*/ } +mixin_class_member(X) ::= states_def(A). { X = A; /*X-overwrites-A*/ } +mixin_class_member(X) ::= default_def(A). { X = A; /*X-overwrites-A*/ } +mixin_class_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ } +mixin_class_member(X) ::= property_def(A). { X = A; /*X-overwrites-A*/ } +mixin_class_member(X) ::= flag_def(A). { X = A; /*X-overwrites-A*/ } +mixin_class_member(X) ::= staticarray_statement(A). { X = A; /*X-overwrites-A*/ } + /************ States ************/ %type states_body {ZCC_StatePart *} diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 76c366d08d8..0e6b179f00a 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -132,6 +132,8 @@ void ZCCCompiler::ProcessClass(ZCC_Class *cnode, PSymbolTreeNode *treenode) } auto node = cnode->Body; + auto origNextNode = cnode->Body; + ZCC_MixinDef *mixinDef = nullptr; PSymbolTreeNode *childnode; ZCC_Enum *enumType = nullptr; @@ -140,6 +142,34 @@ void ZCCCompiler::ProcessClass(ZCC_Class *cnode, PSymbolTreeNode *treenode) { switch (node->NodeType) { + case AST_MixinStmt: + { + auto mixinStmt = static_cast(node); + for (auto mx : Mixins) + { + if (mx->mixin->NodeName == mixinStmt->MixinName) + { + if (mx->mixin->MixinType != ZCC_Mixin_Class) + { + Error(node, "Mixin %s is not a class mixin.", FName(mixinStmt->MixinName).GetChars()); + } + + mixinDef = mx->mixin; + break; + } + } + + if (mixinDef == nullptr) + { + Error(node, "Mixin %s does not exist.", FName(mixinStmt->MixinName).GetChars()); + break; + } + + origNextNode = node->SiblingNext; + node = mixinDef->Body; + } + break; + case AST_Struct: case AST_ConstantDef: case AST_Enum: @@ -211,11 +241,62 @@ void ZCCCompiler::ProcessClass(ZCC_Class *cnode, PSymbolTreeNode *treenode) assert(0 && "Unhandled AST node type"); break; } + node = node->SiblingNext; + + if (mixinDef != nullptr && node == mixinDef->Body) + { + node = origNextNode; + mixinDef = nullptr; + } } while (node != cnode->Body); } +//========================================================================== +// +// ZCCCompiler :: ProcessMixin +// +//========================================================================== + +void ZCCCompiler::ProcessMixin(ZCC_MixinDef *cnode, PSymbolTreeNode *treenode) +{ + ZCC_MixinWork *cls = new ZCC_MixinWork(cnode, treenode); + + Mixins.Push(cls); + + auto node = cnode->Body; + + // Need to check if the class actually has a body. + if (node != nullptr) do + { + if (cnode->MixinType == ZCC_Mixin_Class) + { + switch (node->NodeType) + { + case AST_Struct: + case AST_ConstantDef: + case AST_Enum: + case AST_Property: + case AST_FlagDef: + case AST_VarDeclarator: + case AST_EnumTerminator: + case AST_States: + case AST_FuncDeclarator: + case AST_Default: + case AST_StaticArrayStatement: + break; + + default: + assert(0 && "Unhandled AST node type"); + break; + } + } + + node = node->SiblingNext; + } while (node != cnode->Body); +} + //========================================================================== // // ZCCCompiler :: ProcessStruct @@ -301,6 +382,28 @@ ZCCCompiler::ZCCCompiler(ZCC_AST &ast, DObject *_outer, PSymbolTable &_symbols, { ZCC_TreeNode *node = ast.TopNode; PSymbolTreeNode *tnode; + + // [pbeta] Mixins must be processed before everything else. + do + { + switch (node->NodeType) + { + case AST_MixinDef: + if ((tnode = AddTreeNode(static_cast(node)->NodeName, node, GlobalTreeNodes))) + { + switch (node->NodeType) + { + case AST_MixinDef: + ProcessMixin(static_cast(node), tnode); + break; + } + } + break; + } + node = node->SiblingNext; + } while (node != ast.TopNode); + + node = ast.TopNode; PType *enumType = nullptr; ZCC_Enum *zenumType = nullptr; @@ -308,6 +411,10 @@ ZCCCompiler::ZCCCompiler(ZCC_AST &ast, DObject *_outer, PSymbolTable &_symbols, { switch (node->NodeType) { + case AST_MixinDef: + // [pbeta] We already processed mixins, ignore them here. + break; + case AST_Class: // a class extension should not check the tree node symbols. if (static_cast(node)->Flags == ZCC_Extension) diff --git a/src/scripting/zscript/zcc_compile.h b/src/scripting/zscript/zcc_compile.h index 15497f7f626..5dfa0bb3b6c 100644 --- a/src/scripting/zscript/zcc_compile.h +++ b/src/scripting/zscript/zcc_compile.h @@ -71,6 +71,23 @@ struct ZCC_ClassWork : public ZCC_StructWork } }; +struct ZCC_MixinWork +{ + PSymbolTable TreeNodes; + ZCC_MixinDef *mixin; + PSymbolTreeNode *node; + + ZCC_MixinWork() + { + } + + ZCC_MixinWork(ZCC_MixinDef *m, PSymbolTreeNode *n) + { + mixin = m; + node = n; + } +}; + struct ZCC_PropertyWork { ZCC_Property *prop; @@ -99,6 +116,7 @@ class ZCCCompiler FString StringConstFromNode(ZCC_TreeNode *node, PContainerType *cls); void ProcessClass(ZCC_Class *node, PSymbolTreeNode *tnode); void ProcessStruct(ZCC_Struct *node, PSymbolTreeNode *tnode, ZCC_Class *outer); + void ProcessMixin(ZCC_MixinDef *cnode, PSymbolTreeNode *treenode); void CreateStructTypes(); void CreateClassTypes(); void CopyConstants(TArray &dest, TArray &Constants, PContainerType *cls, PSymbolTable *ot); @@ -131,6 +149,7 @@ class ZCCCompiler TArray Constants; TArray Structs; TArray Classes; + TArray Mixins; TArray Properties; VersionInfo mVersion; diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp index 6a4d5a7be23..4540e3a2042 100644 --- a/src/scripting/zscript/zcc_parser.cpp +++ b/src/scripting/zscript/zcc_parser.cpp @@ -151,6 +151,7 @@ static void InitTokenMap() TOKENDEF (TK_Struct, ZCC_STRUCT); TOKENDEF (TK_Property, ZCC_PROPERTY); TOKENDEF (TK_FlagDef, ZCC_FLAGDEF); + TOKENDEF (TK_Mixin, ZCC_MIXIN); TOKENDEF (TK_Transient, ZCC_TRANSIENT); TOKENDEF (TK_Enum, ZCC_ENUM); TOKENDEF2(TK_SByte, ZCC_SBYTE, NAME_sByte); diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index f09ead5666b..b5507f6af47 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -135,6 +135,8 @@ enum EZCCTreeNodeType AST_StaticArrayStatement, AST_Property, AST_FlagDef, + AST_MixinDef, + AST_MixinStmt, NUM_AST_NODE_TYPES }; @@ -168,6 +170,13 @@ enum EZCCBuiltinType ZCC_NUM_BUILT_IN_TYPES }; +enum EZCCMixinType +{ + ZCC_Mixin_Class, + + ZCC_NUM_MIXIN_TYPES +}; + enum EZCCExprType { #define xx(a,z) PEX_##a, @@ -241,6 +250,13 @@ struct ZCC_Class : ZCC_Struct PClass *CType() { return static_cast(Type)->Descriptor; } }; +struct ZCC_MixinDef : ZCC_NamedNode +{ + ZCC_TreeNode *Body; + + EZCCMixinType MixinType; +}; + struct ZCC_Enum : ZCC_NamedNode { EZCCBuiltinType EnumType; @@ -568,6 +584,11 @@ struct ZCC_FlagStmt : ZCC_Statement bool set; }; +struct ZCC_MixinStmt : ZCC_Statement +{ + ENamedName MixinName; +}; + FString ZCC_PrintAST(ZCC_TreeNode *root); diff --git a/src/utility/sc_man_scanner.re b/src/utility/sc_man_scanner.re index 9d0171e12a2..019327807d9 100644 --- a/src/utility/sc_man_scanner.re +++ b/src/utility/sc_man_scanner.re @@ -157,6 +157,7 @@ std2: 'void' { RET(TK_Void); } 'struct' { RET(TK_Struct); } 'class' { RET(TK_Class); } + 'mixin' { RET(TK_Mixin); } 'enum' { RET(TK_Enum); } 'name' { RET(TK_Name); } 'string' { RET(TK_String); } diff --git a/src/utility/sc_man_tokens.h b/src/utility/sc_man_tokens.h index 82dac84d87a..5aa6360fe9a 100644 --- a/src/utility/sc_man_tokens.h +++ b/src/utility/sc_man_tokens.h @@ -70,6 +70,7 @@ xx(TK_Struct, "'struct'") xx(TK_FlagDef, "'flagdef'") xx(TK_Property, "'property'") xx(TK_Class, "'class'") +xx(TK_Mixin, "'mixin'") xx(TK_Enum, "'enum'") xx(TK_Name, "'name'") xx(TK_String, "'string'")