diff --git a/src/zscript/zcc-parse.lemon b/src/zscript/zcc-parse.lemon index cd2ed8efe..6d22f9ea4 100644 --- a/src/zscript/zcc-parse.lemon +++ b/src/zscript/zcc-parse.lemon @@ -175,6 +175,7 @@ class_head(X) ::= CLASS(T) IDENTIFIER(A) class_ancestry(B) class_flags(C). head->ParentName = B; head->Flags = C.Flags; head->Replaces = C.Replaces; + head->Type = nullptr; X = head; } @@ -184,8 +185,8 @@ class_ancestry(X) ::= COLON dottable_id(A). { X = A; /*X-overwrites-A*/ } %type class_flags{ClassFlagsBlock} class_flags(X) ::= . { X.Flags = 0; X.Replaces = NULL; } -class_flags(X) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | 0/*FIXME*/; X.Replaces = A.Replaces; } -class_flags(X) ::= class_flags(A) NATIVE. { X.Flags = A.Flags | 0/*FIXME*/; X.Replaces = A.Replaces; } +class_flags(X) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | ZCC_Abstract; X.Replaces = A.Replaces; } +class_flags(X) ::= class_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; X.Replaces = A.Replaces; } class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; } /*----- Dottable Identifier -----*/ diff --git a/src/zscript/zcc_compile.cpp b/src/zscript/zcc_compile.cpp index e6d2e6d96..68261635f 100644 --- a/src/zscript/zcc_compile.cpp +++ b/src/zscript/zcc_compile.cpp @@ -181,10 +181,128 @@ void ZCCCompiler::MessageV(ZCC_TreeNode *node, const char *txtcolor, const char int ZCCCompiler::Compile() { + CreateClasses(); CompileConstants(Constants); return ErrorCount; } +//========================================================================== +// +// ZCCCompiler :: CreateClassTypes +// +// Creates a PClass for every class so that we get access to the symbol table +// These will be created with unknown size because for that we need to +// process all fields first, but to do that we need the PClass and some +// other info depending on the PClass. +// +//========================================================================== + +void ZCCCompiler::CreateClasses() +{ + auto OrigClasses = std::move(Classes); + Classes.Clear(); + bool donesomething = true; + while (donesomething) + { + for (unsigned i=0;iParentName != nullptr && c->ParentName->SiblingNext == c->ParentName) parent = PClass::FindClass(c->ParentName->Id); + else if (c->ParentName == nullptr) parent = RUNTIME_CLASS(DObject); + else + { + // The parent is a dotted name which the type system currently does not handle. + // Once it does this needs to be implemented here. + auto p = c->ParentName; + FString build; + + do + { + if (build.IsNotEmpty()) build += '.'; + build += FName(p->Id); + p = static_cast(p->SiblingNext); + } while (p != c->ParentName); + Error(c, "Qualified name '%s' for base class not supported in '%s'", build.GetChars(), FName(c->NodeName).GetChars()); + parent = RUNTIME_CLASS(DObject); + } + + if (parent != nullptr) + { + // The parent exists, we may create a type for this class + if (c->Flags & ZCC_Native) + { + // If this is a native class, its own type must also already exist. + auto me = PClass::FindClass(c->NodeName); + if (me == nullptr) + { + Error(c, "Unknown native class %s", FName(c->NodeName).GetChars()); + me = parent->FindClassTentative(c->NodeName); + } + else + { + DPrintf(DMSG_SPAMMY, "Registered %s as native with parent %s\n", me->TypeName.GetChars(), parent->TypeName.GetChars()); + } + c->Type = me; + } + else + { + auto me = PClass::FindClass(c->NodeName); + if (me != nullptr) + { + Error(c, "Redefining class %s", FName(c->NodeName).GetChars()); + } + else + { + me = parent->FindClassTentative(c->NodeName); + DPrintf(DMSG_SPAMMY, "Created %s with parent %s\n", me->TypeName.GetChars(), parent->TypeName.GetChars()); + } + c->Type = me; + } + Classes.Push(c); + OrigClasses.Delete(i); + i--; + donesomething = true; + } + else + { + // No base class found. Now check if something in the unprocessed classes matches. + // If not, print an error. If something is found let's retry again in the next iteration. + bool found = false; + for (auto d : OrigClasses) + { + if (d->NodeName == c->ParentName->Id) + { + found = true; + break; + } + } + if (!found) + { + Error(c, "Class %s has unknown base class %s", FName(c->NodeName).GetChars(), FName(c->ParentName->Id).GetChars()); + // create a placeholder so that the compiler can continue looking for errors. + c->Type = RUNTIME_CLASS(DObject)->FindClassTentative(c->NodeName); + Classes.Push(c); + OrigClasses.Delete(i); + donesomething = true; + } + } + } + } + + // What's left refers to some other class in the list but could not be resolved. + // This normally means a circular reference. + for (auto c : OrigClasses) + { + Error(c, "Class %s has circular inheritance", FName(c->NodeName).GetChars()); + c->Type = RUNTIME_CLASS(DObject)->FindClassTentative(c->NodeName); + Classes.Push(c); + } +} + //========================================================================== // // ZCCCompiler :: CompileConstants diff --git a/src/zscript/zcc_compile.h b/src/zscript/zcc_compile.h index d034464b7..24497acd9 100644 --- a/src/zscript/zcc_compile.h +++ b/src/zscript/zcc_compile.h @@ -8,6 +8,7 @@ public: int Compile(); private: + void CreateClasses(); void CompileConstants(const TArray &defs); PSymbolConst *CompileConstant(ZCC_ConstantDef *def); diff --git a/src/zscript/zcc_parser.cpp b/src/zscript/zcc_parser.cpp index ed41253e1..3a34abe07 100644 --- a/src/zscript/zcc_parser.cpp +++ b/src/zscript/zcc_parser.cpp @@ -318,8 +318,8 @@ static void DoParse(int lumpnum) ZCCParse(parser, 0, value, &state); ZCCParseFree(parser, free); - ZCCCompiler cc(state, NULL, GlobalSymbols); - cc.Compile(); + { + // Make a dump of the AST before running the compiler for diagnostic purposes. #ifdef _DEBUG if (f != NULL) { @@ -328,7 +328,28 @@ static void DoParse(int lumpnum) FString ast = ZCC_PrintAST(state.TopNode); FString filename = Wads.GetLumpFullName(lumpnum); FString astfile = ExtractFileBase(filename, false); - astfile << ".ast"; + astfile << "-before.ast"; + f = fopen(astfile, "w"); + if (f != NULL) + { + fputs(ast.GetChars(), f); + fclose(f); + } +#endif + } + + ZCCCompiler cc(state, NULL, GlobalSymbols); + cc.Compile(); + // ... and another one afterward so we can see what the compiler does with the data. +#ifdef _DEBUG + if (f != NULL) + { + fclose(f); + } + FString ast = ZCC_PrintAST(state.TopNode); + FString filename = Wads.GetLumpFullName(lumpnum); + FString astfile = ExtractFileBase(filename, false); + astfile << "-after.ast"; f = fopen(astfile, "w"); if (f != NULL) { diff --git a/src/zscript/zcc_parser.h b/src/zscript/zcc_parser.h index 507f80b9e..8b82b7eae 100644 --- a/src/zscript/zcc_parser.h +++ b/src/zscript/zcc_parser.h @@ -16,7 +16,7 @@ struct ZCCToken ENamedName Name() { return ENamedName(Int); } }; -// Variable / Function modifiers +// Variable / Function / Class modifiers enum { ZCC_Native = 1 << 0, @@ -30,6 +30,7 @@ enum ZCC_Deprecated = 1 << 8, ZCC_ReadOnly = 1 << 9, ZCC_FuncConst = 1 << 10, + ZCC_Abstract = 1 << 11, }; // Function parameter modifiers @@ -185,6 +186,7 @@ struct ZCC_Class : ZCC_NamedNode ZCC_Identifier *Replaces; VM_UWORD Flags; ZCC_TreeNode *Body; + PClass *Type; }; struct ZCC_Struct : ZCC_NamedNode