/* ** zcc_expr.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 "filesystem.h" #include "cmdlib.h" #include "m_argv.h" #include "v_text.h" #include "version.h" #include "zcc_parser.h" #include "zcc_compile.h" TArray Includes; TArray IncludeLocs; static FString ResolveIncludePath(const FString &path,const FString &lumpname){ if (path.IndexOf("./") == 0 || path.IndexOf("../") == 0) // relative path resolving { auto start = lumpname.LastIndexOf(":"); // find find separator between wad and path auto end = lumpname.LastIndexOf("/"); // find last '/' // it's a top-level file, if it's a folder being loaded ( /xxx/yyy/:whatever.zs ) end is before than start, or if it's a zip ( xxx.zip/whatever.zs ) end would be -1 bool topLevelFile = start > end ; FString fullPath = topLevelFile ? FString {} : lumpname.Mid(start + 1, end - start - 1); // get path from lumpname (format 'wad:filepath/filename') if (start != -1) { FString relativePath = path; if (relativePath.IndexOf("./") == 0) // strip initial marker { relativePath = relativePath.Mid(2); } bool pathOk = true; while (relativePath.IndexOf("../") == 0) // go back one folder for each '..' { relativePath = relativePath.Mid(3); auto slash_index = fullPath.LastIndexOf("/"); if (slash_index != -1) { fullPath = fullPath.Mid(0, slash_index); } else { pathOk = false; break; } } if (pathOk) // if '..' parsing was successful { return topLevelFile ? relativePath : fullPath + "/" + relativePath; } } } return path; } static FString ZCCTokenName(int terminal); void AddInclude(ZCC_ExprConstant *node) { assert(node->Type == TypeString); FScriptPosition pos(*node); FString path = ResolveIncludePath(*node->StringVal, pos.FileName.GetChars()); if (Includes.Find(path) >= Includes.Size()) { Includes.Push(path); IncludeLocs.Push(pos); } } #include "zcc-parse.h" #include "zcc-parse.c" struct TokenMapEntry { int16_t TokenType; uint16_t TokenName; TokenMapEntry(int16_t a, uint16_t b) : TokenType(a), TokenName(b) { } }; static TMap TokenMap; static int16_t BackTokenMap[YYERRORSYMBOL]; // YYERRORSYMBOL immediately follows the terminals described by the grammar #define TOKENDEF2(sc, zcc, name) { TokenMapEntry tme(zcc, name); TokenMap.Insert(sc, tme); } BackTokenMap[zcc] = sc #define TOKENDEF(sc, zcc) TOKENDEF2(sc, zcc, NAME_None) static void InitTokenMap() { TOKENDEF ('=', ZCC_EQ); TOKENDEF (TK_MulEq, ZCC_MULEQ); TOKENDEF (TK_DivEq, ZCC_DIVEQ); TOKENDEF (TK_ModEq, ZCC_MODEQ); TOKENDEF (TK_AddEq, ZCC_ADDEQ); TOKENDEF (TK_SubEq, ZCC_SUBEQ); TOKENDEF (TK_LShiftEq, ZCC_LSHEQ); TOKENDEF (TK_RShiftEq, ZCC_RSHEQ); TOKENDEF (TK_URShiftEq, ZCC_URSHEQ); TOKENDEF (TK_AndEq, ZCC_ANDEQ); TOKENDEF (TK_OrEq, ZCC_OREQ); TOKENDEF (TK_XorEq, ZCC_XOREQ); TOKENDEF ('?', ZCC_QUESTION); TOKENDEF (':', ZCC_COLON); TOKENDEF ('@', ZCC_ATSIGN); TOKENDEF (TK_OrOr, ZCC_OROR); TOKENDEF (TK_AndAnd, ZCC_ANDAND); TOKENDEF (TK_Eq, ZCC_EQEQ); TOKENDEF (TK_Neq, ZCC_NEQ); TOKENDEF (TK_ApproxEq, ZCC_APPROXEQ); TOKENDEF ('<', ZCC_LT); TOKENDEF ('>', ZCC_GT); TOKENDEF (TK_Leq, ZCC_LTEQ); TOKENDEF (TK_Geq, ZCC_GTEQ); TOKENDEF (TK_LtGtEq, ZCC_LTGTEQ); TOKENDEF (TK_Is, ZCC_IS); TOKENDEF (TK_DotDot, ZCC_DOTDOT); TOKENDEF (TK_Ellipsis, ZCC_ELLIPSIS); TOKENDEF ('|', ZCC_OR); TOKENDEF ('^', ZCC_XOR); TOKENDEF ('&', ZCC_AND); TOKENDEF (TK_LShift, ZCC_LSH); TOKENDEF (TK_RShift, ZCC_RSH); TOKENDEF (TK_URShift, ZCC_URSH); TOKENDEF ('-', ZCC_SUB); TOKENDEF ('+', ZCC_ADD); TOKENDEF ('*', ZCC_MUL); TOKENDEF ('/', ZCC_DIV); TOKENDEF ('%', ZCC_MOD); TOKENDEF (TK_Cross, ZCC_CROSSPROD); TOKENDEF (TK_Dot, ZCC_DOTPROD); TOKENDEF (TK_MulMul, ZCC_POW); TOKENDEF (TK_Incr, ZCC_ADDADD); TOKENDEF (TK_Decr, ZCC_SUBSUB); TOKENDEF ('.', ZCC_DOT); TOKENDEF ('(', ZCC_LPAREN); TOKENDEF (')', ZCC_RPAREN); TOKENDEF (TK_ColonColon, ZCC_SCOPE); TOKENDEF (';', ZCC_SEMICOLON); TOKENDEF (',', ZCC_COMMA); TOKENDEF (TK_Class, ZCC_CLASS); TOKENDEF (TK_Abstract, ZCC_ABSTRACT); TOKENDEF (TK_Native, ZCC_NATIVE); TOKENDEF (TK_Action, ZCC_ACTION); TOKENDEF (TK_Replaces, ZCC_REPLACES); TOKENDEF (TK_Static, ZCC_STATIC); TOKENDEF (TK_Private, ZCC_PRIVATE); TOKENDEF (TK_Protected, ZCC_PROTECTED); TOKENDEF (TK_Latent, ZCC_LATENT); TOKENDEF (TK_Virtual, ZCC_VIRTUAL); TOKENDEF (TK_VarArg, ZCC_VARARG); TOKENDEF (TK_UI, ZCC_UI); TOKENDEF (TK_Play, ZCC_PLAY); TOKENDEF (TK_ClearScope, ZCC_CLEARSCOPE); TOKENDEF (TK_VirtualScope, ZCC_VIRTUALSCOPE); TOKENDEF (TK_Override, ZCC_OVERRIDE); TOKENDEF (TK_Final, ZCC_FINAL); TOKENDEF (TK_Meta, ZCC_META); TOKENDEF (TK_Deprecated, ZCC_DEPRECATED); TOKENDEF (TK_Version, ZCC_VERSION); TOKENDEF (TK_ReadOnly, ZCC_READONLY); TOKENDEF (TK_Internal, ZCC_INTERNAL); TOKENDEF ('{', ZCC_LBRACE); TOKENDEF ('}', ZCC_RBRACE); 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); TOKENDEF2(TK_Byte, ZCC_BYTE, NAME_Byte); TOKENDEF2(TK_Short, ZCC_SHORT, NAME_Short); TOKENDEF2(TK_UShort, ZCC_USHORT, NAME_uShort); TOKENDEF2(TK_Int8, ZCC_SBYTE, NAME_int8); TOKENDEF2(TK_UInt8, ZCC_BYTE, NAME_uint8); TOKENDEF2(TK_Int16, ZCC_SHORT, NAME_int16); TOKENDEF2(TK_UInt16, ZCC_USHORT, NAME_uint16); TOKENDEF2(TK_Int, ZCC_INT, NAME_Int); TOKENDEF2(TK_UInt, ZCC_UINT, NAME_uInt); TOKENDEF2(TK_Bool, ZCC_BOOL, NAME_Bool); TOKENDEF2(TK_Float, ZCC_FLOAT, NAME_Float); TOKENDEF2(TK_Double, ZCC_DOUBLE, NAME_Double); TOKENDEF2(TK_String, ZCC_STRING, NAME_String); TOKENDEF2(TK_Vector2, ZCC_VECTOR2, NAME_Vector2); TOKENDEF2(TK_Vector3, ZCC_VECTOR3, NAME_Vector3); TOKENDEF2(TK_Name, ZCC_NAME, NAME_Name); TOKENDEF2(TK_Map, ZCC_MAP, NAME_Map); TOKENDEF2(TK_Array, ZCC_ARRAY, NAME_Array); TOKENDEF2(TK_Include, ZCC_INCLUDE, NAME_Include); TOKENDEF (TK_Void, ZCC_VOID); TOKENDEF (TK_True, ZCC_TRUE); TOKENDEF (TK_False, ZCC_FALSE); TOKENDEF ('[', ZCC_LBRACKET); TOKENDEF (']', ZCC_RBRACKET); TOKENDEF (TK_In, ZCC_IN); TOKENDEF (TK_Out, ZCC_OUT); TOKENDEF (TK_Super, ZCC_SUPER); TOKENDEF (TK_Null, ZCC_NULLPTR); TOKENDEF ('~', ZCC_TILDE); TOKENDEF ('!', ZCC_BANG); TOKENDEF (TK_SizeOf, ZCC_SIZEOF); TOKENDEF (TK_AlignOf, ZCC_ALIGNOF); TOKENDEF (TK_Continue, ZCC_CONTINUE); TOKENDEF (TK_Break, ZCC_BREAK); TOKENDEF (TK_Return, ZCC_RETURN); TOKENDEF (TK_Do, ZCC_DO); TOKENDEF (TK_For, ZCC_FOR); TOKENDEF (TK_ForEach, ZCC_FOREACH); TOKENDEF (TK_While, ZCC_WHILE); TOKENDEF (TK_Until, ZCC_UNTIL); TOKENDEF (TK_If, ZCC_IF); TOKENDEF (TK_Else, ZCC_ELSE); TOKENDEF (TK_Switch, ZCC_SWITCH); TOKENDEF (TK_Case, ZCC_CASE); TOKENDEF2(TK_Default, ZCC_DEFAULT, NAME_Default); TOKENDEF (TK_Const, ZCC_CONST); TOKENDEF (TK_Stop, ZCC_STOP); TOKENDEF (TK_Wait, ZCC_WAIT); TOKENDEF (TK_Fail, ZCC_FAIL); TOKENDEF (TK_Loop, ZCC_LOOP); TOKENDEF (TK_Goto, ZCC_GOTO); TOKENDEF (TK_States, ZCC_STATES); TOKENDEF2(TK_State, ZCC_STATE, NAME_State); TOKENDEF2(TK_Color, ZCC_COLOR, NAME_Color); TOKENDEF2(TK_Sound, ZCC_SOUND, NAME_Sound); TOKENDEF2(TK_Let, ZCC_LET, NAME_let); TOKENDEF2(TK_StaticConst, ZCC_STATICCONST,NAME_Staticconst); TOKENDEF (TK_Identifier, ZCC_IDENTIFIER); TOKENDEF (TK_StringConst, ZCC_STRCONST); TOKENDEF (TK_NameConst, ZCC_NAMECONST); TOKENDEF (TK_IntConst, ZCC_INTCONST); TOKENDEF (TK_UIntConst, ZCC_UINTCONST); TOKENDEF (TK_FloatConst, ZCC_FLOATCONST); TOKENDEF (TK_NonWhitespace, ZCC_NWS); TOKENDEF (TK_Bright, ZCC_BRIGHT); TOKENDEF (TK_Slow, ZCC_SLOW); TOKENDEF (TK_Fast, ZCC_FAST); TOKENDEF (TK_NoDelay, ZCC_NODELAY); TOKENDEF (TK_Offset, ZCC_OFFSET); TOKENDEF (TK_CanRaise, ZCC_CANRAISE); TOKENDEF (TK_Light, ZCC_LIGHT); TOKENDEF (TK_Extend, ZCC_EXTEND); } #undef TOKENDEF #undef TOKENDEF2 //**-------------------------------------------------------------------------- static void ParseSingleFile(FScanner *pSC, const char *filename, int lump, void *parser, ZCCParseState &state) { int tokentype; //bool failed; ZCCToken value; FScanner lsc; if (pSC == nullptr) { if (filename != nullptr) { lump = fileSystem.CheckNumForFullName(filename, true); if (lump >= 0) { lsc.OpenLumpNum(lump); } else { Printf("Could not find script lump '%s'\n", filename); return; } } else lsc.OpenLumpNum(lump); pSC = &lsc; } FScanner &sc = *pSC; sc.SetParseVersion(state.ParseVersion); state.sc = ≻ while (sc.GetToken()) { value.Largest = 0; value.SourceLoc = sc.GetMessageLine(); switch (sc.TokenType) { case TK_StringConst: value.String = state.Strings.Alloc(sc.String, sc.StringLen); tokentype = ZCC_STRCONST; break; case TK_NameConst: value.Int = FName(sc.String).GetIndex(); tokentype = ZCC_NAMECONST; break; case TK_IntConst: value.Int = sc.Number; tokentype = ZCC_INTCONST; break; case TK_UIntConst: value.Int = sc.Number; tokentype = ZCC_UINTCONST; break; case TK_FloatConst: value.Float = sc.Float; tokentype = ZCC_FLOATCONST; break; case TK_None: // 'NONE' is a token for SBARINFO but not here. case TK_Identifier: value.Int = FName(sc.String).GetIndex(); tokentype = ZCC_IDENTIFIER; break; case TK_NonWhitespace: value.Int = FName(sc.String).GetIndex(); tokentype = ZCC_NWS; break; case TK_Static: sc.MustGetAnyToken(); // The oh so wonderful grammar has problems with the 'const' token thanks to the overly broad rule for constants, // which effectively prevents use of this token nearly anywhere else. So in order to get 'static const' through // on the class/struct level we have to muck around with the token type here so that both words get combined into // a single token that doesn't make the grammar throw a fit. if (sc.TokenType == TK_Const) { tokentype = ZCC_STATICCONST; value.Int = NAME_Staticconst; } else { tokentype = ZCC_STATIC; value.Int = NAME_Static; sc.UnGet(); } break; default: TokenMapEntry *zcctoken = TokenMap.CheckKey(sc.TokenType); if (zcctoken != nullptr) { tokentype = zcctoken->TokenType; value.Int = zcctoken->TokenName; } else { sc.ScriptMessage("Unexpected token %s.\n", sc.TokenName(sc.TokenType).GetChars()); goto parse_end; } break; } ZCCParse(parser, tokentype, value, &state); } parse_end: value.Int = -1; ZCCParse(parser, ZCC_EOF, value, &state); state.sc = nullptr; } //**-------------------------------------------------------------------------- PNamespace *ParseOneScript(const int baselump, ZCCParseState &state) { FScanner sc; void *parser; ZCCToken value; int lumpnum = baselump; auto fileno = fileSystem.GetFileContainer(lumpnum); if (TokenMap.CountUsed() == 0) { InitTokenMap(); } parser = ZCCParseAlloc(malloc); #ifndef NDEBUG FILE *f = nullptr; const char *tracefile = Args->CheckValue("-tracefile"); if (tracefile != nullptr) { f = fopen(tracefile, "w"); char prompt = '\0'; ZCCParseTrace(f, &prompt); } #endif sc.OpenLumpNum(lumpnum); sc.SetParseVersion({ 2, 4 }); // To get 'version' we need parse version 2.4 for the initial test auto saved = sc.SavePos(); if (sc.GetToken()) { if (sc.TokenType == TK_Version) { char *endp; sc.MustGetString(); state.ParseVersion.major = (int16_t)clamp(strtoull(sc.String, &endp, 10), 0, USHRT_MAX); if (*endp != '.') { sc.ScriptError("Bad version directive"); } state.ParseVersion.minor = (int16_t)clamp(strtoll(endp + 1, &endp, 10), 0, USHRT_MAX); if (*endp == '.') { state.ParseVersion.revision = (int16_t)clamp(strtoll(endp + 1, &endp, 10), 0, USHRT_MAX); } else state.ParseVersion.revision = 0; if (*endp != 0) { sc.ScriptError("Bad version directive"); } if (state.ParseVersion.major == USHRT_MAX || state.ParseVersion.minor == USHRT_MAX || state.ParseVersion.revision == USHRT_MAX) { sc.ScriptError("Bad version directive"); } if (state.ParseVersion > MakeVersion(VER_MAJOR, VER_MINOR, VER_REVISION)) { sc.ScriptError("The file you are attempting to run requires a newer version of " GAMENAME ".\n\nA version with ZScript version %d.%d.%d is required, but your copy of " GAMENAME " only supports %d.%d.%d. Please upgrade!", state.ParseVersion.major, state.ParseVersion.minor, state.ParseVersion.revision, VER_MAJOR, VER_MINOR, VER_REVISION); } } else { state.ParseVersion = MakeVersion(2, 3); // 2.3 is the first version of ZScript. sc.RestorePos(saved); } } ParseSingleFile(&sc, nullptr, lumpnum, parser, state); for (unsigned i = 0; i < Includes.Size(); i++) { lumpnum = fileSystem.CheckNumForFullName(Includes[i], true); if (lumpnum == -1) { IncludeLocs[i].Message(MSG_ERROR, "Include script lump %s not found", Includes[i].GetChars()); } else { auto fileno2 = fileSystem.GetFileContainer(lumpnum); if (fileno == 0 && fileno2 != 0) { I_FatalError("File %s is overriding core lump %s.", fileSystem.GetResourceFileFullName(fileSystem.GetFileContainer(lumpnum)), Includes[i].GetChars()); } ParseSingleFile(nullptr, nullptr, lumpnum, parser, state); } } Includes.Clear(); Includes.ShrinkToFit(); IncludeLocs.Clear(); IncludeLocs.ShrinkToFit(); value.Int = -1; value.SourceLoc = sc.GetMessageLine(); ZCCParse(parser, 0, value, &state); ZCCParseFree(parser, free); // If the parser fails, there is no point starting the compiler, because it'd only flood the output with endless errors. if (FScriptPosition::ErrorCounter > 0) { I_Error("%d errors while parsing %s", FScriptPosition::ErrorCounter, fileSystem.GetFileFullPath(baselump).GetChars()); } #ifndef NDEBUG if (f != nullptr) { fclose(f); } #endif // Make a dump of the AST before running the compiler for diagnostic purposes. if (Args->CheckParm("-dumpast")) { FString ast = ZCC_PrintAST(state.TopNode); FString filename = fileSystem.GetFileFullPath(baselump); filename.ReplaceChars(":\\/?|", '.'); filename << ".ast"; FileWriter *ff = FileWriter::Open(filename); if (ff != NULL) { ff->Write(ast.GetChars(), ast.Len()); delete ff; } } auto newns = fileSystem.GetFileContainer(baselump) == 0 ? Namespaces.GlobalNamespace : Namespaces.NewNamespace(fileSystem.GetFileContainer(baselump)); return newns; } static FString ZCCTokenName(int terminal) { if (terminal == ZCC_EOF) { return "end of file"; } int sc_token; if (terminal > 0 && terminal < (int)countof(BackTokenMap)) { sc_token = BackTokenMap[terminal]; if (sc_token == 0) { // This token was not initialized. Whoops! sc_token = -terminal; } } else { // This should never happen. sc_token = -terminal; } return FScanner::TokenName(sc_token); } ZCC_TreeNode *ZCC_AST::InitNode(size_t size, EZCCTreeNodeType type, ZCC_TreeNode *basis) { ZCC_TreeNode *node = (ZCC_TreeNode *)SyntaxArena.Alloc(size); node->SiblingNext = node; node->SiblingPrev = node; node->NodeType = type; if (basis != NULL) { node->SourceName = basis->SourceName; node->SourceLump = basis->SourceLump; node->SourceLoc = basis->SourceLoc; } return node; } ZCC_TreeNode *ZCCParseState::InitNode(size_t size, EZCCTreeNodeType type) { ZCC_TreeNode *node = ZCC_AST::InitNode(size, type, NULL); node->SourceName = Strings.Alloc(sc->ScriptName); node->SourceLump = sc->LumpNum; return node; } // Appends a sibling to this node's sibling list. void AppendTreeNodeSibling(ZCC_TreeNode *thisnode, ZCC_TreeNode *sibling) { if (thisnode == nullptr) { // Some bad syntax can actually get here, so better abort so that the user can see the error which caused this. I_FatalError("Internal script compiler error. Execution aborted."); } if (sibling == nullptr) { return; } ZCC_TreeNode *&SiblingPrev = thisnode->SiblingPrev; ZCC_TreeNode *&SiblingNext = thisnode->SiblingNext; // Check integrity of our sibling list. assert(SiblingPrev->SiblingNext == thisnode); assert(SiblingNext->SiblingPrev == thisnode); // Check integrity of new sibling list. assert(sibling->SiblingPrev->SiblingNext == sibling); assert(sibling->SiblingNext->SiblingPrev == sibling); ZCC_TreeNode *siblingend = sibling->SiblingPrev; SiblingPrev->SiblingNext = sibling; sibling->SiblingPrev = SiblingPrev; SiblingPrev = siblingend; siblingend->SiblingNext = thisnode; } //**-------------------------------------------------------------------------- const char *GetMixinTypeString(EZCCMixinType type) { switch (type) { case ZCC_Mixin_Class: return "class"; default: assert(0 && "Unhandled mixin type"); return "invalid type"; } } //**-------------------------------------------------------------------------- ZCC_TreeNode *TreeNodeDeepCopy_Internal(ZCC_AST *ast, ZCC_TreeNode *orig, bool copySiblings, TMap *copiedNodesList); void TreeNodeDeepCopy_Base(ZCC_AST *ast, ZCC_TreeNode *orig, ZCC_TreeNode *copy, bool copySiblings, TMap *copiedNodesList) { copy->SourceName = orig->SourceName; copy->SourceLump = orig->SourceLump; copy->SourceLoc = orig->SourceLoc; copy->NodeType = orig->NodeType; if (copySiblings) { auto node = orig->SiblingNext; while (node != orig) { auto nextNode = TreeNodeDeepCopy_Internal(ast, node, false, copiedNodesList); auto newLast = nextNode->SiblingPrev; auto firstNode = copy; auto lastNode = firstNode->SiblingPrev; lastNode->SiblingNext = nextNode; firstNode->SiblingPrev = newLast; nextNode->SiblingPrev = lastNode; newLast->SiblingNext = firstNode; node = node->SiblingNext; } } } #define GetTreeNode(type) static_cast(ast->InitNode(sizeof(ZCC_##type), AST_##type, nullptr)); #define TreeNodeDeepCopy_Start(type) \ auto copy = GetTreeNode(type); \ auto origCasted = static_cast(orig); \ ret = copy; \ copiedNodesList->Insert(orig, ret); ZCC_TreeNode *TreeNodeDeepCopy(ZCC_AST *ast, ZCC_TreeNode *orig, bool copySiblings) { TMap copiedNodesList; copiedNodesList.Clear(); return TreeNodeDeepCopy_Internal(ast, orig, copySiblings, &copiedNodesList); } ZCC_TreeNode *TreeNodeDeepCopy_Internal(ZCC_AST *ast, ZCC_TreeNode *orig, bool copySiblings, TMap *copiedNodesList) { // [pbeta] This is a legitimate case as otherwise this function would need // an excessive amount of "if" statements, so just return null. if (orig == nullptr) { return nullptr; } // [pbeta] We need to keep and check a list of already copied nodes, because // some are supposed to be the same, and it can cause infinite loops. auto existingCopy = copiedNodesList->CheckKey(orig); if (existingCopy != nullptr) { return *existingCopy; } ZCC_TreeNode *ret = nullptr; switch (orig->NodeType) { case AST_Identifier: { TreeNodeDeepCopy_Start(Identifier); // ZCC_Identifier copy->Id = origCasted->Id; break; } case AST_Struct: { TreeNodeDeepCopy_Start(Struct); // ZCC_NamedNode copy->NodeName = origCasted->NodeName; copy->Symbol = origCasted->Symbol; // ZCC_Struct copy->Flags = origCasted->Flags; copy->Body = TreeNodeDeepCopy_Internal(ast, origCasted->Body, true, copiedNodesList); copy->Type = origCasted->Type; copy->Version = origCasted->Version; break; } case AST_Class: { TreeNodeDeepCopy_Start(Class); // ZCC_NamedNode copy->NodeName = origCasted->NodeName; copy->Symbol = origCasted->Symbol; // ZCC_Struct copy->Flags = origCasted->Flags; copy->Body = TreeNodeDeepCopy_Internal(ast, origCasted->Body, true, copiedNodesList); copy->Type = origCasted->Type; copy->Version = origCasted->Version; // ZCC_Class copy->ParentName = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ParentName, true, copiedNodesList)); copy->Replaces = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Replaces, true, copiedNodesList)); break; } case AST_Enum: { TreeNodeDeepCopy_Start(Enum); // ZCC_NamedNode copy->NodeName = origCasted->NodeName; copy->Symbol = origCasted->Symbol; // ZCC_Enum copy->EnumType = origCasted->EnumType; copy->Elements = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Elements, false, copiedNodesList)); break; } case AST_EnumTerminator: { TreeNodeDeepCopy_Start (EnumTerminator); break; } case AST_States: { TreeNodeDeepCopy_Start(States); // ZCC_States copy->Body = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Body, true, copiedNodesList)); copy->Flags = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Flags, true, copiedNodesList)); break; } case AST_StatePart: { TreeNodeDeepCopy_Start (StatePart); break; } case AST_StateLabel: { TreeNodeDeepCopy_Start(StateLabel); // ZCC_StateLabel copy->Label = origCasted->Label; break; } case AST_StateStop: { TreeNodeDeepCopy_Start (StateStop); break; } case AST_StateWait: { TreeNodeDeepCopy_Start (StateWait); break; } case AST_StateFail: { TreeNodeDeepCopy_Start (StateFail); break; } case AST_StateLoop: { TreeNodeDeepCopy_Start (StateLoop); break; } case AST_StateGoto: { TreeNodeDeepCopy_Start(StateGoto); // ZCC_StateGoto copy->Qualifier = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Qualifier, true, copiedNodesList)); copy->Label = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Label, true, copiedNodesList)); copy->Offset = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Offset, true, copiedNodesList)); break; } case AST_StateLine: { TreeNodeDeepCopy_Start(StateLine); // ZCC_StateLine copy->Sprite = origCasted->Sprite; copy->bBright = origCasted->bBright; copy->bFast = origCasted->bFast; copy->bSlow = origCasted->bSlow; copy->bNoDelay = origCasted->bNoDelay; copy->bCanRaise = origCasted->bCanRaise; copy->Frames = origCasted->Frames; copy->Duration = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Duration, true, copiedNodesList)); copy->Offset = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Offset, true, copiedNodesList)); copy->Lights = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Lights, true, copiedNodesList)); copy->Action = TreeNodeDeepCopy_Internal(ast, origCasted->Action, true, copiedNodesList); break; } case AST_VarName: { TreeNodeDeepCopy_Start(VarName); // ZCC_VarName copy->Name = origCasted->Name; copy->ArraySize = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ArraySize, true, copiedNodesList)); break; } case AST_VarInit: { TreeNodeDeepCopy_Start(VarInit); // ZCC_VarName copy->Name = origCasted->Name; copy->ArraySize = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ArraySize, true, copiedNodesList)); // ZCC_VarInit copy->Init = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Init, true, copiedNodesList)); copy->InitIsArray = origCasted->InitIsArray; break; } case AST_Type: { TreeNodeDeepCopy_Start(Type); // ZCC_Type copy->ArraySize = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ArraySize, true, copiedNodesList)); break; } case AST_BasicType: { TreeNodeDeepCopy_Start(BasicType); // ZCC_Type copy->ArraySize = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ArraySize, true, copiedNodesList)); // ZCC_BasicType copy->Type = origCasted->Type; copy->UserType = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->UserType, true, copiedNodesList)); copy->isconst = origCasted->isconst; break; } case AST_MapType: { TreeNodeDeepCopy_Start(MapType); // ZCC_Type copy->ArraySize = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ArraySize, true, copiedNodesList)); // ZCC_MapType copy->KeyType = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->KeyType, true, copiedNodesList)); copy->ValueType = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ValueType, true, copiedNodesList)); break; } case AST_DynArrayType: { TreeNodeDeepCopy_Start(DynArrayType); // ZCC_Type copy->ArraySize = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ArraySize, true, copiedNodesList)); // ZCC_DynArrayType copy->ElementType = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ElementType, true, copiedNodesList)); break; } case AST_ClassType: { TreeNodeDeepCopy_Start(ClassType); // ZCC_Type copy->ArraySize = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ArraySize, true, copiedNodesList)); // ZCC_ClassType copy->Restriction = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Restriction, true, copiedNodesList)); break; } case AST_Expression: { TreeNodeDeepCopy_Start(Expression); // ZCC_Expression copy->Operation = origCasted->Operation; copy->Type = origCasted->Type; break; } case AST_ExprID: { TreeNodeDeepCopy_Start(ExprID); // ZCC_Expression copy->Operation = origCasted->Operation; copy->Type = origCasted->Type; // ZCC_ExprID copy->Identifier = origCasted->Identifier; break; } case AST_ExprTypeRef: { TreeNodeDeepCopy_Start(ExprTypeRef); // ZCC_Expression copy->Operation = origCasted->Operation; copy->Type = origCasted->Type; // ZCC_ExprTypeRef copy->RefType = origCasted->RefType; break; } case AST_ExprConstant: { TreeNodeDeepCopy_Start(ExprConstant); // ZCC_Expression copy->Operation = origCasted->Operation; copy->Type = origCasted->Type; // ZCC_ExprConstant // Currently handled: StringVal, IntVal and DoubleVal. (UIntVal appears to be completely unused.) if (origCasted->Type == TypeString) { copy->StringVal = origCasted->StringVal; } else if (origCasted->Type == TypeFloat64 || origCasted->Type == TypeFloat32) { copy->DoubleVal = origCasted->DoubleVal; } else if (origCasted->Type == TypeName || origCasted->Type->isIntCompatible()) { copy->IntVal = origCasted->IntVal; } break; } case AST_ExprFuncCall: { TreeNodeDeepCopy_Start(ExprFuncCall); // ZCC_Expression copy->Operation = origCasted->Operation; copy->Type = origCasted->Type; // ZCC_ExprFuncCall copy->Function = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Function, true, copiedNodesList)); copy->Parameters = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Parameters, true, copiedNodesList)); break; } case AST_ExprMemberAccess: { TreeNodeDeepCopy_Start(ExprMemberAccess); // ZCC_Expression copy->Operation = origCasted->Operation; copy->Type = origCasted->Type; // ZCC_ExprMemberAccess copy->Left = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Left, true, copiedNodesList)); copy->Right = origCasted->Right; break; } case AST_ExprUnary: { TreeNodeDeepCopy_Start(ExprUnary); // ZCC_Expression copy->Operation = origCasted->Operation; copy->Type = origCasted->Type; // ZCC_ExprUnary copy->Operand = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Operand, true, copiedNodesList)); break; } case AST_ExprBinary: { TreeNodeDeepCopy_Start(ExprBinary); // ZCC_Expression copy->Operation = origCasted->Operation; copy->Type = origCasted->Type; // ZCC_ExprBinary copy->Left = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Left, true, copiedNodesList)); copy->Right = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Right, true, copiedNodesList)); break; } case AST_ExprTrinary: { TreeNodeDeepCopy_Start(ExprTrinary); // ZCC_Expression copy->Operation = origCasted->Operation; copy->Type = origCasted->Type; // ZCC_ExprTrinary copy->Test = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Test, true, copiedNodesList)); copy->Left = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Left, true, copiedNodesList)); copy->Right = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Right, true, copiedNodesList)); break; } case AST_FuncParm: { TreeNodeDeepCopy_Start(FuncParm); // ZCC_FuncParm copy->Value = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Value, true, copiedNodesList)); copy->Label = origCasted->Label; break; } case AST_Statement: { TreeNodeDeepCopy_Start (Statement); break; } case AST_CompoundStmt: { TreeNodeDeepCopy_Start(CompoundStmt); // ZCC_CompoundStmt copy->Content = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Content, true, copiedNodesList)); break; } case AST_ContinueStmt: { TreeNodeDeepCopy_Start (ContinueStmt); break; } case AST_BreakStmt: { TreeNodeDeepCopy_Start (BreakStmt); break; } case AST_ReturnStmt: { TreeNodeDeepCopy_Start(ReturnStmt); // ZCC_ReturnStmt copy->Values = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Values, true, copiedNodesList)); break; } case AST_ExpressionStmt: { TreeNodeDeepCopy_Start(ExpressionStmt); // ZCC_ExpressionStmt copy->Expression = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Expression, true, copiedNodesList)); break; } case AST_IterationStmt: { TreeNodeDeepCopy_Start(IterationStmt); // ZCC_IterationStmt copy->LoopCondition = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->LoopCondition, true, copiedNodesList)); copy->LoopStatement = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->LoopStatement, true, copiedNodesList)); copy->LoopBumper = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->LoopBumper, true, copiedNodesList)); copy->CheckAt = origCasted->CheckAt; break; } case AST_ArrayIterationStmt: { TreeNodeDeepCopy_Start(ArrayIterationStmt); // ZCC_IterationStmt copy->ItName = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ItName, true, copiedNodesList)); copy->LoopStatement = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->LoopStatement, true, copiedNodesList)); copy->ItArray = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ItArray, true, copiedNodesList)); break; } case AST_IfStmt: { TreeNodeDeepCopy_Start(IfStmt); // ZCC_IfStmt copy->Condition = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Condition, true, copiedNodesList)); copy->TruePath = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->TruePath, true, copiedNodesList)); copy->FalsePath = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->FalsePath, true, copiedNodesList)); break; } case AST_SwitchStmt: { TreeNodeDeepCopy_Start(SwitchStmt); // ZCC_SwitchStmt copy->Condition = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Condition, true, copiedNodesList)); copy->Content = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Content, true, copiedNodesList)); break; } case AST_CaseStmt: { TreeNodeDeepCopy_Start(CaseStmt); // ZCC_CaseStmt copy->Condition = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Condition, true, copiedNodesList)); break; } case AST_AssignStmt: { TreeNodeDeepCopy_Start(AssignStmt); // ZCC_AssignStmt copy->Dests = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Dests, true, copiedNodesList)); copy->Sources = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Sources, true, copiedNodesList)); copy->AssignOp = origCasted->AssignOp; break; } case AST_LocalVarStmt: { TreeNodeDeepCopy_Start(LocalVarStmt); // ZCC_LocalVarStmt copy->Type = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Type, true, copiedNodesList)); copy->Vars = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Vars, true, copiedNodesList)); break; } case AST_FuncParamDecl: { TreeNodeDeepCopy_Start(FuncParamDecl); // ZCC_FuncParamDecl copy->Type = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Type, true, copiedNodesList)); copy->Default = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Default, true, copiedNodesList)); copy->Name = origCasted->Name; copy->Flags = origCasted->Flags; break; } case AST_ConstantDef: { TreeNodeDeepCopy_Start(ConstantDef); // ZCC_NamedNode copy->NodeName = origCasted->NodeName; copy->Symbol = origCasted->Symbol; // ZCC_ConstantDef copy->Value = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Value, true, copiedNodesList)); copy->Symbol = origCasted->Symbol; if (copy->Symbol != nullptr) { copy->Type = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Type, true, copiedNodesList)); } break; } case AST_Declarator: { TreeNodeDeepCopy_Start(Declarator); // ZCC_Declarator copy->Type = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Type, true, copiedNodesList)); copy->Flags = origCasted->Flags; copy->Version = origCasted->Version; break; } case AST_VarDeclarator: { TreeNodeDeepCopy_Start(VarDeclarator); // ZCC_Declarator copy->Type = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Type, true, copiedNodesList)); copy->Flags = origCasted->Flags; copy->Version = origCasted->Version; // ZCC_VarDeclarator copy->Names = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Names, true, copiedNodesList)); copy->DeprecationMessage = origCasted->DeprecationMessage; break; } case AST_FuncDeclarator: { TreeNodeDeepCopy_Start(FuncDeclarator); // ZCC_Declarator copy->Type = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Type, true, copiedNodesList)); copy->Flags = origCasted->Flags; copy->Version = origCasted->Version; // ZCC_FuncDeclarator copy->Params = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Params, true, copiedNodesList)); copy->Name = origCasted->Name; copy->Body = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Body, true, copiedNodesList)); copy->UseFlags = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->UseFlags, true, copiedNodesList)); copy->DeprecationMessage = origCasted->DeprecationMessage; break; } case AST_Default: { TreeNodeDeepCopy_Start(Default); // ZCC_CompoundStmt copy->Content = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Content, true, copiedNodesList)); break; } case AST_FlagStmt: { TreeNodeDeepCopy_Start(FlagStmt); // ZCC_FlagStmt copy->name = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->name, true, copiedNodesList)); copy->set = origCasted->set; break; } case AST_PropertyStmt: { TreeNodeDeepCopy_Start(PropertyStmt); // ZCC_PropertyStmt copy->Prop = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Prop, true, copiedNodesList)); copy->Values = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Values, true, copiedNodesList)); break; } case AST_VectorValue: { TreeNodeDeepCopy_Start(VectorValue); // ZCC_Expression copy->Operation = origCasted->Operation; copy->Type = origCasted->Type; // ZCC_VectorValue copy->X = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->X, true, copiedNodesList)); copy->Y = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Y, true, copiedNodesList)); copy->Z = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Z, true, copiedNodesList)); copy->W = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->W, true, copiedNodesList)); break; } case AST_DeclFlags: { TreeNodeDeepCopy_Start(DeclFlags); // ZCC_DeclFlags copy->Id = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Id, true, copiedNodesList)); copy->DeprecationMessage = origCasted->DeprecationMessage; copy->Version = origCasted->Version; copy->Flags = origCasted->Flags; break; } case AST_ClassCast: { TreeNodeDeepCopy_Start(ClassCast); // ZCC_Expression copy->Operation = origCasted->Operation; copy->Type = origCasted->Type; // ZCC_ClassCast copy->ClassName = origCasted->ClassName; copy->Parameters = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Parameters, true, copiedNodesList)); break; } case AST_StaticArrayStatement: { TreeNodeDeepCopy_Start(StaticArrayStatement); // ZCC_StaticArrayStatement copy->Type = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Type, true, copiedNodesList)); copy->Id = origCasted->Id; copy->Values = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->Values, true, copiedNodesList)); break; } case AST_Property: { TreeNodeDeepCopy_Start(Property); // ZCC_NamedNode copy->NodeName = origCasted->NodeName; copy->Symbol = origCasted->Symbol; // ZCC_Property copy->Body = TreeNodeDeepCopy_Internal(ast, origCasted->Body, true, copiedNodesList); break; } case AST_FlagDef: { TreeNodeDeepCopy_Start(FlagDef); // ZCC_NamedNode copy->NodeName = origCasted->NodeName; copy->Symbol = origCasted->Symbol; // ZCC_FlagDef copy->RefName = origCasted->RefName; copy->BitValue = origCasted->BitValue; break; } case AST_MixinDef: { TreeNodeDeepCopy_Start(MixinDef); // ZCC_NamedNode copy->NodeName = origCasted->NodeName; copy->Symbol = origCasted->Symbol; // ZCC_MixinDef copy->Body = TreeNodeDeepCopy_Internal(ast, origCasted->Body, true, copiedNodesList); copy->MixinType = origCasted->MixinType; break; } case AST_MixinStmt: { TreeNodeDeepCopy_Start(MixinStmt); // ZCC_MixinStmt copy->MixinName = origCasted->MixinName; break; } default: assert(0 && "Unimplemented node type"); break; } TreeNodeDeepCopy_Base(ast, orig, ret, copySiblings, copiedNodesList); return ret; }