/* ** zcc_compile.cpp ** **--------------------------------------------------------------------------- ** Copyright -2016 Randy Heit ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** */ #include "dobject.h" #include "sc_man.h" #include "c_console.h" #include "c_dispatch.h" #include "w_wad.h" #include "cmdlib.h" #include "m_alloc.h" #include "zcc_parser.h" #include "zcc_compile.h" #include "v_text.h" #include "gdtoa.h" #define DEFINING_CONST ((PSymbolConst *)(void *)1) //========================================================================== // // ZCCCompiler :: ProcessClass // //========================================================================== void ZCCCompiler::ProcessClass(ZCC_Class *cnode, PSymbolTreeNode *tnode) { Classes.Push(ZCC_ClassWork(static_cast(cnode), tnode)); ZCC_ClassWork &cls = Classes.Last(); auto node = cnode->Body; do { switch (node->NodeType) { case AST_Struct: case AST_ConstantDef: if ((tnode = AddNamedNode(static_cast(node)))) { switch (node->NodeType) { case AST_Struct: cls.Structs.Push(ZCC_StructWork(static_cast(node), tnode)); break; case AST_ConstantDef: cls.Constants.Push(static_cast(node)); break; default: assert(0 && "Default case is just here to make GCC happy. It should never be reached"); } } break; case AST_Enum: break; case AST_EnumTerminator:break; // todo case AST_States: case AST_VarDeclarator: case AST_FuncDeclarator: break; } node = node->SiblingNext; } while (node != cnode->Body); } //========================================================================== // // ZCCCompiler Constructor // //========================================================================== ZCCCompiler::ZCCCompiler(ZCC_AST &ast, DObject *_outer, PSymbolTable &_symbols, PSymbolTable &_outsymbols) : Outer(_outer), Symbols(&_symbols), OutputSymbols(&_outsymbols), AST(ast), ErrorCount(0), WarnCount(0) { // Group top-level nodes by type if (ast.TopNode != NULL) { ZCC_TreeNode *node = ast.TopNode; PSymbolTreeNode *tnode; do { switch (node->NodeType) { case AST_Class: case AST_Struct: case AST_ConstantDef: if ((tnode = AddNamedNode(static_cast(node)))) { switch (node->NodeType) { case AST_Class: ProcessClass(static_cast(node), tnode); break; case AST_Struct: Structs.Push(ZCC_StructWork(static_cast(node), tnode)); break; case AST_ConstantDef: Constants.Push(static_cast(node)); break; default: assert(0 && "Default case is just here to make GCC happy. It should never be reached"); } } break; case AST_Enum: break; case AST_EnumTerminator:break; default: assert(0 && "Unhandled AST node type"); break; } node = node->SiblingNext; } while (node != ast.TopNode); } } //========================================================================== // // ZCCCompiler :: AddNamedNode // // Keeps track of definition nodes by their names. Ensures that all names // in this scope are unique. // //========================================================================== PSymbolTreeNode *ZCCCompiler::AddNamedNode(ZCC_NamedNode *node, PSymbolTable *parentsym) { FName name = node->NodeName; PSymbol *check = Symbols->FindSymbol(name, false); if (check != NULL) { assert(check->IsA(RUNTIME_CLASS(PSymbolTreeNode))); Error(node, "Attempt to redefine '%s'", name.GetChars()); Error(static_cast(check)->Node, " Original definition is here"); return nullptr; } else { auto sy = new PSymbolTreeNode(name, node); sy->Symbols.SetParentTable(parentsym); Symbols->AddSymbol(sy); return sy; } } //========================================================================== // // ZCCCompiler :: Warn // // Prints a warning message, and increments WarnCount. // //========================================================================== void ZCCCompiler::Warn(ZCC_TreeNode *node, const char *msg, ...) { va_list argptr; va_start(argptr, msg); MessageV(node, TEXTCOLOR_ORANGE, msg, argptr); va_end(argptr); WarnCount++; } //========================================================================== // // ZCCCompiler :: Error // // Prints an error message, and increments ErrorCount. // //========================================================================== void ZCCCompiler::Error(ZCC_TreeNode *node, const char *msg, ...) { va_list argptr; va_start(argptr, msg); MessageV(node, TEXTCOLOR_RED, msg, argptr); va_end(argptr); ErrorCount++; } //========================================================================== // // ZCCCompiler :: MessageV // // Prints a message, annotated with the source location for the tree node. // //========================================================================== void ZCCCompiler::MessageV(ZCC_TreeNode *node, const char *txtcolor, const char *msg, va_list argptr) { FString composed; composed.Format("%s%s, line %d: ", txtcolor, node->SourceName->GetChars(), node->SourceLoc); composed.VAppendFormat(msg, argptr); composed += '\n'; PrintString(PRINT_HIGH, composed); } //========================================================================== // // ZCCCompiler :: Compile // // Compile everything defined at this level. // //========================================================================== int ZCCCompiler::Compile() { CreateClassTypes(); CreateStructTypes(); CompileConstants(Constants); return ErrorCount; } //========================================================================== // // ZCCCompiler :: CreateStructTypes // // Creates a PStruct for every struct. // //========================================================================== void ZCCCompiler::CreateStructTypes() { for(auto s : Structs) { s->Type = NewStruct(s->NodeName, nullptr); } } //========================================================================== // // 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::CreateClassTypes() { auto OrigClasses = std::move(Classes); Classes.Clear(); bool donesomething = true; while (donesomething) { donesomething = false; 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 and not be a runtime class. 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 if (me->bRuntimeClass) { Error(c, "%s is not a native class", FName(c->NodeName).GetChars()); } else { DPrintf(DMSG_SPAMMY, "Registered %s as native with parent %s\n", me->TypeName.GetChars(), parent->TypeName.GetChars()); } c->Type = me; } else { // We will never get here if the name is a duplicate, so we can just do the assignment. c->Type = parent->FindClassTentative(c->NodeName); } 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 // // Make symbols from every constant defined at this level. // //========================================================================== void ZCCCompiler::CompileConstants(const TArray &defs) { for (unsigned i = 0; i < defs.Size(); ++i) { ZCC_ConstantDef *def = defs[i]; if (def->Symbol == NULL) { PSymbolConst *sym = CompileConstant(def); } } } //========================================================================== // // ZCCCompiler :: CompileConstant // // For every constant definition, evaluate its value (which should result // in a constant), and create a symbol for it. Simplify() uses recursion // to resolve constants used before their declarations. // //========================================================================== PSymbolConst *ZCCCompiler::CompileConstant(ZCC_ConstantDef *def) { assert(def->Symbol == NULL); def->Symbol = DEFINING_CONST; // avoid recursion ZCC_Expression *val = Simplify(def->Value); def->Value = val; PSymbolConst *sym = NULL; if (val->NodeType == AST_ExprConstant) { ZCC_ExprConstant *cval = static_cast(val); if (cval->Type == TypeString) { sym = new PSymbolConstString(def->NodeName, *(cval->StringVal)); } else if (cval->Type->IsA(RUNTIME_CLASS(PInt))) { sym = new PSymbolConstNumeric(def->NodeName, cval->Type, cval->IntVal); } else if (cval->Type->IsA(RUNTIME_CLASS(PFloat))) { sym = new PSymbolConstNumeric(def->NodeName, cval->Type, cval->DoubleVal); } else { Error(def->Value, "Bad type for constant definiton"); } } else { Error(def->Value, "Constant definition requires a constant value"); } if (sym == NULL) { // Create a dummy constant so we don't make any undefined value warnings. sym = new PSymbolConstNumeric(def->NodeName, TypeError, 0); } def->Symbol = sym; OutputSymbols->ReplaceSymbol(sym); return sym; } //========================================================================== // // ZCCCompiler :: Simplify // // For an expression, // Evaluate operators whose arguments are both constants, replacing it // with a new constant. // For a binary operator with one constant argument, put it on the right- // hand operand, where permitted. // Perform automatic type promotion. // //========================================================================== ZCC_Expression *ZCCCompiler::Simplify(ZCC_Expression *root) { if (root->NodeType == AST_ExprUnary) { return SimplifyUnary(static_cast(root)); } else if (root->NodeType == AST_ExprBinary) { return SimplifyBinary(static_cast(root)); } else if (root->Operation == PEX_ID) { return IdentifyIdentifier(static_cast(root)); } else if (root->Operation == PEX_MemberAccess) { return SimplifyMemberAccess(static_cast(root)); } else if (root->Operation == PEX_FuncCall) { return SimplifyFunctionCall(static_cast(root)); } return root; } //========================================================================== // // ZCCCompiler :: SimplifyUnary // //========================================================================== ZCC_Expression *ZCCCompiler::SimplifyUnary(ZCC_ExprUnary *unary) { unary->Operand = Simplify(unary->Operand); ZCC_OpProto *op = PromoteUnary(unary->Operation, unary->Operand); if (op == NULL) { // Oh, poo! unary->Type = TypeError; } else if (unary->Operand->Operation == PEX_ConstValue) { return op->EvalConst1(static_cast(unary->Operand)); } return unary; } //========================================================================== // // ZCCCompiler :: SimplifyBinary // //========================================================================== ZCC_Expression *ZCCCompiler::SimplifyBinary(ZCC_ExprBinary *binary) { binary->Left = Simplify(binary->Left); binary->Right = Simplify(binary->Right); ZCC_OpProto *op = PromoteBinary(binary->Operation, binary->Left, binary->Right); if (op == NULL) { binary->Type = TypeError; } else if (binary->Left->Operation == PEX_ConstValue && binary->Right->Operation == PEX_ConstValue) { return op->EvalConst2(static_cast(binary->Left), static_cast(binary->Right), AST.Strings); } return binary; } //========================================================================== // // ZCCCompiler :: SimplifyMemberAccess // //========================================================================== ZCC_Expression *ZCCCompiler::SimplifyMemberAccess(ZCC_ExprMemberAccess *dotop) { dotop->Left = Simplify(dotop->Left); if (dotop->Left->Operation == PEX_TypeRef) { // Type refs can be evaluated now. PType *ref = static_cast(dotop->Left)->RefType; PSymbolTable *symtable; PSymbol *sym = ref->Symbols.FindSymbolInTable(dotop->Right, symtable); if (sym == NULL) { Error(dotop, "'%s' is not a valid member", FName(dotop->Right).GetChars()); } else { ZCC_Expression *expr = NodeFromSymbol(sym, dotop, symtable); if (expr == NULL) { Error(dotop, "Unhandled symbol type encountered"); } else { return expr; } } } return dotop; } //========================================================================== // // ZCCCompiler :: SimplifyFunctionCall // // This may replace a function call with cast(s), since they look like the // same thing to the parser. // //========================================================================== ZCC_Expression *ZCCCompiler::SimplifyFunctionCall(ZCC_ExprFuncCall *callop) { ZCC_FuncParm *parm; int parmcount = 0; callop->Function = Simplify(callop->Function); parm = callop->Parameters; if (parm != NULL) { do { parmcount++; assert(parm->NodeType == AST_FuncParm); parm->Value = Simplify(parm->Value); parm = static_cast(parm->SiblingNext); } while (parm != callop->Parameters); } // If the left side is a type ref, then this is actually a cast // and not a function call. if (callop->Function->Operation == PEX_TypeRef) { if (parmcount != 1) { Error(callop, "Type cast requires one parameter"); callop->ToErrorNode(); } else { PType *dest = static_cast(callop->Function)->RefType; const PType::Conversion *route[CONVERSION_ROUTE_SIZE]; int routelen = parm->Value->Type->FindConversion(dest, route, countof(route)); if (routelen < 0) { ///FIXME: Need real type names Error(callop, "Cannot convert type 1 to type 2"); callop->ToErrorNode(); } else { ZCC_Expression *val = ApplyConversion(parm->Value, route, routelen); assert(val->Type == dest); return val; } } } return callop; } //========================================================================== // // ZCCCompiler :: PromoteUnary // // Converts the operand into a format preferred by the operator. // //========================================================================== ZCC_OpProto *ZCCCompiler::PromoteUnary(EZCCExprType op, ZCC_Expression *&expr) { if (expr->Type == TypeError) { return NULL; } const PType::Conversion *route[CONVERSION_ROUTE_SIZE]; int routelen = countof(route); ZCC_OpProto *proto = ZCC_OpInfo[op].FindBestProto(expr->Type, route, routelen); if (proto != NULL) { expr = ApplyConversion(expr, route, routelen); } return proto; } //========================================================================== // // ZCCCompiler :: PromoteBinary // // Converts the operands into a format (hopefully) compatible with the // operator. // //========================================================================== ZCC_OpProto *ZCCCompiler::PromoteBinary(EZCCExprType op, ZCC_Expression *&left, ZCC_Expression *&right) { // If either operand is of type 'error', the result is also 'error' if (left->Type == TypeError || right->Type == TypeError) { return NULL; } const PType::Conversion *route1[CONVERSION_ROUTE_SIZE], *route2[CONVERSION_ROUTE_SIZE]; int route1len = countof(route1), route2len = countof(route2); ZCC_OpProto *proto = ZCC_OpInfo[op].FindBestProto(left->Type, route1, route1len, right->Type, route2, route2len); if (proto != NULL) { left = ApplyConversion(left, route1, route1len); right = ApplyConversion(right, route2, route2len); } return proto; } //========================================================================== // // ZCCCompiler :: ApplyConversion // //========================================================================== ZCC_Expression *ZCCCompiler::ApplyConversion(ZCC_Expression *expr, const PType::Conversion **route, int routelen) { for (int i = 0; i < routelen; ++i) { if (expr->Operation != PEX_ConstValue) { expr = AddCastNode(route[i]->TargetType, expr); } else { route[i]->ConvertConstant(static_cast(expr), AST.Strings); } } return expr; } //========================================================================== // // ZCCCompiler :: AddCastNode // //========================================================================== ZCC_Expression *ZCCCompiler::AddCastNode(PType *type, ZCC_Expression *expr) { assert(expr->Operation != PEX_ConstValue && "Expression must not be constant"); // TODO: add a node here return expr; } //========================================================================== // // ZCCCompiler :: IdentifyIdentifier // // Returns a node that represents what the identifer stands for. // //========================================================================== ZCC_Expression *ZCCCompiler::IdentifyIdentifier(ZCC_ExprID *idnode) { // Check the symbol table for the identifier. PSymbolTable *table; PSymbol *sym = Symbols->FindSymbolInTable(idnode->Identifier, table); if (sym != NULL) { ZCC_Expression *node = NodeFromSymbol(sym, idnode, table); if (node != NULL) { return node; } } else { Error(idnode, "Unknown identifier '%s'", FName(idnode->Identifier).GetChars()); } // Identifier didn't refer to anything good, so type error it. idnode->ToErrorNode(); return idnode; } //========================================================================== // // ZCCCompiler :: CompileNode // //========================================================================== PSymbol *ZCCCompiler::CompileNode(ZCC_NamedNode *node) { assert(node != NULL); if (node->NodeType == AST_ConstantDef) { ZCC_ConstantDef *def = static_cast(node); PSymbolConst *sym = def->Symbol; if (sym == DEFINING_CONST) { Error(node, "Definition of '%s' is infinitely recursive", FName(node->NodeName).GetChars()); sym = NULL; } else { assert(sym == NULL); sym = CompileConstant(def); } return sym; } else if (node->NodeType == AST_Struct) { } return NULL; } //========================================================================== // // ZCCCompiler :: NodeFromSymbol // //========================================================================== ZCC_Expression *ZCCCompiler::NodeFromSymbol(PSymbol *sym, ZCC_Expression *source, PSymbolTable *table) { assert(sym != NULL); if (sym->IsA(RUNTIME_CLASS(PSymbolTreeNode))) { PSymbolTable *prevtable = Symbols; Symbols = table; sym = CompileNode(static_cast(sym)->Node); Symbols = prevtable; if (sym == NULL) { return NULL; } } if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConst))) { return NodeFromSymbolConst(static_cast(sym), source); } else if (sym->IsKindOf(RUNTIME_CLASS(PSymbolType))) { return NodeFromSymbolType(static_cast(sym), source); } return NULL; } //========================================================================== // // ZCCCompiler :: NodeFromSymbolConst // // Returns a new AST constant node with the symbol's content. // //========================================================================== ZCC_ExprConstant *ZCCCompiler::NodeFromSymbolConst(PSymbolConst *sym, ZCC_Expression *idnode) { ZCC_ExprConstant *val = static_cast(AST.InitNode(sizeof(*val), AST_ExprConstant, idnode)); val->Operation = PEX_ConstValue; if (sym == NULL) { val->Type = TypeError; val->IntVal = 0; } else if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConstString))) { val->StringVal = AST.Strings.Alloc(static_cast(sym)->Str); val->Type = TypeString; } else { val->Type = sym->ValueType; if (val->Type != TypeError) { assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolConstNumeric))); if (sym->ValueType->IsKindOf(RUNTIME_CLASS(PInt))) { val->IntVal = static_cast(sym)->Value; } else { assert(sym->ValueType->IsKindOf(RUNTIME_CLASS(PFloat))); val->DoubleVal = static_cast(sym)->Float; } } } return val; } //========================================================================== // // ZCCCompiler :: NodeFromSymbolType // // Returns a new AST type ref node with the symbol's content. // //========================================================================== ZCC_ExprTypeRef *ZCCCompiler::NodeFromSymbolType(PSymbolType *sym, ZCC_Expression *idnode) { ZCC_ExprTypeRef *ref = static_cast(AST.InitNode(sizeof(*ref), AST_ExprTypeRef, idnode)); ref->Operation = PEX_TypeRef; ref->RefType = sym->Type; ref->Type = NewClassPointer(RUNTIME_CLASS(PType)); return ref; }