From 1257bb5bccf0e98bd61b4d4c496a539c233845c9 Mon Sep 17 00:00:00 2001 From: Leonardo Silva <99574879+Leonard-The-Wise@users.noreply.github.com> Date: Tue, 25 Jul 2023 17:23:32 -0300 Subject: [PATCH 1/6] Starting up environment for replacing old NscLib compiler to the new native Beamdog's official open-source compiler. --- NWScript-Npp/NWScript-Npp.vcxproj | 16 + NWScript-Npp/NWScript-Npp.vcxproj.filters | 47 + src/NWScriptCompilerV2.cpp | 460 ++ src/NWScriptCompilerV2.h | 179 + src/Native Compiler/exobase.h | 591 ++ src/Native Compiler/exostring.cpp | 1575 +++++ src/Native Compiler/exotypes.h | 93 + src/Native Compiler/gpl-3.0.txt | 674 ++ src/Native Compiler/scriptcomp.h | 644 ++ src/Native Compiler/scriptcompcore.cpp | 1910 +++++ src/Native Compiler/scriptcompfinalcode.cpp | 7059 +++++++++++++++++++ src/Native Compiler/scriptcompidentspec.cpp | 907 +++ src/Native Compiler/scriptcomplexical.cpp | 1774 +++++ src/Native Compiler/scriptcompparsetree.cpp | 4609 ++++++++++++ src/Native Compiler/scripterrors.h | 175 + src/Native Compiler/scriptinternal.h | 772 ++ src/Native Compiler/xxhash.c | 1030 +++ src/Native Compiler/xxhash.h | 328 + 18 files changed, 22843 insertions(+) create mode 100644 src/NWScriptCompilerV2.cpp create mode 100644 src/NWScriptCompilerV2.h create mode 100644 src/Native Compiler/exobase.h create mode 100644 src/Native Compiler/exostring.cpp create mode 100644 src/Native Compiler/exotypes.h create mode 100644 src/Native Compiler/gpl-3.0.txt create mode 100644 src/Native Compiler/scriptcomp.h create mode 100644 src/Native Compiler/scriptcompcore.cpp create mode 100644 src/Native Compiler/scriptcompfinalcode.cpp create mode 100644 src/Native Compiler/scriptcompidentspec.cpp create mode 100644 src/Native Compiler/scriptcomplexical.cpp create mode 100644 src/Native Compiler/scriptcompparsetree.cpp create mode 100644 src/Native Compiler/scripterrors.h create mode 100644 src/Native Compiler/scriptinternal.h create mode 100644 src/Native Compiler/xxhash.c create mode 100644 src/Native Compiler/xxhash.h diff --git a/NWScript-Npp/NWScript-Npp.vcxproj b/NWScript-Npp/NWScript-Npp.vcxproj index fbc11af..49f8507 100644 --- a/NWScript-Npp/NWScript-Npp.vcxproj +++ b/NWScript-Npp/NWScript-Npp.vcxproj @@ -246,6 +246,12 @@ + + + + + + @@ -253,6 +259,7 @@ + @@ -328,9 +335,17 @@ + + + + + + + + @@ -435,6 +450,7 @@ + diff --git a/NWScript-Npp/NWScript-Npp.vcxproj.filters b/NWScript-Npp/NWScript-Npp.vcxproj.filters index ace6252..4737c52 100644 --- a/NWScript-Npp/NWScript-Npp.vcxproj.filters +++ b/NWScript-Npp/NWScript-Npp.vcxproj.filters @@ -38,6 +38,9 @@ {1d74ff3e-534a-469a-a3f1-7c3af1bf8e5e} + + {7df729ef-c3bd-47b9-939f-b92d5bb8c0cf} + @@ -211,6 +214,25 @@ DarkMode + + Native Compiler + + + Native Compiler + + + Native Compiler + + + Native Compiler + + + Native Compiler + + + Native Compiler + + @@ -305,6 +327,28 @@ DarkMode + + Native Compiler + + + Native Compiler + + + Native Compiler + + + Native Compiler + + + Native Compiler + + + Native Compiler + + + Native Compiler + + @@ -447,5 +491,8 @@ Custom Lexers\Lexilla + + Native Compiler + \ No newline at end of file diff --git a/src/NWScriptCompilerV2.cpp b/src/NWScriptCompilerV2.cpp new file mode 100644 index 0000000..0bb0a6b --- /dev/null +++ b/src/NWScriptCompilerV2.cpp @@ -0,0 +1,460 @@ +/** @file NWScriptCompiler.cpp + * Invokes various functions from NscLib compiler/interpreter library. + * + **/ + // Copyright (C) 2022 - Leonardo Silva + // The License.txt file describes the conditions under which this software may be distributed. + + +#include "pch.h" +//#include +//#include "jpcre2.h" + +#include "Utf8_16.h" +#include "NWScriptCompilerV2.h" +#include "VersionInfoEx.h" + +using namespace NWScriptPlugin; +typedef NWScriptLogger::LogType LogType; + +#define DEPENDENCYHEADER " \ +/*************************************************************************************** \r\n\ + * Dependency files descriptor for \"%DEPENDENCYFILE%\"\r\n\ + * Generated by NWScript Tools for Notepad++ (%VERSION%)\r\n\ + *\r\n\ + * Generation date: %GENERATIONDATE%\r\n\ + ***************************************************************************************/\r\n\ +\r\n\ +" + +#define SCRIPTERRORPREFIX "Error" +#define FORMATDISASMREGEX R"(.+)" +#define DEPENDENCYPARSEREGEX R"(([^\/]+)\/([^\\\n]+))" + +typedef jpcre2::select pcre2; +static pcre2::Regex assemblyLine(FORMATDISASMREGEX, PCRE2_MULTILINE, jpcre2::JIT_COMPILE); +static pcre2::Regex dependencyParse(DEPENDENCYPARSEREGEX, 0, jpcre2::JIT_COMPILE); + +bool NWScriptCompilerV2::initialize() { + + // Critical path, initialize resources + try + { + _resourceManager = std::make_unique(&_logger); + } + catch (std::runtime_error& e) + { + _logger.log("Failed to initialize the resources manager: " + std::string(e.what()), LogType::Critical, "NSC2001"); + return false; + } + + NWNHome = getNwnHomePath(_settings->compileVersion); + + return true; +} + +bool NWScriptCompilerV2::loadScriptResources() +{ + ResourceManager::ModuleLoadParams LoadParams; + ResourceManager::StringVec KeyFiles; + + ZeroMemory(&LoadParams, sizeof(LoadParams)); + + LoadParams.SearchOrder = ResourceManager::ModSearch_PrefDirectory; + LoadParams.ResManFlags = ResourceManager::ResManFlagNoGranny2; + LoadParams.ResManFlags |= ResourceManager::ResManFlagErf16; + + if (_settings->compileVersion == 174) + { +#ifdef _WINDOWS + KeyFiles.push_back("data\\nwn_base"); +#else + KeyFiles.emplace_back("data/nwn_base"); +#endif // _WINDOWS + } + else + { + KeyFiles.emplace_back("xp3"); + KeyFiles.emplace_back("xp2patch"); + KeyFiles.emplace_back("xp2"); + KeyFiles.emplace_back("xp1"); + KeyFiles.emplace_back("chitin"); + } + + LoadParams.KeyFiles = &KeyFiles; + LoadParams.ResManFlags |= ResourceManager::ResManFlagBaseResourcesOnly; + + // Legacy code is using ASCII string names. We convert here. Also, many exceptions thrown inside those classes to deal with. + std::string InstallDir = _settings->getChosenInstallDir() + "\\"; + try { + _resourceManager->LoadScriptResources(wstr2str(NWNHome), InstallDir, &LoadParams); + } + catch(...) { + //_resourceManager is writting to the log messages here, so we just return false. + return false; + } + + return true; +} + +void NWScriptCompilerV2::processFile(bool fromMemory, char* fileContents) +{ + NWN::ResType fileResType; + NWN::ResRef32 fileResRef; + std::string inFileContents; + + // First check: safeguard from trying to recompile nwscript.nss + if (_stricmp(_sourcePath.filename().string().c_str(), "nwscript.nss") == 0 && _compilerMode == 0) + { + _logger.log("Compiling script: " + _sourcePath.string(), LogType::ConsoleMessage); + _logger.log("Error: you can't explicitly compile any script named \"nwscript.nss\", this name is reserved for the main engine.", LogType::Critical, "NSC2010"); + _logger.log("File ignored: " + _sourcePath.string() , LogType::Info); + notifyCaller(false); + return; + } + + // Initialize the compiler if not already + if (!isInitialized()) + { + _logger.log("Initializing compiler...", LogType::ConsoleMessage); + _logger.log("", LogType::ConsoleMessage); + + if (!initialize()) + { + notifyCaller(false); + return; + } + + // Start building up search paths. + _includePaths.push_back(wstr2str(_sourcePath.parent_path())); + + if (!_settings->ignoreInstallPaths) + { + if (!loadScriptResources()) + { + _logger.log("Could not load script resources on installation path: " + _settings->getChosenInstallDir(), LogType::Warning); + } + + if (_settings->compileVersion == 174) + { + std::string overrideDir = _settings->getChosenInstallDir() + "\\ovr\\"; + _includePaths.push_back(overrideDir); + } + } + + for (generic_string s : _settings->getIncludeDirsV()) + { + _includePaths.push_back(properDirNameA(wstr2str(s)) + "\\"); + } + + // Create our compiler/disassembler + _compiler = std::make_unique(*_resourceManager, _settings->useNonBiowareExtenstions); + _compiler->NscSetLogger(&_logger); + _compiler->NscSetIncludePaths(_includePaths); + _compiler->NscSetCompilerErrorPrefix(SCRIPTERRORPREFIX); + _compiler->NscSetResourceCacheEnabled(true); + } + + // Acquire information about NWN Resource Type of the file. Warning of ignored result is incorrect. +#pragma warning (push) +#pragma warning (disable : 6031) + fileResType = _resourceManager->ExtToResType(wstr2str(_sourcePath).c_str()); + fileResRef = _resourceManager->ResRef32FromStr(wstr2str(_sourcePath.stem()).c_str()); +#pragma warning (pop) + + // Load file from disk if not from memory + if (fromMemory) + inFileContents = fileContents; + else + { + if (!fileToBuffer(_sourcePath.c_str(), inFileContents)) + { + _logger.log("Could not load the specified file: " + wstr2str(_sourcePath), LogType::Critical, "NSC2002"); + notifyCaller(false); + return; + } + } + + // Determines file encoding. Only a minimal sample is used here since + // we are not interested in capturing UTF-8 multibyte-like strings, only UTF-16 types. + constexpr const int blockSize = IS_TEXT_UNICODE_STATISTICS; + Utf8_16_Read utfConverter; + int encoding = utfConverter.determineEncoding((unsigned char*)inFileContents.c_str(), (blockSize > inFileContents.size()) ? inFileContents.size() : blockSize); + if (encoding == uni16BE || encoding == uni16LE || encoding == uni16BE_NoBOM || encoding == uni16LE_NoBOM) + { + std::ignore = utfConverter.convert(inFileContents.data(), inFileContents.size()); + inFileContents.assign(utfConverter.getNewBuf(), utfConverter.getNewSize()); + } + + // Execute the process + bool bSuccess = false; + if (_compilerMode == 0) + { + if (_fetchPreprocessorOnly) + _logger.log("Fetching preprocessor output for: " + _sourcePath.string(), LogType::ConsoleMessage); + else + _logger.log("Compiling script: " + _sourcePath.string(), LogType::ConsoleMessage); + bSuccess = compileScript(inFileContents, fileResType, fileResRef); + } + else + { + _logger.log("Disassembling binary: " + _sourcePath.string(), LogType::ConsoleMessage); + bSuccess = disassembleBinary(inFileContents, fileResType, fileResRef); + } + + notifyCaller(bSuccess); +} + + +bool NWScriptCompilerV2::compileScript(std::string& fileContents, + const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef) +{ + // We always ignore include files. And for our project, the compiler ALWAYS + // return include dependencies, since message filters are done in a higher application layer. + bool bIgnoreIncludes = true; + bool bOptimize = _settings->optimizeScript; + UINT32 compilerFlags = _settings->compilerFlags; + compilerFlags |= NscCompilerFlag_ShowIncludes; + + // Disable processing overhead for preprocessor messages.. + // Also, since warnings are the type of return, we don't want to suppress them here, no matter what. + if (_fetchPreprocessorOnly) + { + compilerFlags &= ~NscCompilerFlag_GenerateMakeDeps; + bOptimize = false; + compilerFlags &= ~NscCompilerFlag_SuppressWarnings; + compilerFlags |= NscCompilerFlag_ShowPreprocessed; + } + + // Here we are solely worried about creating a human-readable dependencies view + if (_makeDependencyView) + { + compilerFlags |= NscCompilerFlag_GenerateMakeDeps; + compilerFlags |= NscCompilerFlag_SuppressWarnings; + bOptimize = false; + } + + // HACK: Need to know if this will ever be used on this project (we already have a disassembly option, this one generates PCODE while compiling also). + //compilerFlags |= NscCompilerFlag_DumpPCode; + + // Main compilation step + std::string dataRef; // Buffer to file is generic and requires a std::string + swutil::ByteVec generatedCode; + swutil::ByteVec debugSymbols; + std::set fileDependencies; + + NscResult result = _compiler->NscCompileScript(fileResRef, fileContents.c_str(), fileContents.size(), _settings->compileVersion, + bOptimize, bIgnoreIncludes, &_logger, compilerFlags, generatedCode, debugSymbols, fileDependencies, _settings->generateSymbols); + + switch (result) + { + case NscResult_Failure: + { + _logger.log("", LogType::ConsoleMessage); + _logger.log("Compilation aborted with errors.", LogType::ConsoleMessage); + _logger.log("", LogType::ConsoleMessage); + return false; + } + + case NscResult_Include: + { + _logger.log(_sourcePath.filename().string() + " is an include file, ignored.", LogType::ConsoleMessage); + return true; + } + + case NscResult_Success: + break; + + default: + _logger.log("", LogType::ConsoleMessage); + _logger.log("Unknown status code", LogType::Critical, "NSC2004"); + _logger.log("", LogType::ConsoleMessage); + return false; + } + + // If we are only to fetch preprocessor code, we're done here (since the _logger takes care of that inside the Compile function) + if (_fetchPreprocessorOnly) + return true; + + // If we are to create human-readable dependencies, return that + if (_makeDependencyView) + return MakeDependenciesView(fileDependencies); + + // Now save code data + generic_string outputPath = str2wstr(_destDir.string() + "\\" + _sourcePath.stem().string() + compiledScriptSuffix); + dataRef.assign(reinterpret_cast(&generatedCode[0]), generatedCode.size()); + if (!bufferToFile(outputPath, dataRef)) + { + _logger.log("", LogType::ConsoleMessage); + _logger.log(TEXT("Could not write compiled output file: ") + outputPath, LogType::Critical, TEXT("NSC2005")); + _logger.log("", LogType::ConsoleMessage); + return false; + } + + // Save debug symbols if apply + if (_settings->generateSymbols) + { + dataRef.clear(); + outputPath = str2wstr(_destDir.string() + "\\" + _sourcePath.stem().string() + debugSymbolsFileSuffix); + dataRef.assign(reinterpret_cast(&debugSymbols[0]), debugSymbols.size()); + if (!bufferToFile(outputPath, dataRef)) + { + _logger.log("", LogType::ConsoleMessage); + _logger.log(TEXT("Could not write generated symbols output file: ") + outputPath, LogType::Critical, TEXT("NSC2006")); + _logger.log("", LogType::ConsoleMessage); + return false; + } + } + + // And file dependencies if apply + if (_settings->compilerFlags & NscCompilerFlag_GenerateMakeDeps) + return MakeDependenciesFile(fileDependencies); + + return true; +} + +bool NWScriptCompilerV2::disassembleBinary(std::string& fileContents, + const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef) +{ + std::string generatedCode; + + // Main disassemble step. + _compiler->NscDisassembleScript(fileContents.c_str(), fileContents.size(), generatedCode); + + // This is the way the library returns errors to us on that routine... :D + if (generatedCode == "DISASSEMBLY ERROR: COMPILER INITIALIZATION FAILED!") + { + _logger.log("", LogType::ConsoleMessage); + _logger.log("Disassembler - Compiler Initialization failed!", LogType::Critical, "NSC2007"); + _logger.log("", LogType::ConsoleMessage); + return false; + } + + // Save file, but first, we remove extra carriage returns the library is generating... + generic_string outputPath = str2wstr(_destDir.string() + "\\" + _sourcePath.stem().string() + disassembledScriptSuffix); + + std::stringstream formatedCode; + pcre2::VecNum matches; + pcre2::RegexMatch fileMatcher(&assemblyLine); + size_t lineCount = fileMatcher.setSubject(generatedCode).setModifier("gm").setNumberedSubstringVector(&matches).match(); + + for (size_t i = 0; i < lineCount; i++) + formatedCode << matches[i][0]; + + if (!bufferToFile(outputPath, formatedCode.str())) + { + _logger.log("", LogType::ConsoleMessage); + _logger.log(TEXT("Could not write disassembled output file: ") + outputPath, LogType::Critical, TEXT("NSC2008")); + _logger.log("", LogType::ConsoleMessage); + return false; + } + + return true; +} + +bool NWScriptCompilerV2::MakeDependenciesView(const std::set& dependencies) +{ + // Generate some timestamp headers + char timestamp[128]; time_t currTime; struct tm currTimeP; + time(&currTime); + errno_t error = localtime_s(&currTimeP, &currTime); + strftime(timestamp, 64, "%B %d, %Y - %R", &currTimeP); + + // Get version from module's binary file + VersionInfoEx versionInfo = VersionInfoEx::getLocalVersion(); + std::stringstream sVersion = {}; + sVersion << "version " << versionInfo.shortString().c_str() << " - build " << versionInfo.build(); + + std::map variablesMap; + + variablesMap.insert({ "%DEPENDENCYFILE%", _sourcePath.filename().string() }); + variablesMap.insert({ "%VERSION%", sVersion.str() }); + variablesMap.insert({ "%GENERATIONDATE%", timestamp }); + + // Input header information + std::stringstream sdependencies; + sdependencies << replaceStringsA(DEPENDENCYHEADER, variablesMap); + + // Main dependencies + sdependencies << " 1) Main file relation (compiled script -> script)" << "\r\n\r\n"; + sdependencies << " Source Directory: " + _sourcePath.parent_path().string() << "\r\n"; + sdependencies << " Destination Directory: " + _destDir.string() << "\r\n"; + sdependencies << " " + _sourcePath.stem().string() << compiledScriptSuffix << + " <- is generated from -> " << _sourcePath.stem().string() << textScriptSuffix << "\r\n\r\n"; + + // Additional dependencies + if (!dependencies.empty()) + { + sdependencies << " 2) Dependencies of script source: " << _sourcePath.stem().string() << textScriptSuffix << "\r\n\r\n"; + + pcre2::VecNum matches; + pcre2::RegexMatch dependencyParser(&dependencyParse); + dependencyParser.setNumberedSubstringVector(&matches); + + // Get first path in dependencies for comparisons. + auto it = dependencies.begin(); + int count = dependencyParser.setSubject(*it).match(); + fs::path currentPath = matches[0][1]; + fs::path comparePath; + + // For each different path, we write the topic information of that folder and then enumerate the files + int topicNumber = 1; + bool bTopicWritten = false; + for (auto& dependency : dependencies) + { + count = dependencyParser.setSubject(dependency).match(); + comparePath = matches[0][1]; // first capture group = directory name. + if (currentPath != comparePath) + { + sdependencies << "\r\n"; + currentPath = comparePath; + bTopicWritten = false; + topicNumber++; + } + + if (!bTopicWritten) + { + sdependencies << " 2." << topicNumber << ") Dependencies from: " << currentPath.string() << "\r\n\r\n"; + bTopicWritten = true; + } + + sdependencies << " -> " << matches[0][2] << "\r\n"; // Second capture group = filename + } + + sdependencies << "\r\n\r\n"; + sdependencies << "------------------[ END OF FILE DEPENDENCIES ]------------------" << "\r\n\r\n"; + + _logger.setProcessorString(sdependencies.str()); + } + + return true; +} + +bool NWScriptCompilerV2::MakeDependenciesFile(const std::set& dependencies) +{ + + // Additional dependencies + if (!dependencies.empty()) + { + std::stringstream sdependencies; + + sdependencies << _sourcePath.stem() << compiledScriptSuffix << ": " << _sourcePath.stem() << textScriptSuffix; + + for (auto& dep : dependencies) + sdependencies << " \\\n " << dep.c_str(); + + for (auto& dep : dependencies) + sdependencies << "\n" << dep.c_str() << "\n"; + + generic_string outputPath = str2wstr(_destDir.string() + "\\" + _sourcePath.stem().string() + dependencyFileSuffix); + if (!bufferToFile(outputPath, sdependencies.str())) + { + _logger.log("", LogType::ConsoleMessage); + _logger.log(TEXT("Could not write dependency file: ") + outputPath, LogType::Critical, TEXT("NSC2009")); + _logger.log("", LogType::ConsoleMessage); + return false; + } + } + + return true; +} \ No newline at end of file diff --git a/src/NWScriptCompilerV2.h b/src/NWScriptCompilerV2.h new file mode 100644 index 0000000..515f95e --- /dev/null +++ b/src/NWScriptCompilerV2.h @@ -0,0 +1,179 @@ +/** @file NWScriptCompiler.cpp + * Invokes various functions from NscLib compiler/interpreter library. + * + **/ + // Copyright (C) 2022 - Leonardo Silva + // The License.txt file describes the conditions under which this software may be distributed. + +#pragma once + +#include +#include + +#include "Native Compiler/scriptcomp.h" // New oficial compiler provided by Beamdog itself. +#include "Nsc.h" // Here we are using NscLib only for the game's resource manager +#include "Common.h" + +#include "Settings.h" +#include "NWScriptLogger.h" + +namespace NWScriptPlugin +{ + class NWScriptCompilerV2 final + { + public: + + NWScriptCompilerV2() : + _resourceManager(nullptr), _settings(nullptr), _compiler(nullptr) {} + + bool isInitialized() { + return _resourceManager != nullptr; + } + + // Append user settings + void appendSettings(Settings* settings) { + _settings = settings; + } + + // Initialize resource manager + bool initialize(); + + // Reset compiler state + void reset() { + _resourceManager = nullptr; + _compiler = nullptr; + _includePaths.clear(); + _fetchPreprocessorOnly = false; + _makeDependencyView = false; + _sourcePath = ""; + _destDir = ""; + setMode(0); + _processingEndCallback = nullptr; + clearLog(); + } + + // Sets destination to a VALID and existing directory (or else get an error) + void setDestinationDirectory(fs::path dest) { + if (!isValidDirectory(str2wstr(dest.string()).c_str())) + throw; + _destDir = dest; + } + + // Sets source path to a VALID and existing file path (or else get an error) + void setSourceFilePath(fs::path source) { + if (!PathFileExists(source.c_str())) + throw; + _sourcePath = source; + } + + // Returns the current set Destination Directory + fs::path getDestinationDirectory() { + return _destDir; + } + + // Returns the current set Source File path + fs::path getSourceFilePath() { + return _sourcePath; + } + + // Set function callback for calling after finishing processing file + void setProcessingEndCallback(void (*processingEndCallback)(HRESULT returnCode)) + { + _processingEndCallback = processingEndCallback; + } + + // Sets function callback for receiving logger messages + void setLoggerMessageCallback(void (*MessageCallback)(const NWScriptLogger::CompilerMessage&)) { + _logger.setMessageCallback(MessageCallback); + } + + // Only write dependencies view to the logger + void setViewDependencies() { + setMode(0); + _makeDependencyView = true; + } + + // Fetchs only preprocessor's output + void setFetchPreprocessorOnly() { + setMode(0); + _fetchPreprocessorOnly = true; + } + + // Clears the log + void clearLog() { + _logger.clear(); + } + + // Sets compiler mode: 0 = compile, 1 = disassemble + void setMode(int compilerMode) { + if (compilerMode < 0 || compilerMode > 1) + throw; + _compilerMode = compilerMode; + _fetchPreprocessorOnly = false; + _makeDependencyView = false; + } + + int getMode() const { + return _compilerMode; + } + + bool isViewDependencies() const { + return _makeDependencyView; + } + + bool isFetchPreprocessorOnly() const { + return _fetchPreprocessorOnly; + } + + NWScriptLogger& logger() { + return _logger; + } + + // Returns if an output path is required for operation + bool isOutputDirRequired() { + return !(_fetchPreprocessorOnly || _makeDependencyView); + } + + + void processFile(bool fromMemory, char* fileContents); + + private: + std::unique_ptr _resourceManager; + std::unique_ptr _compiler; + + bool _fetchPreprocessorOnly = false; + bool _makeDependencyView = false; + int _compilerMode = 0; + void (*_processingEndCallback)(HRESULT returnCode) = nullptr; + + generic_string NWNHome; + std::vector _includePaths; + fs::path _sourcePath; + fs::path _destDir; + + Settings* _settings; + + NWScriptLogger _logger; + + // Notify Caller of processing results + void notifyCaller(bool success) { + if (_processingEndCallback) + _processingEndCallback(static_cast((int)success)); + } + + // Load Base script resources + bool loadScriptResources(); + + // Compile a plain text script into binary format + bool compileScript(std::string& fileContents, + const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef); + + // Disassemble a binary file into a pcode assembly text format + bool disassembleBinary(std::string& fileContents, + const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef); + + // Dependencies files and views + bool MakeDependenciesView(const std::set& dependencies); + bool MakeDependenciesFile(const std::set& dependencies); + }; +} \ No newline at end of file diff --git a/src/Native Compiler/exobase.h b/src/Native Compiler/exobase.h new file mode 100644 index 0000000..ab17bc2 --- /dev/null +++ b/src/Native Compiler/exobase.h @@ -0,0 +1,591 @@ +// +// SPDX-License-Identifier: GPL-3.0 +// +// This file is part of the NWScript compiler open source release. +// +// The initial source release is licensed under GPL-3.0. +// +// All subsequent changes you submit are required to be licensed under MIT. +// +// However, the project overall will still be GPL-3.0. +// +// The intent is for the base game to be able to pick up changes you explicitly +// submit for inclusion painlessly, while ensuring the overall project source code +// remains available for everyone. +// + +#pragma once + +// This file is a reduced variant taken from the base game. +// Do not port changes here back to the game unless needed for script compiler functionality. + +/////////////////////////////////////////////////////////////////////////////// +// BIOWARE CORP. CONFIDENTIAL INFORMATION. // +// COPYRIGHT BIOWARE CORP. ALL RIGHTS RESERVED // +/////////////////////////////////////////////////////////////////////////////// + +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: ExoBase +//:: +//:: Copyright (c) 1999, BioWare Corp. +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: ExoBase.h +//:: +//:: Header for machine-specific base class stuff. +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Created By: Mark Brockington +//:: Created On: 04/26/99 +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: THIS FILE CONTAINS PORTED CODE +//:://///////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exotypes.h" + +#define EXOASSERTNC() assert(false) +#define EXOASSERTNCSTR(string) assert(!string) +#define EXOASSERT(cond) assert(cond) +#define EXOASSERTSTR(cond, string) assert(cond && string) + +/////////////////////////////////////////////////////////////////////////////// +// class CExoString +/////////////////////////////////////////////////////////////////////////////// +// Created by: Don Yakielashek +// Date: 04/26/99 +// +// Desc: C++ string storage and manipulation class. +// CExoString contains a flag whether the string is single or double byte. +/////////////////////////////////////////////////////////////////////////////// + +class CExoString +{ + + // ************************************************************************* +public: + // ************************************************************************* + + static const char* Whitespace; + static const char* Letters; + static const char* Numbers; + static const char* Alphanumeric; + + /////////////////////////////////////////////////////////////////////////// + CExoString(); + //------------------------------------------------------------------------- + // Desc: Creates an empty CExoString. + /////////////////////////////////////////////////////////////////////////// + + CExoString(CExoString&& other) + { + m_sString = other.m_sString; + m_nBufferLength = other.m_nBufferLength; + other.m_sString = nullptr; + other.m_nBufferLength = 0; + } + CExoString& operator=(CExoString&& other) + { + if (this == &other) return *this; + if (m_sString) + { + delete[] m_sString; + } + m_sString = other.m_sString; + m_nBufferLength = other.m_nBufferLength; + other.m_sString = nullptr; + other.m_nBufferLength = 0; + return *this; + } + + /////////////////////////////////////////////////////////////////////////// + CExoString(const char *source); + //------------------------------------------------------------------------- + // Desc: Creates a CExsoString from a null terminated char array. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + CExoString(const CExoString &source); + //------------------------------------------------------------------------- + // Desc: Creates a copy of a CExoString. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + CExoString(const char *source, int32_t length); + //------------------------------------------------------------------------- + // Desc: Creates a CExoString that contains the first length characters + // of a CExoString. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + CExoString(int32_t value); + //------------------------------------------------------------------------- + // Desc: Creates a CExoString representing the int value. + /////////////////////////////////////////////////////////////////////////// + + + // The most rudimentary of std::string interop. + CExoString(const std::string& other); + CExoString& operator=(const std::string& other); + operator std::string() const; + + /////////////////////////////////////////////////////////////////////////// + ~CExoString(); + //------------------------------------------------------------------------- + // Desc: Destructor. + /////////////////////////////////////////////////////////////////////////// + + + /////////////////////////////////////////////////////////////////////////// + CExoString & operator = (const CExoString &string); + //------------------------------------------------------------------------- + // Desc: Assigns one CExoString to another. + // Params: string: CExoString to be assigned to this CExoString. + // Returns: A reference to this CExoString. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + CExoString & operator = (const char *string); + //------------------------------------------------------------------------- + // Desc: Assigns the value of a null terminated character array to a + // CExoString. + // Params: string: A NULL-terminated character string to be assigned + // to this CExoString. + // Returns: A reference to this CExoString. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + BOOL operator == (const CExoString &string) const; + //------------------------------------------------------------------------- + // Desc: Compares two CExoString's. + // Params: string: CExoString to compare to this CExoString. + // Returns: TRUE if CExoString's are equal. + // FALSE if CExoString's are not equal. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + BOOL operator == (const char *string) const; + //------------------------------------------------------------------------- + // Desc: Compares a CExoString to a null terminated character array. + // Params: string: A NULL-terminated character string to compare to + // this CExoString. + // Returns: TRUE if CExoString's are equal. + // FALSE if CExoString's are not equal. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + BOOL operator != (const CExoString &string) const; + //------------------------------------------------------------------------- + // Desc: Determines if two CExoString's are not equal. + // Params: string: CExoString to compare to this CExoString. + // Returns: TRUE if CExoString's are equal. + // FALSE if CExoString's are not equal. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + BOOL operator != (const char *string) const; + //------------------------------------------------------------------------- + // Desc: Determines if CExoString is not equal to a null terminated + // character array. + // Params: string: A NULL-terminated character string to compare to + // this CExoString. + // Returns: TRUE if CExoString's are equal. + // FALSE if CExoString's are not equal. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + BOOL operator < (const CExoString &string) const; + //------------------------------------------------------------------------- + // Desc: Determines if CExoString is less than another CExoString. + // Params: string: CExoString to compare to this CExoString. + // Returns: TRUE CExoString is less than. + // FALSE CExoString is not less than. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + BOOL operator < (const char *string) const; + //------------------------------------------------------------------------- + // Desc: Determines if CExoString is less than a null terminated + // character array. + // Params: string: A NULL-terminated character string to compare to + // this CExoString. + // Returns: TRUE CExoString is less than. + // FALSE CExoString is not less than. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + BOOL operator > (const CExoString &string) const; + //------------------------------------------------------------------------- + // Desc: Determines if CExoString is greater than another CExoString. + // Params: string: CExoString to compare to this CExoString. + // Returns: TRUE CExoString is greater than. + // FALSE CExoString is not greater than. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + BOOL operator > (const char *string) const; + //------------------------------------------------------------------------- + // Desc: Determines if CExoString is greater than a null terminated + // character array. + // Params: string: A NULL-terminated character string to compare to + // this CExoString. + // Returns: TRUE CExoString is greater than. + // FALSE CExoString is not greater than. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + BOOL operator <= (const CExoString &string) const; + //------------------------------------------------------------------------- + // Desc: Determines if CExoString is less than or equal to another + // CExoString. + // Params: string: CExoString to compare to this CExoString. + // Returns: TRUE CExoString is less than or equal to. + // FALSE CExoString is not less than or equal to. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + BOOL operator <= (const char *string) const; + //------------------------------------------------------------------------- + // Desc: Determines if CExoString is less than or equal to a null + // terminated character array. + // Params: string: A NULL-terminated character string to compare to + // this CExoString. + // Returns: TRUE CExoString is less than or equal to + // FALSE CExoString is not less than or equal to + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + BOOL operator >= (const CExoString &string) const; + //------------------------------------------------------------------------- + // Desc: Determines if CExoString is greater than or equal to another + // CExoString. + // Params: string: CExoString to compare to this CExoString. + // Returns: TRUE CExoString is greater than or equal to. + // FALSE CExoString is not greater than or equal to. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + BOOL operator >= (const char *string) const; + //------------------------------------------------------------------------- + // Desc: Determines if CExoString is greater than or equal toa null + // terminated character array. + // Params: string: A NULL-terminated character string to compare to + // this CExoString. + // Returns: TRUE CExoString is greater than or equal to. + // FALSE CExoString is not greater than or equal to. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + char operator [] (int32_t position) const; + //------------------------------------------------------------------------- + // Desc: Gets character at position. + // Params: position: The position in this CExoString of the + // character desired. + // Returns: The character at position. + /////////////////////////////////////////////////////////////////////////// +//friend AsiString __fastcall operator +(const char* lhs, const AnsiString& rhs); +//AnsiString __fastcall operator +(const AnsiString& rhs) const; + /////////////////////////////////////////////////////////////////////////// + CExoString operator + (const CExoString &string) const; + //------------------------------------------------------------------------- + // Desc: Returns the result of two CExoString joined together. + // Params: string: CExoString to add (append) to this CExoString. + // Returns: CExoString consisting of the two strings added together. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// +// CExoString operator + (const char *string); + //------------------------------------------------------------------------- + // Desc: Returns the result of an CExoString joined with a null terminated + // character array + // + // Return: CExoString consisting of the two strings added together + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + //CExoString operator + (const char *string); + //------------------------------------------------------------------------- + // Desc: Returns the result of an CExoString joined with a null terminated + // character array + // + // Return: CExoString consisting of the two strings added together + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + int32_t AsINT() const; + //------------------------------------------------------------------------- + // Desc: Retuns integer value the string represents. + // Returns: Integer value the string represents. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + float AsFLOAT() const; + //------------------------------------------------------------------------- + // Desc: Retuns float value the string represents. + // Returns: Float value the string represents. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + char* CStr() const; + //------------------------------------------------------------------------- + // Desc: Retuns a null terminated character array. + // Returns: Null terminated character array. + /////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////// + int32_t Find(const CExoString &string, int32_t position=0) const; + + int32_t Find(char ch, int32_t position=0) const; + //------------------------------------------------------------------------- + // Desc: Finds a substring within the string. + // Params: string: The substring to find in this CExoString. + // Returns: Position where substring is. + // -1 if string not found. + /////////////////////////////////////////////////////////////////////////// + + int32_t FindNot(char ch, int32_t position=0) const; + //------------------------------------------------------------------------- + // Desc: Finds the first character that is not ch + // Params: ch: The character to not find in this CExoString. + // Returns: Position where character is. + // -1 if string not found. + /////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////// + CExoString RemoveAll(const char* c) const; + //------------------------------------------------------------------------- + // Returns: A copy of this string with all characters contained in c + // removed, regardless of order. + /////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////// + CExoString RemoveAllExcept(const char* c) const; + //------------------------------------------------------------------------- + // Returns: A copy of this string with all characters NOT contained in c + // removed, regardless of order. + /////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////// + void Format(const char *format,...); + //------------------------------------------------------------------------- + // Desc: Formats a string based on a number of arguments, like sprintf + // does. + // Params: format: A character string containing the format of the + // string to put into this CExoString. + // ...: A series of comma-separated variables or constants + // to be inserted into format as a string at the + // appropriate positions. There can be zero or more + // of these, depending on format. + /////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + inline void DummyFormat(char *format,...) {return;} + //------------------------------------------------------------------------- + // Desc: Does nothing. Takes same parameter list as Format + // Params: format: A character string containing the format of the + // string to put into this CExoString. + // ...: A series of comma-separated variables or constants + // to be inserted into format as a string at the + // appropriate positions. There can be zero or more + // of these, depending on format. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + + template + static CExoString F(const char* fmt, T&& ... args) + { + CExoString f; + f.Format(fmt, args...); + return f; + } + + // Static formatting helper for cleaner code. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + inline int32_t GetLength() const + { + return m_sString ? (int32_t) strlen(m_sString) : 0; + } + //------------------------------------------------------------------------- + // Desc: Retuns the length of the CExoString. + // Returns: The length of the CExoString. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + void Insert(const CExoString &string, int32_t position); + //------------------------------------------------------------------------- + // Desc: Inserts a string at the given position. + // Params: string: The CExoString to insert into this CExoString. + // position: Position in this CExoString where string will be + // inserted. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + BOOL IsEmpty() const; + //------------------------------------------------------------------------- + // Desc: Checks if CExoString is a blank string. + // Returns: TRUE if this CExoString is blank. + // FALSE if this CExoString is not blank. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + CExoString Left(int32_t count) const; + //------------------------------------------------------------------------- + // Desc: Returns leftmost 'count' characters. + // Params: count: Number of characters to extract starting from the + // left. + // Returns: CExoString containing the leftmost 'count' characters. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + CExoString LowerCase() const; + //------------------------------------------------------------------------- + // Desc: Returns the string converted to lowercase. + // Returns: CExoString converted to lowercase. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + CExoString Right(int32_t count) const; + //------------------------------------------------------------------------- + // Desc: Returns rightmost 'count' characters. + // Params: count: Number of characters to extract starting from the + // right. + // Returns: CExoString containing the rightmost 'count' characters. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + CExoString SubString(int32_t start, int32_t count=-1) const; + //------------------------------------------------------------------------- + // Desc: Returns a portion of string consisting of 'count' characters + // starting at 'start'. + // Params: start: The starting position of the substring within this + // CExoString. + // count: Number of characters to extract into the substring. + // <0 means extract to end of string + // Returns: CExoString containing the substring. + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + CExoString UpperCase() const; + //------------------------------------------------------------------------- + // Desc: Returns the string converted to uppercase. + // Returns: CExoString converted to uppercase. + /////////////////////////////////////////////////////////////////////////// + + BOOL CompareNoCase(const CExoString &sString) const; + BOOL ComparePrefixNoCase(const CExoString &sString, int32_t nSize) const; + + /////////////////////////////////////////////////////////////////////////// + BOOL StripNonAlphaNumeric( BOOL bFileName = TRUE, BOOL bEmail = FALSE, BOOL bMasterServer = FALSE ); + //------------------------------------------------------------------------- + // Desc: Removes all non-alpha-numeric symbols from the string + /////////////////////////////////////////////////////////////////////////// + + // Strip leading and/or trailing characters from this string, returning a + // new string. + CExoString Strip(bool leading = true, bool trailing = true, const char* set = Whitespace) const; + + /////////////////////////////////////////////////////////////////////////// + CExoString AsTAG() const; + //------------------------------------------------------------------------- + // Desc: Retuns a proper tag version of the string + /////////////////////////////////////////////////////////////////////////// + + // Returns a cross-platform, stable 32bit hash of the string (xxh32) + int32_t GetHash() const; + + // Static helper that formats a number of bytes as a human-readable + // fractional to the nearest magnitude (i.e. "4.1GB"). + static CExoString FormatBytes(uint64_t bytes); + + // Static helper that formats a number of seconds as a human-readable interval. + static CExoString FormatDuration(uint64_t span, + // Only show the N most significant intervals that aren't zero: + // compact_levels = 1 -> "1d" + // compact_levels = 2 -> "1d 4h" + // compact_levels = 3 -> "1d 4h 2m" + // Note that a compacted value counts as a full "bigger" level. + int compact_levels = 4, + // Minimum number of levels to show, even if zeroes. + int min_level = 1, + // Liberty to abbreviate some values if compaction level and labelling. + // For example, 48s would be abbreviated to "<1m" + //bool abbreviate = true, + // Label fields as "s", "m", "h", "d". + bool label_fields = true, + // Separator between fields + const char* separator = " "); + + // Formats a unix timestamp into a human readable string according to the current locale. + static CExoString FormatUnixTimestamp(uint64_t ts, + // %c -> Thu Aug 23 14:55:02 2001 (locale-dependent) + // %F %T -> 2001-08-23 14:55:02 (NOT locale-dependent but only numbers) + // %x %X -> 08/23/01 14:55:02 (locale-dependent) + // see strftime(3) for more + const char* format = "%F %T"); + + // Split string by delimiter. Strips out empty values and repeated delimiters. + static std::vector Split(const CExoString& str, const CExoString& delimiter); + std::vector Split(const CExoString& delimiter) const; + + // Join string array by delimiter. Will not join in empty strings. + static CExoString Join(const std::vector& ary, const CExoString& delimiter); + + void Clear() + { + delete[] m_sString; + m_sString = 0; + m_nBufferLength = 0; + } + // Explicit move semantics that don't require new C++ features like && + void Steal(CExoString *other) + { + m_sString = other->m_sString; + m_nBufferLength = other->m_nBufferLength; + other->m_sString = NULL; + other->m_nBufferLength = 0; + } + char *Relinquish() + { + char *buf = m_sString; + m_sString = NULL; + m_nBufferLength = 0; + return buf; + } + + // ************************************************************************* +private: + // ************************************************************************* + char *m_sString; + uint32_t m_nBufferLength; + + //static char *CExoStringFormatBuffer; + //static int32_t CExoStringFormatBufferSize; +}; + + +// Allow CExoString to be used as keys for stl maps. +namespace std { +template <> +struct hash +{ + std::size_t operator()(const CExoString& k) const + { + return std::hash{}(k.CStr()); + } +}; +} diff --git a/src/Native Compiler/exostring.cpp b/src/Native Compiler/exostring.cpp new file mode 100644 index 0000000..9387445 --- /dev/null +++ b/src/Native Compiler/exostring.cpp @@ -0,0 +1,1575 @@ +// +// SPDX-License-Identifier: GPL-3.0 +// +// This file is part of the NWScript compiler open source release. +// +// The initial source release is licensed under GPL-3.0. +// +// All subsequent changes you submit are required to be licensed under MIT. +// +// However, the project overall will still be GPL-3.0. +// +// The intent is for the base game to be able to pick up changes you explicitly +// submit for inclusion painlessly, while ensuring the overall project source code +// remains available for everyone. +// + +// This file is a reduced variant taken from the base game. +// Do not port changes here back to the game unless needed for script compiler functionality. + +/////////////////////////////////////////////////////////////////////////////// +// BIOWARE CORP. CONFIDENTIAL INFORMATION. // +// COPYRIGHT BIOWARE CORP. ALL RIGHTS RESERVED // +/////////////////////////////////////////////////////////////////////////////// + +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: ExoBase +//:: +//:: Copyright (c) 1999, BioWare Corp. +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: ExoString.cpp +//:: +//:: String Class +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Created By: Don Yakielashek +//:: Created On: 04/26/99 +//:: +//:://///////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// external header files +#include "exobase.h" + +// We include xxhash.c transitively through other libs which aren't always present (hello borland and macos ARM) +// So rather than wrangling with the build system, we'll just inline the XXH32 function here. It's small enough. +#define XXH_INLINE_ALL +#include "xxhash.h" + +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: class CExoString +//:: +//:://///////////////////////////////////////////////////////////////////////// + +const char* CExoString::Whitespace = " \t\v\r\n\f"; +const char* CExoString::Letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; +const char* CExoString::Numbers = "0123456789"; +const char* CExoString::Alphanumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + +/////////////////////////////////////////////////////////////////////////////// +// CExoString::CExoString() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 04/26/99 +// Description:Constructor +/////////////////////////////////////////////////////////////////////////////// + +// Creates an empty CExoString +CExoString::CExoString() +{ + m_sString = NULL; + m_nBufferLength = 0; +} + +// Creates a CExsoString from a null terminated character array +CExoString::CExoString(const char *source) +{ + if (source && ( strlen( source ) > 0 )) + { + m_nBufferLength = (uint32_t)strlen(source) + 1; + m_sString = new char[m_nBufferLength]; + strcpy(m_sString, source); + } + else + { + m_sString = NULL; + m_nBufferLength = 0; + } +} + +//Creates a copy of a CExoString +CExoString::CExoString(const CExoString &source) +{ + int32_t nSize = 1; + if (source.m_sString && ( strlen( source.m_sString ) > 0 ) ) + { + m_nBufferLength = (uint32_t)strlen(source.m_sString) + 1; + m_sString = new char[m_nBufferLength]; + strcpy(m_sString, source.m_sString); + } + else + { + m_sString = NULL; + m_nBufferLength = 0; + } +} + +// Creates a CExoString that contains the first len characters of a CExoString +CExoString::CExoString(const char *source, int32_t length) +{ + if ( length > 0 ) + { + m_nBufferLength = length + 1; + m_sString = new char[m_nBufferLength]; + strncpy(m_sString, source, length); + m_sString[length] = '\0'; + } + else + { + m_sString = NULL; + m_nBufferLength = 0; + } +} + +// Creates a CExoString representing the int value +CExoString::CExoString(int32_t value) +{ + char buffer[33]; + + sprintf( buffer, "%i", value ); + + m_nBufferLength = (uint32_t)strlen(buffer) + 1; + m_sString = new char[m_nBufferLength]; + strcpy(m_sString, buffer); + +} + + +/////////////////////////////////////////////////////////////////////////////// +// CExoString::~CExoString() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 04/26/99 +// Description:Destructor +/////////////////////////////////////////////////////////////////////////////// +CExoString::~CExoString() +{ + Clear(); +} + +CExoString::CExoString(const std::string& other) +{ + if (!other.empty()) + { + m_nBufferLength = other.size() + 1; + m_sString = new char[m_nBufferLength]; + memmove(m_sString, other.data(), m_nBufferLength - 1); + m_sString[m_nBufferLength - 1] = '\0'; + } + else + { + m_sString = NULL; + m_nBufferLength = 0; + } +} + +CExoString& CExoString::operator=(const std::string& other) +{ + if (other.size() > m_nBufferLength - 1) + { + delete[] m_sString; + m_sString = NULL; + } + + if (!other.empty()) + { + m_nBufferLength = other.size() + 1; + m_sString = new char[m_nBufferLength]; + memmove(m_sString, other.data(), m_nBufferLength - 1); + m_sString[m_nBufferLength - 1] = '\0'; + } + else + { + m_sString = NULL; + m_nBufferLength = 0; + } + + return *this; +} + +CExoString::operator std::string() const +{ + return m_nBufferLength == 0 ? std::string("") : std::string(m_sString, strlen(m_sString)); +} + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: = operator +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 04/26/99 +// Description:Assign operator +/////////////////////////////////////////////////////////////////////////////// + +// Assigning one CExoString to another +CExoString & CExoString::operator = (const CExoString& string) +{ + if (this == &string) + { + return *this; + } + + if (m_sString) + { + if (!string.m_sString || + strlen(string.m_sString) + 1 > m_nBufferLength) + { + Clear(); + } + } + + if (string.m_sString && ( strlen( string.m_sString ) > 0 )) + { + if (m_sString == NULL) + { + m_nBufferLength = (uint32_t)strlen(string.m_sString) + 1; + m_sString = new char[m_nBufferLength]; + } + strcpy(m_sString, string.m_sString); + } + else + { + Clear(); + } + return *this; +} + +// Assigning the value of a null terminated character array to a CExoString +CExoString & CExoString::operator = (const char *string) +{ + if (m_sString) + { + if (!string || + strlen(string) + 1 > m_nBufferLength) + { + Clear(); + } + } + + if (string && ( strlen( string ) > 0 )) + { + if (m_sString == NULL) + { + m_nBufferLength = (uint32_t)strlen(string) + 1; + m_sString = new char[m_nBufferLength]; + } + strcpy(m_sString, string); + } + else + { + Clear(); + } + return *this; +} + + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: == operator +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 04/26/99 +// Description:Equality operator +/////////////////////////////////////////////////////////////////////////////// + +// Compares two CExoString's +BOOL CExoString::operator == (const CExoString &string) const +{ + if ( m_sString && string.m_sString ) + { + return (strcmp(m_sString, string.m_sString) == 0); + } + else + { + return ( ( ( m_sString == NULL ) && ( string.m_sString == NULL ) ) || + ( m_sString && ( m_sString[0] == '\0' ) ) || + ( string.m_sString && ( string.m_sString[0] == '\0' ) ) ); + } +} + +// Compares a CExoString to a null terminated character array +BOOL CExoString::operator == (const char *string) const +{ + if ( m_sString && string ) + { + return (strcmp(m_sString, string) == 0); + } + else + { + return ( ( ( m_sString == NULL ) && ( string == NULL ) ) || + ( m_sString && ( m_sString[0] == '\0' ) ) || + ( string && ( string[0] == '\0' ) ) ); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: != operator +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 04/28/99 +// Description:Not Equals operator +/////////////////////////////////////////////////////////////////////////////// + +// Determines if two CExoString's are not equal +BOOL CExoString::operator != (const CExoString &string) const +{ + if ( m_sString && string.m_sString ) + { + return (strcmp(m_sString, string.m_sString) != 0); + } + else + { + return ( ( m_sString && ( m_sString[0] != '\0' ) ) || + ( string.m_sString && ( string.m_sString[0] != '\0' ) ) ); + } +} + +// Determines if CExoString is not equal to a null terminated character array +BOOL CExoString::operator != (const char *string) const +{ + if ( m_sString && string ) + { + return (strcmp(m_sString, string) != 0); + } + else + { + return ( ( m_sString && ( m_sString[0] != '\0' ) ) || + ( string && ( string[0] != '\0' ) ) ); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: < operator +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 04/28/99 +// Description:Less than operator +/////////////////////////////////////////////////////////////////////////////// + +// Determines if CExoString is less than another CExoString +BOOL CExoString::operator < (const CExoString &string) const +{ + if ( m_sString && string.m_sString ) + { + return (strcmp(m_sString, string.m_sString) < 0); + } + else + { + return ( string.m_sString && ( string.m_sString[0] != '\0' ) ); + } +} + +// Determines if CExoString is less than a null terminated character array +BOOL CExoString::operator < (const char *string) const +{ + if ( m_sString && string ) + { + return (strcmp(m_sString, string) < 0); + } + else + { + return ( string && ( string[0] != '\0' ) ); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: > operator +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 04/28/99 +// Description:Greater than operator +/////////////////////////////////////////////////////////////////////////////// + +// Determines if CExoString is greater than another CExoString +BOOL CExoString::operator > (const CExoString &string) const +{ + if ( m_sString && string.m_sString ) + { + return (strcmp(m_sString, string.m_sString) > 0); + } + else + { + return ( m_sString && ( m_sString[0] != '\0' ) ); + } +} + +// Determines if CExoString is greater than a null terminated character array +BOOL CExoString::operator > (const char *string) const +{ + if ( m_sString && string ) + { + return (strcmp(m_sString, string) > 0); + } + else + { + return ( m_sString && ( m_sString[0] != '\0' ) ); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: <= operator +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 04/28/99 +// Description:Less than or equal to operator +/////////////////////////////////////////////////////////////////////////////// + +// Determines if CExoString is less than or equal to another CExoString +BOOL CExoString::operator <= (const CExoString &string) const +{ + if ( m_sString && string.m_sString ) + { + return (strcmp(m_sString, string.m_sString) <= 0); + } + else + { + return ( ( ( m_sString == NULL ) && ( string.m_sString == NULL ) ) || + ( m_sString && ( m_sString[0] == '\0' ) ) || + ( string.m_sString ) ); + } +} + +// Determines if CExoString is less then or equal to a null terminated character array +BOOL CExoString::operator <= (const char *string) const +{ + if ( m_sString && string ) + { + return (strcmp(m_sString, string) <= 0); + } + else + { + return ( ( ( m_sString == NULL ) && ( string == NULL ) ) || + ( m_sString && ( m_sString[0] == '\0' ) ) || + ( string ) ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: >= operator +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 04/28/99 +// Description:Greater than or equal to operator +/////////////////////////////////////////////////////////////////////////////// + +// Determines if CExoString is greater than or equal to another CExoString +BOOL CExoString::operator >= (const CExoString &string) const +{ + if ( m_sString && string.m_sString ) + { + return (strcmp(m_sString, string.m_sString) >= 0); + } + else + { + return ( ( ( m_sString == NULL ) && ( string.m_sString == NULL ) ) || + ( string.m_sString && ( string.m_sString[0] == '\0' ) ) || + ( m_sString ) ); + } +} + +// Determines if CExoString is greater then or equal to a null terminated character array +BOOL CExoString::operator >= (const char *string) const +{ + if ( m_sString && string ) + { + return (strcmp(m_sString, string) >= 0); + } + else + { + return ( ( ( m_sString == NULL ) && ( string == NULL ) ) || + ( string && ( string[0] == '\0' ) ) || + ( m_sString ) ); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: [] operator +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 04/28/99 +// Description:Retuns character at position +/////////////////////////////////////////////////////////////////////////////// +char CExoString::operator [] (int32_t position) const +{ + if ((position < 0) || !m_sString || (strlen(m_sString) <= ((uint32_t) position))) + { + return '\0'; + } + else + { + return m_sString[position]; + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: + operator +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 04/30/99 +// Description:Joins two strings +/////////////////////////////////////////////////////////////////////////////// + +// Returns the result of two CExoString joined together +CExoString CExoString::operator + (const CExoString &string) const +{ + CExoString newStr; + uint32_t stringLength, m_sStringLength; + + if ( string.m_sString ) + { + stringLength = (uint32_t)strlen(string.m_sString); + } + else + { + stringLength = 0; + } + + if ( m_sString ) + { + m_sStringLength = (uint32_t)strlen(m_sString); + } + else + { + m_sStringLength = 0; + } + + if ( ( stringLength == 0 ) && ( m_sStringLength == 0 ) ) + { + return newStr; + } + + if (newStr.m_sString) + { + // MGB - November 27, 2001 - Should never call this. + EXOASSERT(FALSE); + delete[] newStr.m_sString; + newStr.m_sString = NULL; + newStr.m_nBufferLength = 0; + } + + if (stringLength == 0) + { + newStr.m_nBufferLength = m_sStringLength + 1; + newStr.m_sString = new char[newStr.m_nBufferLength]; + + strcpy(newStr.m_sString, m_sString); + return newStr; + } + + if (m_sStringLength == 0) + { + newStr.m_nBufferLength = stringLength + 1; + newStr.m_sString = new char[newStr.m_nBufferLength]; + + strcpy(newStr.m_sString, string.m_sString); + return newStr; + } + + newStr.m_nBufferLength = m_sStringLength + stringLength + 1; + newStr.m_sString = new char[newStr.m_nBufferLength]; + strcpy(newStr.m_sString, m_sString); + strcat(newStr.m_sString, string.m_sString); + return newStr; +} + + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: AsINT() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Howard Chung +// Created On: 05/15/2001 +// Desc: Retuns integer value the string represents. +/////////////////////////////////////////////////////////////////////////////// +int32_t CExoString::AsINT() const +{ + if ( m_sString ) + { + return atoi( m_sString ); + } + else + { + return 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: AsINT() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Howard Chung +// Created On: 05/15/2001 +// Desc: Retuns float value the string represents. +/////////////////////////////////////////////////////////////////////////////// +float CExoString::AsFLOAT() const +{ + if ( m_sString ) + { + return (float)atof( m_sString ); + } + else + { + return 0.0f; + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: CStr() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 04/27/99 +// Description:Retuns a null terminated character array +/////////////////////////////////////////////////////////////////////////////// +static char g_szEmptyString[1] = { 0 }; +char* CExoString::CStr() const +{ + //EXOWARNINGSTR( m_sString != NULL, "CExoString::CStr(): Obtaining NULL pointer from CExoString." ); + + if ( m_sString == NULL ) + { + return g_szEmptyString; + } + + return m_sString; +} + + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: Find() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 05/27/99 +// Description:Finds a substring within the string +/////////////////////////////////////////////////////////////////////////////// +int32_t CExoString::Find(const CExoString &string, int32_t position) const +{ + int32_t cnt, matchLen; + char *pChar; + + if ( !m_sString || !string.m_sString || position < 0 ) + { + return -1; + } + + pChar = m_sString; + for (cnt=0; cnt m_nBufferLength) + { + Clear(); + m_nBufferLength = requiredSize + 1; + m_sString = new char[m_nBufferLength]; + } + + strncpy(m_sString, CExoStringFormatBuffer, requiredSize); + m_sString[requiredSize] = 0; + + va_end(argList); +} + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: Insert() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 04/28/99 +// Description:Inserts a string at the given position +/////////////////////////////////////////////////////////////////////////////// + +void CExoString::Insert(const CExoString &string, int32_t position) +{ + char *newStr, *pStr; + uint32_t stringLength, m_sStringLength; + + if ( !string.m_sString ) + { + return; + } + + stringLength = (uint32_t)strlen(string.m_sString); + + if ( m_sString ) + { + m_sStringLength = (uint32_t)strlen(m_sString); + } + else + { + m_sStringLength = 0; + } + + if (stringLength == 0) + { + return; + } + + if ((position < 0) || (m_sStringLength <= ((uint32_t) position))) + { + return; + } + + // allocate new string + m_nBufferLength = m_sStringLength + stringLength + 1; + newStr = new char[m_nBufferLength]; + newStr[0] = '\0'; + + // swap pointer to existing string with pointer to new string + pStr = m_sString; + m_sString = newStr; + newStr = pStr; + + // insert string + if ( pStr ) + { + strncpy(m_sString, pStr, position); + } + m_sString[position] = '\0'; + strcat(m_sString, string.m_sString); + strcat(m_sString, &pStr[position]); + + // delete old string + if (pStr) + { + delete[] pStr; + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: IsEmpty() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 04/28/99 +// Description:Checks if CExoString is a blank string +/////////////////////////////////////////////////////////////////////////////// + +BOOL CExoString::IsEmpty() const +{ + return ( !m_sString || (m_sString[0] == '\0') ); +} + + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: Left() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 04/28/99 +// Description:Returns Leftmost 'count' characters +/////////////////////////////////////////////////////////////////////////////// + +CExoString CExoString::Left(int32_t count) const +{ + CExoString newStr; + int32_t count2; + uint32_t m_sStringLength; + + if ( !m_sString ) + { + return newStr; + } + + m_sStringLength = (uint32_t)strlen(m_sString); + + count2 = count; + + if (count2 < 0) + { + return newStr; + } + + if (m_sStringLength == 0) + { + return newStr; + } + if (((uint32_t) count2) > m_sStringLength) + { + count2 = m_sStringLength; + } + + if (newStr.m_sString) + { + delete[] newStr.m_sString; + } + newStr.m_sString = new char[count2+1]; + strncpy(newStr.m_sString, m_sString, count2); + newStr.m_sString[count2] = '\0'; + return newStr; +} + + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: LowerCase() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 05/28/99 +// Description:Returns the string converted to lowsercase +/////////////////////////////////////////////////////////////////////////////// +CExoString CExoString::LowerCase() const +{ + CExoString newStr; + int32_t nPos; + + if ( !m_sString ) + { + return newStr; + } + + newStr.m_nBufferLength = GetLength() + 1; + newStr.m_sString = new char[newStr.m_nBufferLength]; + nPos = 0; + while (m_sString[nPos] != 0) + { + if (m_sString[nPos] >= 'A' && m_sString[nPos] <= 'Z') + { + newStr.m_sString[nPos] = m_sString[nPos] - 'A' + 'a'; + } + else + { + newStr.m_sString[nPos] = m_sString[nPos]; + } + nPos++; + } + newStr.m_sString[nPos] = 0; + + return newStr; +} + + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: Right() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 04/30/99 +// Description:Returns Rightmost 'count' characters +/////////////////////////////////////////////////////////////////////////////// + +CExoString CExoString::Right(int32_t count) const +{ + CExoString newStr; + int32_t count2; + uint32_t m_sStringLength; + + if ( !m_sString ) + { + return newStr; + } + + m_sStringLength = (uint32_t)strlen(m_sString); + + count2 = count; + + if (count2 < 0) + { + return newStr; + } + if (m_sStringLength == 0) + { + return newStr; + } + if (((uint32_t) count2) > m_sStringLength) + { + count2 = m_sStringLength; + } + + if (newStr.m_sString) + { + delete[] newStr.m_sString; + } + newStr.m_sString = new char[count2+1]; + strncpy(newStr.m_sString, &(m_sString[m_sStringLength-count2]), count2); + newStr.m_sString[count2] = '\0'; + return newStr; +} + + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: SubString() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 05/27/99 +// Description:Returns a portion of string consisting of 'count' characters +// starting at 'start', count<0 means extract to end of string +/////////////////////////////////////////////////////////////////////////////// +CExoString CExoString::SubString(int32_t start, int32_t count) const +{ + CExoString newStr; + int32_t count2; + uint32_t m_sStringLength; + + if ( !m_sString ) + { + return newStr; + } + + m_sStringLength = (uint32_t)strlen(m_sString); + + if ((start < 0) || (((uint32_t)start) >= m_sStringLength) || (count==0)) + { + return newStr; + } + + if (count < 0) // extract to end of string + { + count = m_sStringLength - start; + } + + count2 = count; + + if (((uint32_t) start + count2) > m_sStringLength) + { + count2 = m_sStringLength - start; + } + + if (newStr.m_sString) + { + delete[] newStr.m_sString; + } + newStr.m_sString = new char[count+1]; + newStr.m_nBufferLength = count+1; + + strncpy(newStr.m_sString, &(m_sString[start]), count); + newStr.m_sString[count] = '\0'; + return newStr; +} + + +/////////////////////////////////////////////////////////////////////////////// +// CExoString:: UpperCase() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Don Yakielashek +// Created On: 05/28/99 +// Description:Returns the string converted to uppercase +/////////////////////////////////////////////////////////////////////////////// +CExoString CExoString::UpperCase() const +{ + CExoString newStr; + int32_t nPos; + + if ( !m_sString ) + { + return newStr; + } + + newStr.m_nBufferLength = GetLength() + 1; + newStr.m_sString = new char[newStr.m_nBufferLength]; + nPos = 0; + while (m_sString[nPos] != 0) + { + if (m_sString[nPos] >= 'a' && m_sString[nPos] <= 'z') + { + newStr.m_sString[nPos] = m_sString[nPos] - 'a' + 'A'; + } + else + { + newStr.m_sString[nPos] = m_sString[nPos]; + } + nPos++; + } + newStr.m_sString[nPos] = 0; + + return newStr; +} + +/////////////////////////////////////////////////////////////////////////////// +// CExoString::CompareNoCase() +/////////////////////////////////////////////////////////////////////////////// +// Owned By: Mark Brockington +// Description:Returns whether the two strings are the same, irrespective +// of case. +/////////////////////////////////////////////////////////////////////////////// +BOOL CExoString::CompareNoCase(const CExoString &string) const +{ + char *pStringChar; + char *pThisStringChar; + int32_t nLenString, cnt; + + pStringChar = string.m_sString; + nLenString = string.GetLength(); + + pThisStringChar = m_sString; + + if (pStringChar == NULL && pThisStringChar == NULL) + { + return TRUE; + } + else if (pStringChar == NULL || pThisStringChar == NULL) + { + return FALSE; + } + else if (nLenString != GetLength()) + { + return FALSE; + } + + for (cnt=0; cnt= 'A' && pStringChar[cnt] <= 'Z') + { + if (pStringChar[cnt] - 'A' + 'a' != pThisStringChar[cnt]) + { + return FALSE; + } + } + else if (pThisStringChar[cnt] >= 'A' && pThisStringChar[cnt] <= 'Z') + { + if (pThisStringChar[cnt] - 'A' + 'a' != pStringChar[cnt]) + { + return FALSE; + } + } + else + { + return FALSE; + } + } + } + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// +// CExoString::ComparePrefixNoCase() +/////////////////////////////////////////////////////////////////////////////// +// Owned By: Mark Brockington +// Description:Returns whether the two strings are the same, irrespective +// of case. +/////////////////////////////////////////////////////////////////////////////// +BOOL CExoString::ComparePrefixNoCase(const CExoString &string, int32_t nSize) const +{ + char *pStringChar = string.CStr(); + char *pThisStringChar = m_sString; + + if (pStringChar == NULL && pThisStringChar == NULL) + { + return TRUE; + } + else if (pStringChar == NULL || pThisStringChar == NULL) + { + return FALSE; + } + + return !(strnicmp(pStringChar,pThisStringChar,nSize)); +} + +/////////////////////////////////////////////////////////////////////////////// +// CExoString::StripNonAlphaNumeric +/////////////////////////////////////////////////////////////////////////////// +// Created by: Paul Roffel +// Created on: April 29, 2002 +// Desc.: Removes all non-alpha-numeric symbols from the string +/////////////////////////////////////////////////////////////////////////////// +BOOL CExoString::StripNonAlphaNumeric( BOOL bFileName, BOOL bEmail, BOOL bMasterServer ) +{ + uint32_t cnt, cnt2, max; + BOOL bChanged = FALSE; + char szTester[128]; + + if ( !m_sString ) + { + return FALSE; + } + + max = (uint32_t)strlen(m_sString); + if ( max == 0 ) + { + return FALSE; + } + + if ( bFileName ) + { + strcpy(szTester, + "abcdefghijklmnopqrstuvwxyz0123456789'" + "\xdf\xe9\xc9\xe8\xc8\xc7\xe7\xf9\xd9\xf1\xd1\xe0\xc0\xc4\xe4\xdc\xfc\xd6\xf6" + "_-"); + } + else + { + strcpy( szTester, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ); + } + + if ( bEmail ) + { + strcat( szTester, "@." ); + } + + if ( bMasterServer ) // 5/18/02 - PLR - Characters allowed on the master server. + { + strcat(szTester, + ".'," + "\xdf\xe9\xc9\xe8\xc8\xc7\xe7\xf9\xd9\xf1\xd1\xe0\xc0\xc4\xe4\xdc\xfc\xd6\xf6" + "_- "); + } + + for ( cnt = 0; cnt < max ; cnt++ ) + { + if ( bFileName ) // 5/18/02 - PLR - Filenames must be converted to lowercase now... *sigh* + { + if (m_sString[cnt] >= 'A' && m_sString[cnt] <= 'Z') + { + m_sString[cnt] = m_sString[cnt] - 'A' + 'a'; + } + } + + // MGB - August 3, 2016 - Remove all leading spaces and periods. + if ( (cnt == 0 && (m_sString[cnt] == '.' || m_sString[cnt] == ' ') ) || + strchr( szTester, m_sString[cnt] ) == NULL + ) + { + for ( cnt2 = cnt ; cnt2 < max ; cnt2++ ) + { + m_sString[cnt2] = m_sString[cnt2+1]; + } + cnt--; + max--; + bChanged = TRUE; + } + } + + return bChanged; +} + +CExoString CExoString::Strip(bool leading, bool trailing, const char* set) const +{ + int start = 0; + int end = GetLength(); + + while (leading && start < GetLength() && strchr(set, (*this)[start])) + start++; + + while (trailing && end > 0 && strchr(set, (*this)[end - 1])) + end--; + + return SubString(start, end - start); +} + +/////////////////////////////////////////////////////////////////////////////// +// CExoString::AsTAG +/////////////////////////////////////////////////////////////////////////////// +// Created by: Paul Roffel +// Created on: Monday, January 27, 2003 +// Desc.: Returns the string as a tag. +// This is directly from code used in the toolset. +// TNWGlobal::GetTagFromArbitraryString +/////////////////////////////////////////////////////////////////////////////// +CExoString CExoString::AsTAG() const +{ + uint32_t cnt, max; + CExoString newStr; + char newBuffer[64]; + int32_t nIndex = 0; + + if ( !m_sString ) + { + return newStr; + } + + max = (uint32_t)strlen(m_sString); + + for ( cnt = 0 ; cnt < max ; cnt++ ) + { + if ( isalnum(m_sString[cnt]) || m_sString[cnt] == '_' ) + { + newBuffer[nIndex++] = m_sString[cnt]; + if ( nIndex > 62 ) //CNWSTAGNODE_TAG_LENGTH + { + break; + } + } + } + newBuffer[nIndex] = '\0'; + newStr = newBuffer; + +#ifdef _DEBUG // 01/27/03 - PLR - Need to debug this properly! + if ( newStr != m_sString ) + { + LOGSET("TAG CHANGE ===> (%s) -> (%s)\n", m_sString, newStr.CStr())LOGFLUSH + } +#endif + + return newStr; +} + +int32_t CExoString::GetHash() const +{ + // Having the hash of an empty string return zero is a useful property, so we XOR every + // hash with the hash of a null string. This has no cryptographic effect since the + // bit entropy remains the same. Not that XXH is cryptographically secure. + const static int32_t nullhash = (int32_t)XXH32("", 0, 0); + return (int32_t)XXH32(CStr(), GetLength(), 0) ^ nullhash; +} + + +CExoString CExoString::FormatBytes(uint64_t bytes) +{ + CExoString ret; + const char* suffixes[7]; + suffixes[0] = "B"; + suffixes[1] = "KB"; + suffixes[2] = "MB"; + suffixes[3] = "GB"; + suffixes[4] = "TB"; + suffixes[5] = "PB"; + suffixes[6] = "EB"; + unsigned int s = 0; // which suffix to use + double count = (double) bytes; + while (count >= 1024 && s < 7) + { + s++; + count /= 1024; + } + if (count - floor(count) == 0.0) + ret.Format("%d %s", (int)count, suffixes[s]); + else + ret.Format("%.1f %s", count, suffixes[s]); + return ret; +} + +CExoString CExoString::FormatDuration(uint64_t span, + int compact_levels, + int min_levels, + bool label_fields, + const char* separator) +{ + const uint64_t days = span / 3600 / 24; + const uint64_t hours = span / 3600 - (days * 24); + const uint64_t minutes = span / 60 - (hours * 60) - (days * 24 * 60); + const uint64_t seconds = span - (minutes * 60) - (hours * 3600) - (days * 24 * 3600); + + //if (compact_less_than_1m && span < 60) + //{ + // ret.Format(" <1m"); + //} + //else + + int levels_shown = 0; + + CExoString ret; + + if ((days > 0 || min_levels > 3) && compact_levels > levels_shown) + { + ret.Format("%s%s%llu%s", ret.CStr(), separator, days, label_fields ? "d" : ""); + levels_shown++; + } + if ((hours > 0 || min_levels > 2) && compact_levels > levels_shown) + { + ret.Format("%s%s%llu%s", ret.CStr(), separator, hours, label_fields ? "h" : ""); + levels_shown++; + } + if ((minutes > 0 || min_levels > 1) && compact_levels > levels_shown) + { + ret.Format("%s%s%llu%s", ret.CStr(), separator, minutes, label_fields ? "m" : ""); + levels_shown++; + } + if ((seconds > 0 || min_levels > 0) && compact_levels > levels_shown) + { + ret.Format("%s%s%llu%s", ret.CStr(), separator, seconds, label_fields ? "s" : ""); + levels_shown++; + } + + return ret.SubString(strlen(separator)); +} + +CExoString CExoString::FormatUnixTimestamp(uint64_t ts, const char* format) +{ + char buffer[1025] = {0}; + auto t = localtime((time_t*) &ts); + if (ts > 0 && t) + strftime(buffer, 1024, format, t); + else + sprintf(buffer, "-"); + + return buffer; +} + +std::vector CExoString::Split(const CExoString& strin, const CExoString& delimiter) +{ + std::vector ret; + + const std::string str = strin; + const std::string delim = delimiter; + + size_t prev = 0, pos = 0; + do + { + pos = str.find(delim, prev); + if (pos == std::string::npos) + { + pos = str.length(); + } + std::string token = str.substr(prev, pos-prev); + if (!token.empty()) + { + ret.push_back(token); + } + prev = pos + delim.length(); + } + while (pos < str.length() && prev < str.length()); + + return ret; +} + +std::vector CExoString::Split(const CExoString& delimiter) const +{ + return CExoString::Split(*this, delimiter); +} + +CExoString CExoString::Join(const std::vector& arin, const CExoString& delimiter) +{ + std::ostringstream ret; + + for (unsigned i = 0; i < arin.size(); i++) + { + if (!arin[1].IsEmpty()) + { + ret << arin[i].CStr(); + if (i < arin.size() - 1) + { + ret << delimiter.CStr(); + } + } + } + + return ret.str(); +} diff --git a/src/Native Compiler/exotypes.h b/src/Native Compiler/exotypes.h new file mode 100644 index 0000000..c675130 --- /dev/null +++ b/src/Native Compiler/exotypes.h @@ -0,0 +1,93 @@ +// +// SPDX-License-Identifier: GPL-3.0 +// +// This file is part of the NWScript compiler open source release. +// +// The initial source release is licensed under GPL-3.0. +// +// All subsequent changes you submit are required to be licensed under MIT. +// +// However, the project overall will still be GPL-3.0. +// +// The intent is for the base game to be able to pick up changes you explicitly +// submit for inclusion painlessly, while ensuring the overall project source code +// remains available for everyone. +// + +#pragma once + +// This file is a reduced variant taken from the base game. +// Do not port changes here back to the game unless needed for script compiler functionality. + +// WIN32 don't clobber std::min/max. +// This is not what we need in our life. +#define NOMINMAX + +#include +#include +#include +#include + +typedef int BOOL; +typedef uint32_t STRREF; +typedef uint32_t OBJECT_ID; +typedef uint16_t RESTYPE; + +#define FALSE 0 +#define TRUE 1 + +#define INVALID_OBJECT_ID 0x7f000000 +#define RESTYPE_INVALID 0xFFFF + +#define MINSHORT 0x8000 +#define MAXSHORT 0x7fff +#define MAXWORD 0xffff +#define MAXDWORD 0xffffffff +#define MAXBYTE 0xff + + +#include + +// N.B.: C++98 functions only, see http://en.cppreference.com/w/cpp/algorithm + +// Some external libraries (like mss) define their own min/max. +// This is not what we need in our life. +#undef min +#undef max +#undef clamp + +// Legacy. N.B.: semantics changed, now uses < internally. Should not +// have *any* effect as it's only used to compare numbers. +// Left here because used in a LOT of places, and it's bad to break +// assumptions. +#define NWN_max(a,b) (std::max)(a,b) +#define NWN_min(a,b) (std::min)(a,b) + +// This would be in in c++17. +#if __cplusplus < 201500 +namespace std +{ + // Returns value n clamped to inclusive (l, n). Works with any + // type that the compiler knows how to compare. + template const T& clamp(const T& n, const T& l, const T& h) + { + return std::max(l, std::min(n, h)); + } + + // Returns value n clamped to inclusive (l, n). + // + // Uses Cmp to compare, where Cmp is: + // bool(const Type1 &a, const Type2 &b); + // Where Type1 and Type2 are implicitly convertible. + template const T& clamp(const T& n, const T& l, + const T& h, Cmp cmp) + { + return std::max(l, std::min(n, h, cmp), cmp); + } +}; +#endif + +#if ((defined __linux__) || (defined __APPLE__)) +#define stricmp strcasecmp +#define strnicmp strncasecmp +#endif // LINUX diff --git a/src/Native Compiler/gpl-3.0.txt b/src/Native Compiler/gpl-3.0.txt new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/src/Native Compiler/gpl-3.0.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/src/Native Compiler/scriptcomp.h b/src/Native Compiler/scriptcomp.h new file mode 100644 index 0000000..0a1032a --- /dev/null +++ b/src/Native Compiler/scriptcomp.h @@ -0,0 +1,644 @@ +// +// SPDX-License-Identifier: GPL-3.0 +// +// This file is part of the NWScript compiler open source release. +// +// The initial source release is licensed under GPL-3.0. +// +// All subsequent changes you submit are required to be licensed under MIT. +// +// However, the project overall will still be GPL-3.0. +// +// The intent is for the base game to be able to pick up changes you explicitly +// submit for inclusion painlessly, while ensuring the overall project source code +// remains available for everyone. +// + +#pragma once + +#include + +#include "exobase.h" +#include "scripterrors.h" + +// Classes defined in scriptinternal.h +class CScriptParseTreeNode; +class CScriptParseTreeNodeBlock; +class CScriptCompilerStackEntry; +class CScriptCompilerIdListEntry; +class CScriptSourceFile; +class CScriptCompilerVarStackEntry; +class CScriptCompilerStructureEntry; +class CScriptCompilerStructureFieldEntry; +class CScriptCompilerSymbolTableEntry; +class CScriptCompilerKeyWordEntry; +class CScriptCompilerIdentifierHashTableEntry; + +// Defines required for static size of values. +#define CSCRIPTCOMPILER_MAX_TABLE_FILENAMES 512 +#define CSCRIPTCOMPILER_MAX_TOKEN_LENGTH 8192 +#define CSCRIPTCOMPILER_MAX_INCLUDE_LEVELS 16 +#define CSCRIPTCOMPILER_MAX_RUNTIME_VARS 8192 + +#define CSCRIPTCOMPILERIDLISTENTRY_MAX_PARAMETERS 32 + +// +// Compiler optimization flags. +// Note that some optimizations might interfere with NDB generation. +// + +// Removes any functions that cannot possibly be called by any codepath +#define CSCRIPTCOMPILER_OPTIMIZE_DEAD_FUNCTIONS 0x00000001 +// Merges constant expressions into a single constant where possible. +// Note: Only affects runtime expressions, assignments to const variables are always folded. +#define CSCRIPTCOMPILER_OPTIMIZE_FOLD_CONSTANTS 0x00000002 +// Post processes generated instructions to merge sequences into shorter equivalents +#define CSCRIPTCOMPILER_OPTIMIZE_MELD_INSTRUCTIONS 0x00000004 + +#define CSCRIPTCOMPILER_OPTIMIZE_NOTHING 0x00000000 +#define CSCRIPTCOMPILER_OPTIMIZE_EVERYTHING 0xFFFFFFFF + + +class CScriptCompilerIncludeFileStackEntry +{ +public: + CExoString m_sCompiledScriptName; + CExoString m_sSourceScript; + + // A stack of internal variables (used when processing includes to preserve + // the previous state of the scripting language). + int32_t m_nLine; + int32_t m_nCharacterOnLine; + int32_t m_nTokenStatus; + int32_t m_nTokenCharacters; +}; + +class CScriptCompiler; + +// Functions you need to implement when invoking script compiler. +// Default impl for game is in scriptcompapi.cpp. +struct CScriptCompilerAPI +{ + CScriptCompilerAPI() { memset(this, 0, sizeof(CScriptCompilerAPI)); } + + // Update resman resource dir (reload/reindex). + BOOL (*ResManUpdateResourceDirectory)(const char* sAlias); + + // Return 0 if OK, or error STRREF on failure (scripterrors.h) + int32_t (*ResManWriteToFile)(const char* sFileName, RESTYPE nResType, const uint8_t* pData, size_t nSize, bool bBinary); + + // Read the given filename+restype from resman, and return a zero-terminated string containing + // the content (up to the first null terminator). Returns nullptr if the file cannot be read/loaded. + // The returned string is a global static buffer and must not be freed by you. + // Repeated calls to this function will replace the buffer. + // Please see the default impl in scriptcompapi.cpp for load semantics (e.g. it will serve up .nss files for .css files when not found). + const char* (*ResManLoadScriptSourceFile)(const char* sFileName, RESTYPE nResType); + + // Returns zero-terminated string, or "" if lookup failed. + // The returned string is a global static buffer and must not be freed by you. + // Repeated calls to this function will replace the buffer. + const char* (*TlkResolve)(STRREF strRef); +}; + + +class CScriptCompiler +{ + // This can be left unimplemented on custom compilers, as long as you provide + // callbacks to the constructor, + static CScriptCompilerAPI MakeDefaultAPI(); + + CScriptCompilerAPI m_cAPI; + + // ************************************************************************* +public: + // ************************************************************************* + explicit CScriptCompiler(RESTYPE nSource, RESTYPE nCompiled, RESTYPE nDebug, + // When not given, uses default API as defined in scriptcompapi.cpp. + // This is a concession to not changing all callsites. + CScriptCompilerAPI api = MakeDefaultAPI()); + ~CScriptCompiler(); + + /////////////////////////////////////////////////////////////////////// + void SetIdentifierSpecification(const CExoString &sLanguageSource); + //--------------------------------------------------------------------- + // Desc.: This routine will set the identifier specification that you + // want to use when compiling. + // + // sLanguageSource: (IN) An .nss file containing the definition of + // the language + // + /////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////// + void SetOutputAlias(const CExoString &sAlias); + //--------------------------------------------------------------------- + // Desc.: This routine will set the alias used by CompileFile to + // determine where to write the output code. + // + // sAlias: (See ExoAlias for a description of how the aliases are + // used.) This alias will be used by ExoResMan to target + // the output of the code. + // + /////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////// + void SetGenerateDebuggerOutput(int32_t nValue); + //--------------------------------------------------------------------- + // Desc.: This routine will determine whether a .ndb file will also + // be generated at the same time as the .ncs file. + // + // nValue: (IN) A value to determine the type of output. + // + // 0: No .ndb file will be generated. + // 1: An .ndb file will be generated. This information, in + // addition to the .ncs file, will allow the debugger to + // behave correctly. + // + /////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////// + void SetOptimizationFlags(uint32_t nFlags) { m_nOptimizationFlags = nFlags; } + uint32_t GetOptimizationFlags() { return m_nOptimizationFlags; } + + /////////////////////////////////////////////////////////////////////// + void SetAutomaticCleanUpAfterCompiles(BOOL bValue); + //--------------------------------------------------------------------- + // Desc.: This routine will determine whether CompileFile will make + // automatic updates to the Output Directory in the resouce + // manager, and clean up large buffers used to compile a file. + // + // nValue: (IN) A value to determine the type of output. + // + // FALSE: Does not attempt to call the resource manager to + // update the output directory. If you set this value + // to false, you are responsible for calling + // CScriptCompiler::CleanUpAfterCompiles() before + // you attempt to use these files. + // + // TRUE (default): After each .ncs and .ndb file is created, + // the directory where the file is created is updated + // in the resource manager and some "memory leaks" are + // removed. This is the best choice if you don't know + // how to use this. (Other than actually talking to + // Mark, or looking in the Test_CompileAllFilesInDirectory + // function for how to use a value of FALSE correctly!) + // + /////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////// + void CleanUpAfterCompiles(); + //--------------------------------------------------------------------- + // Desc.: This routine will update the output directory (set by + // CScriptCompiler::SetOutputAlias). This is required to be + // done before you can access any files compiled with + // CleanUpAfterCompiles set to FALSE. It will also delete any + // buffers created during compilation that are required. + /////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////// + void SetCompileConditionalFile(BOOL nValue); + //--------------------------------------------------------------------- + // Desc.: This routine will set the type of output generated by the + // parsing routine. + // + // nValue: (IN) A value to determine the type of output. + // + // 0: Will compile files that have a "void main()" in them. + // + // 1: Will compile files that have an + // "int StartingConditional()" in them. + // + // An error will result, depending on the value of this + // parameter, if the wrong type of file is attempted to be + // passed through CompileFile() + // + /////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////// + void SetCompileConditionalOrMain(BOOL nValue); + //--------------------------------------------------------------------- + // Desc.: This routine will set the type of output generated by the + // parsing routine. + // + // bValue: (IN) A value to determine the type of output. + // + // 0: Will compile files based on CompileConditionalFile. + // + // 1: Will compile files that have a void main() by + // preference. It will compile int StartingConditional() + // files if we can't find a void main() function. + // + /////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////// + int32_t CompileFile(const CExoString &sFileName); + //--------------------------------------------------------------------- + // Desc.: This routine will generate whether the code in the file + // specified in sFileName is a valid Script, and then write + // the output to sFileName.ncs + // + // sFileName: (IN) The name of the file to compile. + // + // Returns: 0 if the script is valid and output has been generated, + // and a value other than zero if there is an error during + // compilation! + // + /////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////// + int32_t CompileScriptChunk(const CExoString &sScriptChunk, BOOL bWrapIntoMain); + //--------------------------------------------------------------------- + // Desc.: This routine will attempt to generate code from the string + // passed in. + // + // sFileName: (IN) The script to compile. + // + // Returns: 0 if the script is valid, and a value other than zero if + // there is an error during parsing. + /////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////// + int32_t CompileScriptConditional(const CExoString &sScriptConditional); + //--------------------------------------------------------------------- + // Desc.: This routine will generate whether the code in the string + // specified (sStringData) is a valid Script. + // + // sFileName: (IN) The script to compile. + // + // Returns: 0 if the script is valid, and a value other than zero if + // there is an error during parsing. + /////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////// + int32_t GetCompiledScriptCode(char **pnCode, int32_t *nCodeSize); + //--------------------------------------------------------------------- + // Desc.: This routine will allow you to access the code generated by + // CompileScript + // + // pnCode [OUT]: Will point to the new location of the code. + // + // nCodeSize [OUT]: Will point to the size of the new code. + // + // Returns: 0 if successful, non-zero if there is no code available. + /////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////// + void ClearCompiledScriptCode(); + //--------------------------------------------------------------------- + // Desc.: This routine will remove the code that has been run. + /////////////////////////////////////////////////////////////////////// + + // Returns the captured error string for use by tools that don't need + // to access the log. + CExoString *GetCapturedError() { return &m_sCapturedError; } + + STRREF GetCapturedErrorStrRef() const { return m_nCapturedErrorStrRef; } + + int32_t WriteFinalCodeToFile(const CExoString &sFileName); + int32_t WriteDebuggerOutputToFile(CExoString sFileName); + + // ************************************************************************* +private: + // ************************************************************************* + + RESTYPE m_nResTypeSource; + RESTYPE m_nResTypeCompiled; + RESTYPE m_nResTypeDebug; + + void Initialize(); + void ShutDown(); + + uint32_t HashString(const CExoString &sString); + uint32_t HashString(const char *pString); + void InitializePreDefinedStructures(); + void InitializeIncludeFile(int32_t nCompileFileLevel); + void ShutdownIncludeFile(int32_t nCompileFileLevel); + + void TokenInitialize(); + + void PushSRStack(int32_t nState, int32_t nRule, int32_t nTerm, + CScriptParseTreeNode *pCurrentTree); + int32_t PopSRStack(int32_t *nState,int32_t *nRule, int32_t *nTerm, + CScriptParseTreeNode **pCurrentTree, + CScriptParseTreeNode **pReturnTree ); + void ModifySRStackReturnTree( CScriptParseTreeNode *pReturnTree ); + + int32_t GenerateParseTree(); + + float ParseFloatFromTokenString(); + + int32_t HandleToken(); + int32_t TestIdentifierToken(); + int32_t HandleIdentifierToken(); + int32_t m_nKeyWords; + CScriptCompilerKeyWordEntry *m_pcKeyWords; + + int32_t ParseCharacterNumeric(int32_t ch); + int32_t ParseCharacterPeriod(int32_t chNext); + int32_t ParseCharacterSlash(int32_t chNext); + int32_t ParseCharacterAsterisk(int32_t chNext); + int32_t ParseCharacterAmpersand(int32_t chNext); + int32_t ParseCharacterVerticalBar(int32_t chNext); + int32_t ParseCharacterAlphabet(int32_t ch); + int32_t ParseStringCharacter(int32_t ch, int32_t chNext, char *pScript, int32_t nScriptLength); + int32_t ParseRawStringCharacter(int32_t ch, int32_t chNext); + int32_t ParseCharacterQuotationMark(); + int32_t ParseCharacterHyphen(int32_t chNext); + int32_t ParseCharacterLeftBrace(); + int32_t ParseCharacterRightBrace(); + int32_t ParseCharacterLeftBracket(); + int32_t ParseCharacterRightBracket(); + int32_t ParseCharacterLeftSquareBracket(); + int32_t ParseCharacterRightSquareBracket(); + int32_t ParseCharacterLeftAngle(int32_t chNext); + int32_t ParseCharacterRightAngle(int32_t chNext); + int32_t ParseCharacterExclamationPoint(int32_t chNext); + int32_t ParseCharacterEqualSign(int32_t chNext); + int32_t ParseCharacterPlusSign(int32_t chNext); + int32_t ParseCharacterPercentSign(int32_t chNext); + int32_t ParseCharacterSemicolon(); + int32_t ParseCharacterComma(); + int32_t ParseCharacterCarat(int32_t chNext); + int32_t ParseCharacterTilde(); + int32_t ParseCharacterEllipsis(); + int32_t ParseCharacterQuestionMark(); + int32_t ParseCharacterColon(); + + int32_t ParseCommentedOutCharacter(int32_t ch); + + int32_t ParseNextCharacter(int32_t ch, int32_t chNext, char *pScript, int32_t nScriptLength); + + int32_t PrintParseSourceError(int32_t nParseCharacterError); + int32_t ParseSource(char *pScript, int32_t nScriptLength); + + int32_t OutputError(int32_t nError, CExoString *psFileName, int32_t nLineNumber, const CExoString &sErrorText); + CScriptParseTreeNode *DuplicateScriptParseTree(CScriptParseTreeNode *pNode); + CScriptParseTreeNode *CreateScriptParseTreeNode(int32_t nNodeOperation, CScriptParseTreeNode *pNodeLeft, CScriptParseTreeNode *pNodeRight); + BOOL CheckForBadLValue(CScriptParseTreeNode *pNode); + void DeleteScriptParseTreeNode(CScriptParseTreeNode *pParseTreeNode); + CScriptParseTreeNode *GetNewScriptParseTreeNode(); + + int32_t m_nParseTreeNodeBlockEmptyNodes; + CScriptParseTreeNodeBlock *m_pCurrentParseTreeNodeBlock; + CScriptParseTreeNodeBlock *m_pParseTreeNodeBlockHead; + CScriptParseTreeNodeBlock *m_pParseTreeNodeBlockTail; + + int32_t OutputWalkTreeError(int32_t nError, CScriptParseTreeNode *pNode); + int32_t PreVisitGenerateCode(CScriptParseTreeNode *pNode); + int32_t InVisitGenerateCode(CScriptParseTreeNode *pNode); + int32_t PostVisitGenerateCode(CScriptParseTreeNode *pNode); + + void WriteByteSwap32(char *buffer, int32_t value); + int32_t ReadByteSwap32(char *buffer); + char *EmitInstruction(uint8_t nOpCode, uint8_t nAuxCode = 0, int32_t nDataSize = 0); + void EmitModifyStackPointer(int32_t nModifyBy); + + CExoString **m_ppsParseTreeFileNames; + int32_t m_nNextParseTreeFileName; + int32_t m_nCurrentParseTreeFileName; + void StartLineNumberAtBinaryInstruction(int32_t nFileReference, int32_t nLineNumber, int32_t nBinaryInstruction); + void EndLineNumberAtBinaryInstruction(int32_t nFileReference, int32_t nLineNumber, int32_t nBinaryInstruction); + void ResolveDebuggingInformation(); + void ResolveDebuggingInformationForIdentifier(int32_t nIdentifier); + + int32_t m_nCurrentLineNumber; + int32_t m_nCurrentLineNumberFileReference; + int32_t m_nCurrentLineNumberReferences; + int32_t m_nCurrentLineNumberBinaryStartInstruction; + int32_t m_nCurrentLineNumberBinaryEndInstruction; + + int32_t m_nTableFileNames; + CExoString m_psTableFileNames[CSCRIPTCOMPILER_MAX_TABLE_FILENAMES]; + int32_t m_nLineNumberEntries; + int32_t m_nFinalLineNumberEntries; + std::vector m_pnTableInstructionFileReference; + std::vector m_pnTableInstructionLineNumber; + std::vector m_pnTableInstructionBinaryStart; + std::vector m_pnTableInstructionBinaryEnd; + std::vector m_pnTableInstructionBinaryFinal; + std::vector m_pnTableInstructionBinarySortedOrder; + + int32_t m_nSymbolTableVariables; + int32_t m_nFinalSymbolTableVariables; + std::vector m_pnSymbolTableVarType; + std::vector m_psSymbolTableVarName; + std::vector m_psSymbolTableVarStructureName; + std::vector m_pnSymbolTableVarStackLoc; + std::vector m_pnSymbolTableVarBegin; + std::vector m_pnSymbolTableVarEnd; + std::vector m_pnSymbolTableBinaryFinal; + std::vector m_pnSymbolTableBinarySortedOrder; + + + void DeleteCompileStack(); + void DeleteParseTree(BOOL bStack, CScriptParseTreeNode *pNode); + int32_t WalkParseTree(CScriptParseTreeNode *pNode); + + void InitializeFinalCode(); + void FinalizeFinalCode(); + int32_t GenerateFinalCodeFromParseTree(CExoString sFileName); + + CExoString GenerateDebuggerTypeAbbreviation(int32_t nType, CExoString sStructureName); + + BOOL m_bCompileConditionalFile; + BOOL m_bOldCompileConditionalFile; + BOOL m_bCompileConditionalOrMain; + CExoString m_sLanguageSource; + CExoString m_sOutputAlias; + + int32_t m_nLines; + int32_t m_nCharacterOnLine; + + // Variable for storing the values for each character (assembled in HashString) for identifiers + int32_t *m_pnHashString; + // The actual hash table. + CScriptCompilerIdentifierHashTableEntry *m_pIdentifierHashTable; + uint32_t HashManagerAdd(uint32_t nType, uint32_t nTypeIndice); + uint32_t HashManagerDelete(uint32_t nType, uint32_t nTypeIndice); + int32_t GetHashEntryByName(const char *psIdentifierName); + + // Status of the current token + int32_t m_nTokenStatus; + int32_t m_nTokenCharacters; + char m_pchToken[CSCRIPTCOMPILER_MAX_TOKEN_LENGTH]; + + // Status of the current "compile stack" + CScriptCompilerStackEntry *m_pSRStack; + int32_t m_nSRStackEntries; + int32_t m_nSRStackStates; + + + // Identifiers (read from language definition file) + int32_t m_bCompileIdentifierList; + int32_t m_bCompileIdentifierConstants; + int32_t m_nIdentifierListState; + int32_t m_nIdentifierListVector; + int32_t m_nIdentifierListEngineStructure; + int32_t m_nIdentifierListReturnType; + CScriptCompilerIdListEntry *m_pcIdentifierList; + int32_t m_nOccupiedIdentifiers; + int32_t m_nMaxPredefinedIdentifierId; + int32_t m_nPredefinedIdentifierOrder; + + int32_t PrintParseIdentifierFileError(int32_t nParseCharacterError); + int32_t ParseIdentifierFile(); + int32_t GenerateIdentifierList(); + int32_t AddUserDefinedIdentifier(CScriptParseTreeNode *pFunctionDeclaration, BOOL bFunctionImplementation); + void ClearUserDefinedIdentifiers(); + + + // A stack of includes. + int32_t m_nCompileFileLevel; + CScriptCompilerIncludeFileStackEntry m_pcIncludeFileStack[CSCRIPTCOMPILER_MAX_INCLUDE_LEVELS]; + + + // A Variable Stack + int32_t m_nVarStackRecursionLevel; + CScriptCompilerVarStackEntry *m_pcVarStackList; + int32_t m_nOccupiedVariables; + int32_t m_nVarStackVariableType; + CExoString m_sVarStackVariableTypeName; + + // Global variable, Structure and structure field information. + CScriptCompilerStructureEntry *m_pcStructList; + CScriptCompilerStructureFieldEntry *m_pcStructFieldList; + int32_t m_nMaxStructures; + int32_t m_nMaxStructureFields; + int32_t m_nStructureDefinition; + int32_t m_nStructureDefinitionFieldStart; + int32_t GetStructureField(const CExoString &sStructureName, const CExoString &sFieldName); + int32_t GetStructureSize(const CExoString &sStructureName); + int32_t GetIdentifierByName(const CExoString &sIdentifierName); + + BOOL m_bGlobalVariableDefinition; + int32_t m_nGlobalVariables; + int32_t m_nGlobalVariableSize; // size of all global variables in bytes. + CScriptParseTreeNode *m_pGlobalVariableParseTree; + int32_t AddToGlobalVariableList(CScriptParseTreeNode *pGlobalVariableNode); + + BOOL ConstantFoldNode(CScriptParseTreeNode *pNode, BOOL bForce=FALSE); + + BOOL m_bConstantVariableDefinition; + + int32_t m_nLoopIdentifier; + int32_t m_nLoopStackDepth; + + int32_t m_nSwitchLevel; + int32_t m_nSwitchIdentifier; + int32_t m_nSwitchStackDepth; + + CExoString m_sUndefinedIdentifier; + + BOOL m_bSwitchLabelDefault; + int32_t m_nSwitchLabelNumber; + int32_t m_nSwitchLabelArraySize; + int32_t* m_pnSwitchLabelStatements; + void InitializeSwitchLabelList(); + int32_t TraverseTreeForSwitchLabels(CScriptParseTreeNode *pNode); + void ClearSwitchLabelList(); + int32_t GenerateCodeForSwitchLabels(CScriptParseTreeNode *pNode); + + int32_t GenerateIdentifiersFromConstantVariables(CScriptParseTreeNode *pNode); + + // Engine-defined structures that can be compiled, but + // we don't actually do a lot with them during compilation + int32_t m_nNumEngineDefinedStructures; + BOOL *m_pbEngineDefinedStructureValid; + CExoString *m_psEngineDefinedStructureName; + + int32_t m_bAssignmentToVariable; // we're in the middle of processing an "assignment to a + // variable. This is a good thing. + BOOL m_bInStructurePart; // In an expression, we want to prevent the part name from + // being looked up as a variable ... that'll be done at the + // structure part node above this one. + + int32_t FoundReturnStatementOnAllBranches(CScriptParseTreeNode *pNode); + + BOOL m_bFunctionImp; + CExoString m_sFunctionImpName; + int32_t m_nFunctionImpReturnType; + CExoString m_sFunctionImpReturnStructureName; + int32_t m_nFunctionImpAbortStackPointer; + + // The Run-Time Stacks for Stuff. + int32_t m_nStackCurrentDepth; + char m_pchStackTypes[CSCRIPTCOMPILER_MAX_RUNTIME_VARS]; + + void AddVariableToStack(int32_t nVariableType, CExoString *psVariableTypeName, BOOL bGenerateCode); + void AddStructureToStack(const CExoString &sStructureName, BOOL bGenerateCode); + + void AddToSymbolTableVarStack(int32_t nOccupiedIdentifier, int32_t nStackCurrentDepth, int32_t nGlobalVariableSize); + void RemoveFromSymbolTableVarStack(int32_t nOccupiedIdentifier, int32_t nStackCurrentDepth, int32_t nGlobalVariableSize); + + int32_t m_nRunTimeIntegers; + int32_t m_nRunTimeFloats; + int32_t m_nRunTimeStrings; + int32_t m_nRunTimeObjects; + int32_t m_nRunTimeActions; + + // The symbols to be added in between the code generation and output passes. + + int32_t m_nSymbolQueryListSize; + int32_t m_nSymbolQueryList; + CScriptCompilerSymbolTableEntry *m_pSymbolQueryList; + + int32_t m_nSymbolLabelListSize; + int32_t m_nSymbolLabelList; + CScriptCompilerSymbolTableEntry *m_pSymbolLabelList; + int32_t m_pSymbolLabelStartEntry[512]; + + CExoString GetFunctionNameFromSymbolSubTypes(int32_t nSubType1,int32_t nSubType2); + int32_t AddSymbolToQueryList(int32_t nLocationPointer, int32_t nSymbolType, int32_t nSymbolSubType1, int32_t nSymbolSubType2 = 0); + int32_t AddSymbolToLabelList(int32_t nLocationPointer, int32_t nSymbolType, int32_t nSymbolSubType1, int32_t nSymbolSubType2 = 0); + + void ClearAllSymbolLists(); + + int32_t CleanUpDuringCompile(int32_t nReturnValue); + int32_t CleanUpAfterCompile(int32_t nReturnValue,CScriptParseTreeNode *pReturnTree); + + // Generation of Code + int32_t m_nGenerateDebuggerOutput; + + BOOL m_bAutomaticCleanUpAfterCompiles; + uint32_t m_nOptimizationFlags; + int32_t m_nTotalCompileNodes; + BOOL m_bCompilingConditional; + char *m_pchOutputCode; + int32_t m_nOutputCodeSize; + int32_t m_nOutputCodeLength; + std::vector m_aOutputCodeInstructionBoundaries; + + char *InstructionLookback(uint32_t last=1); + + // Resolving code to its proper location ... some buffers! + char *m_pchResolvedOutputBuffer; + int32_t m_nResolvedOutputBufferSize; + + // Generation of Debug Code + char *m_pchDebuggerCode; + int32_t m_nDebuggerCodeSize; + int32_t m_nDebuggerCodeLength; + + // These are used when parsing "operation action" commands to keep track of + // what the actual parameters are. + char m_pchActionParameters[CSCRIPTCOMPILERIDLISTENTRY_MAX_PARAMETERS]; + CExoString m_pchActionParameterStructureNames[CSCRIPTCOMPILERIDLISTENTRY_MAX_PARAMETERS]; + + // Second Stage Of Code Generation + int32_t InstallLoader(); + CScriptParseTreeNode *InsertGlobalVariablesInParseTree(CScriptParseTreeNode *pOldTree); + int32_t OutputIdentifierError(const CExoString &sFunctionName, int32_t nError, int32_t nFileStackDrop = 0); + int32_t ValidateLocationOfIdentifier(const CExoString &sFunctionName); + int32_t DetermineLocationOfCode(); + int32_t ResolveLabels(); + int32_t WriteResolvedOutput(); + + int32_t m_nFinalBinarySize; + + // Error generation. + + CExoString m_sCapturedError; + STRREF m_nCapturedErrorStrRef; + + void* m_pUserData; +}; diff --git a/src/Native Compiler/scriptcompcore.cpp b/src/Native Compiler/scriptcompcore.cpp new file mode 100644 index 0000000..69e959d --- /dev/null +++ b/src/Native Compiler/scriptcompcore.cpp @@ -0,0 +1,1910 @@ +// +// SPDX-License-Identifier: GPL-3.0 +// +// This file is part of the NWScript compiler open source release. +// +// The initial source release is licensed under GPL-3.0. +// +// All subsequent changes you submit are required to be licensed under MIT. +// +// However, the project overall will still be GPL-3.0. +// +// The intent is for the base game to be able to pick up changes you explicitly +// submit for inclusion painlessly, while ensuring the overall project source code +// remains available for everyone. +// + +/////////////////////////////////////////////////////////////////////////////// +// BIOWARE CORP. CONFIDENTIAL INFORMATION. // +// COPYRIGHT BIOWARE CORP. ALL RIGHTS RESERVED // +/////////////////////////////////////////////////////////////////////////////// + +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Script Project +//:: +//:: Copyright (c) 2002, BioWare Corp. +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: ScriptCompCore.cpp +//:: +//:: Implementation of conversion from scripting language to virtual machine +//:: code. +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Created By: Mark Brockington +//:: Created On: Oct. 8, 2002 +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: This file contains "ported" code (used when writing out floating point +//:: numbers on the MacIntosh platforms. +//:: +//:://///////////////////////////////////////////////////////////////////////// + +#include +#include + +// external header files +#include "exobase.h" +#include "scriptcomp.h" + +// internal header files +#include "scriptinternal.h" + +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: class CScriptCompilerIdListEntry +//:: +//:://///////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompilerIdListEntry::CScriptCompilerIdListEntry() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 05/18/2001 +// Description: Default constructor for class. +/////////////////////////////////////////////////////////////////////////////// + +CScriptCompilerIdListEntry::CScriptCompilerIdListEntry() +{ + m_nIdentifierType = 0; + m_nReturnType = 0; + m_bImplementationInPlace = FALSE; + + m_nIntegerData = 0; + m_fFloatData = 0.0f; + m_fVectorData[0] = 0.0f; + m_fVectorData[1] = 0.0f; + m_fVectorData[2] = 0.0f; + m_nParameters = 0; + m_nNonOptionalParameters = 0; + + m_nParameterSpace = 0; + m_pchParameters = NULL; + m_psStructureParameterNames = NULL; + m_pbOptionalParameters = NULL; + m_pnOptionalParameterIntegerData = NULL; + m_pfOptionalParameterFloatData = NULL; + m_psOptionalParameterStringData = NULL; + m_poidOptionalParameterObjectData = NULL; + m_pfOptionalParameterVectorData = NULL; + + m_nBinarySourceStart = -1; + m_nBinarySourceFinish = -1; + m_nBinaryDestinationStart = -1; + m_nBinaryDestinationFinish = -1; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompilerIdListEntry::~CScriptCompilerIdListEntry() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/10/2001 +// Description: Default destructor for class. +/////////////////////////////////////////////////////////////////////////////// +CScriptCompilerIdListEntry::~CScriptCompilerIdListEntry() +{ + + // Delete the allocated arrays. + if (m_pchParameters) + { + delete[] m_pchParameters; + } + if (m_psStructureParameterNames) + { + delete[] m_psStructureParameterNames; + } + if (m_pbOptionalParameters) + { + delete[] m_pbOptionalParameters; + } + if (m_pnOptionalParameterIntegerData) + { + delete[] m_pnOptionalParameterIntegerData; + } + if (m_pfOptionalParameterFloatData) + { + delete[] m_pfOptionalParameterFloatData; + } + if (m_psOptionalParameterStringData) + { + delete[] m_psOptionalParameterStringData; + } + if (m_poidOptionalParameterObjectData) + { + delete[] m_poidOptionalParameterObjectData; + } + if (m_pfOptionalParameterVectorData) + { + delete[] m_pfOptionalParameterVectorData; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompilerIdListEntry::ExpandParameterSpace() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 05/18/2001 +// Description: Used to expand the parameter space (when required). +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompilerIdListEntry::ExpandParameterSpace() +{ + int32_t nNewParameterSpace; + + if (m_nParameterSpace == CSCRIPTCOMPILERIDLISTENTRY_MAX_PARAMETERS) + { + return STRREF_CSCRIPTCOMPILER_ERROR_TOO_MANY_PARAMETERS_ON_FUNCTION; + } + + if (m_nParameterSpace == 0) + { + nNewParameterSpace = 4; + } + else + { + nNewParameterSpace = m_nParameterSpace * 2; + } + + // Declare the new arrays. + char *pchNewParameters = new char[nNewParameterSpace]; + CExoString *psNewStructureParameterNames = new CExoString[nNewParameterSpace]; + BOOL *pbNewOptionalParameters = new BOOL[nNewParameterSpace]; + int32_t *pnNewOptionalParameterIntegerData = new int32_t[nNewParameterSpace]; + float *pfNewOptionalParameterFloatData = new float[nNewParameterSpace]; + CExoString *psNewOptionalParameterStringData = new CExoString[nNewParameterSpace]; + OBJECT_ID *poidNewOptionalParameterObjectData = new OBJECT_ID[nNewParameterSpace]; + float *pfNewOptionalParameterVectorData = new float[nNewParameterSpace * 3]; + + // Copy the old values to the new arrays. + int32_t nCount; + + if (m_nParameterSpace != 0) + { + for (nCount = 0; nCount < m_nParameterSpace; nCount++) + { + pchNewParameters[nCount] = m_pchParameters[nCount]; + } + for (nCount = 0; nCount < m_nParameterSpace; nCount++) + { + psNewStructureParameterNames[nCount] = m_psStructureParameterNames[nCount]; + } + for (nCount = 0; nCount < m_nParameterSpace; nCount++) + { + pbNewOptionalParameters[nCount] = m_pbOptionalParameters[nCount]; + } + for (nCount = 0; nCount < m_nParameterSpace; nCount++) + { + pnNewOptionalParameterIntegerData[nCount] = m_pnOptionalParameterIntegerData[nCount]; + } + for (nCount = 0; nCount < m_nParameterSpace; nCount++) + { + pfNewOptionalParameterFloatData[nCount] = m_pfOptionalParameterFloatData[nCount]; + } + for (nCount = 0; nCount < m_nParameterSpace; nCount++) + { + psNewOptionalParameterStringData[nCount] = m_psOptionalParameterStringData[nCount]; + } + for (nCount = 0; nCount < m_nParameterSpace; nCount++) + { + poidNewOptionalParameterObjectData[nCount] = m_poidOptionalParameterObjectData[nCount]; + } + for (nCount = 0; nCount < m_nParameterSpace * 3; nCount++) + { + pfNewOptionalParameterVectorData[nCount] = m_pfOptionalParameterVectorData[nCount]; + } + } + + // Oh, yeah, it's a good idea to declare what the values are! + for (nCount = m_nParameterSpace; nCount < nNewParameterSpace; nCount++) + { + pchNewParameters[nCount] = 0; + psNewStructureParameterNames[nCount] = ""; + pbNewOptionalParameters[nCount] = FALSE; + pnNewOptionalParameterIntegerData[nCount] = 0; + pfNewOptionalParameterFloatData[nCount] = 0.0f; + psNewOptionalParameterStringData[nCount] = ""; + poidNewOptionalParameterObjectData[nCount] = INVALID_OBJECT_ID; + } + + for (nCount = m_nParameterSpace * 3; nCount < nNewParameterSpace * 3; nCount++) + { + pfNewOptionalParameterVectorData[nCount] = 0.0f; + } + + m_nParameterSpace = nNewParameterSpace; + + // Delete the old arrays. + if (m_pchParameters) + { + delete[] m_pchParameters; + } + if (m_psStructureParameterNames) + { + delete[] m_psStructureParameterNames; + } + if (m_pbOptionalParameters) + { + delete[] m_pbOptionalParameters; + } + if (m_pnOptionalParameterIntegerData) + { + delete[] m_pnOptionalParameterIntegerData; + } + if (m_pfOptionalParameterFloatData) + { + delete[] m_pfOptionalParameterFloatData; + } + if (m_psOptionalParameterStringData) + { + delete[] m_psOptionalParameterStringData; + } + if (m_poidOptionalParameterObjectData) + { + delete[] m_poidOptionalParameterObjectData; + } + if (m_pfOptionalParameterVectorData) + { + delete[] m_pfOptionalParameterVectorData; + } + + m_pchParameters = pchNewParameters; + m_psStructureParameterNames = psNewStructureParameterNames; + m_pbOptionalParameters = pbNewOptionalParameters; + m_pnOptionalParameterIntegerData = pnNewOptionalParameterIntegerData; + m_pfOptionalParameterFloatData = pfNewOptionalParameterFloatData; + m_psOptionalParameterStringData = psNewOptionalParameterStringData; + m_poidOptionalParameterObjectData = poidNewOptionalParameterObjectData; + m_pfOptionalParameterVectorData = pfNewOptionalParameterVectorData; + + return 0; +} + +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: class CScriptCompiler +//:: +//:://///////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::CScriptCompiler() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Default constructor for class. +/////////////////////////////////////////////////////////////////////////////// + +CScriptCompiler::CScriptCompiler(RESTYPE nSource, RESTYPE nCompiled, RESTYPE nDebug, CScriptCompilerAPI api) +{ + m_cAPI = api; + + m_nGenerateDebuggerOutput = 1; + + m_sLanguageSource = ""; + m_sOutputAlias = "OVERRIDE"; + m_nOptimizationFlags = CSCRIPTCOMPILER_OPTIMIZE_EVERYTHING; + m_nIdentifierListState = 0; + + m_pSRStack = NULL; + m_pcIdentifierList = NULL; + m_pcVarStackList = NULL; + m_pcStructList = NULL; + m_pcStructFieldList = NULL; + m_pcKeyWords = NULL; + m_pSymbolQueryList = NULL; + m_pSymbolLabelList = NULL; + m_pIdentifierHashTable = NULL; + m_ppsParseTreeFileNames = NULL; + + m_pParseTreeNodeBlockHead = NULL; + m_pParseTreeNodeBlockTail = NULL; + m_nParseTreeNodeBlockEmptyNodes = -1; + m_pCurrentParseTreeNodeBlock = NULL; + + m_pnHashString = new int32_t[256]; + int32_t nHashCount; + for (nHashCount = 0; nHashCount < 256; nHashCount++) + { + m_pnHashString[nHashCount] = rand(); + } + + m_pIdentifierHashTable = new CScriptCompilerIdentifierHashTableEntry[CSCRIPTCOMPILER_SIZE_IDENTIFIER_HASH_TABLE]; + + m_nCompileFileLevel = 0; + m_bCompileConditionalFile = FALSE; + m_bOldCompileConditionalFile = FALSE; + m_bCompileConditionalOrMain = FALSE; + m_bAutomaticCleanUpAfterCompiles = TRUE; + + m_nNumEngineDefinedStructures = 0; + m_pbEngineDefinedStructureValid = NULL; + m_psEngineDefinedStructureName = NULL; + m_nIdentifierListEngineStructure = 0; + + m_pchOutputCode = NULL; + m_pchDebuggerCode = NULL; + m_pchResolvedOutputBuffer = NULL; + m_nResolvedOutputBufferSize = 0; + + m_nResTypeSource = nSource; + m_nResTypeCompiled = nCompiled; + m_nResTypeDebug = nDebug; + + Initialize(); + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::~CScriptCompiler() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Default destructor for class. +/////////////////////////////////////////////////////////////////////////////// + +CScriptCompiler::~CScriptCompiler() +{ + ShutDown(); + + if (m_pIdentifierHashTable) + { + delete[] m_pIdentifierHashTable; + m_pIdentifierHashTable = NULL; + } + + if (m_pnHashString) + { + delete[] m_pnHashString; + m_pnHashString = NULL; + } + + // Delete linked list of ParseTreeNodeBlock structures. + if (m_pParseTreeNodeBlockHead) + { + CScriptParseTreeNodeBlock *pBlockPtr = m_pParseTreeNodeBlockHead; + + while (pBlockPtr != NULL) + { + CScriptParseTreeNodeBlock *pCurrentPtr = pBlockPtr; + pBlockPtr = pBlockPtr->m_pNextBlock; + delete pCurrentPtr; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ShutDown() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Removes memory allocated by CScriptCompiler during process +// of running. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::ShutDown() +{ + + if (m_pSRStack) + { + delete[] m_pSRStack; + } + + if (m_pcIdentifierList) + { + delete[] m_pcIdentifierList; + } + + if (m_pcVarStackList) + { + delete[] m_pcVarStackList; + } + + int32_t nCount = CSCRIPTCOMPILER_MAX_KEYWORDS - 1; + while (nCount >= 0) + { + HashManagerDelete(CSCRIPTCOMPILER_HASH_MANAGER_TYPE_KEYWORD, nCount); + --nCount; + } + + if (m_pcKeyWords != NULL) + { + delete[] m_pcKeyWords; + } + + if (m_pcStructList != NULL) + { + delete[] m_pcStructList; + m_pcStructList = NULL; + } + + if (m_pcStructFieldList != NULL) + { + delete[] m_pcStructFieldList; + m_pcStructFieldList = NULL; + } + + if (m_psEngineDefinedStructureName != NULL) + { + delete[] m_psEngineDefinedStructureName; + m_psEngineDefinedStructureName = NULL; + } + + if (m_pbEngineDefinedStructureValid) + { + delete[] m_pbEngineDefinedStructureValid; + m_pbEngineDefinedStructureValid = NULL; + } + + if (m_ppsParseTreeFileNames) + { + for (int32_t count = 0; count < CSCRIPTCOMPILER_MAX_TABLE_FILENAMES; count++) + { + if (m_ppsParseTreeFileNames[count] != NULL) + { + delete m_ppsParseTreeFileNames[count]; + m_ppsParseTreeFileNames[count] = NULL; + } + } + delete[] m_ppsParseTreeFileNames; + m_ppsParseTreeFileNames = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::Initialize() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Sets up parsing routine to be ready to accept a new script. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::Initialize( ) +{ + + + m_sCapturedError = ""; + m_nCapturedErrorStrRef = 0; + + m_nLines = 1; + m_nCharacterOnLine = 1; + + m_nSRStackStates = -1; + m_nSRStackEntries = CSCRIPTCOMPILER_MAX_STACK_ENTRIES; + if (m_pSRStack == NULL) + { + m_pSRStack = new CScriptCompilerStackEntry[m_nSRStackEntries]; + } + PushSRStack(0,0,0,NULL); + + if (m_pcStructList == NULL) + { + m_pcStructList = new CScriptCompilerStructureEntry[CSCRIPTCOMPILER_MAX_STRUCTURES]; + } + + if (m_pcStructFieldList == NULL) + { + m_pcStructFieldList = new CScriptCompilerStructureFieldEntry[CSCRIPTCOMPILER_MAX_STRUCTURE_FIELDS]; + } + + if (m_ppsParseTreeFileNames == NULL) + { + m_ppsParseTreeFileNames = new CExoString *[CSCRIPTCOMPILER_MAX_TABLE_FILENAMES]; + for (int32_t count = 0; count < CSCRIPTCOMPILER_MAX_TABLE_FILENAMES; count++) + { + m_ppsParseTreeFileNames[count] = NULL; + } + } + + int32_t count; + + for (count = 0; count < CSCRIPTCOMPILER_MAX_TABLE_FILENAMES; count++) + { + if (m_ppsParseTreeFileNames[count] != NULL) + { + delete m_ppsParseTreeFileNames[count]; + m_ppsParseTreeFileNames[count] = NULL; + } + } + + for (count = 0; count < 512; count++) + { + m_pSymbolLabelStartEntry[count] = -1; + } + + m_nCurrentParseTreeFileName = -1; + m_nNextParseTreeFileName = 0; + + m_nParseTreeNodeBlockEmptyNodes = -1; + m_pCurrentParseTreeNodeBlock = m_pParseTreeNodeBlockHead; + if (m_pCurrentParseTreeNodeBlock != NULL) + { + m_pCurrentParseTreeNodeBlock->CleanBlockEntries(); + m_nParseTreeNodeBlockEmptyNodes = CSCRIPTCOMPILER_PARSETREENODEBLOCK_SIZE - 1; + } + + if (m_pcKeyWords == NULL) + { + m_nKeyWords = CSCRIPTCOMPILER_MAX_KEYWORDS; + m_pcKeyWords = new CScriptCompilerKeyWordEntry[CSCRIPTCOMPILER_MAX_KEYWORDS]; + m_pcKeyWords[0] .Add("if" ,HashString("if"), CSCRIPTCOMPILER_TOKEN_KEYWORD_IF); + m_pcKeyWords[1] .Add("do" ,HashString("do"), CSCRIPTCOMPILER_TOKEN_KEYWORD_DO); + m_pcKeyWords[2] .Add("else" ,HashString("else"), CSCRIPTCOMPILER_TOKEN_KEYWORD_ELSE); + m_pcKeyWords[3] .Add("int" ,HashString("int"), CSCRIPTCOMPILER_TOKEN_KEYWORD_INT); + m_pcKeyWords[4] .Add("float" ,HashString("float"), CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT); + m_pcKeyWords[5] .Add("string" ,HashString("string"),CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING); + m_pcKeyWords[6] .Add("object" ,HashString("object"),CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT); + m_pcKeyWords[7] .Add("return" ,HashString("return"),CSCRIPTCOMPILER_TOKEN_KEYWORD_RETURN); + m_pcKeyWords[8] .Add("while" ,HashString("while"), CSCRIPTCOMPILER_TOKEN_KEYWORD_WHILE); + m_pcKeyWords[9] .Add("for" ,HashString("for"), CSCRIPTCOMPILER_TOKEN_KEYWORD_FOR); + m_pcKeyWords[10].Add("void" ,HashString("void"), CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID); + m_pcKeyWords[11].Add("case" ,HashString("case"), CSCRIPTCOMPILER_TOKEN_KEYWORD_CASE); + m_pcKeyWords[12].Add("break" ,HashString("break"), CSCRIPTCOMPILER_TOKEN_KEYWORD_BREAK); + m_pcKeyWords[13].Add("struct" ,HashString("struct"),CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT); + m_pcKeyWords[14].Add("action" ,HashString("action"),CSCRIPTCOMPILER_TOKEN_KEYWORD_ACTION); + m_pcKeyWords[15].Add("switch" ,HashString("switch"),CSCRIPTCOMPILER_TOKEN_KEYWORD_SWITCH); + m_pcKeyWords[16].Add("default" ,HashString("default"), CSCRIPTCOMPILER_TOKEN_KEYWORD_DEFAULT); + m_pcKeyWords[17].Add("#include",HashString("#include"), CSCRIPTCOMPILER_TOKEN_KEYWORD_INCLUDE); + m_pcKeyWords[18].Add("continue",HashString("continue"), CSCRIPTCOMPILER_TOKEN_KEYWORD_CONTINUE); + m_pcKeyWords[19].Add("vector" ,HashString("vector"), CSCRIPTCOMPILER_TOKEN_KEYWORD_VECTOR); + m_pcKeyWords[20].Add("const" ,HashString("const"), CSCRIPTCOMPILER_TOKEN_KEYWORD_CONST); + m_pcKeyWords[21].Add("#define" ,HashString("#define"), CSCRIPTCOMPILER_TOKEN_KEYWORD_DEFINE); + m_pcKeyWords[22].Add("OBJECT_SELF" ,HashString("OBJECT_SELF"), CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT_SELF); + m_pcKeyWords[23].Add("OBJECT_INVALID" ,HashString("OBJECT_INVALID"), CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT_INVALID); + m_pcKeyWords[24].Add("ENGINE_NUM_STRUCTURES" ,HashString("ENGINE_NUM_STRUCTURES"),CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_NUM_STRUCTURES_DEFINITION); + m_pcKeyWords[25].Add("ENGINE_STRUCTURE_0" ,HashString("ENGINE_STRUCTURE_0"), CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE_DEFINITION); + m_pcKeyWords[26].Add("ENGINE_STRUCTURE_1" ,HashString("ENGINE_STRUCTURE_1"), CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE_DEFINITION); + m_pcKeyWords[27].Add("ENGINE_STRUCTURE_2" ,HashString("ENGINE_STRUCTURE_2"), CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE_DEFINITION); + m_pcKeyWords[28].Add("ENGINE_STRUCTURE_3" ,HashString("ENGINE_STRUCTURE_3"), CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE_DEFINITION); + m_pcKeyWords[29].Add("ENGINE_STRUCTURE_4" ,HashString("ENGINE_STRUCTURE_4"), CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE_DEFINITION); + m_pcKeyWords[30].Add("ENGINE_STRUCTURE_5" ,HashString("ENGINE_STRUCTURE_5"), CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE_DEFINITION); + m_pcKeyWords[31].Add("ENGINE_STRUCTURE_6" ,HashString("ENGINE_STRUCTURE_6"), CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE_DEFINITION); + m_pcKeyWords[32].Add("ENGINE_STRUCTURE_7" ,HashString("ENGINE_STRUCTURE_7"), CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE_DEFINITION); + m_pcKeyWords[33].Add("ENGINE_STRUCTURE_8" ,HashString("ENGINE_STRUCTURE_8"), CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE_DEFINITION); + m_pcKeyWords[34].Add("ENGINE_STRUCTURE_9" ,HashString("ENGINE_STRUCTURE_9"), CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE_DEFINITION); + m_pcKeyWords[35].Add("JSON_NULL" ,HashString("JSON_NULL"), CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_NULL); + m_pcKeyWords[36].Add("JSON_FALSE" ,HashString("JSON_FALSE"), CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_FALSE); + m_pcKeyWords[37].Add("JSON_TRUE" ,HashString("JSON_TRUE"), CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_TRUE); + m_pcKeyWords[38].Add("JSON_OBJECT" ,HashString("JSON_OBJECT"), CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_OBJECT); + m_pcKeyWords[39].Add("JSON_ARRAY" ,HashString("JSON_ARRAY"), CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_ARRAY); + m_pcKeyWords[40].Add("JSON_STRING" ,HashString("JSON_STRING"), CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_STRING); + m_pcKeyWords[41].Add("LOCATION_INVALID" ,HashString("LOCATION_INVALID"), CSCRIPTCOMPILER_TOKEN_KEYWORD_LOCATION_INVALID); + + int32_t nCount; + for (nCount = 0; nCount < CSCRIPTCOMPILER_MAX_KEYWORDS; ++nCount) + { + HashManagerAdd(CSCRIPTCOMPILER_HASH_MANAGER_TYPE_KEYWORD,nCount); + } + } + + //HashManagerAddKeywords(); + + m_nMaxStructures = 0; + m_nMaxStructureFields = 0; + m_nStructureDefinition = 0; + m_bGlobalVariableDefinition = FALSE; + m_nGlobalVariables = 0; + m_nGlobalVariableSize = 0; + m_bConstantVariableDefinition = FALSE; + + m_nVarStackRecursionLevel = 0; + + m_nSwitchLevel = 0; + m_nSwitchIdentifier = 0; + m_nSwitchStackDepth = 0; + m_bSwitchLabelDefault = FALSE; + m_nSwitchLabelNumber = 0; + m_nSwitchLabelArraySize = 16; + m_pnSwitchLabelStatements = NULL; + + m_nLoopIdentifier = 0; + m_nLoopStackDepth = 0; + + InitializePreDefinedStructures(); + + m_bCompileIdentifierConstants = FALSE; + m_bCompileIdentifierList = FALSE; + + if (m_pcVarStackList == NULL) + { + m_pcVarStackList = new CScriptCompilerVarStackEntry[CSCRIPTCOMPILER_MAX_VARIABLES]; + } + + // MGB - 06/07/2001 - Moved this out of the first declaration of the var stack list, + // since this should really be run every time we're initializing the compiler. + m_nOccupiedVariables = -1; + m_nVarStackVariableType = CSCRIPTCOMPILER_OPERATION_KEYWORD_DECLARATION; + + // Set up the runtime stacks. + m_nStackCurrentDepth = 0; + m_bAssignmentToVariable = FALSE; + m_bInStructurePart = FALSE; + + ClearAllSymbolLists(); + + m_bFunctionImp = FALSE; + m_sFunctionImpName = ""; + m_nFunctionImpReturnType = 0; + m_sFunctionImpReturnStructureName = ""; + m_nFunctionImpAbortStackPointer = 0; + + m_pGlobalVariableParseTree = NULL; + m_nCurrentLineNumber = 0; + m_nCurrentLineNumberFileReference = -1; + m_nCurrentLineNumberReferences = 0; + m_nCurrentLineNumberBinaryStartInstruction = 0; + m_nCurrentLineNumberBinaryEndInstruction = 0; + + m_nTableFileNames = 0; + + m_nFinalLineNumberEntries = 0; + m_nLineNumberEntries = 0; + + m_nSymbolTableVariables = 0; + m_nFinalSymbolTableVariables = 0; + + TokenInitialize(); + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::HashString() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: Nov. 20, 2002 +// Description: Hashes a string and returns a 32-bit value associated with +// the string. (The theory is that two identifiers will not +// match unless the hash values match.) +/////////////////////////////////////////////////////////////////////////////// + +uint32_t CScriptCompiler::HashString(const char *pString) +{ + if (m_pnHashString == NULL || pString == NULL) + { + return 0; + } + + uint32_t nHashValue = 0; + uint32_t nStringLength = (uint32_t)strlen(pString); + uint32_t nStringCount = 0; + + for (nStringCount = 0; nStringCount < nStringLength; nStringCount++) + { + nHashValue ^= m_pnHashString[pString[nStringCount]]; + nHashValue += (nStringCount + 512); + } + + return nHashValue; +} + + +uint32_t CScriptCompiler::HashString(const CExoString &sString) +{ + if (m_pnHashString == NULL) + { + return 0; + } + + uint32_t nHashValue = 0; + uint32_t nStringLength = sString.GetLength(); + uint32_t nStringCount = 0; + char *pcString = sString.CStr(); + + for (nStringCount = 0; nStringCount < nStringLength; nStringCount++) + { + nHashValue ^= m_pnHashString[pcString[nStringCount]]; + nHashValue += (nStringCount + 512); + } + + return nHashValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::GetHashEntryByName() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: Dec. 3, 2002 +// Description: This routine will return the location of the entry in the +// hash table for the given token. Returns -1 if no hash entry +// has been found. +/////////////////////////////////////////////////////////////////////////////// +int32_t CScriptCompiler::GetHashEntryByName(const char *psIdentifierName) +{ + BOOL bInfiniteLoop = TRUE; + uint32_t nOriginalHash = HashString(psIdentifierName); + + // Search for the exact entry. + uint32_t nHash = nOriginalHash % CSCRIPTCOMPILER_SIZE_IDENTIFIER_HASH_TABLE; + uint32_t nEndHash = nHash + (CSCRIPTCOMPILER_SIZE_IDENTIFIER_HASH_TABLE - 1) & CSCRIPTCOMPILER_MASK_SIZE_IDENTIFIER_HASH_TABLE; + + while (bInfiniteLoop) + { + // If we're at the correct entry, confirm that the strings are identical and then + // return to the main routine. + if (m_pIdentifierHashTable[nHash].m_nHashValue == nOriginalHash) + { + int32_t nIndex = m_pIdentifierHashTable[nHash].m_nIdentifierIndex; + if (m_pIdentifierHashTable[nHash].m_nIdentifierType == CSCRIPTCOMPILER_HASH_MANAGER_TYPE_IDENTIFIER) + { + if (strcmp(m_pcIdentifierList[nIndex].m_psIdentifier.CStr(),psIdentifierName) == 0) + { + return nHash; + } + } + else if (m_pIdentifierHashTable[nHash].m_nIdentifierType == CSCRIPTCOMPILER_HASH_MANAGER_TYPE_KEYWORD) + { + if (strcmp((m_pcKeyWords[nIndex].GetPointerToName())->CStr(),psIdentifierName) == 0) + { + return nHash; + } + } + else if (m_pIdentifierHashTable[nHash].m_nIdentifierType == CSCRIPTCOMPILER_HASH_MANAGER_TYPE_ENGINE_STRUCTURE) + { + if (strcmp(m_psEngineDefinedStructureName[nIndex].CStr(),psIdentifierName) == 0) + { + return nHash; + } + } + } + + // Have we hit a blank entry? Then it's not in the list. + if (m_pIdentifierHashTable[nHash].m_nIdentifierType == CSCRIPTCOMPILER_HASH_MANAGER_TYPE_UNKNOWN) + { + return STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER; + } + + // Have we searched the entire list? + if (nEndHash == nHash) + { + return STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER; + } + + // Move to the next entry in the list. + ++nHash; + nHash &= CSCRIPTCOMPILER_MASK_SIZE_IDENTIFIER_HASH_TABLE; + + } + + return STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::HashManagerAdd() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: Nov. 21, 2002 +// Description: Adds a identifier or keyword to the identifier hash table +// so that we can figure out what the identifier is quickly! +/////////////////////////////////////////////////////////////////////////////// +uint32_t CScriptCompiler::HashManagerAdd(uint32_t nType, uint32_t nIndice) +{ + uint32_t nHash; + uint32_t nOriginalHash=0; + + // Get the hash value based on the type of entry. + if (nType == CSCRIPTCOMPILER_HASH_MANAGER_TYPE_IDENTIFIER) + { + CScriptCompilerIdListEntry *pIdentifier = &(m_pcIdentifierList[nIndice]); + nOriginalHash = HashString(pIdentifier->m_psIdentifier); + } + else if (nType == CSCRIPTCOMPILER_HASH_MANAGER_TYPE_KEYWORD) + { + CScriptCompilerKeyWordEntry *pEntry = &(m_pcKeyWords[nIndice]); + nOriginalHash = HashString(pEntry->GetAlphanumericName()); + } + else if (nType == CSCRIPTCOMPILER_HASH_MANAGER_TYPE_ENGINE_STRUCTURE) + { + nOriginalHash = HashString(m_psEngineDefinedStructureName[nIndice]); + } + + + // Search for an empty entry. + nHash = nOriginalHash % CSCRIPTCOMPILER_SIZE_IDENTIFIER_HASH_TABLE; + uint32_t nEndHash = nHash + (CSCRIPTCOMPILER_SIZE_IDENTIFIER_HASH_TABLE - 1) & CSCRIPTCOMPILER_MASK_SIZE_IDENTIFIER_HASH_TABLE; + while (m_pIdentifierHashTable[nHash].m_nIdentifierType != CSCRIPTCOMPILER_HASH_MANAGER_TYPE_UNKNOWN && nHash != nEndHash) + { + ++nHash; + nHash &= CSCRIPTCOMPILER_MASK_SIZE_IDENTIFIER_HASH_TABLE; + } + + // If we found an empty entry, do something about it. + if (m_pIdentifierHashTable[nHash].m_nIdentifierType == CSCRIPTCOMPILER_HASH_MANAGER_TYPE_UNKNOWN) + { + m_pIdentifierHashTable[nHash].m_nHashValue = nOriginalHash; + m_pIdentifierHashTable[nHash].m_nIdentifierType = nType; + m_pIdentifierHashTable[nHash].m_nIdentifierIndex = nIndice; + + return 0; + } + + // We didn't find an empty entry. This is bad. + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::HashManagerDelete() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: Nov. 21, 2002 +// Description: Removes a identifier or keyword from the identifier hash table +// so that we don't look for it any more in the table! +/////////////////////////////////////////////////////////////////////////////// +uint32_t CScriptCompiler::HashManagerDelete(uint32_t nType, uint32_t nIndice) +{ + uint32_t nHash; + uint32_t nOriginalHash=0; + + // Generate the hash value based on the type + if (nType == CSCRIPTCOMPILER_HASH_MANAGER_TYPE_IDENTIFIER) + { + CScriptCompilerIdListEntry *pIdentifier = &(m_pcIdentifierList[nIndice]); + nOriginalHash = HashString(pIdentifier->m_psIdentifier); + } + else if (nType == CSCRIPTCOMPILER_HASH_MANAGER_TYPE_KEYWORD) + { + CScriptCompilerKeyWordEntry *pEntry = &(m_pcKeyWords[nIndice]); + nOriginalHash = HashString(pEntry->GetAlphanumericName()); + } + else if (nType == CSCRIPTCOMPILER_HASH_MANAGER_TYPE_ENGINE_STRUCTURE) + { + nOriginalHash = HashString(m_psEngineDefinedStructureName[nIndice]); + } + + // Search for the exact entry. + nHash = nOriginalHash % CSCRIPTCOMPILER_SIZE_IDENTIFIER_HASH_TABLE; + uint32_t nEndHash = nHash + (CSCRIPTCOMPILER_SIZE_IDENTIFIER_HASH_TABLE - 1) & CSCRIPTCOMPILER_MASK_SIZE_IDENTIFIER_HASH_TABLE; + while (((m_pIdentifierHashTable[nHash].m_nHashValue != nOriginalHash) || + (m_pIdentifierHashTable[nHash].m_nIdentifierType != nType) || + (m_pIdentifierHashTable[nHash].m_nIdentifierIndex != nIndice)) && nHash != nEndHash) + { + ++nHash; + nHash &= CSCRIPTCOMPILER_MASK_SIZE_IDENTIFIER_HASH_TABLE; + } + + // Delete the entry if we find its exact match. + if (m_pIdentifierHashTable[nHash].m_nIdentifierType == nType && + m_pIdentifierHashTable[nHash].m_nIdentifierIndex == nIndice && + m_pIdentifierHashTable[nHash].m_nHashValue == nOriginalHash) + { + m_pIdentifierHashTable[nHash].m_nHashValue = 0; + m_pIdentifierHashTable[nHash].m_nIdentifierType = CSCRIPTCOMPILER_HASH_MANAGER_TYPE_UNKNOWN; + m_pIdentifierHashTable[nHash].m_nIdentifierIndex = 0; + return 0; + } + + // We didn't find it ... this is also bad. + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::InitializePreDefinedStructures() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 10/17/2000 +// Description: Sets up script compiler internal variables for the next +// include file. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::InitializePreDefinedStructures() +{ + + // Define the "vector" structure (used to compile vector calls). + m_nMaxStructures = 1; + + m_pcStructList[0].m_nByteSize = 12; + m_pcStructList[0].m_nFieldStart = 0; + m_pcStructList[0].m_nFieldEnd = 2; + m_pcStructList[0].m_psName = "vector"; + + m_nMaxStructureFields = 3; + + m_pcStructFieldList[0].m_psVarName = "x"; + m_pcStructFieldList[0].m_nLocation = 0; + m_pcStructFieldList[0].m_pchType = CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT; + + m_pcStructFieldList[1].m_psVarName = "y"; + m_pcStructFieldList[1].m_nLocation = 4; + m_pcStructFieldList[1].m_pchType = CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT; + + m_pcStructFieldList[2].m_psVarName = "z"; + m_pcStructFieldList[2].m_nLocation = 8; + m_pcStructFieldList[2].m_pchType = CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT; + +} + +void CScriptCompiler::InitializeIncludeFile(int32_t nCompileFileLevel) +{ + + // Preserve the old state + if (nCompileFileLevel >= 1) + { + m_pcIncludeFileStack[nCompileFileLevel-1].m_nLine = m_nLines; + m_pcIncludeFileStack[nCompileFileLevel-1].m_nCharacterOnLine = m_nCharacterOnLine; + m_pcIncludeFileStack[nCompileFileLevel-1].m_nTokenStatus = m_nTokenStatus; + m_pcIncludeFileStack[nCompileFileLevel-1].m_nTokenCharacters = m_nTokenCharacters; + + m_nLines = 1; + m_nCharacterOnLine = 1; + TokenInitialize(); + } + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ShutdownIncludeFile() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/18/2000 +// Description: Reinstantiates script compiler internal variables when going +// back to the earlier script. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::ShutdownIncludeFile(int32_t nCompileFileLevel) +{ + + // Retrieve the old state + if (nCompileFileLevel >= 1) + { + m_nLines = m_pcIncludeFileStack[nCompileFileLevel - 1].m_nLine; + m_nCharacterOnLine = m_pcIncludeFileStack[nCompileFileLevel - 1].m_nCharacterOnLine; + m_nTokenStatus = m_pcIncludeFileStack[nCompileFileLevel - 1].m_nTokenStatus; + m_nTokenCharacters = m_pcIncludeFileStack[nCompileFileLevel - 1].m_nTokenCharacters; + } + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::DeleteParseTree() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 08/05/99 +// Description: This routine will take the root of a compile tree, ensure that +// each of its branches have been deleted (recursively), and +// then we can delete the node itself! +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::DeleteParseTree(BOOL bStack, CScriptParseTreeNode *pNode) +{ + if (pNode != NULL) + { + // Delete all of the subtrees + DeleteParseTree(bStack, pNode->pLeft); + DeleteParseTree(bStack, pNode->pRight); + + if (bStack == TRUE) + { + // Delete any references to this node from the stack. + int32_t i; + + for (i=0; i <= m_nSRStackStates; i++) + { + if (m_pSRStack[i].pCurrentTree != NULL) + { + m_pSRStack[i].pCurrentTree = NULL; + } + if (m_pSRStack[i].pReturnTree != NULL) + { + m_pSRStack[i].pReturnTree = NULL; + } + } + } + + // Finally delete the node itself. + DeleteScriptParseTreeNode(pNode); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::SetGenerateDebuggerOutput() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: August 30, 2002 +// Description: This determines whether the debugger. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::SetGenerateDebuggerOutput(int32_t nValue) +{ + m_nGenerateDebuggerOutput = nValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::SetIdentifierSpecification() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/15/2001 +// Description: This routine will set whether the compiler should generate +// a symbolic output or a regular output. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::SetIdentifierSpecification(const CExoString &sLanguageSource) +{ + + if (m_sLanguageSource != sLanguageSource) + { + m_sLanguageSource = sLanguageSource; + + if (m_pcIdentifierList != NULL) + { + while (m_nOccupiedIdentifiers > 0) + { + --m_nOccupiedIdentifiers; + HashManagerDelete(CSCRIPTCOMPILER_HASH_MANAGER_TYPE_IDENTIFIER, m_nOccupiedIdentifiers); + } + + delete[] m_pcIdentifierList; + } + + if (m_pbEngineDefinedStructureValid != NULL) + { + int32_t nCount = 9; + while (nCount >= 0) + { + if (m_pbEngineDefinedStructureValid[nCount] == TRUE) + { + HashManagerDelete(CSCRIPTCOMPILER_HASH_MANAGER_TYPE_ENGINE_STRUCTURE, nCount); + m_pbEngineDefinedStructureValid[nCount] = FALSE; + } + --nCount; + } + } + + if (m_pcIdentifierList == NULL) + { + m_pcIdentifierList = new CScriptCompilerIdListEntry[CSCRIPTCOMPILER_MAX_IDENTIFIERS]; + m_bCompileIdentifierList = TRUE; + m_bCompileIdentifierConstants = TRUE; + m_nOccupiedIdentifiers = 0; + m_nMaxPredefinedIdentifierId = 0; + ParseIdentifierFile(); + + m_nLines = 1; + m_nCharacterOnLine = 1; + m_nSRStackStates = -1; + + m_bCompileIdentifierList = FALSE; + m_bCompileIdentifierConstants = FALSE; + } + } + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::SetOutputAlias() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 02/09/2001 +// Description: This routine will set the output alias for where we wish +// to output the result of CompileFile. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::SetOutputAlias(const CExoString &sAlias) +{ + m_sOutputAlias = sAlias; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::SetCompileConditionalFile() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/25/2000 +// Description: This routine will set whether the compiler should generate +// all functions (whether or not they are called), or only +// those functions that could possibly be called via any +// execution. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::SetCompileConditionalFile(BOOL bValue) +{ + m_bCompileConditionalFile = bValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::SetCompileConditionalOrMain() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: Feb. 17, 2003 +/////////////////////////////////////////////////////////////////////////////// +void CScriptCompiler::SetCompileConditionalOrMain(BOOL bValue) +{ + m_bCompileConditionalOrMain = bValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::SetAutomaticCleanUpAfterCompiles() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: Feb/06/2003 +// Description: +/////////////////////////////////////////////////////////////////////////////// +void CScriptCompiler::SetAutomaticCleanUpAfterCompiles(BOOL bValue) +{ + m_bAutomaticCleanUpAfterCompiles = bValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::UpdateOutputDirectory() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: Feb/06/2003 +// Description: Updates the current output alias to allow the resource +// manager to use the scripts that were compiled! (Use of this +// function is ONLY required when you have set the automatic +// update of the output directory to FALSE. If the value was +// TRUE during a CompileFile instruction, this code has already +// been run on the newly created files!) +/////////////////////////////////////////////////////////////////////////////// +void CScriptCompiler::CleanUpAfterCompiles() +{ + // Update the resource directory. + CExoString sDirectoryFileName; + + sDirectoryFileName.Format("%s:",m_sOutputAlias.CStr()); + m_cAPI.ResManUpdateResourceDirectory(sDirectoryFileName.CStr()); + + // Delete the Debugger code buffer. + if (m_pchDebuggerCode != NULL) + { + delete[] m_pchDebuggerCode; + m_pchDebuggerCode = NULL; + m_nDebuggerCodeSize = 0; + } + + // The resolved output buffer should go, too. + if (m_pchResolvedOutputBuffer != NULL) + { + delete[] m_pchResolvedOutputBuffer; + m_pchResolvedOutputBuffer = NULL; + m_nResolvedOutputBufferSize = 0; + } + + // And the script code buffer should also be deallocated. + ClearCompiledScriptCode(); +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::CompileFile() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: This routine will compile the file specified in sFileName +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::CompileFile(const CExoString &sFileName) +{ + + char *pScript; + uint32_t nScriptLength; + + if (m_nCompileFileLevel == 0) + { + Initialize(); + } + + if (m_nCompileFileLevel >= CSCRIPTCOMPILER_MAX_INCLUDE_LEVELS) + { + return STRREF_CSCRIPTCOMPILER_ERROR_INCLUDE_TOO_MANY_LEVELS; + } + + if (m_nCompileFileLevel > 0) + { + int count; + + for (count = 0; count < m_nCompileFileLevel; ++count) + { + if (m_pcIncludeFileStack[count].m_sCompiledScriptName == sFileName) + { + return STRREF_CSCRIPTCOMPILER_ERROR_INCLUDE_RECURSIVE; + } + } + + InitializeIncludeFile(m_nCompileFileLevel); + } + + m_pcIncludeFileStack[m_nCompileFileLevel].m_sCompiledScriptName = sFileName; + + const char* sTest = m_cAPI.ResManLoadScriptSourceFile(sFileName.CStr(), m_nResTypeSource); + if (!sTest) + { + if (m_nCompileFileLevel > 0) + { + ShutdownIncludeFile(m_nCompileFileLevel); + } + + return STRREF_CSCRIPTCOMPILER_ERROR_FILE_NOT_FOUND; + } + m_pcIncludeFileStack[m_nCompileFileLevel].m_sSourceScript = sTest; + pScript = m_pcIncludeFileStack[m_nCompileFileLevel].m_sSourceScript.CStr(); + nScriptLength = m_pcIncludeFileStack[m_nCompileFileLevel].m_sSourceScript.GetLength(); + + ++m_nCompileFileLevel; + + int32_t nReturnValue = ParseSource(pScript,nScriptLength); + + if (nReturnValue < 0) + { + // MGB - 02/15/2001 - DO NOT SUBTRACT ONE FROM COMPILEFILELEVEL. + // Why? It is taken off in CleanUpDuringCompile. + m_pcIncludeFileStack[m_nCompileFileLevel].m_sSourceScript = ""; + return nReturnValue; + } + + // We have successfully compiled this file. If we are still in + // the middle of another CompileFile (i.e. this is an included + // file, return success back to the main routine. + + m_pcIncludeFileStack[m_nCompileFileLevel-1].m_sSourceScript = ""; + + --m_nCompileFileLevel; + if (m_nCompileFileLevel > 0) + { + ShutdownIncludeFile(m_nCompileFileLevel); + return 0; + } + + // This is the "outermost" script level. Here, we should + // attempt to write out the virtual machine code. + + InitializeFinalCode(); + + nReturnValue = GenerateFinalCodeFromParseTree(sFileName); + + if (nReturnValue < 0) + { + return nReturnValue; + } + + FinalizeFinalCode(); + + nReturnValue = WriteFinalCodeToFile(sFileName); + + if (nReturnValue < 0) + { + return nReturnValue; + } + + + + return nReturnValue; +} + + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::CompileScriptChunk() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/11/2001 +// Description: This routine will compile the string specified in sStringData. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::CompileScriptChunk(const CExoString &sScriptChunk, BOOL bWrapIntoMain) +{ + char *pScript; + uint32_t nScriptLength; + + Initialize(); + + if (m_nCompileFileLevel != 0) + { + return STRREF_CSCRIPTCOMPILER_ERROR_INCLUDE_TOO_MANY_LEVELS; + } + + m_pcIncludeFileStack[m_nCompileFileLevel].m_sCompiledScriptName = "!Chunk"; + + if (bWrapIntoMain) + { + nScriptLength = sScriptChunk.GetLength() + 13; + pScript = new char[nScriptLength + 13]; + sprintf(pScript, "void main(){%s}", sScriptChunk.CStr()); + } + else + { + nScriptLength = sScriptChunk.GetLength(); + pScript = new char[nScriptLength]; + memmove(pScript, sScriptChunk.CStr(), nScriptLength); + } + + ++m_nCompileFileLevel; + + int32_t nReturnValue = ParseSource(pScript,nScriptLength); + + if (nReturnValue < 0) + { + delete[] pScript; + return nReturnValue; + } + + --m_nCompileFileLevel; + + InitializeFinalCode(); + + nReturnValue = GenerateFinalCodeFromParseTree("!Chunk"); + + if (nReturnValue < 0) + { + delete[] pScript; + return nReturnValue; + } + + FinalizeFinalCode(); + + delete[] pScript; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::CompileScriptConditional() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/11/2001 +// Description: This routine will compile the string specified in sStringData. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::CompileScriptConditional(const CExoString &sScriptConditional) +{ + + char *pScript; + uint32_t nScriptLength; + + Initialize(); + + if (m_nCompileFileLevel != 0) + { + return STRREF_CSCRIPTCOMPILER_ERROR_INCLUDE_TOO_MANY_LEVELS; + } + + m_pcIncludeFileStack[m_nCompileFileLevel].m_sCompiledScriptName = "!Conditional"; + + nScriptLength = sScriptConditional.GetLength() + 22; + pScript = new char[nScriptLength + 22]; + sprintf(pScript,"int main(){ return(%s);}",sScriptConditional.CStr()); + + ++m_nCompileFileLevel; + + int32_t nReturnValue = ParseSource(pScript,nScriptLength); + + if (nReturnValue < 0) + { + return nReturnValue; + } + + --m_nCompileFileLevel; + + InitializeFinalCode(); + + nReturnValue = GenerateFinalCodeFromParseTree("!Conditional"); + + if (nReturnValue < 0) + { + return nReturnValue; + } + + FinalizeFinalCode(); + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::GetCompiledScriptCode() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/11/2001 +// Description: This routine will return the data that has already been +// compiled. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::GetCompiledScriptCode(char **ppnCode, int32_t *pnCodeSize) +{ + *pnCodeSize = m_nOutputCodeSize; + *ppnCode = m_pchOutputCode; + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ClearCompiledScriptCode() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/11/2001 +// Description: This routine will return delete any data that has already +// been compiled. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::ClearCompiledScriptCode() +{ + m_nOutputCodeSize = 0; + if (m_pchOutputCode) + { + delete[] m_pchOutputCode; + m_pchOutputCode = NULL; + } + m_nOutputCodeLength = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::OutputError() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 02/23/2001 +// Description: A standardization of ALL error messages that could possibly +// come out of the compilation of any/all code. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::OutputError(int32_t nError, CExoString *psFileName, int32_t nLineNumber, const CExoString &sErrorText) +{ + // Construct the error string, if appropriate + if (nError == STRREF_CSCRIPTCOMPILER_ERROR_ALREADY_PRINTED) + { + return nError; + } + + CExoString sFullErrorText; + + if (psFileName->Left(1) == "!") + { + if (nLineNumber > 0) + { + sFullErrorText.Format("%s(%d): %s\n",psFileName->Right(psFileName->GetLength()-1).CStr(),nLineNumber,sErrorText.CStr()); + } + else + { + sFullErrorText.Format("%s: %s\n",psFileName->Right(psFileName->GetLength()-1).CStr(),sErrorText.CStr()); + } + } + else + { + if (nLineNumber > 0) + { + sFullErrorText.Format("%s.nss(%d): %s\n",psFileName->CStr(),nLineNumber,sErrorText.CStr()); + } + else + { + sFullErrorText.Format("%s.nss: %s\n",psFileName->CStr(),sErrorText.CStr()); + } + } + + m_sCapturedError = sFullErrorText; + m_nCapturedErrorStrRef = nError; + + // Print the full error text to the log file. + // This is used and parsed by the toolset :( Do not remove. +#ifdef BORLAND + g_pExoBase->m_pcExoDebug->m_sLogString.Format("%s",sFullErrorText.CStr()); + g_pExoBase->m_pcExoDebug->WriteToLogFile(g_pExoBase->m_pcExoDebug->m_sLogString); + g_pExoBase->m_pcExoDebug->FlushLogFile(); +#endif + + return nError; +} + + +// Destructively modify a node and all its children to decay it into a single +// CONSTANT operation, if possible. +// This function is safe to call multiple times on the same node. +BOOL CScriptCompiler::ConstantFoldNode(CScriptParseTreeNode *pNode, BOOL bForce) +{ + if (!bForce && !(m_nOptimizationFlags & CSCRIPTCOMPILER_OPTIMIZE_FOLD_CONSTANTS)) + return FALSE; + + if (!pNode) + return FALSE; + + // Only fold operations that have two operands + // TODO: ~0 unary op? + if (!pNode->pLeft || !pNode->pRight) + return FALSE; + + // In case of complex expression, start folding at the leaf nodes + // e.g.: C = 3 + 2*4 - First fold 2*4 into 8, then 3+8 into 11 + ConstantFoldNode(pNode->pLeft, bForce); + ConstantFoldNode(pNode->pRight, bForce); + + // Can only fold if the operands are constants. + if (pNode->pLeft->nOperation != CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER && + pNode->pLeft->nOperation != CSCRIPTCOMPILER_OPERATION_CONSTANT_FLOAT && + pNode->pLeft->nOperation != CSCRIPTCOMPILER_OPERATION_CONSTANT_STRING) + { + return FALSE; + } + + // Only fold operations on same type. Expressions like "3.0f + 1" are not folded + if (pNode->pLeft->nOperation != pNode->pRight->nOperation) + return FALSE; + + if (pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER) + { + int32_t result; + int32_t left = pNode->pLeft->nIntegerData; + int32_t right = pNode->pRight->nIntegerData; + switch (pNode->nOperation) + { + case CSCRIPTCOMPILER_OPERATION_LOGICAL_OR: result = left || right; break; + case CSCRIPTCOMPILER_OPERATION_LOGICAL_AND: result = left && right; break; + case CSCRIPTCOMPILER_OPERATION_INCLUSIVE_OR: result = left | right; break; + case CSCRIPTCOMPILER_OPERATION_EXCLUSIVE_OR: result = left ^ right; break; + case CSCRIPTCOMPILER_OPERATION_BOOLEAN_AND: result = left & right; break; + case CSCRIPTCOMPILER_OPERATION_CONDITION_EQUAL: result = left == right; break; + case CSCRIPTCOMPILER_OPERATION_CONDITION_NOT_EQUAL: result = left != right; break; + case CSCRIPTCOMPILER_OPERATION_CONDITION_GEQ: result = left >= right; break; + case CSCRIPTCOMPILER_OPERATION_CONDITION_GT: result = left > right; break; + case CSCRIPTCOMPILER_OPERATION_CONDITION_LT: result = left < right; break; + case CSCRIPTCOMPILER_OPERATION_CONDITION_LEQ: result = left <= right; break; + case CSCRIPTCOMPILER_OPERATION_SHIFT_LEFT: result = left << right; break; + case CSCRIPTCOMPILER_OPERATION_SHIFT_RIGHT: result = left >> right; break; + case CSCRIPTCOMPILER_OPERATION_ADD: result = left + right; break; + case CSCRIPTCOMPILER_OPERATION_SUBTRACT: result = left - right; break; + case CSCRIPTCOMPILER_OPERATION_MULTIPLY: result = left * right; break; + case CSCRIPTCOMPILER_OPERATION_DIVIDE: result = left / right; break; + case CSCRIPTCOMPILER_OPERATION_MODULUS: result = left % right; break; + default: return FALSE; + } + pNode->pLeft->Clean(); + pNode->pRight->Clean(); + pNode->Clean(); + pNode->nOperation = CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER; + pNode->nIntegerData = result; + return TRUE; + } + + if (pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_FLOAT) + { + float result; + int resultBool = -1; + float left = pNode->pLeft->fFloatData; + float right = pNode->pRight->fFloatData; + switch (pNode->nOperation) + { + case CSCRIPTCOMPILER_OPERATION_ADD: result = left + right; break; + case CSCRIPTCOMPILER_OPERATION_SUBTRACT: result = left - right; break; + case CSCRIPTCOMPILER_OPERATION_MULTIPLY: result = left * right; break; + case CSCRIPTCOMPILER_OPERATION_DIVIDE: result = left / right; break; + // Relational ops on floats produce a bool result + case CSCRIPTCOMPILER_OPERATION_CONDITION_EQUAL: resultBool = left == right; break; + case CSCRIPTCOMPILER_OPERATION_CONDITION_NOT_EQUAL: resultBool = left != right; break; + case CSCRIPTCOMPILER_OPERATION_CONDITION_GEQ: resultBool = left >= right; break; + case CSCRIPTCOMPILER_OPERATION_CONDITION_GT: resultBool = left > right; break; + case CSCRIPTCOMPILER_OPERATION_CONDITION_LT: resultBool = left < right; break; + case CSCRIPTCOMPILER_OPERATION_CONDITION_LEQ: resultBool = left <= right; break; + default: return FALSE; + } + pNode->pLeft->Clean(); + pNode->pRight->Clean(); + pNode->Clean(); + if (resultBool != -1) + { + pNode->nOperation = CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER; + pNode->nIntegerData = resultBool; + } + else + { + pNode->nOperation = CSCRIPTCOMPILER_OPERATION_CONSTANT_FLOAT; + pNode->fFloatData = result; + } + return TRUE; + } + + if (pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_STRING) + { + CExoString left = (pNode->pLeft->m_psStringData ? *pNode->pLeft->m_psStringData : CExoString("")); + CExoString right = (pNode->pRight->m_psStringData ? *pNode->pRight->m_psStringData : CExoString("")); + CExoString result; + int resultBool = -1; + switch (pNode->nOperation) + { + case CSCRIPTCOMPILER_OPERATION_ADD: result = left + right; break; + // Relational ops on string produce a bool result + case CSCRIPTCOMPILER_OPERATION_CONDITION_EQUAL: resultBool = left == right; break; + case CSCRIPTCOMPILER_OPERATION_CONDITION_NOT_EQUAL: resultBool = left != right; break; + default: return FALSE; + } + pNode->pLeft->Clean(); + pNode->pRight->Clean(); + pNode->Clean(); + if (resultBool != -1) + { + pNode->nOperation = CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER; + pNode->nIntegerData = resultBool; + } + else + { + pNode->nOperation = CSCRIPTCOMPILER_OPERATION_CONSTANT_STRING; + pNode->m_psStringData = new CExoString(result); + } + return TRUE; + } + + return FALSE; +} + + +const char *TokenKeywordToString(int nTokenKeyword) +{ + switch (nTokenKeyword) + { + case CSCRIPTCOMPILER_TOKEN_UNKNOWN: return "UNKNOWN"; + case CSCRIPTCOMPILER_TOKEN_DIVIDE: return "DIVIDE"; + case CSCRIPTCOMPILER_TOKEN_CPLUSCOMMENT: return "CPLUSCOMMENT"; + case CSCRIPTCOMPILER_TOKEN_CCOMMENT: return "CCOMMENT"; + case CSCRIPTCOMPILER_TOKEN_INTEGER: return "INTEGER"; + case CSCRIPTCOMPILER_TOKEN_FLOAT: return "FLOAT"; + case CSCRIPTCOMPILER_TOKEN_IDENTIFIER: return "IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_STRING: return "STRING"; + case CSCRIPTCOMPILER_TOKEN_LOGICAL_AND: return "LOGICAL_AND"; + case CSCRIPTCOMPILER_TOKEN_LOGICAL_OR: return "LOGICAL_OR"; + case CSCRIPTCOMPILER_TOKEN_MINUS: return "MINUS"; + case CSCRIPTCOMPILER_TOKEN_LEFT_BRACE: return "LEFT_BRACE"; + case CSCRIPTCOMPILER_TOKEN_RIGHT_BRACE: return "RIGHT_BRACE"; + case CSCRIPTCOMPILER_TOKEN_LEFT_BRACKET: return "LEFT_BRACKET"; + case CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET: return "RIGHT_BRACKET"; + case CSCRIPTCOMPILER_TOKEN_SEMICOLON: return "SEMICOLON"; + case CSCRIPTCOMPILER_TOKEN_COMMA: return "COMMA"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_IF: return "KEYWORD_IF"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_ELSE: return "KEYWORD_ELSE"; + case CSCRIPTCOMPILER_TOKEN_EOF: return "EOF"; + case CSCRIPTCOMPILER_TOKEN_COND_GREATER_EQUAL: return "COND_GREATER_EQUAL"; + case CSCRIPTCOMPILER_TOKEN_COND_LESS_EQUAL: return "COND_LESS_EQUAL"; + case CSCRIPTCOMPILER_TOKEN_COND_GREATER_THAN: return "COND_GREATER_THAN"; + case CSCRIPTCOMPILER_TOKEN_COND_LESS_THAN: return "COND_LESS_THAN"; + case CSCRIPTCOMPILER_TOKEN_COND_NOT_EQUAL: return "COND_NOT_EQUAL"; + case CSCRIPTCOMPILER_TOKEN_COND_EQUAL: return "COND_EQUAL"; + case CSCRIPTCOMPILER_TOKEN_PLUS: return "PLUS"; + case CSCRIPTCOMPILER_TOKEN_MODULUS: return "MODULUS"; + case CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_EQUAL: return "ASSIGNMENT_EQUAL"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_INT: return "KEYWORD_INT"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT: return "KEYWORD_FLOAT"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING: return "KEYWORD_STRING"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT: return "KEYWORD_OBJECT"; + case CSCRIPTCOMPILER_TOKEN_VARIABLE: return "VARIABLE"; + case CSCRIPTCOMPILER_TOKEN_INTEGER_IDENTIFIER: return "INTEGER_IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_FLOAT_IDENTIFIER: return "FLOAT_IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_STRING_IDENTIFIER: return "STRING_IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_OBJECT_IDENTIFIER: return "OBJECT_IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_VOID_IDENTIFIER: return "VOID_IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_INCLUSIVE_OR: return "INCLUSIVE_OR"; + case CSCRIPTCOMPILER_TOKEN_EXCLUSIVE_OR: return "EXCLUSIVE_OR"; + case CSCRIPTCOMPILER_TOKEN_BOOLEAN_AND: return "BOOLEAN_AND"; + case CSCRIPTCOMPILER_TOKEN_SHIFT_LEFT: return "SHIFT_LEFT"; + case CSCRIPTCOMPILER_TOKEN_SHIFT_RIGHT: return "SHIFT_RIGHT"; + case CSCRIPTCOMPILER_TOKEN_MULTIPLY: return "MULTIPLY"; + case CSCRIPTCOMPILER_TOKEN_HEX_INTEGER: return "HEX_INTEGER"; + case CSCRIPTCOMPILER_TOKEN_UNSIGNED_SHIFT_RIGHT: return "UNSIGNED_SHIFT_RIGHT"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_ACTION: return "KEYWORD_ACTION"; + case CSCRIPTCOMPILER_TOKEN_TILDE: return "TILDE"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_RETURN: return "KEYWORD_RETURN"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_WHILE: return "KEYWORD_WHILE"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_FOR: return "KEYWORD_FOR"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_DO: return "KEYWORD_DO"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID: return "KEYWORD_VOID"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT: return "KEYWORD_STRUCT"; + case CSCRIPTCOMPILER_TOKEN_STRUCTURE_PART_SPECIFY: return "STRUCTURE_PART_SPECIFY"; + case CSCRIPTCOMPILER_TOKEN_STRUCTURE_IDENTIFIER: return "STRUCTURE_IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_INCLUDE: return "KEYWORD_INCLUDE"; + case CSCRIPTCOMPILER_TOKEN_BOOLEAN_NOT: return "BOOLEAN_NOT"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_VECTOR: return "KEYWORD_VECTOR"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_DEFINE: return "KEYWORD_DEFINE"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_NUM_STRUCTURES_DEFINITION: return "KEYWORD_ENGINE_NUM_STRUCTURES_DEFINITION"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE_DEFINITION: return "KEYWORD_ENGINE_STRUCTURE_DEFINITION"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0: return "KEYWORD_ENGINE_STRUCTURE0"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE1: return "KEYWORD_ENGINE_STRUCTURE1"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE2: return "KEYWORD_ENGINE_STRUCTURE2"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE3: return "KEYWORD_ENGINE_STRUCTURE3"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE4: return "KEYWORD_ENGINE_STRUCTURE4"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE5: return "KEYWORD_ENGINE_STRUCTURE5"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE6: return "KEYWORD_ENGINE_STRUCTURE6"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE7: return "KEYWORD_ENGINE_STRUCTURE7"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE8: return "KEYWORD_ENGINE_STRUCTURE8"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9: return "KEYWORD_ENGINE_STRUCTURE9"; + case CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE0_IDENTIFIER: return "ENGINE_STRUCTURE0_IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE1_IDENTIFIER: return "ENGINE_STRUCTURE1_IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE2_IDENTIFIER: return "ENGINE_STRUCTURE2_IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE3_IDENTIFIER: return "ENGINE_STRUCTURE3_IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE4_IDENTIFIER: return "ENGINE_STRUCTURE4_IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE5_IDENTIFIER: return "ENGINE_STRUCTURE5_IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE6_IDENTIFIER: return "ENGINE_STRUCTURE6_IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE7_IDENTIFIER: return "ENGINE_STRUCTURE7_IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE8_IDENTIFIER: return "ENGINE_STRUCTURE8_IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE9_IDENTIFIER: return "ENGINE_STRUCTURE9_IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT_SELF: return "KEYWORD_OBJECT_SELF"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT_INVALID: return "KEYWORD_OBJECT_INVALID"; + case CSCRIPTCOMPILER_TOKEN_VECTOR_IDENTIFIER: return "VECTOR_IDENTIFIER"; + case CSCRIPTCOMPILER_TOKEN_LEFT_SQUARE_BRACKET: return "LEFT_SQUARE_BRACKET"; + case CSCRIPTCOMPILER_TOKEN_RIGHT_SQUARE_BRACKET: return "RIGHT_SQUARE_BRACKET"; + case CSCRIPTCOMPILER_TOKEN_INCREMENT: return "INCREMENT"; + case CSCRIPTCOMPILER_TOKEN_DECREMENT: return "DECREMENT"; + case CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_MINUS: return "ASSIGNMENT_MINUS"; + case CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_PLUS: return "ASSIGNMENT_PLUS"; + case CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_MULTIPLY: return "ASSIGNMENT_MULTIPLY"; + case CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_DIVIDE: return "ASSIGNMENT_DIVIDE"; + case CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_MODULUS: return "ASSIGNMENT_MODULUS"; + case CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_AND: return "ASSIGNMENT_AND"; + case CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_XOR: return "ASSIGNMENT_XOR"; + case CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_OR: return "ASSIGNMENT_OR"; + case CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_SHIFT_LEFT: return "ASSIGNMENT_SHIFT_LEFT"; + case CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_SHIFT_RIGHT: return "ASSIGNMENT_SHIFT_RIGHT"; + case CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_USHIFT_RIGHT: return "ASSIGNMENT_USHIFT_RIGHT"; + case CSCRIPTCOMPILER_TOKEN_QUESTION_MARK: return "QUESTION_MARK"; + case CSCRIPTCOMPILER_TOKEN_COLON: return "COLON"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_CASE: return "KEYWORD_CASE"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_BREAK: return "KEYWORD_BREAK"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_SWITCH: return "KEYWORD_SWITCH"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_DEFAULT: return "KEYWORD_DEFAULT"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_CONTINUE: return "KEYWORD_CONTINUE"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_CONST: return "KEYWORD_CONST"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_NULL: return "KEYWORD_JSON_NULL"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_FALSE: return "KEYWORD_JSON_FALSE"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_TRUE: return "KEYWORD_JSON_TRUE"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_OBJECT: return "KEYWORD_JSON_OBJECT"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_ARRAY: return "KEYWORD_JSON_ARRAY"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_STRING: return "KEYWORD_JSON_STRING"; + case CSCRIPTCOMPILER_TOKEN_KEYWORD_LOCATION_INVALID: return "KEYWORD_LOCATION_INVALID"; + } + return "(unknown token keyword)"; +} + +const char *GrammarToString(int nGrammar) +{ + switch (nGrammar) + { + case CSCRIPTCOMPILER_GRAMMAR_PROGRAM: return "PROGRAM"; + case CSCRIPTCOMPILER_GRAMMAR_FUNCTIONAL_UNIT: return "FUNCTIONAL_UNIT"; + case CSCRIPTCOMPILER_GRAMMAR_AFTER_PROGRAM: return "AFTER_PROGRAM"; + case CSCRIPTCOMPILER_GRAMMAR_FUNCTION_PARAM_LIST: return "FUNCTION_PARAM_LIST"; + case CSCRIPTCOMPILER_GRAMMAR_AFTER_FUNCTION_PARAM: return "AFTER_FUNCTION_PARAM"; + case CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_LIST: return "NON_INIT_DECL_LIST"; + case CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_VARLIST: return "NON_INIT_DECL_VARLIST"; + case CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_VARLIST_SEPARATOR: return "NON_INIT_DECL_VARLIST_SEPARATOR"; + case CSCRIPTCOMPILER_GRAMMAR_WITHIN_COMPOUND_STATEMENT: return "WITHIN_COMPOUND_STATEMENT"; + case CSCRIPTCOMPILER_GRAMMAR_WITHIN_STATEMENT_LIST: return "WITHIN_STATEMENT_LIST"; + case CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT: return "WITHIN_A_STATEMENT"; + case CSCRIPTCOMPILER_GRAMMAR_ANY_TYPE_SPECIFIER: return "ANY_TYPE_SPECIFIER"; + case CSCRIPTCOMPILER_GRAMMAR_NON_VOID_TYPE_SPECIFIER: return "NON_VOID_TYPE_SPECIFIER"; + case CSCRIPTCOMPILER_GRAMMAR_DECL_VARLIST: return "DECL_VARLIST"; + case CSCRIPTCOMPILER_GRAMMAR_DECL_VARLIST_SEPARATOR: return "DECL_VARLIST_SEPARATOR"; + case CSCRIPTCOMPILER_GRAMMAR_ARGUMENT_EXPRESSION_LIST: return "ARGUMENT_EXPRESSION_LIST"; + case CSCRIPTCOMPILER_GRAMMAR_AFTER_ARGUMENT_EXPRESSION: return "AFTER_ARGUMENT_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_BOOLEAN_EXPRESSION: return "BOOLEAN_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_NON_VOID_EXPRESSION: return "NON_VOID_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_EXPRESSION: return "EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_ASSIGNMENT_EXPRESSION: return "ASSIGNMENT_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_CONDITIONAL_EXPRESSION: return "CONDITIONAL_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_LOGICAL_OR_EXPRESSION: return "LOGICAL_OR_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_LOGICAL_AND_EXPRESSION: return "LOGICAL_AND_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_INCLUSIVE_OR_EXPRESSION: return "INCLUSIVE_OR_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_EXCLUSIVE_OR_EXPRESSION: return "EXCLUSIVE_OR_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_BOOLEAN_AND_EXPRESSION: return "BOOLEAN_AND_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_EQUALITY_EXPRESSION: return "EQUALITY_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_RELATIONAL_EXPRESSION: return "RELATIONAL_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_SHIFT_EXPRESSION: return "SHIFT_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_ADDITIVE_EXPRESSION: return "ADDITIVE_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_MULTIPLICATIVE_EXPRESSION: return "MULTIPLICATIVE_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_UNARY_EXPRESSION: return "UNARY_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_POST_EXPRESSION: return "POST_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_PRIMARY_EXPRESSION: return "PRIMARY_EXPRESSION"; + case CSCRIPTCOMPILER_GRAMMAR_CONSTANT: return "CONSTANT"; + } + return "(unknown grammar)"; +} + + +const char *OperationToString(int nOperation) +{ + switch (nOperation) + { + case CSCRIPTCOMPILER_OPERATION_COMPOUND_STATEMENT: return "COMPOUND_STATEMENT"; + case CSCRIPTCOMPILER_OPERATION_STATEMENT: return "STATEMENT"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_DECLARATION: return "KEYWORD_DECLARATION"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_INT: return "KEYWORD_INT"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_FLOAT: return "KEYWORD_FLOAT"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_STRING: return "KEYWORD_STRING"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_OBJECT: return "KEYWORD_OBJECT"; + case CSCRIPTCOMPILER_OPERATION_VARIABLE_LIST: return "VARIABLE_LIST"; + case CSCRIPTCOMPILER_OPERATION_VARIABLE: return "VARIABLE"; + case CSCRIPTCOMPILER_OPERATION_STATEMENT_LIST: return "STATEMENT_LIST"; + case CSCRIPTCOMPILER_OPERATION_IF_BLOCK: return "IF_BLOCK"; + case CSCRIPTCOMPILER_OPERATION_IF_CHOICE: return "IF_CHOICE"; + case CSCRIPTCOMPILER_OPERATION_IF_CONDITION: return "IF_CONDITION"; + case CSCRIPTCOMPILER_OPERATION_ACTION: return "ACTION"; + case CSCRIPTCOMPILER_OPERATION_ACTION_ID: return "ACTION_ID"; + case CSCRIPTCOMPILER_OPERATION_ASSIGNMENT: return "ASSIGNMENT"; + case CSCRIPTCOMPILER_OPERATION_ACTION_ARG_LIST: return "ACTION_ARG_LIST"; + case CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER: return "CONSTANT_INTEGER"; + case CSCRIPTCOMPILER_OPERATION_CONSTANT_FLOAT: return "CONSTANT_FLOAT"; + case CSCRIPTCOMPILER_OPERATION_CONSTANT_STRING: return "CONSTANT_STRING"; + case CSCRIPTCOMPILER_OPERATION_INTEGER_EXPRESSION: return "INTEGER_EXPRESSION"; + case CSCRIPTCOMPILER_OPERATION_NON_VOID_EXPRESSION: return "NON_VOID_EXPRESSION"; + case CSCRIPTCOMPILER_OPERATION_LOGICAL_OR: return "LOGICAL_OR"; + case CSCRIPTCOMPILER_OPERATION_LOGICAL_AND: return "LOGICAL_AND"; + case CSCRIPTCOMPILER_OPERATION_INCLUSIVE_OR: return "INCLUSIVE_OR"; + case CSCRIPTCOMPILER_OPERATION_EXCLUSIVE_OR: return "EXCLUSIVE_OR"; + case CSCRIPTCOMPILER_OPERATION_BOOLEAN_AND: return "BOOLEAN_AND"; + case CSCRIPTCOMPILER_OPERATION_CONDITION_EQUAL: return "CONDITION_EQUAL"; + case CSCRIPTCOMPILER_OPERATION_CONDITION_NOT_EQUAL: return "CONDITION_NOT_EQUAL"; + case CSCRIPTCOMPILER_OPERATION_CONDITION_GEQ: return "CONDITION_GEQ"; + case CSCRIPTCOMPILER_OPERATION_CONDITION_GT: return "CONDITION_GT"; + case CSCRIPTCOMPILER_OPERATION_CONDITION_LT: return "CONDITION_LT"; + case CSCRIPTCOMPILER_OPERATION_CONDITION_LEQ: return "CONDITION_LEQ"; + case CSCRIPTCOMPILER_OPERATION_SHIFT_LEFT: return "SHIFT_LEFT"; + case CSCRIPTCOMPILER_OPERATION_SHIFT_RIGHT: return "SHIFT_RIGHT"; + case CSCRIPTCOMPILER_OPERATION_ADD: return "ADD"; + case CSCRIPTCOMPILER_OPERATION_SUBTRACT: return "SUBTRACT"; + case CSCRIPTCOMPILER_OPERATION_MULTIPLY: return "MULTIPLY"; + case CSCRIPTCOMPILER_OPERATION_DIVIDE: return "DIVIDE"; + case CSCRIPTCOMPILER_OPERATION_MODULUS: return "MODULUS"; + case CSCRIPTCOMPILER_OPERATION_NEGATION: return "NEGATION"; + case CSCRIPTCOMPILER_OPERATION_ACTION_PARAMETER: return "ACTION_PARAMETER"; + case CSCRIPTCOMPILER_OPERATION_UNSIGNED_SHIFT_RIGHT: return "UNSIGNED_SHIFT_RIGHT"; + case CSCRIPTCOMPILER_OPERATION_STRUCTURE_PART: return "STRUCTURE_PART"; + case CSCRIPTCOMPILER_OPERATION_ONES_COMPLEMENT: return "ONES_COMPLEMENT"; + case CSCRIPTCOMPILER_OPERATION_WHILE_BLOCK: return "WHILE_BLOCK"; + case CSCRIPTCOMPILER_OPERATION_WHILE_CHOICE: return "WHILE_CHOICE"; + case CSCRIPTCOMPILER_OPERATION_WHILE_CONDITION: return "WHILE_CONDITION"; + case CSCRIPTCOMPILER_OPERATION_DOWHILE_BLOCK: return "DOWHILE_BLOCK"; + case CSCRIPTCOMPILER_OPERATION_DOWHILE_CONDITION: return "DOWHILE_CONDITION"; + case CSCRIPTCOMPILER_OPERATION_FUNCTIONAL_UNIT: return "FUNCTIONAL_UNIT"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_STRUCT: return "KEYWORD_STRUCT"; + case CSCRIPTCOMPILER_OPERATION_STRUCTURE_DEFINITION: return "STRUCTURE_DEFINITION"; + case CSCRIPTCOMPILER_OPERATION_FUNCTION_IDENTIFIER: return "FUNCTION_IDENTIFIER"; + case CSCRIPTCOMPILER_OPERATION_FUNCTION_DECLARATION: return "FUNCTION_DECLARATION"; + case CSCRIPTCOMPILER_OPERATION_FUNCTION: return "FUNCTION"; + case CSCRIPTCOMPILER_OPERATION_FUNCTION_PARAM_NAME: return "FUNCTION_PARAM_NAME"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_VOID: return "KEYWORD_VOID"; + case CSCRIPTCOMPILER_OPERATION_RETURN: return "RETURN"; + case CSCRIPTCOMPILER_OPERATION_BOOLEAN_NOT: return "BOOLEAN_NOT"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE0: return "KEYWORD_ENGINE_STRUCTURE0"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE1: return "KEYWORD_ENGINE_STRUCTURE1"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE2: return "KEYWORD_ENGINE_STRUCTURE2"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE3: return "KEYWORD_ENGINE_STRUCTURE3"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE4: return "KEYWORD_ENGINE_STRUCTURE4"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE5: return "KEYWORD_ENGINE_STRUCTURE5"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE6: return "KEYWORD_ENGINE_STRUCTURE6"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE7: return "KEYWORD_ENGINE_STRUCTURE7"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE8: return "KEYWORD_ENGINE_STRUCTURE8"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE9: return "KEYWORD_ENGINE_STRUCTURE9"; + case CSCRIPTCOMPILER_OPERATION_CONSTANT_OBJECT: return "CONSTANT_OBJECT"; + case CSCRIPTCOMPILER_OPERATION_KEYWORD_VECTOR: return "KEYWORD_VECTOR"; + case CSCRIPTCOMPILER_OPERATION_CONSTANT_VECTOR: return "CONSTANT_VECTOR"; + case CSCRIPTCOMPILER_OPERATION_GLOBAL_VARIABLES: return "GLOBAL_VARIABLES"; + case CSCRIPTCOMPILER_OPERATION_POST_INCREMENT: return "POST_INCREMENT"; + case CSCRIPTCOMPILER_OPERATION_POST_DECREMENT: return "POST_DECREMENT"; + case CSCRIPTCOMPILER_OPERATION_PRE_INCREMENT: return "PRE_INCREMENT"; + case CSCRIPTCOMPILER_OPERATION_PRE_DECREMENT: return "PRE_DECREMENT"; + case CSCRIPTCOMPILER_OPERATION_COND_BLOCK: return "COND_BLOCK"; + case CSCRIPTCOMPILER_OPERATION_COND_CHOICE: return "COND_CHOICE"; + case CSCRIPTCOMPILER_OPERATION_COND_CONDITION: return "COND_CONDITION"; + case CSCRIPTCOMPILER_OPERATION_SWITCH_BLOCK: return "SWITCH_BLOCK"; + case CSCRIPTCOMPILER_OPERATION_SWITCH_CONDITION: return "SWITCH_CONDITION"; + case CSCRIPTCOMPILER_OPERATION_DEFAULT: return "DEFAULT"; + case CSCRIPTCOMPILER_OPERATION_CASE: return "CASE"; + case CSCRIPTCOMPILER_OPERATION_BREAK: return "BREAK"; + case CSCRIPTCOMPILER_OPERATION_CONTINUE: return "CONTINUE"; + case CSCRIPTCOMPILER_OPERATION_WHILE_CONTINUE: return "WHILE_CONTINUE"; + case CSCRIPTCOMPILER_OPERATION_STATEMENT_NO_DEBUG: return "STATEMENT_NO_DEBUG"; + case CSCRIPTCOMPILER_OPERATION_FOR_BLOCK: return "FOR_BLOCK"; + case CSCRIPTCOMPILER_OPERATION_CONST_DECLARATION: return "CONST_DECLARATION"; + case CSCRIPTCOMPILER_OPERATION_CONSTANT_JSON: return "CONSTANT_JSON"; + case CSCRIPTCOMPILER_OPERATION_CONSTANT_LOCATION: return "CONSTANT_LOCATION"; + } + return "(unknown operation)"; +} + diff --git a/src/Native Compiler/scriptcompfinalcode.cpp b/src/Native Compiler/scriptcompfinalcode.cpp new file mode 100644 index 0000000..47ef2bc --- /dev/null +++ b/src/Native Compiler/scriptcompfinalcode.cpp @@ -0,0 +1,7059 @@ +// +// SPDX-License-Identifier: GPL-3.0 +// +// This file is part of the NWScript compiler open source release. +// +// The initial source release is licensed under GPL-3.0. +// +// All subsequent changes you submit are required to be licensed under MIT. +// +// However, the project overall will still be GPL-3.0. +// +// The intent is for the base game to be able to pick up changes you explicitly +// submit for inclusion painlessly, while ensuring the overall project source code +// remains available for everyone. +// + +/////////////////////////////////////////////////////////////////////////////// +// BIOWARE CORP. CONFIDENTIAL INFORMATION. // +// COPYRIGHT BIOWARE CORP. ALL RIGHTS RESERVED // +/////////////////////////////////////////////////////////////////////////////// + +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Script Project +//:: +//:: Copyright (c) 2001, BioWare Corp. +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: ScriptCompFinalCode.cpp +//:: +//:: Implementation of changing the parse tree into a piece of code that we +//:: can deal with. +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Created By: Mark Brockington +//:: Created On: October 8, 2002 +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: This file contains "ported" code (used when writing out floating point +//:: numbers on the MacIntosh platforms). +//:: +//:://///////////////////////////////////////////////////////////////////////// + +#include +#include + +// external header files +#include "exobase.h" +#include "scriptcomp.h" + +// internal header files +#include "scriptinternal.h" + +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Class CScriptCompiler +//:: +//:://///////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::GenerateCodeFromParseTree() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/26/2000 +// Description: Clears out the user defined identifiers. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::GenerateFinalCodeFromParseTree(CExoString sFileName) +{ + + int nState; + int nRule; + int nTerm; + CScriptParseTreeNode *pCurrentTree; + CScriptParseTreeNode *pReturnTree; + CScriptParseTreeNode *pNewReturnTree; // after the global variables have been re-added. + + PopSRStack(&nState,&nRule,&nTerm,&pCurrentTree,&pReturnTree); + + m_nTotalCompileNodes = 1; + + int32_t nReturnValue = InstallLoader(); + pNewReturnTree = InsertGlobalVariablesInParseTree(pReturnTree); + if (nReturnValue >= 0) + { + nReturnValue = WalkParseTree(pNewReturnTree); + } + else + { + OutputWalkTreeError(nReturnValue, NULL); + } + + if (nReturnValue < 0) + { + return CleanUpAfterCompile(nReturnValue,pNewReturnTree); + } + else + { + nReturnValue = DetermineLocationOfCode(); + if (nReturnValue >= 0) + { + nReturnValue = ResolveLabels(); + } + if (nReturnValue >= 0) + { + ResolveDebuggingInformation(); + + nReturnValue = WriteResolvedOutput(); + } + if (nReturnValue >= 0) + { + nReturnValue = WriteDebuggerOutputToFile(sFileName); + } + if (nReturnValue < 0) + { + return CleanUpAfterCompile(nReturnValue,pNewReturnTree); + } + return CleanUpAfterCompile(0,pNewReturnTree); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::InitializeFinalCode() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/16/2001 +// Description: Sets up all of the data structures for the development of +// the final game code. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::InitializeFinalCode() +{ + if (m_pchOutputCode == NULL) + { + m_pchOutputCode = new char[CSCRIPTCOMPILER_MAX_CODE_SIZE]; + m_nOutputCodeSize = CSCRIPTCOMPILER_MAX_CODE_SIZE; + } + + sprintf(m_pchOutputCode,"NCS V1.0"); + m_nOutputCodeLength = CVIRTUALMACHINE_BINARY_SCRIPT_HEADER; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::FinalizeFinalCode() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/16/2001 +// Description: Finishes off the data structures for the development of +// the final game code. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::FinalizeFinalCode() +{ + m_pchOutputCode[8] = (char) 'B'; + m_pchOutputCode[9] = (char) ((m_nOutputCodeLength >> 24) & 0xff); + m_pchOutputCode[10] = (char) ((m_nOutputCodeLength >> 16) & 0xff); + m_pchOutputCode[11] = (char) ((m_nOutputCodeLength >> 8) & 0xff); + m_pchOutputCode[12] = (char) ((m_nOutputCodeLength) & 0xff); +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::WriteFinalCodeToFile() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/16/2001 +// Description: Outputs the final code to the file for later use. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::WriteFinalCodeToFile(const CExoString &sFileName) +{ + + CExoString sModifiedFileName; + sModifiedFileName.Format("%s:%s",m_sOutputAlias.CStr(),sFileName.CStr()); + + const int32_t ret = m_cAPI.ResManWriteToFile( + sModifiedFileName.CStr(), m_nResTypeCompiled, + (const uint8_t*) m_pchOutputCode, m_nOutputCodeLength, true); + + if (ret != 0) + { + return ret; + } + + if (m_bAutomaticCleanUpAfterCompiles == TRUE) + { + CExoString sDirectoryFileName; + + sDirectoryFileName.Format("%s:",m_sOutputAlias.CStr()); + m_cAPI.ResManUpdateResourceDirectory(sDirectoryFileName.CStr()); + + // Delete the code. + if (m_pchOutputCode != NULL) + { + delete[] m_pchOutputCode; + m_pchOutputCode = NULL; + } + + m_nOutputCodeLength = 0; + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ClearUserDefinedIdentifiers() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/26/2000 +// Description: Clears out the user defined identifiers. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::ClearUserDefinedIdentifiers() +{ + for (int32_t count = m_nOccupiedIdentifiers - 1; count >= m_nMaxPredefinedIdentifierId; --count) + { + HashManagerDelete(CSCRIPTCOMPILER_HASH_MANAGER_TYPE_IDENTIFIER, count); + + m_pcIdentifierList[count].m_psIdentifier = ""; + m_pcIdentifierList[count].m_nIdentifierLength = 0; + m_pcIdentifierList[count].m_nIdentifierHash = 0; + m_pcIdentifierList[count].m_nIdentifierType = 0; + m_pcIdentifierList[count].m_nReturnType = 0; + m_pcIdentifierList[count].m_bImplementationInPlace = FALSE; + m_pcIdentifierList[count].m_psStructureReturnName = ""; + + // For constants ... + m_pcIdentifierList[count].m_psStringData = ""; + m_pcIdentifierList[count].m_nIntegerData = 0; + m_pcIdentifierList[count].m_fFloatData = 0.0; + // For identifiers .. + m_pcIdentifierList[count].m_nIdIdentifier = -1; + m_pcIdentifierList[count].m_nParameters = 0; + m_pcIdentifierList[count].m_nNonOptionalParameters = 0; + m_pcIdentifierList[count].m_nParameterSpace = 0; + + if (m_pcIdentifierList[count].m_pchParameters) + { + delete[] m_pcIdentifierList[count].m_pchParameters; + m_pcIdentifierList[count].m_pchParameters = NULL; + } + if (m_pcIdentifierList[count].m_psStructureParameterNames) + { + delete[] m_pcIdentifierList[count].m_psStructureParameterNames; + m_pcIdentifierList[count].m_psStructureParameterNames = NULL; + } + if (m_pcIdentifierList[count].m_pbOptionalParameters) + { + delete[] m_pcIdentifierList[count].m_pbOptionalParameters; + m_pcIdentifierList[count].m_pbOptionalParameters = NULL; + } + if (m_pcIdentifierList[count].m_pnOptionalParameterIntegerData) + { + delete[] m_pcIdentifierList[count].m_pnOptionalParameterIntegerData; + m_pcIdentifierList[count].m_pnOptionalParameterIntegerData = NULL; + } + if (m_pcIdentifierList[count].m_pfOptionalParameterFloatData) + { + delete[] m_pcIdentifierList[count].m_pfOptionalParameterFloatData; + m_pcIdentifierList[count].m_pfOptionalParameterFloatData = NULL; + } + if (m_pcIdentifierList[count].m_psOptionalParameterStringData) + { + delete[] m_pcIdentifierList[count].m_psOptionalParameterStringData; + m_pcIdentifierList[count].m_psOptionalParameterStringData = NULL; + } + if (m_pcIdentifierList[count].m_poidOptionalParameterObjectData) + { + delete[] m_pcIdentifierList[count].m_poidOptionalParameterObjectData; + m_pcIdentifierList[count].m_poidOptionalParameterObjectData = NULL; + } + if (m_pcIdentifierList[count].m_pfOptionalParameterVectorData) + { + delete[] m_pcIdentifierList[count].m_pfOptionalParameterVectorData; + m_pcIdentifierList[count].m_pfOptionalParameterVectorData = NULL; + } + + + // For user-defined identifiers + m_pcIdentifierList[count].m_nBinarySourceStart = -1; + m_pcIdentifierList[count].m_nBinarySourceFinish = -1; + m_pcIdentifierList[count].m_nBinaryDestinationStart = -1; + m_pcIdentifierList[count].m_nBinaryDestinationFinish = -1; + + } + + m_nOccupiedIdentifiers = m_nMaxPredefinedIdentifierId; + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ClearAllSymbolLists() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/26/2000 +// Description: Clears out the query and label symbol lists. +/////////////////////////////////////////////////////////////////////////////// + + +void CScriptCompiler::ClearAllSymbolLists() +{ + if (m_pSymbolQueryList != NULL) + { + delete[] m_pSymbolQueryList; + m_pSymbolQueryList = NULL; + } + + m_nSymbolQueryListSize = 0; + m_nSymbolQueryList = 0; + + if (m_pSymbolLabelList != NULL) + { + delete[] m_pSymbolLabelList; + m_pSymbolLabelList = NULL; + } + + m_nSymbolLabelListSize = 0; + m_nSymbolLabelList = 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::CleanUpAfterCompile() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/26/2000 +// Description: This routine will delete all of the data associated with the +// current compile tree. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::CleanUpAfterCompile(int32_t nReturnValue,CScriptParseTreeNode *pReturnTree) +{ + DeleteParseTree(FALSE, pReturnTree); + if (nReturnValue < 0) + { + if (m_bAutomaticCleanUpAfterCompiles == TRUE) + { + ClearCompiledScriptCode(); + } + else + { + m_nOutputCodeLength = 0; + } + } + DeleteParseTree(FALSE, m_pGlobalVariableParseTree); + m_pGlobalVariableParseTree = NULL; + ClearUserDefinedIdentifiers(); + ClearAllSymbolLists(); + m_aOutputCodeInstructionBoundaries.resize(0); + + // Reset the state of the ConditionalFile boolean. + if (m_bCompileConditionalOrMain == TRUE) + { + m_bCompileConditionalFile = m_bOldCompileConditionalFile; + } + + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::OutputWalkTreeError() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/27/99 +// Description: This routine spews out an error message to the log file when +// we encounter an error during the semantic phase of the +// parsing (i.e. we're now walking the tree). +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::OutputWalkTreeError(int32_t nError, CScriptParseTreeNode *pNode) +{ + CExoString strRes = m_cAPI.TlkResolve(-nError); + + CExoString *psFileName; + if (pNode != NULL && pNode->m_nFileReference != -1) + { + psFileName = m_ppsParseTreeFileNames[pNode->m_nFileReference]; + } + else + { + psFileName = &(m_pcIncludeFileStack[m_nCompileFileLevel].m_sCompiledScriptName); + } + + int32_t nLine; + if (pNode != NULL) + { + nLine = pNode->nLine; + } + else + { + nLine = 0; + } + + OutputError(nError,psFileName,nLine,strRes); + + return STRREF_CSCRIPTCOMPILER_ERROR_ALREADY_PRINTED; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::InsertGlobalVariablesInParseTree() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/09/2001 +// Description: Adds the global variable tree into the main compile tree. +/////////////////////////////////////////////////////////////////////////////// + +CScriptParseTreeNode *CScriptCompiler::InsertGlobalVariablesInParseTree(CScriptParseTreeNode *pOldTree) +{ + // If there's nothing to add, why are we here? + if (m_pGlobalVariableParseTree == NULL) + { + return pOldTree; + } + + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_FUNCTIONAL_UNIT,m_pGlobalVariableParseTree,pOldTree); + m_pGlobalVariableParseTree = NULL; + + return pNewNode; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::InstallLoader() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/25/2000 +// Description: A quick utility ... this routine should be run before we +// start compiling code, because this will add the important +// JSR FE_main/RET pair that closes out a script. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::InstallLoader() +{ + // Mark instruction boundary before the first JSR instruction. + // This is immediately after the header and is always at offset 13 + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // Verify "main" is a void-returning function that is actually + // declared within this script. + + int32_t nMainIdentifier; + + if (m_bCompileConditionalOrMain == TRUE) + { + // Save the state of the CompileConditionalFile flag. + // It will be restored in CleanUpAfterCompile! + m_bOldCompileConditionalFile = m_bCompileConditionalFile; + + nMainIdentifier = GetIdentifierByName("main"); + if (nMainIdentifier >= 0) + { + // This is a void main() script, and should compile! + m_bCompileConditionalFile = FALSE; + } + else + { + nMainIdentifier = GetIdentifierByName("StartingConditional"); + if (nMainIdentifier >= 0) + { + // This is a conditional script, and should compile! + m_bCompileConditionalFile = TRUE; + } + else + { + // Neither are present, so we're going to error just as + // if we are expecting a void main() function! + m_bCompileConditionalFile = FALSE; + } + } + } + + if (m_bCompileConditionalFile == FALSE) + { + nMainIdentifier = GetIdentifierByName("main"); + if (nMainIdentifier < 0) + { + return STRREF_CSCRIPTCOMPILER_ERROR_NO_FUNCTION_MAIN_IN_SCRIPT; + } + + if (m_pcIdentifierList[nMainIdentifier].m_nReturnType != CSCRIPTCOMPILER_TOKEN_VOID_IDENTIFIER) + { + return STRREF_CSCRIPTCOMPILER_ERROR_FUNCTION_MAIN_MUST_HAVE_VOID_RETURN_VALUE; + } + + if (m_pcIdentifierList[nMainIdentifier].m_nParameters != 0) + { + return STRREF_CSCRIPTCOMPILER_ERROR_FUNCTION_MAIN_MUST_HAVE_NO_PARAMETERS; + } + } + else + { + // If m_bCompileConditionalFile == TRUE, then the only possible choice + // for what to compile is int StartingConditional() + + nMainIdentifier = GetIdentifierByName("StartingConditional"); + if (nMainIdentifier < 0) + { + return STRREF_CSCRIPTCOMPILER_ERROR_NO_FUNCTION_INTSC_IN_SCRIPT; + } + + if (m_pcIdentifierList[nMainIdentifier].m_nReturnType != CSCRIPTCOMPILER_TOKEN_INTEGER_IDENTIFIER) + { + return STRREF_CSCRIPTCOMPILER_ERROR_FUNCTION_INTSC_MUST_HAVE_VOID_RETURN_VALUE; + } + + if (m_pcIdentifierList[nMainIdentifier].m_nParameters != 0) + { + return STRREF_CSCRIPTCOMPILER_ERROR_FUNCTION_INTSC_MUST_HAVE_NO_PARAMETERS; + } + } + + + BOOL bGlobalVariablesPresent = FALSE; + if (m_pGlobalVariableParseTree != NULL) + { + bGlobalVariablesPresent = TRUE; + } + + m_pcIdentifierList[m_nOccupiedIdentifiers].m_psIdentifier = "#loader"; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nIdentifierHash = HashString("#loader"); + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nIdentifierLength = 7; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nBinarySourceStart = m_nOutputCodeLength; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nBinaryDestinationStart = -1; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nBinaryDestinationFinish = -1; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nParameters = 0; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_VOID_IDENTIFIER; + HashManagerAdd(CSCRIPTCOMPILER_HASH_MANAGER_TYPE_IDENTIFIER, m_nOccupiedIdentifiers); + + // Add the JSR FE_main OR FE_#globals instruction, depending on whether + // there are global variables or not. + // + // Here, we will need to write a symbol into the code and + // mark the location for updating during the label generation pass. + + if (m_pcIdentifierList[nMainIdentifier].m_nReturnType == CSCRIPTCOMPILER_TOKEN_INTEGER_IDENTIFIER) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_INTEGER_IDENTIFIER; + + ++m_nOccupiedVariables; + + ++m_nVarStackRecursionLevel; + ++m_nGlobalVariables; + + + m_pcVarStackList[m_nOccupiedVariables].m_psVarName = "#retval"; + m_pcVarStackList[m_nOccupiedVariables].m_nVarType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + m_pcVarStackList[m_nOccupiedVariables].m_nVarLevel = m_nVarStackRecursionLevel; + m_pcVarStackList[m_nOccupiedVariables].m_nVarRunTimeLocation = m_nStackCurrentDepth * 4; + + int32_t nOccupiedVariables = m_nOccupiedVariables; + int32_t nStackCurrentDepth = m_nStackCurrentDepth; + int32_t nGlobalVariableSize = m_nGlobalVariableSize; + + // Now, we can add the variable to the run time stack (to write the code), and then + // immediately remove it. This makes NO sense whatsoever, right? Well, imagine this ... + // what happens if we already have a spot allocated from the calling function for this + // return variable? + + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_INT, NULL, TRUE); + AddToSymbolTableVarStack(nOccupiedVariables,nStackCurrentDepth,nGlobalVariableSize); + + --m_nStackCurrentDepth; + } + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JSR; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + // There is no point in expressing this yet, because we haven't decided on the + // locations of any of the final functions in the executable. So, write + // the symbol into the table. + + //CExoString sSymbolName; + int32_t nSymbolSubType1 = 0; + int32_t nSymbolSubType2 = 0; + if (bGlobalVariablesPresent) + { + //sSymbolName.Format("FE_#globals"); + nSymbolSubType2 = 1; + } + else + { + nSymbolSubType2 = 2; + /* + if (m_bCompileConditionalFile == TRUE) + { + sSymbolName.Format("FE_StartingConditional"); + } + else + { + sSymbolName.Format("FE_main"); + } + */ + } + AddSymbolToQueryList(m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION, + CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_FUNCTION_ENTRY, + nSymbolSubType1,nSymbolSubType2); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_RET; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // Last, but not least, add a function to the user-defined identifier list + // that specifies where this code is located. + + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nBinarySourceFinish = m_nOutputCodeLength; + + ++m_nOccupiedIdentifiers; + if (m_nOccupiedIdentifiers >= CSCRIPTCOMPILER_MAX_IDENTIFIERS) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_IDENTIFIER_LIST_FULL, NULL); + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::DetermineLocationOfCode() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/25/2000 +// Description: This function is responsible for determining which functions +// are required in the script, and which ones will be left out. +// Calls ValidateLocationOfIdentifier (recursively) to determine +// all of this. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::DetermineLocationOfCode() +{ + if (m_nOptimizationFlags & CSCRIPTCOMPILER_OPTIMIZE_DEAD_FUNCTIONS) + { + // Here, we have to compress the language into something that + // we can deal with on a small level. + m_nFinalBinarySize = CVIRTUALMACHINE_BINARY_SCRIPT_HEADER; + return ValidateLocationOfIdentifier("#loader"); + + } + else + { + m_nFinalBinarySize = m_nOutputCodeLength; + + for (int32_t count2 = m_nMaxPredefinedIdentifierId; count2 < m_nOccupiedIdentifiers; count2++) + { + if (m_pcIdentifierList[count2].m_nBinarySourceStart != -1) + { + m_pcIdentifierList[count2].m_nBinaryDestinationStart = m_pcIdentifierList[count2].m_nBinarySourceStart; + m_pcIdentifierList[count2].m_nBinaryDestinationFinish = m_pcIdentifierList[count2].m_nBinarySourceFinish; + } + } + + return 0; + } + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ValidateLocationOfIdentifier() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/25/2000 +// Description: This function is responsible for determining which functions +// are required in the script, and which ones will be left out. +// Calls itself recursively on all of the functions that are +// called within this function to see if they're already in the +// script, and add them if they aren't. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ValidateLocationOfIdentifier(const CExoString &sFunctionName) +{ + int32_t nIdentifier = GetIdentifierByName(sFunctionName); + + if (nIdentifier < 0) + { + return OutputIdentifierError(sFunctionName,nIdentifier,0); + } + + if (m_pcIdentifierList[nIdentifier].m_nBinaryDestinationStart != -1) + { + return 0; + } + + int32_t nFunctionSize = m_pcIdentifierList[nIdentifier].m_nBinarySourceFinish - + m_pcIdentifierList[nIdentifier].m_nBinarySourceStart; + + m_pcIdentifierList[nIdentifier].m_nBinaryDestinationStart = m_nFinalBinarySize; + m_pcIdentifierList[nIdentifier].m_nBinaryDestinationFinish = m_nFinalBinarySize + nFunctionSize; + + m_nFinalBinarySize += nFunctionSize; + + // Now, this is where it gets EXCITING. We reference all of the + // functions that this routine could potentially call, and add + // them to the list, too! + + CExoString sNewFunctionName; + int32_t nReturnValue; + + for (int32_t count = 0; count < m_nSymbolQueryList; count++) + { + if (m_pSymbolQueryList[count].m_nLocationPointer >= m_pcIdentifierList[nIdentifier].m_nBinarySourceStart && + m_pSymbolQueryList[count].m_nLocationPointer < m_pcIdentifierList[nIdentifier].m_nBinarySourceFinish) + { + // Make sure the query is for a function label, and NOT a switch or continue statement. + if (m_pSymbolQueryList[count].m_nSymbolType == CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_FUNCTION_ENTRY) + { + sNewFunctionName = GetFunctionNameFromSymbolSubTypes(m_pSymbolQueryList[count].m_nSymbolSubType1, + m_pSymbolQueryList[count].m_nSymbolSubType2); + + // Get the function name! + //sNewFunctionName = m_pSymbolQueryList[count].m_sSymbolName.Right(m_pSymbolQueryList[count].m_sSymbolName.GetLength() - 3); + nReturnValue = ValidateLocationOfIdentifier(sNewFunctionName); + if (nReturnValue < 0) + { + return nReturnValue; + } + } + } + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::OutputIdentifierError() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/25/2000 +// Description: Spits out the error when accessing things in the resolution +// identifiers (nLine and nChar aren't really available at this +// point, so it makes sense to just refer to the function. +/////////////////////////////////////////////////////////////////////////////// +int32_t CScriptCompiler::OutputIdentifierError(const CExoString &sFunctionName, int32_t nError, int32_t nFileStackDrop) +{ + CExoString strRes = m_cAPI.TlkResolve(-nError); + + //g_pTlkTable->Fetch(-nError, strRes); + int32_t nFileStackEntry = m_nCompileFileLevel - nFileStackDrop; + if (nFileStackEntry < 0) + { + nFileStackEntry = 0; + } + + CExoString *psFileName = &(m_pcIncludeFileStack[nFileStackEntry].m_sCompiledScriptName); + CExoString sErrorText; + sErrorText.Format("%s (%s)",strRes.CStr(),sFunctionName.CStr()); + OutputError(nError,psFileName,0,sErrorText); + + return STRREF_CSCRIPTCOMPILER_ERROR_ALREADY_PRINTED; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ResolveLabels() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/25/2000 +// Description: This function will resolve all of the labels, based on the +// final location of the functions within the script. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ResolveLabels() +{ + + CExoString sFunctionName; + BOOL bFoundLabel; + BOOL bFoundIdentifier; + + for (int32_t count = 0; count < m_nSymbolQueryList; count++) + { + + // Resolve the location of where we will be writing the address + // for the label we're lookin' for. + int32_t nResolvedLocationOfAddress = m_pSymbolQueryList[count].m_nLocationPointer; + + int32_t nQueryIdentifier = 0; + int32_t count2; + bFoundIdentifier = FALSE; + for (count2 = m_nMaxPredefinedIdentifierId; count2 < m_nOccupiedIdentifiers; count2++) + { + if (nResolvedLocationOfAddress >= m_pcIdentifierList[count2].m_nBinarySourceStart && + nResolvedLocationOfAddress < m_pcIdentifierList[count2].m_nBinarySourceFinish) + { + bFoundIdentifier = TRUE; + nQueryIdentifier = count2; + } + } + + // If this happens, we're in REAL trouble, but we'll return out of + // this routine in any case (the query had to be derived from ONE + // of the functions!) + + if (bFoundIdentifier == FALSE) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER, NULL); + } + + // Now, check to see if the query even BELONGS in the final file. + // If it does, follow up and generate a value for it. + + if (m_pcIdentifierList[nQueryIdentifier].m_nBinaryDestinationStart != -1) + { + + nResolvedLocationOfAddress += (m_pcIdentifierList[nQueryIdentifier].m_nBinaryDestinationStart - + m_pcIdentifierList[nQueryIdentifier].m_nBinarySourceStart); + + // Now, we actually dig out where the "label" has been moved to + // in the code. + + bFoundLabel = FALSE; + int32_t nQueryHash = m_pSymbolQueryList[count].m_nSymbolSubType1 & 0x01ff; + + count2 = m_pSymbolLabelStartEntry[nQueryHash]; + + while (count2 != -1 && bFoundLabel == FALSE) + { + if (m_pSymbolQueryList[count].m_nSymbolSubType1 == m_pSymbolLabelList[count2].m_nSymbolSubType1 && + m_pSymbolQueryList[count].m_nSymbolSubType2 == m_pSymbolLabelList[count2].m_nSymbolSubType2 && + m_pSymbolQueryList[count].m_nSymbolType == m_pSymbolLabelList[count2].m_nSymbolType) + { + int32_t nIdentifier; + if (m_pSymbolQueryList[count].m_nSymbolType == CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_FUNCTION_ENTRY || + m_pSymbolQueryList[count].m_nSymbolType == CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_FUNCTION_EXIT) + { + if (m_pSymbolQueryList[count].m_nSymbolSubType2 == 0 && + m_pSymbolQueryList[count].m_nSymbolSubType1 != 0) + { + nIdentifier = m_pSymbolQueryList[count].m_nSymbolSubType1; + } + else + { + // For main, StartingConditional and #globals, we should go through these hoops. + CExoString sNewFunctionName = GetFunctionNameFromSymbolSubTypes(m_pSymbolQueryList[count].m_nSymbolSubType1, + m_pSymbolQueryList[count].m_nSymbolSubType2); + nIdentifier = GetIdentifierByName(sNewFunctionName); + if (nIdentifier < 0) + { + if (sNewFunctionName == "") + { + // Sorry, dude ... this shouldn't have happened under any circumstance. + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER, NULL); + } + + return OutputIdentifierError(sNewFunctionName,nIdentifier,0); + } + } + } + else + { + // The label does not contain a function name, so it is more than likely within the same function as the query. + nIdentifier = nQueryIdentifier; + } + + int32_t nResolvedAddress = m_pSymbolLabelList[count2].m_nLocationPointer + (m_pcIdentifierList[nIdentifier].m_nBinaryDestinationStart - m_pcIdentifierList[nIdentifier].m_nBinarySourceStart); + + // Now that we have the two, we can subtract the two from + // one another, and write that relative JMP into the code. + + // DON'T FORGET TO REMOVE THE OPERATION_BASE_SIZE, TOO! + // Without it, you won't have the true instruction pointer, + // since the label only stores where the label belongs in + // the source code! + + int32_t nJmpLength = nResolvedAddress - (nResolvedLocationOfAddress - CVIRTUALMACHINE_OPERATION_BASE_SIZE); + + // ... and write it into its appropriate location. + m_pchOutputCode[m_pSymbolQueryList[count].m_nLocationPointer] = (char) (((nJmpLength) >> 24) & 0x0ff); + m_pchOutputCode[m_pSymbolQueryList[count].m_nLocationPointer + 1] = (char) (((nJmpLength) >> 16) & 0x0ff); + m_pchOutputCode[m_pSymbolQueryList[count].m_nLocationPointer + 2] = (char) (((nJmpLength) >> 8 ) & 0x0ff); + m_pchOutputCode[m_pSymbolQueryList[count].m_nLocationPointer + 3] = (char) (((nJmpLength) ) & 0x0ff); + + bFoundLabel = TRUE; + } + + count2 = m_pSymbolLabelList[count2].m_nNextEntryPointer; + } + + if (bFoundLabel == FALSE) + { + CExoString sNewFunctionName = GetFunctionNameFromSymbolSubTypes(m_pSymbolQueryList[count].m_nSymbolSubType1, + m_pSymbolQueryList[count].m_nSymbolSubType2); + if (sNewFunctionName == "") + { + // Sorry, dude ... this shouldn't have happened under any circumstance. + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER, NULL); + } + + return OutputIdentifierError(sFunctionName,STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER,0); + } + } + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::InitializeSwitchLabelList() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 02/25/2000 +// Description: This function will resolve the labels within this level of +// switch statement. +/////////////////////////////////////////////////////////////////////////////// +void CScriptCompiler::InitializeSwitchLabelList() +{ + m_bSwitchLabelDefault = FALSE; + m_nSwitchLabelNumber = 0; + m_nSwitchLabelArraySize = 16; + m_pnSwitchLabelStatements = new int32_t[m_nSwitchLabelArraySize]; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::TraverseTreeForSwitchLabels() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 02/25/2000 +// Description: This function will resolve the labels within this level of +// switch statement. +/////////////////////////////////////////////////////////////////////////////// +int32_t CScriptCompiler::TraverseTreeForSwitchLabels(CScriptParseTreeNode *pNode) +{ + // First, we scan to see if there are multiple labels of the same type. + int nReturnValue; + + if (pNode == NULL) + { + return 0; + } + // First of all, if we are about to go into another switch block, abort! + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_SWITCH_BLOCK) + { + return 0; + } + + nReturnValue = TraverseTreeForSwitchLabels(pNode->pLeft); + if (nReturnValue < 0) + { + return nReturnValue; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_DEFAULT) + { + if (m_bSwitchLabelDefault == TRUE) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_MULTIPLE_DEFAULT_STATEMENTS_WITHIN_SWITCH, pNode); + } + m_bSwitchLabelDefault = TRUE; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CASE) + { + int32_t nCaseValue; + + ConstantFoldNode(pNode->pLeft, TRUE); + // Evaluate the constant value that is contained. + if (pNode->pLeft != NULL && + pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_NEGATION && + pNode->pLeft->pLeft != NULL && + pNode->pLeft->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER) + { + nCaseValue = -pNode->pLeft->pLeft->nIntegerData; + } + else if (pNode->pLeft != NULL && + pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER) + { + nCaseValue = pNode->pLeft->nIntegerData; + } + else if (pNode->pLeft != NULL && + pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_STRING) + { + nCaseValue = pNode->pLeft->m_psStringData->GetHash(); + } + else + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_CASE_PARAMETER_NOT_A_CONSTANT_INTEGER,pNode); + } + + // Now, we have to check if any of the previous case statements have the same value. + int nCount; + + for (nCount = 0; nCount < m_nSwitchLabelNumber; ++nCount) + { + if (m_pnSwitchLabelStatements[nCount] == nCaseValue) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_MULTIPLE_CASE_CONSTANT_STATEMENTS_WITHIN_SWITCH,pNode); + } + } + + // Add the case statement to the list. + if (m_nSwitchLabelNumber >= m_nSwitchLabelArraySize) + { + int32_t *pNewIntArray = new int32_t[m_nSwitchLabelArraySize * 2]; + for (nCount = 0; nCount < m_nSwitchLabelNumber; ++nCount) + { + pNewIntArray[nCount] = m_pnSwitchLabelStatements[nCount]; + } + m_nSwitchLabelArraySize *= 2; + delete[] m_pnSwitchLabelStatements; + m_pnSwitchLabelStatements = pNewIntArray; + } + + m_pnSwitchLabelStatements[m_nSwitchLabelNumber] = nCaseValue; + ++m_nSwitchLabelNumber; + + // Now, we add the pseudocode: + // COPYTOP fffffffc,0004 // copies the switch result so that we can use it. + // CONSTI nCaseValue // adds the constant that we're to compare against. + // EQUALII // compares the two, leaving the result on the stack. + // JNZ _SC_nCaseValue_nSwitchIdentifier // result goes away, jump executed. + + // CODE GENERATION + // Here, we would dump the "appropriate" data from the run-time stack + // on to the top of the stack, making a copy of it ... that's why + // we're adding one to the appropriate run time stack. + + int32_t nStackElementsDown = -4; + int32_t nSize = 4; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_RUNSTACK_COPY; + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_VOID; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nStackElementsDown) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nStackElementsDown) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nStackElementsDown) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nStackElementsDown)) & 0x0ff); + + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+4] = (char) (((nSize) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+5] = (char) (((nSize)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 6; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // CODE GENERATION + // Here, we have a "constant integer" op-code that would be added. + int32_t nIntegerData = nCaseValue; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_CONSTANT; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER; + + // Enter the integer constant. + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nIntegerData) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nIntegerData) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nIntegerData) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nIntegerData)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // CODE GENERATION + // Write an "condition EQUALII" operation. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_EQUAL; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_INTEGER_INTEGER; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // CODE GENERATION + // Add the "JNZ _SC_nCaseValue_nSwitchIdentifier" operation. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JNZ; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + /* CExoString sSymbolName; + sSymbolName.Format("_SC_%08x_%08x",nCaseValue,m_nSwitchIdentifier); */ + AddSymbolToQueryList(m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION, + CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_SWITCH_CASE, + nCaseValue,m_nSwitchIdentifier); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + } + + nReturnValue = TraverseTreeForSwitchLabels(pNode->pRight); + if (nReturnValue < 0) + { + return nReturnValue; + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ClearSwitchLabelList() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 02/25/2000 +// Description: This function will resolve the labels within this level of +// switch statement. +/////////////////////////////////////////////////////////////////////////////// +void CScriptCompiler::ClearSwitchLabelList() +{ + // Finally, don't forget the last piece of code, where we jump to the + // default label, if it exists. + + if (m_bSwitchLabelDefault == TRUE) + { + // There is a default statement ... we jump there immediately. + + // CODE GENERATION + // Add the "JMP _SC_DEFAULT_nSwitchIdentifier" operation. + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JMP; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + /* CExoString sSymbolName; + sSymbolName.Format("_SC_DEFAULT_%08x",m_nSwitchIdentifier); */ + AddSymbolToQueryList(m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION, + CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_SWITCH_DEFAULT, + m_nSwitchIdentifier,0); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + } + else + { + // There is no default statement ... we jump over the entire switch once + // we've evaded all of the case statements. + + // CODE GENERATION + // Add the "JMP _BR_nSwitchIdentifier" operation. + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JMP; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + /*CExoString sSymbolName; + sSymbolName.Format("_BR_%08x",m_nSwitchIdentifier);*/ + AddSymbolToQueryList(m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION, + CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_BREAK, + m_nSwitchIdentifier,0); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + } + + + m_bSwitchLabelDefault = FALSE; + m_nSwitchLabelNumber = 0; + m_nSwitchLabelArraySize = 16; + delete[] m_pnSwitchLabelStatements; + m_pnSwitchLabelStatements = NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::GenerateIdentifiersFromConstantVariables() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: Nov. 27, 2002 +// Description: This function will resolve all of the constant declarations +// into "identifiers" that can be used in case statements. +/////////////////////////////////////////////////////////////////////////////// +int32_t CScriptCompiler::GenerateIdentifiersFromConstantVariables(CScriptParseTreeNode *pNode) +{ + if (pNode != NULL) + { + if (pNode != NULL && + pNode->pLeft != NULL && + pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_DECLARATION && + pNode->pLeft->pRight != NULL && + pNode->pLeft->pRight->pLeft != NULL) + { + int32_t nVariableTypeOperation = pNode->pLeft->pLeft->nOperation; + CScriptParseTreeNode *pNodeVariableName = pNode->pLeft->pRight->pLeft; + + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nIdentifierType = 0; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_psIdentifier = *(pNodeVariableName->m_psStringData); + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nIdentifierHash = HashString(pNodeVariableName->m_psStringData->CStr()); + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nIdentifierLength = pNodeVariableName->m_psStringData->GetLength(); + + // MGB - If a previous declaration and implementation match, an identifier + // gets removed from the stack. Thus, m_nOccupiedIdentifiers should be cleared. + // This is the biggest "remaining" bug, since it prevents a future function from + // compiling. + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nParameters = 0; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nNonOptionalParameters = 0; + + if (nVariableTypeOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_INT) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_INTEGER_IDENTIFIER; + } + else if (nVariableTypeOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_FLOAT) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_FLOAT_IDENTIFIER; + } + else if (nVariableTypeOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_STRING) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_STRING_IDENTIFIER; + } + else + { + // MGB - Nov. 22, 2002 - It should not have been possible to get to here without + // going through an integer, floating point or string constant, so UNKNOWN_STATE + // makes perfect sense. + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNodeVariableName); + } + + // If there is an assignment ... then we should do something about this! + if (pNode->pRight != NULL && + pNode->pRight->pLeft != NULL && + pNode->pRight->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_ASSIGNMENT && + pNode->pRight->pLeft->pLeft != NULL && + pNode->pRight->pLeft->pLeft->pLeft != NULL) + { + CScriptParseTreeNode *pNodeConstant = pNode->pRight->pLeft->pLeft->pLeft; + ConstantFoldNode(pNodeConstant, TRUE); + + int32_t nConstantOperation = pNodeConstant->nOperation; + int32_t nSign = 1; + + if (nConstantOperation == CSCRIPTCOMPILER_OPERATION_NEGATION) + { + if (nVariableTypeOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_STRING) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_INVALID_VALUE_ASSIGNED_TO_CONSTANT,pNodeConstant); + } + nSign = -1; + pNodeConstant = pNodeConstant->pLeft; + nConstantOperation = pNodeConstant->nOperation; + } + + if ((nVariableTypeOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_INT && + nConstantOperation != CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER) || + (nVariableTypeOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_FLOAT && + nConstantOperation != CSCRIPTCOMPILER_OPERATION_CONSTANT_FLOAT) || + (nVariableTypeOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_STRING && + nConstantOperation != CSCRIPTCOMPILER_OPERATION_CONSTANT_STRING)) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_INVALID_VALUE_ASSIGNED_TO_CONSTANT,pNodeConstant); + } + + // Fill in data for the constant. + if (nConstantOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nIntegerData = nSign * pNodeConstant->nIntegerData; + (m_pcIdentifierList[m_nOccupiedIdentifiers].m_psStringData).Format("%d",m_pcIdentifierList[m_nOccupiedIdentifiers].m_nIntegerData); + } + else if (nConstantOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_STRING) + { + (m_pcIdentifierList[m_nOccupiedIdentifiers].m_psStringData).Format("%s",pNodeConstant->m_psStringData->CStr()); + } + else if (nConstantOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_FLOAT) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_fFloatData = (float) nSign * pNodeConstant->fFloatData; + if (nSign == -1) + { + (m_pcIdentifierList[m_nOccupiedIdentifiers].m_psStringData).Format("-%f",pNodeConstant->fFloatData); + } + else + { + (m_pcIdentifierList[m_nOccupiedIdentifiers].m_psStringData).Format("%f",pNodeConstant->fFloatData); + } + } + } + else + { + // No initialization, or it is so malformed, that the parse tree code + // doesn't recognize it ... so we're going to set it equal to its default + // value. + if (nVariableTypeOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_INT) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nIntegerData = 0; + (m_pcIdentifierList[m_nOccupiedIdentifiers].m_psStringData) = "0"; + } + else if (nVariableTypeOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_STRING) + { + (m_pcIdentifierList[m_nOccupiedIdentifiers].m_psStringData) = ""; + } + else if (nVariableTypeOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_FLOAT) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_fFloatData = 0.0f; + (m_pcIdentifierList[m_nOccupiedIdentifiers].m_psStringData) = "0.0f"; + } + } + HashManagerAdd(CSCRIPTCOMPILER_HASH_MANAGER_TYPE_IDENTIFIER, m_nOccupiedIdentifiers); + ++m_nOccupiedIdentifiers; + } + int32_t nReturnValue = GenerateIdentifiersFromConstantVariables(pNode->pRight); + if (nReturnValue < 0) + { + return nReturnValue; + } + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::GenerateCodeForSwitchLabels() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 02/25/2000 +// Description: This function will resolve the labels within this level of +// switch statement. +/////////////////////////////////////////////////////////////////////////////// +int32_t CScriptCompiler::GenerateCodeForSwitchLabels(CScriptParseTreeNode *pNode) +{ + int32_t nReturnValue = 0; + + InitializeSwitchLabelList(); + if (pNode != NULL) + { + nReturnValue = TraverseTreeForSwitchLabels(pNode->pRight); + } + if (nReturnValue < 0) + { + return nReturnValue; + } + ClearSwitchLabelList(); + + // MGB - For Script Debugger + if (m_nGenerateDebuggerOutput != 0) + { + EndLineNumberAtBinaryInstruction(pNode->m_nFileReference,pNode->nLine,m_nOutputCodeLength); + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::WriteResolvedOutput() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/25/2000 +// Description: This function will resolve all of the labels, based on the +// final location of the functions within the script. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::WriteResolvedOutput() +{ + + if (m_pchResolvedOutputBuffer == NULL || + m_nResolvedOutputBufferSize < m_nFinalBinarySize) + { + if (m_pchResolvedOutputBuffer != NULL) + { + delete[] m_pchResolvedOutputBuffer; + } + // The * 2 is intentional (so we avoid calling this + // each time the compiled file increases by a few bytes!) + m_pchResolvedOutputBuffer = new char[m_nFinalBinarySize * 2]; + m_nResolvedOutputBufferSize = m_nFinalBinarySize; + } + + memset(m_pchResolvedOutputBuffer,0,m_nResolvedOutputBufferSize); + + memcpy(m_pchResolvedOutputBuffer,m_pchOutputCode,CVIRTUALMACHINE_BINARY_SCRIPT_HEADER * sizeof(char)); + + for (int32_t count = m_nMaxPredefinedIdentifierId; count < m_nOccupiedIdentifiers; count++) + { + if (m_pcIdentifierList[count].m_nBinaryDestinationStart != -1) + { + int32_t nSize = (m_pcIdentifierList[count].m_nBinaryDestinationFinish - m_pcIdentifierList[count].m_nBinaryDestinationStart) * sizeof (char); + memcpy(m_pchResolvedOutputBuffer + m_pcIdentifierList[count].m_nBinaryDestinationStart, + m_pchOutputCode + m_pcIdentifierList[count].m_nBinarySourceStart, + nSize); + } + } + + // Now, we copy it back into the output code buffer! + memcpy(m_pchOutputCode,m_pchResolvedOutputBuffer,m_nFinalBinarySize); + m_nOutputCodeLength = m_nFinalBinarySize; + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::PreVisitGenerateCode() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: This routine will generate code to be spat out into the +// necessary file. Will return 0 if the operation is okay, +// and will return a negative number if it isn't. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::PreVisitGenerateCode(CScriptParseTreeNode *pNode) +{ + // MGB - For Script Debugger + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STATEMENT) + { + if (m_nGenerateDebuggerOutput != 0) + { + StartLineNumberAtBinaryInstruction(pNode->m_nFileReference,pNode->nLine,m_nOutputCodeLength); + } + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_COMPOUND_STATEMENT || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STATEMENT || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STATEMENT_NO_DEBUG || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_NON_VOID_EXPRESSION || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_INTEGER_EXPRESSION) + { + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_COMPOUND_STATEMENT) + { + m_nVarStackRecursionLevel++; + } + + // Save the state of the run-time stacks, so that we can check 'em at the + // end. This way, we can verify if we're following the code correctly. + + pNode->m_nStackPointer = m_nStackCurrentDepth; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_FUNCTION) + { + m_bFunctionImp = FALSE; + if (pNode->pRight != NULL) + { + m_bFunctionImp = TRUE; + } + + if (m_bFunctionImp == TRUE) + { + + m_nVarStackRecursionLevel++; + + m_sFunctionImpName = *(pNode->pLeft->pLeft->m_psStringData); + + int32_t nIdentifier = GetIdentifierByName(m_sFunctionImpName); + + if (nIdentifier < 0) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + m_pcIdentifierList[nIdentifier].m_nBinarySourceStart = m_nOutputCodeLength; + m_pcIdentifierList[nIdentifier].m_nBinarySourceFinish = -1; + m_pcIdentifierList[nIdentifier].m_nBinaryDestinationStart = -1; + m_pcIdentifierList[nIdentifier].m_nBinaryDestinationFinish = -1; + + /* CExoString sSymbolName; + sSymbolName.Format("FE_%s",m_sFunctionImpName.CStr()); */ + AddSymbolToLabelList(m_nOutputCodeLength, + CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_FUNCTION_ENTRY, + nIdentifier,0); + + int32_t nIdentifierLength = m_sFunctionImpName.GetLength(); + + if (m_bCompileConditionalFile == 0) + { + if (m_sFunctionImpName.GetLength() == 4 && + strncmp(m_sFunctionImpName.CStr(),"main",4) == 0) + { + AddSymbolToLabelList(m_nOutputCodeLength, + CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_FUNCTION_ENTRY, + 0,2); + } + } + else + { + if (nIdentifierLength == 19 && + strncmp(m_sFunctionImpName.CStr(),"StartingConditional",19) == 0) + { + AddSymbolToLabelList(m_nOutputCodeLength, + CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_FUNCTION_ENTRY, + 0,2); + } + } + + //m_bFunctionImpHasReturn = FALSE; + } + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_GLOBAL_VARIABLES) + { + m_bFunctionImp = FALSE; + if (pNode->pRight != NULL) + { + m_bFunctionImp = TRUE; + } + + if (m_bFunctionImp == TRUE) + { + m_nVarStackRecursionLevel++; + + if (m_bGlobalVariableDefinition != FALSE) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + m_bGlobalVariableDefinition = TRUE; + // MGB - September 13, 2001 - This can't be called + // when you are using an integer-returning function. This is bad. + /* + m_nGlobalVariables = 0; + m_nGlobalVariableSize = 0; + */ + + m_pcIdentifierList[m_nOccupiedIdentifiers].m_psIdentifier = "#globals"; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nIdentifierHash = HashString("#globals"); + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nIdentifierLength = 8; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nBinarySourceStart = m_nOutputCodeLength; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nBinaryDestinationStart = -1; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nBinaryDestinationFinish = -1; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nParameters = 0; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_VOID_IDENTIFIER; + HashManagerAdd(CSCRIPTCOMPILER_HASH_MANAGER_TYPE_IDENTIFIER, m_nOccupiedIdentifiers); + + /* CExoString sSymbolName; + sSymbolName.Format("FE_#globals"); */ + + AddSymbolToLabelList(m_nOutputCodeLength, + CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_FUNCTION_ENTRY, + m_nOccupiedIdentifiers,0); + + AddSymbolToLabelList(m_nOutputCodeLength, + CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_FUNCTION_ENTRY, + 0,1); + } + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CASE) + { + int32_t nCaseValue; + + ConstantFoldNode(pNode->pLeft, TRUE); + // Evaluate the constant value that is contained. + if (pNode->pLeft != NULL && + pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_NEGATION && + pNode->pLeft->pLeft != NULL && + pNode->pLeft->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER) + { + nCaseValue = -pNode->pLeft->pLeft->nIntegerData; + } + else if (pNode->pLeft != NULL && + pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER) + { + nCaseValue = pNode->pLeft->nIntegerData; + } + else if (pNode->pLeft != NULL && + pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_STRING) + { + nCaseValue = pNode->pLeft->m_psStringData->GetHash(); + } + else + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_CASE_PARAMETER_NOT_A_CONSTANT_INTEGER,pNode); + } + + // Generate a label for the jump caused by the switch + /*CExoString sSymbolName; + sSymbolName.Format("_SC_%08x_%08x",nCaseValue,m_nSwitchIdentifier); */ + + AddSymbolToLabelList(m_nOutputCodeLength, + CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_SWITCH_CASE, + nCaseValue,m_nSwitchIdentifier); + + if (m_nSwitchStackDepth + 1 != m_nStackCurrentDepth) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_JUMPING_OVER_DECLARATION_STATEMENTS_CASE_DISALLOWED,pNode); + } + + // Do not allow the tree to continue parsing ... we've done everything required. + return 1; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STRUCTURE_DEFINITION) + { + // We should be in the variable-processing stage of the strucutre + // definition at this point. + + if (m_nStructureDefinition != 0) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + m_nStructureDefinition = 1; + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_ACTION_PARAMETER) + { + // CODE GENERATION + // This is a "action parameter" instruction. Thus, we don't intend + // on running the instruction contained by this part of the compile tree + // right away. Therefore, what we're going to do is store the state of + // the instruction pointer, and keep cruisin'. This will give us a + // fighting chance of identifying the start of the action when someone + // wishes to rerun this action. + + // + // First, we add the STORE_IP instruction. + // + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_STORE_STATE; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = (CVIRTUALMACHINE_OPERATION_BASE_SIZE * 2 + 12); + + int32_t nIntegerData = m_nGlobalVariableSize; + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nIntegerData) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nIntegerData) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nIntegerData) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nIntegerData)) & 0x0ff); + + nIntegerData = m_nStackCurrentDepth * 4 - m_nGlobalVariableSize; + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+4] = (char) (((nIntegerData) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+5] = (char) (((nIntegerData) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+6] = (char) (((nIntegerData) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+7] = (char) (((nIntegerData)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 8; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // + // Next, we add the JMP instruction. + // + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JMP; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + // Save the current state of the binary code length for the purpose + // of generating a good jump over the code within this instruction in + // the PostVisitGenerateCode() call. + + pNode->nIntegerData = m_nOutputCodeLength; + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_ACTION) + { + //if (m_bGlobalVariableDefinition == TRUE) + //{ + // return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + //} + + // Determine, based on the ID of the function, whether we're + // dealing with a function of the user's creation, or one of our + // own. This makes a difference to the code that we generate. + + if (pNode->pRight != NULL) + { + int32_t nCount = GetIdentifierByName(*(pNode->pRight->m_psStringData)); + if (nCount == STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + pNode->pRight->nIntegerData = nCount; + { + if (nCount >= m_nMaxPredefinedIdentifierId) + { + pNode->nIntegerData = 1; + + // Because this is a user-defined identifier, we + // have to reserve space on the runtime stack + // right away for the return type, if it is non-void + // (of course!) + + int32_t nReturnType = 0; + CExoString sStructureName = ""; + + if (m_pcIdentifierList[nCount].m_nReturnType != CSCRIPTCOMPILER_TOKEN_VOID_IDENTIFIER) + { + if (m_pcIdentifierList[nCount].m_nReturnType == CSCRIPTCOMPILER_TOKEN_INTEGER_IDENTIFIER) + { + nReturnType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + } + else if (m_pcIdentifierList[nCount].m_nReturnType == CSCRIPTCOMPILER_TOKEN_FLOAT_IDENTIFIER) + { + nReturnType = CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT; + } + else if (m_pcIdentifierList[nCount].m_nReturnType == CSCRIPTCOMPILER_TOKEN_STRING_IDENTIFIER) + { + nReturnType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING; + } + else if (m_pcIdentifierList[nCount].m_nReturnType == CSCRIPTCOMPILER_TOKEN_OBJECT_IDENTIFIER) + { + nReturnType = CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT; + } + else if (m_pcIdentifierList[nCount].m_nReturnType == CSCRIPTCOMPILER_TOKEN_STRUCTURE_IDENTIFIER) + { + nReturnType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT; + sStructureName = m_pcIdentifierList[nCount].m_psStructureReturnName; + } + else if (m_pcIdentifierList[nCount].m_nReturnType >= CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE0_IDENTIFIER && + m_pcIdentifierList[nCount].m_nReturnType <= CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE9_IDENTIFIER) + { + nReturnType = CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 + (m_pcIdentifierList[nCount].m_nReturnType - CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE0_IDENTIFIER); + } + else + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + AddVariableToStack(nReturnType, &sStructureName, TRUE); + } + } + else + { + pNode->nIntegerData = 0; + } + + // This part of the code attempts to precompile all optional parameters. + // It is incredibly similar to the code in PostVisit + CScriptParseTreeNode *pTracePtr; + int nParameters = 0; + + pTracePtr = pNode->pLeft; + + while (pTracePtr != NULL) + { + nParameters++; + pTracePtr = pTracePtr->pLeft; + } + + int nCount = pNode->pRight->nIntegerData; + BOOL bMatch = TRUE; + + if (nParameters > m_pcIdentifierList[nCount].m_nParameters) + { + bMatch = FALSE; + } + + if (nParameters < m_pcIdentifierList[nCount].m_nNonOptionalParameters) + { + bMatch = FALSE; + } + + if (bMatch == TRUE) + { + if (nParameters != m_pcIdentifierList[nCount].m_nParameters) + { + int32_t nCount2; + for (nCount2 = m_pcIdentifierList[nCount].m_nParameters - 1; nCount2 >= nParameters; --nCount2) + { + if (m_pcIdentifierList[nCount].m_pbOptionalParameters[nCount2] != TRUE) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_DECLARATION_DOES_NOT_MATCH_PARAMETERS,pNode); + } + + if (m_pcIdentifierList[nCount].m_pchParameters[nCount2] != CSCRIPTCOMPILER_TOKEN_KEYWORD_INT && + m_pcIdentifierList[nCount].m_pchParameters[nCount2] != CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT && + m_pcIdentifierList[nCount].m_pchParameters[nCount2] != CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING && + m_pcIdentifierList[nCount].m_pchParameters[nCount2] != CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT && + m_pcIdentifierList[nCount].m_pchParameters[nCount2] != CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE2 /* location */ && + m_pcIdentifierList[nCount].m_pchParameters[nCount2] != CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE7 /* json */ && + (m_pcIdentifierList[nCount].m_pchParameters[nCount2] != CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT || + m_pcIdentifierList[nCount].m_psStructureParameterNames[nCount2] != "vector")) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_TYPE_DOES_NOT_HAVE_AN_OPTIONAL_PARAMETER,pNode); + } + + if (m_pcIdentifierList[nCount].m_pchParameters[nCount2] == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + // CODE GENERATION + // Here, we have a "constant integer" op-code that would be added. + int32_t nIntegerData = m_pcIdentifierList[nCount].m_pnOptionalParameterIntegerData[nCount2]; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_CONSTANT; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER; + + // Enter the integer constant. + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nIntegerData) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nIntegerData) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nIntegerData) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nIntegerData)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + m_pchStackTypes[m_nStackCurrentDepth] = CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER; + ++m_nStackCurrentDepth; + } + + if (m_pcIdentifierList[nCount].m_pchParameters[nCount2] == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT) + { + // CODE GENERATION + // Here, we have a "constant float" op-code that would be added. + + float fFloatData = m_pcIdentifierList[nCount].m_pfOptionalParameterFloatData[nCount2]; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_CONSTANT; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_FLOAT; + + // Enter the floating point constant. + + /////////////////////////////////////////////////////////////////////////// + // This may need some explaining. The MacIntosh deals with floating point + // numbers in the same way as integers ... it reverses the bytes. Thus, if + // we want to avoid doing byte swaps, what we should do is write the data + // out in the specified byte order, and then read it back in with the + // specified byte order. However, you can't just "shift left" on floats, + // so we cast the data to an integer, and then write that out! Tricky, but + // it should work. + // + // -- Mark Brockington, 01/22/2000 + /////////////////////////////////////////////////////////////////////////// + + + int32_t *pFloatAsInt = (int32_t *) &fFloatData; + m_pchOutputCode[m_nOutputCodeLength+ CVIRTUALMACHINE_EXTRA_DATA_LOCATION ] = (char) (((*pFloatAsInt) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((*pFloatAsInt) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((*pFloatAsInt) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((*pFloatAsInt)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + m_pchStackTypes[m_nStackCurrentDepth] = CVIRTUALMACHINE_AUXCODE_TYPE_FLOAT; + ++m_nStackCurrentDepth; + } + + if (m_pcIdentifierList[nCount].m_pchParameters[nCount2] == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT && + m_pcIdentifierList[nCount].m_psStructureParameterNames[nCount2] == "vector") + { + // CODE GENERATION + // Here, we have a "constant float" op-code that would be added. + + for (int32_t temp = 0; temp < 3; ++temp) + { + float fFloatData = m_pcIdentifierList[nCount].m_pfOptionalParameterVectorData[nCount2 * 3 + temp]; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_CONSTANT; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_FLOAT; + + // Enter the floating point constant. + + int32_t *pFloatAsInt = (int32_t *) &fFloatData; + m_pchOutputCode[m_nOutputCodeLength+ CVIRTUALMACHINE_EXTRA_DATA_LOCATION ] = (char) (((*pFloatAsInt) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((*pFloatAsInt) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((*pFloatAsInt) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((*pFloatAsInt)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + m_pchStackTypes[m_nStackCurrentDepth] = CVIRTUALMACHINE_AUXCODE_TYPE_FLOAT; + ++m_nStackCurrentDepth; + } + } + + if (m_pcIdentifierList[nCount].m_pchParameters[nCount2] == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING) + { + // CODE GENERATION + // Here, we have a "constant string" op-code that would be added. + + CExoString sStringData = m_pcIdentifierList[nCount].m_psOptionalParameterStringData[nCount2]; + int nLength = sStringData.GetLength(); + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_CONSTANT; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_STRING; + + // Enter the string constant. + + m_pchOutputCode[m_nOutputCodeLength+ CVIRTUALMACHINE_EXTRA_DATA_LOCATION ] = (char) (((nLength) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nLength)) & 0x0ff); + + int nCount3; + for (nCount3 = 0; nCount3 < nLength; nCount3++) + { + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+nCount3+2] = (sStringData.CStr())[nCount3]; + } + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + nLength + 2; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + m_pchStackTypes[m_nStackCurrentDepth] = CVIRTUALMACHINE_AUXCODE_TYPE_STRING; + ++m_nStackCurrentDepth; + } + + if (m_pcIdentifierList[nCount].m_pchParameters[nCount2] == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT) + { + // CODE GENERATION + // Here, we have a "constant object" op-code that would be added. + OBJECT_ID oidObjectData = m_pcIdentifierList[nCount].m_poidOptionalParameterObjectData[nCount2]; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_CONSTANT; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_OBJECT; + + // Enter the integer constant. + int32_t nIntegerData = (int32_t) oidObjectData; + + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nIntegerData) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nIntegerData) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nIntegerData) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nIntegerData)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + m_pchStackTypes[m_nStackCurrentDepth] = CVIRTUALMACHINE_AUXCODE_TYPE_OBJECT; + ++m_nStackCurrentDepth; + } + + if (m_pcIdentifierList[nCount].m_pchParameters[nCount2] == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE2) // location + { + // Encode the location as a int for now. + + int32_t nIntegerData = m_pcIdentifierList[nCount].m_pnOptionalParameterIntegerData[nCount2]; + + // only LOCATION_INVALID supported for now + if (nIntegerData != 0) + { + return OutputWalkTreeError(STRREF_CVIRTUALMACHINE_ERROR_INVALID_EXTRA_DATA_ON_OP_CODE, pNode); + } + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_CONSTANT; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_ENGST2; + + // Enter the integer constant. + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nIntegerData) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nIntegerData) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nIntegerData) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nIntegerData)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + m_pchStackTypes[m_nStackCurrentDepth] = CVIRTUALMACHINE_AUXCODE_TYPE_ENGST2; + ++m_nStackCurrentDepth; + } + + if (m_pcIdentifierList[nCount].m_pchParameters[nCount2] == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE7) // json + { + // encode the json as a variable-size payload string + + CExoString sStringData = m_pcIdentifierList[nCount].m_psOptionalParameterStringData[nCount2]; + EXOASSERT(sStringData.GetLength() <= 0xffff); + if (sStringData.GetLength() > 0xffff) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_TOKEN_TOO_LONG,pNode); + } + + const uint16_t nLength = sStringData.GetLength(); + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_CONSTANT; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_ENGST7; + + // Enter the jsonified string constant. + + m_pchOutputCode[m_nOutputCodeLength+ CVIRTUALMACHINE_EXTRA_DATA_LOCATION ] = (char) (((nLength) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nLength)) & 0x0ff); + + int nCount3; + for (nCount3 = 0; nCount3 < nLength; nCount3++) + { + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+nCount3+2] = (sStringData.CStr())[nCount3]; + } + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + nLength + 2; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + m_pchStackTypes[m_nStackCurrentDepth] = CVIRTUALMACHINE_AUXCODE_TYPE_ENGST7; + ++m_nStackCurrentDepth; + + } + + + + } + } + + + return 0; + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_DECLARATION_DOES_NOT_MATCH_PARAMETERS,pNode); + } + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_WHILE_BLOCK) + { + + // Set the label for continue jumps. + pNode->nIntegerData3 = m_nLoopIdentifier; + pNode->nIntegerData4 = m_nLoopStackDepth; + m_nLoopIdentifier = m_nOutputCodeLength; + m_nLoopStackDepth = m_nStackCurrentDepth; + + // First things first, we may need to jump all the way back to + // this point in the code, so we should save the location of this + // part of the code. + + pNode->nIntegerData = m_nOutputCodeLength; + + if (m_nGenerateDebuggerOutput != 0) + { + StartLineNumberAtBinaryInstruction(pNode->m_nFileReference,pNode->nLine,m_nOutputCodeLength); + } + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_SWITCH_BLOCK) + { + ++m_nSwitchLevel; + + // Store the old value of the switch identifier, and then + // create our own value for the switch identifier. + + pNode->nIntegerData2 = m_nSwitchIdentifier; + pNode->nIntegerData3 = m_nSwitchStackDepth; + m_nSwitchIdentifier = m_nOutputCodeLength; + m_nSwitchStackDepth = m_nStackCurrentDepth; + + if (m_nGenerateDebuggerOutput != 0) + { + StartLineNumberAtBinaryInstruction(pNode->m_nFileReference,pNode->nLine,m_nOutputCodeLength); + } + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_DOWHILE_BLOCK) + { + + // Set label for continue/break jumps. + pNode->nIntegerData3 = m_nLoopIdentifier; + pNode->nIntegerData4 = m_nLoopStackDepth; + m_nLoopIdentifier = m_nOutputCodeLength; + m_nLoopStackDepth = m_nStackCurrentDepth; + + // First things first, we may need to jump all the way back to + // this point in the code, so we should save the location of this + // part of the code. + + pNode->nIntegerData = m_nOutputCodeLength; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_IF_CHOICE) + { + // First things first, we may need to jump all the way back to + // this point in the code, so we should save the location of this + // part of the code. + + pNode->nIntegerData = m_nOutputCodeLength; + + if (m_pchStackTypes[m_nStackCurrentDepth-1] != CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_INTEGER_NOT_AT_TOP_OF_STACK,pNode); + } + --m_nStackCurrentDepth; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JZ; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + // Save the current state of the binary code length for the purpose + // of generating a good jump over the code within this instruction in + // the PostVisitGenerateCode() call. + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_COND_CHOICE) + { + // First things first, we may need to jump all the way back to + // this point in the code, so we should save the location of this + // part of the code. + + pNode->nIntegerData = m_nOutputCodeLength; + + if (m_pchStackTypes[m_nStackCurrentDepth-1] != CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_INTEGER_NOT_AT_TOP_OF_STACK,pNode); + } + --m_nStackCurrentDepth; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JZ; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + // Save the current state of the binary code length for the purpose + // of generating a good jump over the code within this instruction in + // the PostVisitGenerateCode() call. + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_IF_CONDITION) + { + if (m_nGenerateDebuggerOutput != 0) + { + StartLineNumberAtBinaryInstruction(pNode->m_nFileReference,pNode->nLine,m_nOutputCodeLength); + } + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_PRE_INCREMENT || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_PRE_DECREMENT) + { + // Here we write the code for doing a pre-increment or a pre-decrement, and + // we actually do all the checking (and filling in the address) in the + // PostVisitGenerateCode() call. Makes sense? I sure hope so when I have + // to look at this four months down the road. :-) + + // For writing out the code, just store a blank location. + int32_t nStackElementsDown = 0; + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_PRE_INCREMENT) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_INCREMENT; + } + else + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_DECREMENT; + } + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nStackElementsDown) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nStackElementsDown) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nStackElementsDown) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nStackElementsDown)) & 0x0ff); + + // The address for the "blank" address should be stored in pNode->nIntegerData2; + pNode->nIntegerData2 = m_nOutputCodeLength; + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + return 0; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::InVisitGenerateCode() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: This routine will generate code to be spat out into the +// necessary file. Will return 0 if the operation is okay, +// and will return a negative number if it isn't. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::InVisitGenerateCode(CScriptParseTreeNode *pNode) +{ + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STRUCTURE_DEFINITION) + { + // We should be in the variable-processing stage of the strucutre + // definition at this point. + + if (m_nStructureDefinition != 1) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + m_nStructureDefinition = 2; + + if (pNode->pLeft == NULL || pNode->pLeft->nOperation != CSCRIPTCOMPILER_OPERATION_KEYWORD_STRUCT) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + int32_t count; + for (count = 0; count < m_nMaxStructures; count++) + { + if (*(pNode->pLeft->m_psStringData) == m_pcStructList[count].m_psName) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_STRUCTURE_REDEFINED,pNode); + } + } + + m_pcStructList[m_nMaxStructures].m_psName = *(pNode->pLeft->m_psStringData); + m_pcStructList[m_nMaxStructures].m_nByteSize = 0; + m_pcStructList[m_nMaxStructures].m_nFieldStart = m_nMaxStructureFields; + m_pcStructList[m_nMaxStructures].m_nFieldEnd = -1; + + m_nStructureDefinitionFieldStart = m_nMaxStructureFields; + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_ASSIGNMENT) + { + if (pNode->pRight != NULL && pNode->pLeft != NULL) + { + // Is the storage location a valid LValue? + if (pNode->pRight->nOperation == CSCRIPTCOMPILER_OPERATION_VARIABLE || pNode->pRight->nOperation == CSCRIPTCOMPILER_OPERATION_STRUCTURE_PART) + { + m_bAssignmentToVariable = TRUE; + return 0; + } + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_INVALID_PARAMETERS_FOR_ASSIGNMENT,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STATEMENT || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STATEMENT_NO_DEBUG) + { + + // Do NOTHING on a normal keyword declaration statement ... leave it as a + // statement, since this is equivalent to a STATEMENT_VOID. Also, we can add + // a whole pile of variables in one statement, and we don't want to remove them. + // So, we should bail out of the routine right now if we're dealing with a + // declaration keyword. + // + // Note that we also have complex chains of statements within a "statement list" + // (since we now allow these to be statically initialized). Hence, the really + // gruesome looking second condition. + + if ((pNode->pLeft != NULL && pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_DECLARATION) || + //(pNode->pLeft != NULL && pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_RETURN) || + (pNode->pLeft != NULL && pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_STATEMENT_LIST && + ((pNode->pLeft->pLeft != NULL && pNode->pLeft->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_STATEMENT) || (pNode->pLeft->pLeft != NULL && pNode->pLeft->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_STATEMENT_NO_DEBUG)) && + pNode->pLeft->pLeft->pLeft != NULL && pNode->pLeft->pLeft->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_DECLARATION)) + { + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STATEMENT) + { + if (m_nGenerateDebuggerOutput != 0) + { + EndLineNumberAtBinaryInstruction(pNode->m_nFileReference,pNode->nLine,m_nOutputCodeLength); + } + } + return 0; + } + + // Update the state of the stack to where it should be. We will + // be writing out the code to do this here. + pNode->nIntegerData = (pNode->m_nStackPointer - m_nStackCurrentDepth) * 4; + m_nStackCurrentDepth = pNode->m_nStackPointer; + + // CODE GENERATION + // Move the stack to update the current location of the code. + + if (pNode->nIntegerData != 0) + { + EmitModifyStackPointer(pNode->nIntegerData); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STATEMENT) + { + if (m_nGenerateDebuggerOutput != 0) + { + EndLineNumberAtBinaryInstruction(pNode->m_nFileReference,pNode->nLine,m_nOutputCodeLength); + } + } + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_SWITCH_BLOCK) + { + return GenerateCodeForSwitchLabels(pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_WHILE_BLOCK) + { + // + // Set up our conditional jump at this point in the code. We may + // also need to reference this location at a later time to add the + // address of where to jump to! + + pNode->nIntegerData2 = m_nOutputCodeLength; + + if (m_pchStackTypes[m_nStackCurrentDepth-1] != CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_INTEGER_NOT_AT_TOP_OF_STACK,pNode); + } + --m_nStackCurrentDepth; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JZ; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + // Save the current state of the binary code length for the purpose + // of generating a good jump over the code within this instruction in + // the PostVisitGenerateCode() call. + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + if (m_nGenerateDebuggerOutput != 0) + { + EndLineNumberAtBinaryInstruction(pNode->m_nFileReference,pNode->nLine,m_nOutputCodeLength); + } + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_IF_CHOICE) + { + pNode->nIntegerData2 = m_nOutputCodeLength; + + // Here, we implement the skip-ahead to _I2_ and install the + // label at _I1_. + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JMP; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // Here, we go back and add the relative offset to get beyond this return + // statement to the JZ command we gave earlier during + // the PreVisit() [code located at pNode->nIntegerdata]. + // This is an awfully good thing to do. + + int32_t nJmpLength = m_nOutputCodeLength - pNode->nIntegerData; + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nJmpLength) >> 24) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 1] = (char) (((nJmpLength) >> 16) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 2] = (char) (((nJmpLength) >> 8 ) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 3] = (char) (((nJmpLength) ) & 0x0ff); + + // MGB - For Script Debugger + if (pNode->pRight != NULL) + { + + if (m_nGenerateDebuggerOutput != 0) + { + StartLineNumberAtBinaryInstruction(pNode->m_nFileReference,pNode->nLine,m_nOutputCodeLength); + } + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_NO_OPERATION; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + if (m_nGenerateDebuggerOutput != 0) + { + EndLineNumberAtBinaryInstruction(pNode->m_nFileReference,pNode->nLine,m_nOutputCodeLength); + } + + } + + + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_COND_CHOICE) + { + + pNode->nIntegerData2 = m_nOutputCodeLength; + + // Here, we implement the skip-ahead to _CH2_ and install the + // label at _CH1_. + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JMP; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // Here, we go back and add the relative offset to get beyond this return + // statement to the JZ command we gave earlier during + // the PreVisit() [code located at pNode->nIntegerdata]. + // This is an awfully good thing to do. + + int32_t nJmpLength = m_nOutputCodeLength - pNode->nIntegerData; + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nJmpLength) >> 24) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 1] = (char) (((nJmpLength) >> 16) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 2] = (char) (((nJmpLength) >> 8 ) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 3] = (char) (((nJmpLength) ) & 0x0ff); + + // NOTE: We've got to get rid of the first thing on the stack here, otherwise, we're + // TOTALLY boned when using variables in the second part. + + int32_t nElementsToDelete = 4; + if (pNode->pLeft && pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + nElementsToDelete = GetStructureSize(*(pNode->pLeft->m_psTypeName)); + } + + m_nStackCurrentDepth -= (nElementsToDelete >> 2); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_FUNCTION && m_bFunctionImp == TRUE) + { + // Get the current stack pointer, now that we've added fake + // versions of all of the variables on to the stack. Woo-hoo! + + pNode->m_nStackPointer = m_nStackCurrentDepth; + + // Store it for use by the RETURN command, too! + m_nFunctionImpAbortStackPointer = m_nStackCurrentDepth; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_LOGICAL_AND) + { + if (pNode->pLeft != NULL) + { + // Okay, this is the scoop. If the top value is zero, we should + // just ignore the code that does the rest of the "if choice". + + // ... the integer at the top of the stack. + + // CODE GENERATION + // Here, we would dump the "appropriate" data from the run-time stack + // on to the top of the stack, making a copy of it ... that's why + // we're adding one to the appropriate run time stack. + + int32_t nStackElementsDown = -4; + int32_t nSize = 4; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_RUNSTACK_COPY; + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_VOID; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nStackElementsDown) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nStackElementsDown) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nStackElementsDown) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nStackElementsDown)) & 0x0ff); + + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+4] = (char) (((nSize) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+5] = (char) (((nSize)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 6; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_INT, NULL, FALSE); + + // + // ... and a JZ to skip the right branch. + // + + pNode->nIntegerData = m_nOutputCodeLength; + if (m_pchStackTypes[m_nStackCurrentDepth-1] != CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_INTEGER_NOT_AT_TOP_OF_STACK,pNode); + } + --m_nStackCurrentDepth; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JZ; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + // The jump length is over this instruction and the jmp instruction, both of + // which are OPERATION_BASE_SIZE + 4 (for the address). + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + } + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_LOGICAL_OR) + { + if (pNode->pLeft != NULL) + { + // Okay, this is the scoop. If the top value is zero, we should + // just ignore the code that does the rest of the "if choice". + + // ... the integer at the top of the stack. + + // CODE GENERATION + // Here, we would dump the "appropriate" data from the run-time stack + // on to the top of the stack, making a copy of it ... that's why + // we're adding one to the appropriate run time stack. + + int32_t nStackElementsDown = -4; + int32_t nSize = 4; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_RUNSTACK_COPY; + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_VOID; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nStackElementsDown) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nStackElementsDown) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nStackElementsDown) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nStackElementsDown)) & 0x0ff); + + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+4] = (char) (((nSize) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+5] = (char) (((nSize)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 6; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_INT, NULL, FALSE); + + // + // ... and a JZ to skip to the right branch! + // + + if (m_pchStackTypes[m_nStackCurrentDepth-1] != CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_INTEGER_NOT_AT_TOP_OF_STACK,pNode); + } + --m_nStackCurrentDepth; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JZ; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + // The jump length is over this instruction and the jmp instruction, and + // the copy top instruction (an additional 4+6+4 bytes above the BASE_SIZE + // operations). + + int32_t nJmpLength = (CVIRTUALMACHINE_OPERATION_BASE_SIZE * 3) + 4 + 6 + 4; + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nJmpLength) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nJmpLength) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nJmpLength) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nJmpLength)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // .... the integer at the top of the stack. + + // CODE GENERATION + // Here, we would dump the "appropriate" data from the run-time stack + // on to the top of the stack, making a copy of it ... that's why + // we're adding one to the appropriate run time stack. + + nStackElementsDown = -4; + nSize = 4; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_RUNSTACK_COPY; + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_VOID; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nStackElementsDown) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nStackElementsDown) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nStackElementsDown) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nStackElementsDown)) & 0x0ff); + + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+4] = (char) (((nSize) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+5] = (char) (((nSize)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 6; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // IMPORTANT NOTE: It is absolutely vital to NOT add this variable to the + // stack, since this path is completely optional ... and the right branch + // will be adding an integer anyway! + + pNode->nIntegerData = m_nOutputCodeLength; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JMP; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + // The jump length is over this instruction and the jmp instruction, both of + // which are OPERATION_BASE_SIZE + 4 (for the address). + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + + } + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STRUCTURE_PART) + { + m_bInStructurePart = TRUE; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_DOWHILE_BLOCK) + { + // Generate a label for the continue keyword. + + if (m_nGenerateDebuggerOutput != 0) + { + StartLineNumberAtBinaryInstruction(pNode->m_nFileReference,pNode->nLine,m_nOutputCodeLength); + } + + /* CExoString sSymbolName; + sSymbolName.Format("_CN_%08x",m_nLoopIdentifier); */ + AddSymbolToLabelList(m_nOutputCodeLength, + CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_CONTINUE, + m_nLoopIdentifier); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STATEMENT) + { + if (m_nGenerateDebuggerOutput != 0) + { + EndLineNumberAtBinaryInstruction(pNode->m_nFileReference,pNode->nLine,m_nOutputCodeLength); + } + } + + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::PostVisitGenerateCode() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: This routine will generate code to be spat out into the +// necessary file. Will return 0 if the operation is okay, +// and will return a negative number if it isn't. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::PostVisitGenerateCode(CScriptParseTreeNode *pNode) +{ + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_ASSIGNMENT) + { + if (pNode->pRight != NULL && pNode->pLeft != NULL) + { + // Variable is referred to in an expression. + // Look for the variable in the list. When we find it, generate its op-code. + + if (pNode->pRight->nType == pNode->pLeft->nType) + { + if (pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT && + *(pNode->pRight->m_psTypeName) != *(pNode->pLeft->m_psTypeName)) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_MISMATCHED_TYPES,pNode); + } + + pNode->nType = pNode->pRight->nType; + if (pNode->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + if (pNode->m_psTypeName != NULL) + { + delete pNode->m_psTypeName; + } + (pNode->m_psTypeName) = new CExoString(pNode->pLeft->m_psTypeName->CStr()); + } + + // CODE GENERATION + // We should generate a op-code that is consistent with the + // type of assignment that is taking place (i.e. integer, + // float, string or object). This operation has no effect + // on the run time stacks, but copies the code from the top + // of the stack to the code. + + int32_t nStackElementsDown = pNode->pRight->nIntegerData - (m_nStackCurrentDepth * 4); + int32_t nSize = 4; + if (pNode->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + nSize = GetStructureSize(*(pNode->m_psTypeName)); + } + + // MGB - August 10, 2001 - Determine whether we are going to operate on + // the stack pointer (for a locally defined variable), or the base pointer + // (for a global variable). + + int32_t bOperateOnStackPointer = TRUE; + if (pNode->pRight->nIntegerData < m_nGlobalVariableSize && m_bGlobalVariableDefinition == FALSE) + { + bOperateOnStackPointer = FALSE; + nStackElementsDown = pNode->pRight->nIntegerData - (m_nGlobalVariableSize); + } + + if (bOperateOnStackPointer == TRUE) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_ASSIGNMENT; + } + else + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_ASSIGNMENT_BASE; + } + + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_VOID; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nStackElementsDown) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nStackElementsDown) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nStackElementsDown) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nStackElementsDown)) & 0x0ff); + + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+4] = (char) (((nSize) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+5] = (char) (((nSize)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 6; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + m_bAssignmentToVariable = FALSE; + return 0; + } + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_MISMATCHED_TYPES,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_VOID) + { + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_INT) + { + m_nVarStackVariableType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_FLOAT) + { + m_nVarStackVariableType = CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT; + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_STRING) + { + m_nVarStackVariableType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING; + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_OBJECT) + { + m_nVarStackVariableType = CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT; + return 0; + } + + if (pNode->nOperation >= CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE0 && + pNode->nOperation <= CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE9) + { + m_nVarStackVariableType = CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 + (pNode->nOperation - CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE0); + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_STRUCT) + { + // When structure definition is 1, then we're in the + // process of defining a structure name, and it's not + // necessary to set these variables ... only when we're + // actually defining a structure as a variable to be + // used somewhere else. Thus, we do it here. + + if (m_nStructureDefinition != 1) + { + m_nVarStackVariableType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT; + m_sVarStackVariableTypeName = *(pNode->m_psStringData); + } + + return 0; + } + + int32_t nCount, nCount2; + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_VARIABLE) + { + + // There are only two occasions where we should get a variable as a "terminal" + // within the compile tree: as a branch from a variable list node (in the + // declaration part of the tree) or as a part of an expression. The first + // case is determined within this phase by checking to see if m_nVarStackVariableType + // is not equal to OPERATION_KEYWORD_DECLARATION, because the type in this + // variable specifies that we're in the middle of handling a declaration list! + + // There is a third case where a variable is handled as a terminal node, but the + // assignment to a variable is handled in the case just above this one ... so + // we don't have to worry about generating a value here. + + if (m_nVarStackVariableType != CSCRIPTCOMPILER_OPERATION_KEYWORD_DECLARATION) + { + if (m_nStructureDefinition == 0) + { + for (nCount = m_nOccupiedVariables; nCount >= 0; --nCount) + { + if (m_pcVarStackList[nCount].m_psVarName == *(pNode->m_psStringData) && + m_pcVarStackList[nCount].m_nVarLevel == m_nVarStackRecursionLevel) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_VARIABLE_ALREADY_USED_WITHIN_SCOPE,pNode); + } + } + + ++m_nOccupiedVariables; + if (m_bGlobalVariableDefinition) + { + ++m_nGlobalVariables; + if (m_nVarStackVariableType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + m_nGlobalVariableSize += GetStructureSize(m_sVarStackVariableTypeName); + } + else + { + m_nGlobalVariableSize += 4; + } + + } + + m_pcVarStackList[m_nOccupiedVariables].m_psVarName = *(pNode->m_psStringData); + m_pcVarStackList[m_nOccupiedVariables].m_nVarType = m_nVarStackVariableType; + if (m_nVarStackVariableType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + m_pcVarStackList[m_nOccupiedVariables].m_sVarStructureName = m_sVarStackVariableTypeName; + + int32_t count; + BOOL bFoundStructure = FALSE; + for (count = 0; count < m_nMaxStructures; count++) + { + if (m_sVarStackVariableTypeName == m_pcStructList[count].m_psName) + { + bFoundStructure = TRUE; + } + } + if (bFoundStructure == FALSE) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_STRUCTURE,pNode); + } + + } + else + { + m_sVarStackVariableTypeName = ""; + } + + m_pcVarStackList[m_nOccupiedVariables].m_nVarLevel = m_nVarStackRecursionLevel; + m_pcVarStackList[m_nOccupiedVariables].m_nVarRunTimeLocation = m_nStackCurrentDepth * 4; + + + + int32_t nOccupiedVariables = m_nOccupiedVariables; + int32_t nStackCurrentDepth = m_nStackCurrentDepth; + int32_t nGlobalVariableSize = m_nGlobalVariableSize; + + // Now, we can add the variable to the stack! + AddVariableToStack(m_nVarStackVariableType, &m_sVarStackVariableTypeName, TRUE); + + //AddToSymbolTableVarStack(nOccupiedVariables,nStackCurrentDepth,nGlobalVariableSize); + if (m_bGlobalVariableDefinition) + { + // Global variables are always at the bottom of the stack, + // so we fake out the SymbolTableVarStack to think that this + // is its own stack context (even though we're in the middle + // of building it!) + AddToSymbolTableVarStack(nOccupiedVariables, nStackCurrentDepth, 0); + } + else + { + AddToSymbolTableVarStack(nOccupiedVariables, nStackCurrentDepth, nGlobalVariableSize); + } + + } + else + { + // This part checks instantiates variables within a structure + // definition. Thus, we really don't need to worry about writing + // code out at this point ... we're just worried about semantic + // checking of the various parts. + + // First, we should check to see if this name is used in any other + // field within this structure + for (nCount = m_nStructureDefinitionFieldStart; nCount < m_nMaxStructureFields; nCount++) + { + if (m_pcStructFieldList[nCount].m_psVarName == *(pNode->m_psStringData)) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_VARIABLE_USED_TWICE_IN_SAME_STRUCTURE,pNode); + } + } + + // Okay, now we're done ... store the structure field in its + // proper location. + + m_pcStructFieldList[m_nMaxStructureFields].m_pchType = (char) m_nVarStackVariableType; + if (m_nVarStackVariableType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + m_pcStructFieldList[m_nMaxStructureFields].m_psStructureName = m_sVarStackVariableTypeName; + } + m_pcStructFieldList[m_nMaxStructureFields].m_psVarName = *(pNode->m_psStringData); + m_pcStructFieldList[m_nMaxStructureFields].m_nLocation = 0; + + ++m_nMaxStructureFields; + } + } + else + { + // Variable is referred to in an expression. + + // Look for the variable in the list. When we find it, generate its op-code. + // The first part of this handles the case where the variable is used within + // an expression. In some cases, (i.e. an assignment to a variable), one + // does not want this copy to happen. Thus, we actually avoid writing out + // any code in the case where we don't have to copy data up. + + if (m_bInStructurePart == TRUE) + { + return 0; + } + + for (nCount = m_nOccupiedVariables; nCount >= 0; --nCount) + { + if (m_pcVarStackList[nCount].m_psVarName == *(pNode->m_psStringData)) + { + // Now, we can get rid of the data. + delete (pNode->m_psStringData); + pNode->m_psStringData = NULL; + + pNode->nType = m_pcVarStackList[nCount].m_nVarType; + + if (pNode->m_psTypeName != NULL) + { + delete pNode->m_psTypeName; + } + + if (pNode->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + pNode->m_psTypeName = new CExoString(m_pcVarStackList[nCount].m_sVarStructureName.CStr()); + } + else + { + pNode->m_psTypeName = NULL; + } + + // For the purposes of writing out code, we need to do this operation. + pNode->nIntegerData = m_pcVarStackList[nCount].m_nVarRunTimeLocation; + + + if (m_bAssignmentToVariable == FALSE) + { + + // CODE GENERATION + // Here, we would dump the "appropriate" data from the run-time stack + // on to the top of the stack, making a copy of it ... that's why + // we're adding one to the appropriate run time stack. + + int32_t nStackElementsDown = pNode->nIntegerData - (m_nStackCurrentDepth * 4); + int32_t nSize = 4; + if (pNode->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + nSize = GetStructureSize(*(pNode->m_psTypeName)); + } + + // MGB - August 10, 2001 - Determine whether we are going to operate on + // the stack pointer (for a locally defined variable), or the base pointer + // (for a global variable). + + int32_t bOperateOnStackPointer = TRUE; + if (pNode->nIntegerData < m_nGlobalVariableSize && m_bGlobalVariableDefinition == FALSE) + { + bOperateOnStackPointer = FALSE; + nStackElementsDown = pNode->nIntegerData - (m_nGlobalVariableSize); + } + + if (bOperateOnStackPointer == TRUE) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_RUNSTACK_COPY; + } + else + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_RUNSTACK_COPY_BASE; + } + + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_VOID; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nStackElementsDown) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nStackElementsDown) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nStackElementsDown) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nStackElementsDown)) & 0x0ff); + + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+4] = (char) (((nSize) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+5] = (char) (((nSize)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 6; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + + // Add variable on to the stack, too. + AddVariableToStack(pNode->nType, pNode->m_psTypeName, FALSE); + + } + + return 0; + } + } + + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_VARIABLE_DEFINED_WITHOUT_TYPE,pNode); + + } + return 0; + + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER) + { + // CODE GENERATION + // Here, we have a "constant integer" op-code that would be added. + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_CONSTANT; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER; + + // Enter the integer constant. + m_pchOutputCode[m_nOutputCodeLength+ CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((pNode->nIntegerData) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((pNode->nIntegerData) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((pNode->nIntegerData) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((pNode->nIntegerData)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + m_pchStackTypes[m_nStackCurrentDepth] = CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER; + ++m_nStackCurrentDepth; + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_OBJECT) + { + // CODE GENERATION + // Here, we have a "constant object" op-code that would be added. + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_CONSTANT; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_OBJECT; + + // Enter the integer constant, since that is where we stored + // the value. :-) + m_pchOutputCode[m_nOutputCodeLength+ CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((pNode->nIntegerData) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((pNode->nIntegerData) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((pNode->nIntegerData) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((pNode->nIntegerData)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT; + m_pchStackTypes[m_nStackCurrentDepth] = CVIRTUALMACHINE_AUXCODE_TYPE_OBJECT; + ++m_nStackCurrentDepth; + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_FLOAT) + { + // CODE GENERATION + // Here, we have a "constant float" op-code that would be added. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_CONSTANT; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_FLOAT; + + // Enter the floating point constant. + + /////////////////////////////////////////////////////////////////////////// + // This may need some explaining. The MacIntosh deals with floating point + // numbers in the same way as integers ... it reverses the bytes. Thus, if + // we want to avoid doing byte swaps, what we should do is write the data + // out in the specified byte order, and then read it back in with the + // specified byte order. However, you can't just "shift left" on floats, + // so we cast the data to an integer, and then write that out! Tricky, but + // it should work. + // + // -- Mark Brockington, 01/22/2000 + /////////////////////////////////////////////////////////////////////////// + + int32_t *pFloatAsInt = (int32_t *) &pNode->fFloatData; + m_pchOutputCode[m_nOutputCodeLength+ CVIRTUALMACHINE_EXTRA_DATA_LOCATION ] = (char) (((*pFloatAsInt) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((*pFloatAsInt) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((*pFloatAsInt) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((*pFloatAsInt)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT; + m_pchStackTypes[m_nStackCurrentDepth] = CVIRTUALMACHINE_AUXCODE_TYPE_FLOAT; + ++m_nStackCurrentDepth; + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_VECTOR) + { + // CODE GENERATION + // Here, we have a "constant float" op-code that would be added. + + for (int32_t nCount = 0; nCount < 3; nCount++) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_CONSTANT; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_FLOAT; + + // Enter the floating point constant. + + /////////////////////////////////////////////////////////////////////////// + // This may need some explaining. The MacIntosh deals with floating point + // numbers in the same way as integers ... it reverses the bytes. Thus, if + // we want to avoid doing byte swaps, what we should do is write the data + // out in the specified byte order, and then read it back in with the + // specified byte order. However, you can't just "shift left" on floats, + // so we cast the data to an integer, and then write that out! Tricky, but + // it should work. + // + // -- Mark Brockington, 01/22/2000 + /////////////////////////////////////////////////////////////////////////// + + int32_t *pFloatAsInt = (int32_t *) &(pNode->fVectorData[nCount]); + m_pchOutputCode[m_nOutputCodeLength+ CVIRTUALMACHINE_EXTRA_DATA_LOCATION ] = (char) (((*pFloatAsInt) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((*pFloatAsInt) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((*pFloatAsInt) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((*pFloatAsInt)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + m_pchStackTypes[m_nStackCurrentDepth] = CVIRTUALMACHINE_AUXCODE_TYPE_FLOAT; + ++m_nStackCurrentDepth; + } + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT; + pNode->m_psTypeName = new CExoString("vector"); + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_STRING) + { + // CODE GENERATION + // Here, we have a "constant string" op-code that would be added. + + int nLength = pNode->m_psStringData->GetLength(); + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_CONSTANT; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_STRING; + + // Enter the string constant. + + m_pchOutputCode[m_nOutputCodeLength+ CVIRTUALMACHINE_EXTRA_DATA_LOCATION ] = (char) (((nLength) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nLength)) & 0x0ff); + + for (nCount = 0; nCount < nLength; nCount++) + { + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+nCount+2] = (pNode->m_psStringData->CStr())[nCount]; + } + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + nLength + 2; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING; + m_pchStackTypes[m_nStackCurrentDepth] = CVIRTUALMACHINE_AUXCODE_TYPE_STRING; + ++m_nStackCurrentDepth; + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_LOCATION) + { + // CODE GENERATION + // Here, we have a "constant location" op-code that would be added. + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_CONSTANT; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_ENGST2; + + // Enter the integer constant. + m_pchOutputCode[m_nOutputCodeLength+ CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((pNode->nIntegerData) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((pNode->nIntegerData) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((pNode->nIntegerData) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((pNode->nIntegerData)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE2; + m_pchStackTypes[m_nStackCurrentDepth] = CVIRTUALMACHINE_AUXCODE_TYPE_ENGST2; + ++m_nStackCurrentDepth; + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_JSON) + { + // CODE GENERATION + // Here, we have a "constant json" op-code that would be added. + + int nLength = pNode->m_psStringData->GetLength(); + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_CONSTANT; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_ENGST7; + + // Enter the string constant. + + m_pchOutputCode[m_nOutputCodeLength+ CVIRTUALMACHINE_EXTRA_DATA_LOCATION ] = (char) (((nLength) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nLength)) & 0x0ff); + + for (nCount = 0; nCount < nLength; nCount++) + { + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+nCount+2] = (pNode->m_psStringData->CStr())[nCount]; + } + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + nLength + 2; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE7; + m_pchStackTypes[m_nStackCurrentDepth] = CVIRTUALMACHINE_AUXCODE_TYPE_ENGST7; + ++m_nStackCurrentDepth; + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_DECLARATION) + { + // This is simply for the purposes of "declaring" that we're out of this + // one for defining variables. This doesn't need to be carried over into + // code, since each of the variables has the type appended on to it, but + // the name isn't stored anywhere. + + m_nVarStackVariableType = CSCRIPTCOMPILER_OPERATION_KEYWORD_DECLARATION; + pNode->nType = 0; + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_VARIABLE_LIST || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STATEMENT_LIST) + { + pNode->nType = 0; + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_ACTION_ARG_LIST) + { + // FOR TESTING PURPOSES + // Transfer the type of the argument up to this level, so that when + // we test the action's parameters, we need only travel down "left" + // branches of ACTION_ARG_LIST calls. + if (pNode->pRight != NULL) + { + pNode->nType = pNode->pRight->nType; + + if (pNode->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + if (pNode->m_psTypeName != NULL) + { + delete pNode->m_psTypeName; + } + + (pNode->m_psTypeName) = new CExoString(pNode->pRight->m_psTypeName->CStr()); + } + } + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_ACTION_PARAMETER) + { + // CODE GENERATION + + // The first instruction that we need to run is a RETURN command. + // since we have to abort out of whatever statement we're in the middle + // of. + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_RET; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // Here, we go back and add the relative offset to get beyond this return + // statement to the JMP command we gave earlier. This is an awfully good + // thing to do. + + int32_t nJmpLength = m_nOutputCodeLength - pNode->nIntegerData; + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nJmpLength) >> 24) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 1] = (char) (((nJmpLength) >> 16) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 2] = (char) (((nJmpLength) >> 8 ) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 3] = (char) (((nJmpLength) ) & 0x0ff); + + // FOR TESTING PURPOSES + // Transfer the type of the argument through the node, so that we can + // pass it up to the ACTION_ARG_LIST call. + + if (pNode->pLeft != NULL) + { + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_ACTION; + } + + // ++m_nRunTimeActions; + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_ACTION_ID) + { + pNode->nIntegerData = GetIdentifierByName(*(pNode->m_psStringData)); + + if (pNode->nIntegerData < 0) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_ACTION) + { + // FOR TESTING PURPOSES + // This code is responsible for checking the types of the parameters + // and verifying that we've got a viable parameter list for the given + // ID. + + if (pNode->pRight != NULL) + { + CScriptParseTreeNode *pTracePtr; + int nParameters = 0; + + pTracePtr = pNode->pLeft; + + while (pTracePtr != NULL) + { + if (pTracePtr->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT || + pTracePtr->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ACTION || + pTracePtr->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT || + pTracePtr->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING || + pTracePtr->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT || + pTracePtr->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT || + pTracePtr->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 || + pTracePtr->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE1 || + pTracePtr->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE2 || + pTracePtr->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE3 || + pTracePtr->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE4 || + pTracePtr->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE5 || + pTracePtr->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE6 || + pTracePtr->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE7 || + pTracePtr->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE8 || + pTracePtr->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9) + { + m_pchActionParameters[nParameters] = (char) pTracePtr->nType; + + if (pTracePtr->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + CExoString sTypeName = *(pTracePtr->m_psTypeName); + m_pchActionParameterStructureNames[nParameters] = sTypeName; + int32_t nSize = GetStructureSize(sTypeName) >> 2; + m_nStackCurrentDepth -= nSize; + } + else if (pTracePtr->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_ACTION) + { + --m_nStackCurrentDepth; + } + } + else + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + nParameters++; + pTracePtr = pTracePtr->pLeft; + } + + int nCount = pNode->pRight->nIntegerData; + BOOL bMatch = TRUE; + + if (nParameters > m_pcIdentifierList[nCount].m_nParameters) + { + bMatch = FALSE; + } + + if (nParameters < m_pcIdentifierList[nCount].m_nNonOptionalParameters) + { + bMatch = FALSE; + } + + for (nCount2 = 0; nCount2 < nParameters; nCount2++) + { + if ((m_pchActionParameters[nCount2] != m_pcIdentifierList[nCount].m_pchParameters[nCount2]) || + (m_pchActionParameters[nCount2] == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT && + m_pchActionParameterStructureNames[nCount2] != m_pcIdentifierList[nCount].m_psStructureParameterNames[nCount2])) + { + bMatch = FALSE; + } + } + + if (bMatch == TRUE) + { + // Remove all of the optional parameters that haven't been specified, + // but were added the PreVisitGenerateCode. + + if (nParameters < m_pcIdentifierList[nCount].m_nParameters) + { + for (int32_t cnt = nParameters; cnt < m_pcIdentifierList[nCount].m_nParameters; cnt++) + { + if (m_pcIdentifierList[nCount].m_pchParameters[cnt] == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + m_pchActionParameterStructureNames[nParameters] = m_pcIdentifierList[nCount].m_psStructureParameterNames[cnt]; + int32_t nSize = GetStructureSize(m_pcIdentifierList[nCount].m_psStructureParameterNames[cnt]) >> 2; + m_nStackCurrentDepth -= nSize; + } + else if (m_pcIdentifierList[nCount].m_pchParameters[cnt] != CSCRIPTCOMPILER_TOKEN_KEYWORD_ACTION) + { + --m_nStackCurrentDepth; + } + } + } + + // pNode->pRight->nIntegerData = nCount; + // Based on the return type, add a value to the stack. + + // We've already added the return value to the stack ... so + // why do we need to do it here? + + + if (m_pcIdentifierList[nCount].m_nReturnType != CSCRIPTCOMPILER_TOKEN_VOID_IDENTIFIER) + { + if (pNode->m_psTypeName != NULL) + { + delete pNode->m_psTypeName; + } + pNode->m_psTypeName = NULL; + + if (m_pcIdentifierList[nCount].m_nReturnType == CSCRIPTCOMPILER_TOKEN_INTEGER_IDENTIFIER) + { + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + } + else if (m_pcIdentifierList[nCount].m_nReturnType == CSCRIPTCOMPILER_TOKEN_FLOAT_IDENTIFIER) + { + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT; + } + else if (m_pcIdentifierList[nCount].m_nReturnType == CSCRIPTCOMPILER_TOKEN_OBJECT_IDENTIFIER) + { + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT; + } + else if (m_pcIdentifierList[nCount].m_nReturnType == CSCRIPTCOMPILER_TOKEN_STRING_IDENTIFIER) + { + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING; + } + else if (m_pcIdentifierList[nCount].m_nReturnType >= CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE0_IDENTIFIER && + m_pcIdentifierList[nCount].m_nReturnType <= CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE9_IDENTIFIER) + { + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 + (m_pcIdentifierList[nCount].m_nReturnType - CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE0_IDENTIFIER); + } + else if (m_pcIdentifierList[nCount].m_nReturnType == CSCRIPTCOMPILER_TOKEN_STRUCTURE_IDENTIFIER) + { + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT; + pNode->m_psTypeName = new CExoString(m_pcIdentifierList[nCount].m_psStructureReturnName.CStr()); + } + + if (pNode->nIntegerData == 0) + { + AddVariableToStack(pNode->nType,pNode->m_psTypeName,FALSE); + } + } + + + // CODE GENERATION + // Generate "execute action" code here, now that we know the id. + + if (pNode->nIntegerData == 0) + { + int32_t nIdentifierOrder = m_pcIdentifierList[nCount].m_nIdIdentifier; + + // Write the command that calls a predefined function. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_EXECUTE_COMMAND; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + // Enter the integer constant. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) ((nIdentifierOrder >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 1] = (char) (nIdentifierOrder & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 2] = (char) (m_pcIdentifierList[nCount].m_nParameters & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 3; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + } + else + { + // Write the command that calls a user-defined function. + + // Here, we will need to write a symbol into the code and + // mark the location for updating during the label generation pass. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JSR; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + // There is no point in expressing this yet, because we haven't decided on the + // locations of any of the final functions in the executable. So, write + // the symbol into the table. + + /* CExoString sSymbolName; + sSymbolName.Format("FE_%s",m_pcIdentifierList[nCount].m_psIdentifier.CStr()); */ + AddSymbolToQueryList(m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION, + CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_FUNCTION_ENTRY, + nCount,0); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + } + + return 0; + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_DECLARATION_DOES_NOT_MATCH_PARAMETERS,pNode); + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STATEMENT || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STATEMENT_NO_DEBUG) + { + // Handled in the InVisit call, so we don't need to do anything + // here to handle this case. + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_FOR_BLOCK) + { + // This instruction is a placeholder for script debugging when + // we don't include compound statements on the inside of FOR loops. + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_COMPOUND_STATEMENT) + { + + // We keep track of all of the variables added at this recursion level, and + // peel them off one by one. This is actually done within the script itself + // as well, since we need to keep track of what's happening. However, we + // don't need to worry about the address of a variable. + + int32_t nStackAtStart = m_nStackCurrentDepth; + + while (m_nOccupiedVariables >= 0 && + m_pcVarStackList[m_nOccupiedVariables].m_nVarLevel == m_nVarStackRecursionLevel) + { + if (m_pcVarStackList[m_nOccupiedVariables].m_nVarType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + int32_t nSize = GetStructureSize(m_pcVarStackList[m_nOccupiedVariables].m_sVarStructureName) >> 2; + m_nStackCurrentDepth -= nSize; + } + else + { + --m_nStackCurrentDepth; + } + RemoveFromSymbolTableVarStack(m_nOccupiedVariables, m_nStackCurrentDepth, m_nGlobalVariableSize); + --m_nOccupiedVariables; + } + + --m_nVarStackRecursionLevel; + + // Code Generation + + int32_t nStackModifier = (m_nStackCurrentDepth - nStackAtStart) * 4; + + if (nStackModifier != 0) + { + EmitModifyStackPointer(nStackModifier); + } + + // At this point we should have had the same state that we saved earlier. If we + // don't, there's a big problem, and we should be alerted to it. This is really + // a compiler error, rather than something the user has done. + + if (pNode->m_nStackPointer != m_nStackCurrentDepth) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_INCORRECT_VARIABLE_STATE_LEFT_ON_STACK,pNode); + } + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_IF_CONDITION || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_COND_CONDITION || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_WHILE_CONDITION || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_DOWHILE_CONDITION || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_SWITCH_CONDITION) + { + if (pNode->pLeft != NULL) + { + pNode->nType = pNode->pLeft->nType; + // I *HOPE* this is not a structure, but just in case. + if (pNode->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + if (pNode->m_psTypeName != NULL) + { + delete pNode->m_psTypeName; + } + + (pNode->m_psTypeName) = new CExoString(pNode->pLeft->m_psTypeName->CStr()); + } + } + + // If we're a switch condition, confirm that we're an integer at this stage! + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_SWITCH_CONDITION) + { + if (m_pchStackTypes[m_nStackCurrentDepth-1] != CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_SWITCH_MUST_EVALUATE_TO_AN_INTEGER,pNode); + } + + // MGB - 12/06/2004 - START FIX + // + // Why didn't anyone tell me that the debugger was screwed when + // entering/leaving case statements. It's been broken for nearly 18 months! + // + // Okay, stop ranting ... we know it's a problem NOW. The fix is as follows: + // + // Here, we must add the code to hang a "#switcheval" parameter on to the + // variable stack. However, the integer is already on the stack (what do + // you think we're checking above?), so we simply have to delude the variable + // stack to add the variable at this point (and hand it to the Symbol Table). + // At the end of the SWITCH_BLOCK, we will remove the "#switcheval" + // variable from the Symbol Table. + + ++m_nOccupiedVariables; + + m_pcVarStackList[m_nOccupiedVariables].m_psVarName = "#switcheval"; + m_pcVarStackList[m_nOccupiedVariables].m_nVarType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + m_pcVarStackList[m_nOccupiedVariables].m_nVarLevel = m_nVarStackRecursionLevel; + + // Note we must subtract four from the current top, since the variable is really at + // (m_nStackCurrentDepth - 1), instead of the top of the list. + m_pcVarStackList[m_nOccupiedVariables].m_nVarRunTimeLocation = (m_nStackCurrentDepth - 1) * 4; + + // Now it has been added to the list + AddToSymbolTableVarStack(m_nOccupiedVariables, m_nStackCurrentDepth - 1, m_nGlobalVariableSize); + + // MGB - 12/06/2004 - END FIX + } + + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_IF_CONDITION) + { + if (m_nGenerateDebuggerOutput != 0) + { + EndLineNumberAtBinaryInstruction(pNode->m_nFileReference,pNode->nLine,m_nOutputCodeLength); + } + } + + return 0; + } + + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_SWITCH_BLOCK) + { + // MGB - 12/06/2004 - START FIX + // + // If you look a few lines up, I rant about the need to put in this + // "#switcheval" variable for a switch statement, since the game + // will proceed to emit fiery chunks by running a case through the + // debugger when there is a string (or other deferencable object) + // as the top thing on the stack. It basically screws the stack + // by one. This is our attempt to undo the hack at this point! + // + // At the end of the SWITCH_BLOCK, we will remove the "#switcheval" + // variable from the Symbol Table. + + RemoveFromSymbolTableVarStack(m_nOccupiedVariables, m_nStackCurrentDepth - 1, m_nGlobalVariableSize); + --m_nOccupiedVariables; + + // MGB - 12/06/2004 - END FIX + + // Generate a label for the end of the function (used by the break keyword) + /* CExoString sSymbolName; + sSymbolName.Format("_BR_%08x",m_nSwitchIdentifier); */ + AddSymbolToLabelList(m_nOutputCodeLength, + CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_BREAK, + m_nSwitchIdentifier); + + // Restore the old values of switch level and the switch identifier. + --m_nSwitchLevel; + m_nSwitchIdentifier = pNode->nIntegerData2; + m_nSwitchStackDepth = pNode->nIntegerData3; + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_WHILE_BLOCK) + { + + // Here, we implement the loop back to location _W1_ and insert + // the information on jumping to _W2_ at this point. + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JMP; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + int32_t nJmpLength = pNode->nIntegerData - m_nOutputCodeLength; + m_pchOutputCode[m_nOutputCodeLength+ CVIRTUALMACHINE_EXTRA_DATA_LOCATION ] = (char) (((nJmpLength) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nJmpLength) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nJmpLength) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nJmpLength)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // Here, we go back and add the relative offset to get beyond this return + // statement to the JZ command we gave earlier during + // the InVisit() [code located at pNode->nIntegerdata2]. + // This is an awfully good thing to do. + + nJmpLength = m_nOutputCodeLength - pNode->nIntegerData2; + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nJmpLength) >> 24) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 1] = (char) (((nJmpLength) >> 16) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 2] = (char) (((nJmpLength) >> 8 ) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 3] = (char) (((nJmpLength) ) & 0x0ff); + + // Generate a label for the end of the function (used by the break keyword) + /* CExoString sSymbolName; + sSymbolName.Format("_BR_%08x",m_nLoopIdentifier); */ + AddSymbolToLabelList(m_nOutputCodeLength, CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_BREAK,m_nLoopIdentifier,0); + + + pNode->nType = pNode->pLeft->nType; + if (pNode->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + if (pNode->m_psTypeName != NULL) + { + delete pNode->m_psTypeName; + } + + pNode->m_psTypeName = new CExoString(pNode->pLeft->m_psTypeName->CStr()); + } + + // Reset the loop identifier. + m_nLoopIdentifier = pNode->nIntegerData3; + m_nLoopStackDepth = pNode->nIntegerData4; + + return 0; + + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_WHILE_CHOICE) + { + // FOR TESTING PURPOSES + // We do nothing. Why? It's all done by WHILE_BLOCK + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_DOWHILE_BLOCK) + { + // CODE GENERATION + // Here, we implement a JZ to _DW2_, which clears the JMP back to _DW1_. + + // + // First, let's deal with the JZ instruction. + // + + if (m_pchStackTypes[m_nStackCurrentDepth-1] != CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_INTEGER_NOT_AT_TOP_OF_STACK,pNode); + } + --m_nStackCurrentDepth; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JZ; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + // The jump length is over this instruction and the jmp instruction, both of + // which are OPERATION_BASE_SIZE + 4 (for the address). + + int32_t nJmpLength = (CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4) << 1; + m_pchOutputCode[m_nOutputCodeLength+ CVIRTUALMACHINE_EXTRA_DATA_LOCATION ] = (char) (((nJmpLength) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nJmpLength) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nJmpLength) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nJmpLength)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // + // And now, the JMP instruction. + // + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JMP; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + // The jmp length is back to the beginning of the do/while loop, + // which is stored in pNode->nIntegerData; + // which are OPERATION_BASE_SIZE + 4 (for the address). + + nJmpLength = pNode->nIntegerData - m_nOutputCodeLength; + m_pchOutputCode[m_nOutputCodeLength+ CVIRTUALMACHINE_EXTRA_DATA_LOCATION ] = (char) (((nJmpLength) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nJmpLength) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nJmpLength) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nJmpLength)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // Generate a label for the end of the function (used by the break keyword) + /* CExoString sSymbolName; + sSymbolName.Format("_BR_%08x",m_nLoopIdentifier); */ + AddSymbolToLabelList(m_nOutputCodeLength, CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_BREAK,m_nLoopIdentifier); + + if (m_nGenerateDebuggerOutput != 0) + { + EndLineNumberAtBinaryInstruction(pNode->m_nFileReference,pNode->nLine,m_nOutputCodeLength); + } + + pNode->nType = pNode->pRight->nType; + if (pNode->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + if (pNode->m_psTypeName != NULL) + { + delete pNode->m_psTypeName; + } + + pNode->m_psTypeName = new CExoString(pNode->pRight->m_psTypeName->CStr()); + } + + // Reset the loop identifier. + m_nLoopIdentifier = pNode->nIntegerData3; + m_nLoopStackDepth = pNode->nIntegerData4; + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_IF_CHOICE) + { + // Here, we implement the loop back to location _W1_ and insert + // the information on jumping to _W2_ at this point. + + // Here, we go back and add the relative offset to get beyond this return + // statement to the JMP command we gave earlier during + // the InVisit() [code located at pNode->nIntegerdata2]. + // This is an awfully good thing to do. + + int32_t nJmpLength = m_nOutputCodeLength - pNode->nIntegerData2; + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nJmpLength) >> 24) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 1] = (char) (((nJmpLength) >> 16) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 2] = (char) (((nJmpLength) >> 8 ) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 3] = (char) (((nJmpLength) ) & 0x0ff); + + // FOR TESTING PURPOSES + // We do nothing. Why? It's been done in pregenerate code + // (to reflect where the variable will be removed from the + // stack during the run-time execution!) + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_IF_BLOCK) + { + pNode->nType = pNode->pLeft->nType; + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_COND_CHOICE) + { + // Here, we implement the loop back to location _CH1_ and insert + // the information on jumping to _CH2_ at this point. + + // Here, we go back and add the relative offset to get beyond this return + // statement to the JMP command we gave earlier during + // the InVisit() [code located at pNode->nIntegerdata2]. + // This is an awfully good thing to do. + + int32_t nJmpLength = m_nOutputCodeLength - pNode->nIntegerData2; + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nJmpLength) >> 24) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 1] = (char) (((nJmpLength) >> 16) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 2] = (char) (((nJmpLength) >> 8 ) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 3] = (char) (((nJmpLength) ) & 0x0ff); + + // FOR TESTING PURPOSES + // Check to see if the types are the same. + + if (pNode->pLeft->nType != pNode->pRight->nType || + (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT && + *(pNode->pLeft->m_psTypeName) != *(pNode->pRight->m_psTypeName))) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_CONDITIONAL_MUST_HAVE_MATCHING_RETURN_TYPES,pNode); + } + + pNode->nType = pNode->pLeft->nType; + if (pNode->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + if (pNode->m_psTypeName != NULL) + { + delete pNode->m_psTypeName; + } + + pNode->m_psTypeName = new CExoString(pNode->pLeft->m_psTypeName->CStr()); + } + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_COND_BLOCK) + { + // Type of the conditional block is the same as the type of the conditional choice + // underneath it (pRight). + pNode->nType = pNode->pRight->nType; + if (pNode->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + if (pNode->m_psTypeName != NULL) + { + delete pNode->m_psTypeName; + } + + pNode->m_psTypeName = new CExoString(pNode->pRight->m_psTypeName->CStr()); + } + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_NON_VOID_EXPRESSION || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_INTEGER_EXPRESSION) + { + + // MGB - October 29, 2002 - START CHANGE + // Code changed to properly account for "action" variables (which aren't + // actually stored on the stack) as non-void expressions. + + int32_t nTargetRemovedTokens = 1; + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ACTION) + { + nTargetRemovedTokens = 0; + } + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + nTargetRemovedTokens = (GetStructureSize(*(pNode->pLeft->m_psTypeName)) >> 2); + } + + int nActualRemovedTokens = m_nStackCurrentDepth - pNode->m_nStackPointer; + + if (nActualRemovedTokens == 0 && nTargetRemovedTokens != 0) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_VOID_EXPRESSION_WHERE_NON_VOID_REQUIRED,pNode); + } + + if (nActualRemovedTokens != nTargetRemovedTokens) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_INCORRECT_VARIABLE_STATE_LEFT_ON_STACK,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_INTEGER_EXPRESSION && + m_pchStackTypes[m_nStackCurrentDepth-1] != CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_NON_INTEGER_EXPRESSION_WHERE_INTEGER_REQUIRED,pNode); + } + + // Now, that we've "verified" the type, we should be able to set the value + // of the type of the expression underneath it. + pNode->nType = pNode->pLeft->nType; + + if (pNode->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + if (pNode->m_psTypeName != NULL) + { + delete pNode->m_psTypeName; + } + + pNode->m_psTypeName = new CExoString(pNode->pLeft->m_psTypeName->CStr()); + } + + // MGB - October 29, 2002 - END CHANGE + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_LOGICAL_AND) + { + if (pNode->pLeft != NULL && pNode->pRight != NULL) + { + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + // CODE GENERATION + // Write a "logical AND of two ints" operation. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_LOGICAL_AND; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_INTEGER_INTEGER; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // FOR TESTING PURPOSES + // Remove an integer. + --m_nStackCurrentDepth; + + // Now, we insert the label _ILA_ to indicate this is where we jump + // to on a "short cut". + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + + // Here, we go back and add the relative offset to get beyond this return + // statement to the JZ command we gave earlier during + // the InVisit() [code located at pNode->nIntegerData]. + // This is an awfully good thing to do. + + int32_t nJmpLength = m_nOutputCodeLength - pNode->nIntegerData; + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nJmpLength) >> 24) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 1] = (char) (((nJmpLength) >> 16) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 2] = (char) (((nJmpLength) >> 8 ) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 3] = (char) (((nJmpLength) ) & 0x0ff); + + return 0; + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_LOGICAL_OPERATION_HAS_INVALID_OPERANDS,pNode); + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_LOGICAL_OR) + { + if (pNode->pLeft != NULL && pNode->pRight != NULL) + { + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + // First, we insert the label _ILO_ to indicate this is where we jump + // to on a "short cut". + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + + // Here, we go back and add the relative offset to get beyond this return + // statement to the JMP command we gave earlier during + // the InVisit() [code located at pNode->nIntegerData]. + // This is an awfully good thing to do. + + int32_t nJmpLength = m_nOutputCodeLength - pNode->nIntegerData; + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nJmpLength) >> 24) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 1] = (char) (((nJmpLength) >> 16) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 2] = (char) (((nJmpLength) >> 8 ) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 3] = (char) (((nJmpLength) ) & 0x0ff); + + // CODE GENERATION + // Write a "logical OR of two ints" operation. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_LOGICAL_OR; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_INTEGER_INTEGER; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // FOR TESTING PURPOSES + // Remove an integer. + --m_nStackCurrentDepth; + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_LOGICAL_OPERATION_HAS_INVALID_OPERANDS,pNode); + + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_INCLUSIVE_OR) + { + if (pNode->pLeft != NULL && pNode->pRight != NULL) + { + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + // CODE GENERATION + // Write an "inclusive OR of two ints" operation. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_INCLUSIVE_OR; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_INTEGER_INTEGER; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // FOR TESTING PURPOSES + // Remove an integer. + --m_nStackCurrentDepth; + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_LOGICAL_OPERATION_HAS_INVALID_OPERANDS,pNode); + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_EXCLUSIVE_OR) + { + if (pNode->pLeft != NULL && pNode->pRight != NULL) + { + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + // CODE GENERATION + // Write an "exclusive OR of two ints" operation. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_EXCLUSIVE_OR; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_INTEGER_INTEGER; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // FOR TESTING PURPOSES + // Remove an integer. + --m_nStackCurrentDepth; + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_LOGICAL_OPERATION_HAS_INVALID_OPERANDS,pNode); + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_BOOLEAN_AND) + { + if (pNode->pLeft != NULL && pNode->pRight != NULL) + { + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + // CODE GENERATION + // Write an "boolean AND of two ints" operation. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_BOOLEAN_AND; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_INTEGER_INTEGER; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // FOR TESTING PURPOSES + // Remove an integer. + --m_nStackCurrentDepth; + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_LOGICAL_OPERATION_HAS_INVALID_OPERANDS,pNode); + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONDITION_EQUAL || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONDITION_NOT_EQUAL) + { + if (pNode->pLeft != NULL && pNode->pRight != NULL) + { + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + // CODE GENERATION + // Write an "condition EQUAL/NOT EQUAL of two ints" operation. + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONDITION_EQUAL) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_EQUAL; + } + else + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_NOT_EQUAL; + } + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_INTEGER_INTEGER; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // FOR TESTING PURPOSES + // Remove an integer. + --m_nStackCurrentDepth; + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT) + { + // CODE GENERATION + // Write an "condition EQUAL/NOT EQUAL of two floats" operation. + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONDITION_EQUAL) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_EQUAL; + } + else + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_NOT_EQUAL; + } + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_FLOAT_FLOAT; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // FOR TESTING PURPOSES + // Remove two floats, add an integer. + --m_nStackCurrentDepth; + --m_nStackCurrentDepth; + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_INT,NULL,FALSE); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT) + { + // CODE GENERATION + // Write an "condition EQUAL/NOT EQUAL of two objects" operation. + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONDITION_EQUAL) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_EQUAL; + } + else + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_NOT_EQUAL; + } + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_OBJECT_OBJECT; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // FOR TESTING PURPOSES + // Remove two objects, add an integer. + --m_nStackCurrentDepth; + --m_nStackCurrentDepth; + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_INT,NULL,FALSE); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING) + { + // CODE GENERATION + // Write an "condition EQUAL/NOT EQUAL of two strings" operation. + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONDITION_EQUAL) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_EQUAL; + } + else + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_NOT_EQUAL; + } + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_STRING_STRING; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // FOR TESTING PURPOSES + // Remove an integer. + --m_nStackCurrentDepth; + --m_nStackCurrentDepth; + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_INT,NULL,FALSE); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + + if (pNode->pLeft->nType >= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 && + pNode->pLeft->nType <= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9 && + pNode->pRight->nType == pNode->pLeft->nType) + { + + uint8_t nEngineStructureNumber = (uint8_t) (pNode->pLeft->nType - CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0); + + // CODE GENERATION + // Write an "condition EQUAL/NOT EQUAL of two strings" operation. + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONDITION_EQUAL) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_EQUAL; + } + else + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_NOT_EQUAL; + } + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = (char) (CVIRTUALMACHINE_AUXCODE_TYPETYPE_ENGST0_ENGST0 + nEngineStructureNumber); + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // FOR TESTING PURPOSES + // Remove an integer. + --m_nStackCurrentDepth; + --m_nStackCurrentDepth; + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_INT,NULL,FALSE); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + + } + + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + if (*(pNode->pLeft->m_psTypeName) == *(pNode->pRight->m_psTypeName)) + { + int32_t nSize = GetStructureSize(*(pNode->pLeft->m_psTypeName)); + + // CODE GENERATION + // Write an "condition EQUAL/NOT EQUAL of two strings" operation. + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONDITION_EQUAL) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_EQUAL; + } + else + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_NOT_EQUAL; + } + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_STRUCT_STRUCT; + + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+0] = (char) (((nSize) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nSize)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 2; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + + // FOR TESTING PURPOSES + // Remove the two structures, and add an INT. + + // Why remove half of the size? We're actually removing two structures, but + // the size reported is actually in bytes, not in the number of entries the + // stack is measured in. Thus, nSize * 2 / 4 = nSize >> 1. + + m_nStackCurrentDepth -= (nSize >> 1); + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_INT,NULL,FALSE); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + } + + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_EQUALITY_TEST_HAS_INVALID_OPERANDS,pNode); + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONDITION_GEQ || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONDITION_GT || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONDITION_LT || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONDITION_LEQ) + { + + // CODE GENERATION + // Write the base of the arithmetic operation. + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONDITION_GEQ) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_GEQ; + } + else if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONDITION_GT) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_GT; + } + else if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONDITION_LT) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_LT; + } + else + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_LEQ; + } + + if (pNode->pLeft != NULL && pNode->pRight != NULL) + { + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + // CODE GENERATION + // Write an "condition GEQ/GT/LT/LEQ of two ints" operation. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_INTEGER_INTEGER; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // FOR TESTING PURPOSES + // Remove an integer. + --m_nStackCurrentDepth; + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT) + { + // CODE GENERATION + // Write an "condition GEQ/GT/LT/LEQ of two floats" operation. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_FLOAT_FLOAT; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // FOR TESTING PURPOSES + // Remove an integer. + --m_nStackCurrentDepth; + --m_nStackCurrentDepth; + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_INT,NULL,FALSE); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_COMPARISON_TEST_HAS_INVALID_OPERANDS,pNode); + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_SHIFT_LEFT || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_SHIFT_RIGHT || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_UNSIGNED_SHIFT_RIGHT) + { + + + + if (pNode->pLeft != NULL && pNode->pRight != NULL) + { + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + // CODE GENERATION + // Write an "<>/>>> of two ints" operation. + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_SHIFT_LEFT) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_SHIFT_LEFT; + } + else if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_SHIFT_RIGHT) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_SHIFT_RIGHT; + } + else + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_USHIFT_RIGHT; + } + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_INTEGER_INTEGER; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // FOR TESTING PURPOSES + // Remove an integer. + --m_nStackCurrentDepth; + --m_nStackCurrentDepth; + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_INT,NULL,FALSE); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_SHIFT_OPERATION_HAS_INVALID_OPERANDS,pNode); + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_ADD || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_SUBTRACT || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_MULTIPLY || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_DIVIDE) + { + + // CODE GENERATION + // Write the base of the arithmetic operation. + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_ADD) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_ADD; + } + else if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_SUBTRACT) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_SUB; + } + else if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_MULTIPLY) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_MUL; + } + else + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_DIV; + } + + if (pNode->pLeft != NULL && pNode->pRight != NULL) + { + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + // CODE GENERATION + // Write a +-*/ operation on two ints. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_INTEGER_INTEGER; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // For TESTING PURPOSES + // Remove an integer. + --m_nStackCurrentDepth; + --m_nStackCurrentDepth; + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_INT,NULL,FALSE); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + // CODE GENERATION + // Write a +-*/ operation on a float and a PROMOTED int. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_FLOAT_INTEGER; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // For TESTING PURPOSES + // Remove an integer. + --m_nStackCurrentDepth; + --m_nStackCurrentDepth; + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT,NULL,FALSE); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT; + return 0; + } + + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT) + { + // CODE GENERATION + // Write a +-*/ operation on a PROMOTED int and a float. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_INTEGER_FLOAT; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // For TESTING PURPOSES + // Remove an integer. + --m_nStackCurrentDepth; + --m_nStackCurrentDepth; + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT,NULL,FALSE); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT; + return 0; + } + + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT) + { + // CODE GENERATION + // Write a +-*/ operation on a PROMOTED int and a float. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_FLOAT_FLOAT; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // For TESTING PURPOSES + // Remove a float. + --m_nStackCurrentDepth; + --m_nStackCurrentDepth; + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT,NULL,FALSE); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT; + return 0; + } + + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING) + { + if (pNode->nOperation != CSCRIPTCOMPILER_OPERATION_ADD) + { { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_ARITHMETIC_OPERATION_HAS_INVALID_OPERANDS,pNode); + } + } + // CODE GENERATION + // Write a s+s operation on two strings. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_STRING_STRING; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // For TESTING PURPOSES + // Remove both vectors, and add one back. + m_nStackCurrentDepth -= 2; + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING,NULL,FALSE); + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING; + return 0; + } + + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + if (*(pNode->pLeft->m_psTypeName) == "vector" && *(pNode->pRight->m_psTypeName) == *(pNode->pLeft->m_psTypeName)) + { + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_MULTIPLY || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_DIVIDE) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_ARITHMETIC_OPERATION_HAS_INVALID_OPERANDS,pNode); + } + // CODE GENERATION + // Write a v+-v operation on two vectors. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_VECTOR_VECTOR; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // For TESTING PURPOSES + // Remove both vectors, and add one back. + m_nStackCurrentDepth -= 6; + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT,pNode->pLeft->m_psTypeName,FALSE); + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT; + if (pNode->m_psTypeName != NULL) + { + delete pNode->m_psTypeName; + } + pNode->m_psTypeName = new CExoString(pNode->pLeft->m_psTypeName->CStr()); + return 0; + + } + } + + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT) + { + if (*(pNode->pLeft->m_psTypeName) == "vector") + { + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_ADD || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_SUBTRACT) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_ARITHMETIC_OPERATION_HAS_INVALID_OPERANDS,pNode); + } + // CODE GENERATION + // Write a v*/f operation on two vectors. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_VECTOR_FLOAT; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // For TESTING PURPOSES + // Remove the vector and scalar, and add the back back. + m_nStackCurrentDepth -= 4; + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT,pNode->pLeft->m_psTypeName,FALSE); + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT; + if (pNode->m_psTypeName != NULL) + { + delete pNode->m_psTypeName; + } + pNode->m_psTypeName = new CExoString(pNode->pLeft->m_psTypeName->CStr()); + return 0; + } + } + + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + if (*(pNode->pRight->m_psTypeName) == "vector") + { + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_ADD || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_SUBTRACT || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_DIVIDE) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_ARITHMETIC_OPERATION_HAS_INVALID_OPERANDS,pNode); + } + + // CODE GENERATION + // Write a f*v operation. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_FLOAT_VECTOR; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // For TESTING PURPOSES + // Remove the vector and scalar, and add the vector back. + m_nStackCurrentDepth -= 4; + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT,pNode->pRight->m_psTypeName,FALSE); + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT; + if (pNode->m_psTypeName != NULL) + { + delete pNode->m_psTypeName; + } + pNode->m_psTypeName = new CExoString(pNode->pRight->m_psTypeName->CStr()); + return 0; + } + } + + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_ARITHMETIC_OPERATION_HAS_INVALID_OPERANDS,pNode); + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_MODULUS) + { + if (pNode->pLeft != NULL && pNode->pRight != NULL) + { + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT && pNode->pRight->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + // CODE GENERATION + // Write a % operation on two ints. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_MODULUS; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPETYPE_INTEGER_INTEGER; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // For TESTING PURPOSES + // Remove an integer. + --m_nStackCurrentDepth; + --m_nStackCurrentDepth; + AddVariableToStack(CSCRIPTCOMPILER_TOKEN_KEYWORD_INT,NULL,FALSE); + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_ARITHMETIC_OPERATION_HAS_INVALID_OPERANDS,pNode); + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_NEGATION) + { + if (pNode->pLeft != NULL) + { + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + // CODE GENERATION + // Write a negation operation on an int. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_NEGATION; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // For TESTING PURPOSES + // Do nothing. + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT) + { + // CODE GENERATION + // Write a negation operation on a float. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_NEGATION; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_FLOAT; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + // For TESTING PURPOSES + // Do nothing. + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT; + return 0; + } + + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_ARITHMETIC_OPERATION_HAS_INVALID_OPERANDS,pNode); + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_ONES_COMPLEMENT) + { + if (pNode->pLeft != NULL) + { + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + // CODE GENERATION + // Write a negation operation on an int. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_ONES_COMPLEMENT; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // For TESTING PURPOSES + // Do nothing. + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_ARITHMETIC_OPERATION_HAS_INVALID_OPERANDS,pNode); + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_BOOLEAN_NOT) + { + if (pNode->pLeft != NULL) + { + if (pNode->pLeft->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + // CODE GENERATION + // Write a negation operation on an int. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_BOOLEAN_NOT; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // For TESTING PURPOSES + // Do nothing. + + pNode->nType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + return 0; + } + + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_ARITHMETIC_OPERATION_HAS_INVALID_OPERANDS,pNode); + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STRUCTURE_DEFINITION) + { + // We should be in the variable-processing stage of the strucutre + // definition at this point. + + if (m_nStructureDefinition != 2) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + // Set where the last field for this structure is located. + + m_pcStructList[m_nMaxStructures].m_nFieldEnd = m_nMaxStructureFields - 1; + + // Count the size of all the components in this structure. + + int32_t nTotalSize = 0; + + int32_t count; + for (count = m_pcStructList[m_nMaxStructures].m_nFieldStart; + count <= m_pcStructList[m_nMaxStructures].m_nFieldEnd; + ++count) + { + // Since we haven't set the location of the variables within this + // structure, we might as well do it here. + m_pcStructFieldList[count].m_nLocation = nTotalSize; + + // All variables are of size "4" (i.e. 4 bytes), unless you've got + // a structure, which could take a lot more than 4 bytes on the + // stack. Thus, one would like to make these into 4 byte values. + if (m_pcStructFieldList[count].m_pchType != CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + nTotalSize += 4; + } + else + { + int32_t count2; + for (count2 = 0; count2 < m_nMaxStructures; count2++) + { + if (m_pcStructFieldList[count].m_psStructureName == m_pcStructList[count2].m_psName) + { + nTotalSize += m_pcStructList[count2].m_nByteSize; + } + } + } + } + + m_pcStructList[m_nMaxStructures].m_nByteSize = nTotalSize; + + if (nTotalSize == 0) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + // Clear out the fact that we're in a structure definition ... since + // we're done this definition. + m_nStructureDefinition = 0; + + // Finally, add the structure definition to the ones that we can look at. + ++m_nMaxStructures; + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STRUCTURE_PART) + { + m_bInStructurePart = FALSE; + + if (pNode->pLeft != NULL && pNode->pRight != NULL) + { + if (pNode->pLeft->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_LEFT_OF_STRUCTURE_PART_NOT_STRUCTURE,pNode); + } + /* + if (pNode->pRight->nOperation != CSCRIPTCOMPILER_OPERATION_VARIABLE) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_RIGHT_OF_STRUCTURE_PART_NOT_FIELD_IN_STRUCTURE,pNode); + } + */ + + // Now that we have verified we have a structure on the left, + // and a variable on the right ... we can determine if we've + // got a field within the specified structure. + int32_t nValue = GetStructureField(*(pNode->pLeft->m_psTypeName), *(pNode->pRight->m_psStringData)); + if (nValue < 0) + { + return OutputWalkTreeError(nValue,pNode); + } + + // Yes, we have a valid structure field. Fetch the type, + // and structure name (if necessary) out of the structure field. + pNode->nType = m_pcStructFieldList[nValue].m_pchType; + if (pNode->nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + pNode->m_psTypeName = new CExoString(m_pcStructFieldList[nValue].m_psStructureName.CStr()); + } + + // Now, we update the pointer to the location of the variable. In + // all cases, we should still have a pointer into the stack that will + // help us determine the location of the structure part. + pNode->nIntegerData = pNode->pLeft->nIntegerData + m_pcStructFieldList[nValue].m_nLocation; + + if (m_bAssignmentToVariable == FALSE) + { + // Let's do some code down here! We want to be able to remove + // the structure at the top of the stack and bust it down to + // the current thing on the stack. Thus, we create a new + // opcode for handling the division of a structure. It will + // have three parameters. (1) Size Of Original Structure + // (2) Start Location of New Component (3) Size of New + // Component. All of these will be with respect to the run-time + // stack. + + int32_t nSizeOriginal = GetStructureSize(*(pNode->pLeft->m_psTypeName)); + + int32_t nSize = 4; + if (m_pcStructFieldList[nValue].m_pchType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + nSize = GetStructureSize(*(pNode->m_psTypeName)); + } + + int32_t nStartComponent = m_pcStructFieldList[nValue].m_nLocation; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_DE_STRUCT; + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_VOID; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nSizeOriginal) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nSizeOriginal)) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nStartComponent) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nStartComponent)) & 0x0ff); + + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+4] = (char) (((nSize) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+5] = (char) (((nSize)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 6; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // Okay, now we have to actually apply the de-struct information. + int32_t nCutSizeOriginal = (nSizeOriginal >> 2); + int32_t nCutSize = (nSize >> 2); + int32_t nCutStartComponent = (nStartComponent >> 2); + + // Let's see what we want ... + // + // + // C C C C C C C + // + // B + // A A A + // + // nCutSize = A; + // nCutStartComponent = B; + // nCutSizeOriginal = C; + // + // StackPointer = Base + C; + // if (A+B < C) + // { + // StackPointer = Base + A + B; (Thus, subtract + // } + // + // Copy To Base ... Base+A-1 from Base+B to Base+A+B-1 + // + // StackPointer = Base + A; // Remove B + + if (nCutSize + nCutStartComponent < nCutSizeOriginal) + { + m_nStackCurrentDepth -= nCutSizeOriginal; + m_nStackCurrentDepth += nCutSize + nCutStartComponent; + } + + for (int32_t count = m_nStackCurrentDepth - nCutSize - nCutStartComponent; count < m_nStackCurrentDepth - nCutStartComponent; ++count) + { + m_pchStackTypes[count] = m_pchStackTypes[count + nCutStartComponent]; + } + + m_nStackCurrentDepth -= nCutStartComponent; + } + + return 0; + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_FUNCTIONAL_UNIT) + { + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_FUNCTION ) + { + // If there's no function implementation, we do not need to do anything. + if (m_bFunctionImp == FALSE) + { + return 0; + } + + // Remove all variables, aside from #retval (only save #retval if the + // function is non-void). + + int32_t nReturnValueLevel = m_nGlobalVariables; + + if (m_nFunctionImpReturnType != CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID) + { + ++nReturnValueLevel; + } + + while (m_nOccupiedVariables >= nReturnValueLevel) + { + + if (m_pcVarStackList[m_nOccupiedVariables].m_nVarType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + int32_t nSize = GetStructureSize(m_pcVarStackList[m_nOccupiedVariables].m_sVarStructureName) >> 2; + m_nStackCurrentDepth -= nSize; + } + else + { + --m_nStackCurrentDepth; + } + RemoveFromSymbolTableVarStack(m_nOccupiedVariables, m_nStackCurrentDepth, m_nGlobalVariableSize); + --m_nOccupiedVariables; + } + + // MGB - August 14, 2002 - For Scripting Debugger + // The COMPOUND_STATEMENT on the inside of the function declaration contains + // the placement of the final bracket and, hence, where the final line of + // code in the function belongs. + int32_t nLineEndOfFunction = pNode->nLine; + if (pNode->pRight != NULL) + { + nLineEndOfFunction = pNode->pRight->nLine; + } + + + // MGB - August 9, 2002 - Final Bracket Debugging + if (m_nGenerateDebuggerOutput != 0) + { + StartLineNumberAtBinaryInstruction(pNode->m_nFileReference,nLineEndOfFunction,m_nOutputCodeLength); + } + + // At this point we should have the state with just the return value + // on the stack. We should use this opportunity to generate the shift + // in the run-time stack. + + // Generate a label for the end of the function (used by the RETURN keyword) + /* CExoString sSymbolName; + sSymbolName.Format("FX_%s",m_sFunctionImpName.CStr()); */ + int32_t nIdentifier = GetIdentifierByName(m_sFunctionImpName); + if (nIdentifier == STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + AddSymbolToLabelList(m_nOutputCodeLength, CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_FUNCTION_EXIT, nIdentifier, 0); + + // Move the stack to update the current location of the code. + + int32_t nStackModifier = (m_nStackCurrentDepth - pNode->m_nStackPointer) * 4; + + if (nStackModifier != 0) + { + EmitModifyStackPointer(nStackModifier); + } + + // MGB - August 22, 2002 - For Scripting Debugger + if (nReturnValueLevel != m_nGlobalVariables) + { + // Remove the variable from the StackCurrentDepth before + // handing it off to the code to get rid of! + int32_t nStackCurrentDepth = m_nStackCurrentDepth; + + if (m_pcVarStackList[m_nOccupiedVariables].m_nVarType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + int32_t nSize = GetStructureSize(m_pcVarStackList[m_nOccupiedVariables].m_sVarStructureName) >> 2; + nStackCurrentDepth -= nSize; + } + else + { + --nStackCurrentDepth; + } + + RemoveFromSymbolTableVarStack(m_nOccupiedVariables, nStackCurrentDepth, m_nGlobalVariableSize); + } + + // Add the RET statement + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_RET; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE ; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // Remove the last variable (#retval), and reset the state of the + // variable stack. + + if (m_nFunctionImpReturnType != CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID) + { + // Check the function using our custom function to handle this. + if (FoundReturnStatementOnAllBranches(pNode->pRight) == FALSE) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_NOT_ALL_CONTROL_PATHS_RETURN_A_VALUE,pNode); + } + } + + // If the function is non-void, remove #retval from the stack. + // The way we know the function is non-void can be found above. + + + if (nReturnValueLevel != m_nGlobalVariables) + { + if (m_pcVarStackList[m_nOccupiedVariables].m_nVarType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + int32_t nSize = GetStructureSize(m_pcVarStackList[m_nOccupiedVariables].m_sVarStructureName) >> 2; + m_nStackCurrentDepth -= nSize; + } + else + { + --m_nStackCurrentDepth; + } + --m_nOccupiedVariables; + } + + --m_nVarStackRecursionLevel; + + nIdentifier = GetIdentifierByName(m_sFunctionImpName); + + if (nIdentifier < 0) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + m_pcIdentifierList[nIdentifier].m_nBinarySourceFinish = m_nOutputCodeLength; + + m_bFunctionImp = FALSE; + + // MGB - August 9, 2002 - Final Bracket Debugging + if (m_nGenerateDebuggerOutput != 0) + { + EndLineNumberAtBinaryInstruction(pNode->m_nFileReference,nLineEndOfFunction,m_nOutputCodeLength); + } + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_GLOBAL_VARIABLES) + { + m_bGlobalVariableDefinition = FALSE; + + // Add the SAVE_BASE_POINTER statement so that these globals can be used. + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_SAVE_BASE_POINTER; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + //////////////////////////////////////////////////// + // MGB - September 13, 2001 - Start of added chunk. + //////////////////////////////////////////////////// + + if (m_bCompileConditionalFile == TRUE) + { + // Write the integer that we're going to store the return value into! + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_RUNSTACK_ADD; + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + } + + ////////////////////////////////////////////////// + // MGB - September 13, 2001 - End of added chunk. + ////////////////////////////////////////////////// + + // Write the JSR FE_main instruction that is guaranteed to be successful + // because of the checks in InstallLoader(). + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JSR; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + // There is no point in expressing this yet, because we haven't decided on the + // locations of any of the final functions in the executable. So, write + // the symbol into the table. + + /* + CExoString sSymbolName; + if (m_bCompileConditionalFile == FALSE) + { + sSymbolName.Format("FE_main"); + } + else + { + sSymbolName.Format("FE_StartingConditional"); + } + */ + + AddSymbolToQueryList(m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION, + CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_FUNCTION_ENTRY,0,2); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + + //////////////////////////////////////////////////// + // MGB - September 13, 2001 - Start of added chunk. + //////////////////////////////////////////////////// + + if (m_bCompileConditionalFile == TRUE) + { + // We need to assign the return value to the first value on the stack (which is the return value + // of the full function). + + // Set up a "write to #retval" instruction. + + // Must move beyond Global Variables + [#retval + basepointer (on stack) + RSADDI = 12] + int32_t nStackElementsDown = -(m_nGlobalVariableSize + 12); + int32_t nSize = 4; + if (m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + nSize = GetStructureSize(m_sFunctionImpReturnStructureName); + } + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_ASSIGNMENT; + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_VOID; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nStackElementsDown) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nStackElementsDown) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nStackElementsDown) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nStackElementsDown)) & 0x0ff); + + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+4] = (char) (((nSize) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+5] = (char) (((nSize)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 6; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + + // Add the "modify stack pointer" statement so that the return value is never used again. + EmitModifyStackPointer(-4); + } + + ////////////////////////////////////////////////// + // MGB - September 13, 2001 - End of added chunk. + ////////////////////////////////////////////////// + + // Add the RESTORE_BASE_POINTER statement so that these globals can be popped off. + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_RESTORE_BASE_POINTER; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + //////////////////////////////////////////////////// + // MGB - September 13, 2001 - Start of added chunk. + //////////////////////////////////////////////////// + + // Add the "modify stack pointer" statement so that these globals are never used again + + int32_t nStackPointer = -m_nGlobalVariableSize; + + // MGB - April 12, 2002 - Removing a bug where this instruction is added needlessly + // (causing great grief to the virtual machine) when the stack pointer doesn't need + // to be moved at all! + if (nStackPointer != 0) + { + EmitModifyStackPointer(nStackPointer); + } + + ////////////////////////////////////////////////// + // MGB - September 13, 2001 - End of added chunk. + ////////////////////////////////////////////////// + + + + // Finally, add the RET statement so that we can bail out of this routine. + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_RET; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + // Last, but not least, add a function to the user-defined identifier list + // that specifies where this code is located. + + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nBinarySourceFinish = m_nOutputCodeLength; + + ++m_nOccupiedIdentifiers; + if (m_nOccupiedIdentifiers >= CSCRIPTCOMPILER_MAX_IDENTIFIERS) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_IDENTIFIER_LIST_FULL,pNode); + } + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_FUNCTION_IDENTIFIER) + { + // We've just read a "type", but we really don't need to propogate this type to + // anyone ... we'll simply feed it + m_nVarStackVariableType = CSCRIPTCOMPILER_OPERATION_KEYWORD_DECLARATION; + + if (m_bFunctionImp == FALSE) + { + return 0; + } + + if (pNode->pLeft == NULL) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + // + // We want to add #retval to the run-time stack, and record the + // return type. + // + + // We know the "operation" of the type via the left branch, but + // we don't actually know the "token" type. + int32_t nTokenType; + CExoString sTokenStructureName = ""; + + if (pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_STRUCT) + { + nTokenType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT; + sTokenStructureName = *(pNode->pLeft->m_psStringData); + } + else if (pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_INT) + { + nTokenType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + } + else if (pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_FLOAT) + { + nTokenType = CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT; + } + else if (pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_STRING) + { + nTokenType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING; + } + else if (pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_OBJECT) + { + nTokenType = CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT; + } + else if (pNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_VOID) + { + nTokenType = CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID; + } + else if (pNode->pLeft->nOperation >= CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE0 && + pNode->pLeft->nOperation <= CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE9) + { + nTokenType = CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 + + (pNode->pLeft->nOperation - CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE0); + } + else + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + // Add the variable "#retval" to the occupied variables list. + + if (nTokenType != CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID) + { + ++m_nOccupiedVariables; + if (m_bGlobalVariableDefinition) + { + ++m_nGlobalVariables; + if (nTokenType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + m_nGlobalVariableSize += GetStructureSize(sTokenStructureName); + } + else + { + m_nGlobalVariableSize += 4; + } + } + + m_pcVarStackList[m_nOccupiedVariables].m_psVarName = "#retval"; + m_pcVarStackList[m_nOccupiedVariables].m_nVarType = nTokenType; + if (nTokenType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + m_pcVarStackList[m_nOccupiedVariables].m_sVarStructureName = sTokenStructureName; + } + m_pcVarStackList[m_nOccupiedVariables].m_nVarLevel = m_nVarStackRecursionLevel; + m_pcVarStackList[m_nOccupiedVariables].m_nVarRunTimeLocation = m_nStackCurrentDepth * 4; + + // Now, we can add the variable to the run time stack! + + int32_t nOccupiedVariables = m_nOccupiedVariables; + int32_t nStackCurrentDepth = m_nStackCurrentDepth; + int32_t nGlobalVariableSize = m_nGlobalVariableSize; + AddVariableToStack(nTokenType, &sTokenStructureName, FALSE); + AddToSymbolTableVarStack(nOccupiedVariables,nStackCurrentDepth,nGlobalVariableSize); + } + + m_nFunctionImpReturnType = nTokenType; + m_sFunctionImpReturnStructureName = sTokenStructureName; + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_FUNCTION_PARAM_NAME) + { + + m_nVarStackVariableType = CSCRIPTCOMPILER_OPERATION_KEYWORD_DECLARATION; + + if (m_bFunctionImp == FALSE) + { + // This is required here, since we've just discovered that we're + // actually parsing a function and not a series of declarations. + return 0; + } + + if (pNode->pRight == NULL) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + for (nCount = m_nOccupiedVariables; nCount >= 0; --nCount) + { + if (m_pcVarStackList[nCount].m_psVarName == *(pNode->m_psStringData) && + m_pcVarStackList[nCount].m_nVarLevel == m_nVarStackRecursionLevel) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_VARIABLE_ALREADY_USED_WITHIN_SCOPE,pNode); + } + } + + // We know the "operation" of the type via the left branch, but + // we don't actually know the "token" type. + int32_t nTokenType; + CExoString sTokenStructureName = ""; + + if (pNode->pRight->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_STRUCT) + { + nTokenType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT; + sTokenStructureName = *(pNode->pRight->m_psStringData); + } + else if (pNode->pRight->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_INT) + { + nTokenType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + } + else if (pNode->pRight->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_FLOAT) + { + nTokenType = CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT; + } + else if (pNode->pRight->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_STRING) + { + nTokenType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING; + } + else if (pNode->pRight->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_OBJECT) + { + nTokenType = CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT; + } + else if (pNode->pRight->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_VOID) + { + nTokenType = CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID; + } + else if (pNode->pRight->nOperation >= CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE0 && + pNode->pRight->nOperation <= CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE9) + { + nTokenType = CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 + + (pNode->pRight->nOperation - CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE0); + } + + else + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + // Add the variable named at this node to the occupied variables list. + + if (nTokenType != CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID) + { + ++m_nOccupiedVariables; + if (m_bGlobalVariableDefinition) + { + ++m_nGlobalVariables; + if (nTokenType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + m_nGlobalVariableSize += GetStructureSize(sTokenStructureName); + } + else + { + m_nGlobalVariableSize += 4; + } + + } + + m_pcVarStackList[m_nOccupiedVariables].m_psVarName = *(pNode->m_psStringData); + m_pcVarStackList[m_nOccupiedVariables].m_nVarType = nTokenType; + if (nTokenType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + m_pcVarStackList[m_nOccupiedVariables].m_sVarStructureName = sTokenStructureName; + } + m_pcVarStackList[m_nOccupiedVariables].m_nVarLevel = m_nVarStackRecursionLevel; + m_pcVarStackList[m_nOccupiedVariables].m_nVarRunTimeLocation = m_nStackCurrentDepth * 4; + + // Now, we can add the variable to the run time stack! + int32_t nOccupiedVariables = m_nOccupiedVariables; + int32_t nStackCurrentDepth = m_nStackCurrentDepth; + int32_t nGlobalVariableSize = m_nGlobalVariableSize; + AddVariableToStack(nTokenType, &sTokenStructureName, FALSE); + AddToSymbolTableVarStack(nOccupiedVariables,nStackCurrentDepth,nGlobalVariableSize); + } + + // This is required here, since we've just discovered that we're + // actually parsing a function and not a series of declarations. + + + + return 0; + + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_FUNCTION_DECLARATION) + { + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_RETURN) + { + + if (m_bFunctionImp == FALSE) + { + return 0; + } + + if ((m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID && pNode->pRight != NULL) || + (m_nFunctionImpReturnType != CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID && pNode->pRight == NULL)) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_RETURN_TYPE_AND_FUNCTION_TYPE_MISMATCHED,pNode); + } + + if ((m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT && pNode->pRight->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) || + (m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT && pNode->pRight->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT) || + (m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING && pNode->pRight->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING) || + (m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT && pNode->pRight->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT) || + (m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 && pNode->pRight->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0) || + (m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE1 && pNode->pRight->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE1) || + (m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE2 && pNode->pRight->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE2) || + (m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE3 && pNode->pRight->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE3) || + (m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE4 && pNode->pRight->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE4) || + (m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE5 && pNode->pRight->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE5) || + (m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE6 && pNode->pRight->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE6) || + (m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE7 && pNode->pRight->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE7) || + (m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE8 && pNode->pRight->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE8) || + (m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9 && pNode->pRight->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9)) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_RETURN_TYPE_AND_FUNCTION_TYPE_MISMATCHED,pNode); + } + + if ((m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT && pNode->pRight->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) || + (m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT && m_sFunctionImpReturnStructureName != *(pNode->pRight->m_psTypeName))) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_RETURN_TYPE_AND_FUNCTION_TYPE_MISMATCHED,pNode); + } + + // Okay, now we've got fun! + + if (m_nFunctionImpReturnType != CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID) + { + // Set up a "write to #retval" instruction. + + int32_t nStackElementsDown = m_nGlobalVariableSize - m_nStackCurrentDepth * 4; + int32_t nSize = 4; + if (m_nFunctionImpReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + nSize = GetStructureSize(m_sFunctionImpReturnStructureName); + } + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_ASSIGNMENT; + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_VOID; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nStackElementsDown) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nStackElementsDown) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nStackElementsDown) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nStackElementsDown)) & 0x0ff); + + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+4] = (char) (((nSize) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+5] = (char) (((nSize)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 6; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + } + + // Write the MODIFY_STACK_POINTER instruction to modify the stack. + + pNode->nIntegerData = (m_nFunctionImpAbortStackPointer - m_nStackCurrentDepth) * 4; + //m_nStackCurrentDepth = m_nFunctionImpAbortStackPointer; + + if (pNode->nIntegerData != 0) + { + EmitModifyStackPointer(pNode->nIntegerData); + } + + // And, finally, write the JMP statement. + + // Here, we will need to write a symbol into the code and + // mark the location for updating during the label generation pass. + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JMP; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + // There is no point in expressing this yet, because we haven't decided on the + // locations of any of the final functions in the executable. So, write + // the symbol into the table. + + /* CExoString sSymbolName; + sSymbolName.Format("FX_%s",m_sFunctionImpName.CStr()); */ + int32_t nIdentifier = GetIdentifierByName(m_sFunctionImpName); + if (nIdentifier == STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + AddSymbolToQueryList(m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION, CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_FUNCTION_EXIT, nIdentifier, 0); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + return 0; + + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_PRE_INCREMENT || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_PRE_DECREMENT) + { + if (pNode->pLeft != NULL) + { + if (pNode->pLeft->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_OPERAND_MUST_BE_AN_INTEGER_LVALUE,pNode); + } + + // Is pNode->pLeft actually a valid LValue? + if (pNode->pLeft->nOperation != CSCRIPTCOMPILER_OPERATION_VARIABLE && + pNode->pLeft->nOperation != CSCRIPTCOMPILER_OPERATION_STRUCTURE_PART) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_OPERAND_MUST_BE_AN_INTEGER_LVALUE,pNode->pLeft); + } + + pNode->nType = pNode->pLeft->nType; + + // Current location of the variable is stored in pNode->pLeft->nIntegerData because + // it is a valid LValue. + + int32_t nStackElementsDown = pNode->pLeft->nIntegerData - m_nStackCurrentDepth * 4; + + // MGB - August 10, 2001 - Determine whether we should be operating on the base + // pointer or the stack pointer. + + int32_t bOperateOnStackPointer; + if (pNode->pLeft->nIntegerData < m_nGlobalVariableSize && m_bGlobalVariableDefinition == FALSE) + { + bOperateOnStackPointer = FALSE; + nStackElementsDown = pNode->pLeft->nIntegerData - (m_nGlobalVariableSize); + } + else + { + bOperateOnStackPointer = TRUE; + // And don't forget to add the ADDITIONAL integer that is now sitting on top of the stack! + nStackElementsDown += 4; + } + + // And now, fill in the appropriate code (pNode->nIntegerData2 points to the location + // of the EXTRA_DATA that we reserved for the data pointer!) + + // MGB - August 10, 2001 + // NOTE that we only write data into the opcode if we are not using the stack pointer, + // since the code has already been written for using the stack pointer in the PreIncrement + // call. + if (bOperateOnStackPointer == FALSE) + { + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_PRE_INCREMENT) + { + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_INCREMENT_BASE; + } + else + { + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_DECREMENT_BASE; + } + } + + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nStackElementsDown) >> 24) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 1] = (char) (((nStackElementsDown) >> 16) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 2] = (char) (((nStackElementsDown) >> 8) & 0x0ff); + m_pchOutputCode[pNode->nIntegerData2 + CVIRTUALMACHINE_EXTRA_DATA_LOCATION + 3] = (char) (((nStackElementsDown)) & 0x0ff); + + return 0; + } + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_POST_INCREMENT || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_POST_DECREMENT) + { + if (pNode->pLeft != NULL) + { + if (pNode->pLeft->nType != CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_OPERAND_MUST_BE_AN_INTEGER_LVALUE,pNode); + } + + // Is pNode->pLeft actually a valid LValue? + if (pNode->pLeft->nOperation != CSCRIPTCOMPILER_OPERATION_VARIABLE && + pNode->pLeft->nOperation != CSCRIPTCOMPILER_OPERATION_STRUCTURE_PART) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_OPERAND_MUST_BE_AN_INTEGER_LVALUE,pNode->pLeft); + } + + pNode->nType = pNode->pLeft->nType; + + // Current location of the variable is stored in pNode->pLeft->nIntegerData because + // it is a valid LValue. + + int32_t nStackElementsDown = pNode->pLeft->nIntegerData - m_nStackCurrentDepth * 4; + + int32_t bOperateOnStackPointer = TRUE; + if (pNode->pLeft->nIntegerData < m_nGlobalVariableSize && m_bGlobalVariableDefinition == FALSE) + { + bOperateOnStackPointer = FALSE; + nStackElementsDown = pNode->pLeft->nIntegerData - (m_nGlobalVariableSize); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_POST_INCREMENT) + { + if (bOperateOnStackPointer == TRUE) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_INCREMENT; + } + else + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_INCREMENT_BASE; + } + } + else + { + if (bOperateOnStackPointer == TRUE) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_DECREMENT; + } + else + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_DECREMENT_BASE; + } + } + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_AUXCODE_LOCATION] = CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER; + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION] = (char) (((nStackElementsDown) >> 24) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+1] = (char) (((nStackElementsDown) >> 16) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+2] = (char) (((nStackElementsDown) >> 8) & 0x0ff); + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_EXTRA_DATA_LOCATION+3] = (char) (((nStackElementsDown)) & 0x0ff); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + return 0; + } + + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER,pNode); + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CASE) + { + + return 0; + } + + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_DEFAULT) + { + // Generate a label for the jump caused by the switch + /* CExoString sSymbolName; + sSymbolName.Format("_SC_DEFAULT_%08x",m_nSwitchIdentifier); */ + AddSymbolToLabelList(m_nOutputCodeLength, CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_SWITCH_DEFAULT,m_nSwitchIdentifier,0); + + if (m_nSwitchStackDepth + 1 != m_nStackCurrentDepth) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_JUMPING_OVER_DECLARATION_STATEMENTS_DEFAULT_DISALLOWED,pNode); + } + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_BREAK) + { + // CODE GENERATION + // Add the "JMP _BR_nIdentifierBreak" operation, where nIdentifierBreak + // represents the innermost for/while/dowhile/case statement! + + // Compute the location of the innermost loop that we are allowed + // to break out of. + + int32_t nIdentifierBreak; + + // MGB - December 6, 2004 - START FIX + // The line below fixes a bug where a + // do/while loop is immediately followed by a switch statement; + // both the loop and the switch will be equal, causing the break + // in the switch to jump out of the do loop. This is a bad thing. + // The if used to simply be greater than (>). Note that this fix + // is safe because only do/while loops emit no code before they + // execute interior code. switch statements always emit code if they + // are the "outer" function, so it is safe to assign equality always + // to the switch statement. + + if (m_nSwitchIdentifier >= m_nLoopIdentifier) + { + nIdentifierBreak = m_nSwitchIdentifier; + } + else + { + nIdentifierBreak = m_nLoopIdentifier; + } + + // MGB - December 6, 2004 - END FIX + + if (nIdentifierBreak == 0) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_BREAK_OUTSIDE_OF_LOOP_OR_CASE_STATEMENT,pNode); + } + + // MGB - October 15, 2002 - Start Change + // End-User reported bug: break/continue do not modify stack properly. + // We must ditch any/all stack values that weren't there at the start of the loop ... + // MGB - December 8, 2004 - FIX Attempt 2. + if (nIdentifierBreak != m_nSwitchIdentifier) + { + if (m_nLoopStackDepth != m_nStackCurrentDepth) + { + int32_t nStackChange = (m_nLoopStackDepth - m_nStackCurrentDepth) * 4; + EmitModifyStackPointer(nStackChange); + } + } + // MGB - November 8, 2002 - Start Change + // Apparently, we forgot to deal with break statements in case statements as well! + // MGB - December 8, 2004 - FIX Attempt 2. + else + { + if ((m_nSwitchStackDepth + 1) != m_nStackCurrentDepth) + { + int32_t nStackChange = ((m_nSwitchStackDepth + 1) - m_nStackCurrentDepth) * 4; + EmitModifyStackPointer(nStackChange); + } + } + // MGB - November 8, 2002 - End Change + // MGB - October 15, 2002 - End Change + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JMP; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + /* CExoString sSymbolName; + sSymbolName.Format("_BR_%08x",nIdentifierBreak); */ + AddSymbolToQueryList(m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION, CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_BREAK,nIdentifierBreak,0); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONTINUE) + { + // MGB - October 15, 2002 - Start Change + // End-User reported bug: break/continue do not modify stack properly. + // We must ditch any/all stack values that weren't there at the start of the loop ... + if (m_nLoopStackDepth != m_nStackCurrentDepth) + { + int32_t nStackChange = (m_nLoopStackDepth - m_nStackCurrentDepth) * 4; + EmitModifyStackPointer(nStackChange); + } + // MGB - October 15, 2002 - End Change + + // CODE GENERATION + // Add the "JMP _CN_nLoopIdentifier" operation. + + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_JMP; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = 0; + + /* CExoString sSymbolName; + sSymbolName.Format("_CN_%08x",m_nLoopIdentifier); */ + AddSymbolToQueryList(m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION, CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_CONTINUE,m_nLoopIdentifier,0); + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + 4; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_WHILE_CONTINUE) + { + // Generate a label for the continue keyword.for a while loop. + /* CExoString sSymbolName; + sSymbolName.Format("_CN_%08x",m_nLoopIdentifier); */ + AddSymbolToLabelList(m_nOutputCodeLength, CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_CONTINUE, m_nLoopIdentifier, 0); + return 0; + } + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_CONST_DECLARATION) + { + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_CONST_KEYWORD_CANNOT_BE_USED_ON_NON_GLOBAL_VARIABLES,pNode); + } + + // Finally, if we've gone through *all* of these checks, we should abort immediately, because + // it's not in the list of operations that I understand. + + return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_OPERATION_IN_SEMANTIC_CHECK,pNode); + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::FoundReturnStatementOnAllBranches() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/24/2000 +// +// This routine attempts to determine whether there is a RETURN statement on +// all control paths. Note that it will only follow if/else choices. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::FoundReturnStatementOnAllBranches(CScriptParseTreeNode *pNode) +{ + + // Clearly, if the NODE is NULL, then we don't need to + if (pNode == NULL) + { + return FALSE; + } + + // Here, we want to verify that BOTH paths have a RETURN statement + // before letting people know that both sides of it are protected. + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_RETURN) + { + return TRUE; + } + + // Here, we want to verify that BOTH paths have a RETURN statement + // before letting people know that both sides of it are protected. + + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_IF_CHOICE) + { + if (FoundReturnStatementOnAllBranches(pNode->pLeft) == TRUE) + { + if (FoundReturnStatementOnAllBranches(pNode->pRight) == TRUE) + { + return TRUE; + } + } + return FALSE; + } + + // We want to pass through these functions, and if either side reports that + // they have a RETURN statement in them, that's good enough for me. + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_COMPOUND_STATEMENT || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STATEMENT || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STATEMENT_NO_DEBUG || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STATEMENT_LIST || + pNode->nOperation == CSCRIPTCOMPILER_OPERATION_IF_BLOCK) + { + if (FoundReturnStatementOnAllBranches(pNode->pLeft) == TRUE) + { + return TRUE; + } + + if (FoundReturnStatementOnAllBranches(pNode->pRight) == TRUE) + { + return TRUE; + } + + return FALSE; + } + + // We don't want to scan through any other part of the tree, so we + // should return FALSE here. + + return FALSE; + +} + + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::GetStructureSize() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/21/2000 +// Description: This routine will get a field from a strucutre, based on the +// name of the structure and the field. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::GetStructureSize(const CExoString &sStructureName) +{ + + int32_t count; + + for (count = 0; count < m_nMaxStructures; count++) + { + if (sStructureName == m_pcStructList[count].m_psName) + { + return m_pcStructList[count].m_nByteSize; + } + } + + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::GetIdentifierByName() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/25/2000 +// Description: This routine will get the information on an identifier, based +// on the name of the identifier. +/////////////////////////////////////////////////////////////////////////////// +int32_t CScriptCompiler::GetIdentifierByName(const CExoString &sIdentifierName) +{ + BOOL bInfiniteLoop = TRUE; + uint32_t nOriginalHash = HashString(sIdentifierName); + + // Search for the exact entry. + uint32_t nHash = nOriginalHash % CSCRIPTCOMPILER_SIZE_IDENTIFIER_HASH_TABLE; + uint32_t nEndHash = nHash + (CSCRIPTCOMPILER_SIZE_IDENTIFIER_HASH_TABLE - 1) & CSCRIPTCOMPILER_MASK_SIZE_IDENTIFIER_HASH_TABLE; + + while (bInfiniteLoop) + { + // If we're at the correct entry, confirm that the strings are identical and then + // return to the main routine. + if (m_pIdentifierHashTable[nHash].m_nHashValue == nOriginalHash && + m_pIdentifierHashTable[nHash].m_nIdentifierType == CSCRIPTCOMPILER_HASH_MANAGER_TYPE_IDENTIFIER) + { + int32_t count = m_pIdentifierHashTable[nHash].m_nIdentifierIndex; + if (m_pcIdentifierList[count].m_psIdentifier == sIdentifierName) + { + return count; + } + } + + // Have we hit a blank entry? Then it's not in the list. + if (m_pIdentifierHashTable[nHash].m_nIdentifierType == CSCRIPTCOMPILER_HASH_MANAGER_TYPE_UNKNOWN) + { + return STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER; + } + + // Have we searched the entire list? + if (nEndHash == nHash) + { + return STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER; + } + + // Move to the next entry in the list. + ++nHash; + nHash &= CSCRIPTCOMPILER_MASK_SIZE_IDENTIFIER_HASH_TABLE; + + } + + return STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER; + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::GetStructureField() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/20/2000 +// Description: This routine will get a field from a strucutre, based on the +// name of the structure and the field. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::GetStructureField(const CExoString &sStructureName, const CExoString &sFieldName) +{ + + int32_t count, count2; + + for (count = 0; count < m_nMaxStructures; count++) + { + if (sStructureName == m_pcStructList[count].m_psName) + { + for (count2 = m_pcStructList[count].m_nFieldStart; count2 <= m_pcStructList[count].m_nFieldEnd; count2++) + { + if (sFieldName == m_pcStructFieldList[count2].m_psVarName) + { + return count2; + } + } + return STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_FIELD_IN_STRUCTURE; + } + } + + return STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_STRUCTURE; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::AddVariableToStack() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/22/2000 +// Description: This routine will add a variable to the run-time stack +// based on the type (handing off to AddStructureToStack if +// it is too complicated for me to handle). +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::AddVariableToStack(int32_t nVariableType, CExoString *psVariableTypeName, BOOL bGenerateCode) +{ + if (nVariableType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT || + nVariableType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT || + nVariableType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING || + nVariableType == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT || + (nVariableType >= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 && + nVariableType <= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9)) + { + + int32_t nAuxCodeType = 0; + + if (nVariableType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + nAuxCodeType = CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER; + } + else if (nVariableType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT) + { + nAuxCodeType = CVIRTUALMACHINE_AUXCODE_TYPE_FLOAT; + } + else if (nVariableType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING) + { + nAuxCodeType = CVIRTUALMACHINE_AUXCODE_TYPE_STRING; + } + else if (nVariableType == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT) + { + nAuxCodeType = CVIRTUALMACHINE_AUXCODE_TYPE_OBJECT; + } + else if (nVariableType >= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 && + nVariableType <= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9) + { + nAuxCodeType = CVIRTUALMACHINE_AUXCODE_TYPE_ENGST0 + (nVariableType - CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0); + } + + m_pchStackTypes[m_nStackCurrentDepth] = (char) nAuxCodeType; + ++m_nStackCurrentDepth; + + if (bGenerateCode == TRUE) + { + // CODE GENERATION + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_RUNSTACK_ADD; + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_AUXCODE_LOCATION] = (char) nAuxCodeType; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + } + } + else if (nVariableType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + // Function to recursively do all structures within the structure on the stack. + AddStructureToStack(*psVariableTypeName, bGenerateCode); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::AddStructureToStack() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/22/2000 +// Description: This routine will add the strucutre to the run-time stack, +// based on the name of the structure. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::AddStructureToStack(const CExoString &sStructureName, BOOL bGenerateCode) +{ + int32_t count, count2; + + for (count = 0; count < m_nMaxStructures; count++) + { + if (sStructureName == m_pcStructList[count].m_psName) + { + for (count2 = m_pcStructList[count].m_nFieldStart; count2 <= m_pcStructList[count].m_nFieldEnd; count2++) + { + int32_t nFieldType = m_pcStructFieldList[count2].m_pchType; + + if (nFieldType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT || + nFieldType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT || + nFieldType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING || + nFieldType == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT || + (nFieldType >= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 && + nFieldType <= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9)) + { + + int32_t nAuxCodeType = 0; + + if (nFieldType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + nAuxCodeType = CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER; + } + else if (nFieldType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT) + { + nAuxCodeType = CVIRTUALMACHINE_AUXCODE_TYPE_FLOAT; + } + else if (nFieldType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING) + { + nAuxCodeType = CVIRTUALMACHINE_AUXCODE_TYPE_STRING; + } + else if (nFieldType == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT) + { + nAuxCodeType = CVIRTUALMACHINE_AUXCODE_TYPE_OBJECT; + } + else if (nFieldType >= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 && + nFieldType <= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9) + { + nAuxCodeType = CVIRTUALMACHINE_AUXCODE_TYPE_ENGST0 + (nFieldType - CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0); + } + + m_pchStackTypes[m_nStackCurrentDepth] = (char) nAuxCodeType; + ++m_nStackCurrentDepth; + + // CODE GENERATION + if (bGenerateCode == TRUE) + { + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = CVIRTUALMACHINE_OPCODE_RUNSTACK_ADD; + m_pchOutputCode[m_nOutputCodeLength+CVIRTUALMACHINE_AUXCODE_LOCATION] = (char) nAuxCodeType; + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + } + } + else if (nFieldType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + // Function to recursively do all structures within the structure on the stack. + AddStructureToStack(m_pcStructFieldList[count2].m_psStructureName, bGenerateCode); + } + + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::GetFunctionNameFromSymbolSubTypes() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: Nov. 21, 2002 +// Description: A utility function to return a function name from the symbol +// subtypes you've passed in. +/////////////////////////////////////////////////////////////////////////////// +CExoString CScriptCompiler::GetFunctionNameFromSymbolSubTypes(int32_t nSubType1,int32_t nSubType2) +{ + CExoString sNewFunctionName; + + if (nSubType1 == 0 && + nSubType2 != 0) + { + // Starting functions, need to be handled with kid gloves. + if (nSubType2 == 2) + { + if (m_bCompileConditionalFile == 0) + { + sNewFunctionName = "main"; + } + else + { + sNewFunctionName = "StartingConditional"; + } + } + else + { + sNewFunctionName = "#globals"; + } + + } + else if (nSubType1 != 0 && + nSubType2 == 0) + { + // These can be looked up on the Occupied Identifiers list. + uint32_t nNewFunctionIdentifier = nSubType1; + sNewFunctionName = m_pcIdentifierList[nNewFunctionIdentifier].m_psIdentifier; + } + else + { + // Sorry, dude ... this shouldn't have happened under any circumstance. + sNewFunctionName = ""; + } + + return sNewFunctionName; +} + + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::AddSymbolToLabelList() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/22/2000 +// Description: Adds information to the symbol label list for examination at +// a later date. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::AddSymbolToLabelList(int32_t nLocationPointer, int32_t nSymbolType, int32_t nSymbolSubType1, int32_t nSymbolSubType2) +{ + if (m_nSymbolLabelListSize == m_nSymbolLabelList) + { + CScriptCompilerSymbolTableEntry *m_pNewLabelList; + + // Create a new list. + + m_nSymbolLabelListSize += 8192; + m_pNewLabelList = new CScriptCompilerSymbolTableEntry[m_nSymbolLabelListSize]; + + if (m_pSymbolLabelList != NULL) + { + for (int32_t count = 0; count < m_nSymbolLabelList; ++count) + { + m_pNewLabelList[count].m_nSymbolType = m_pSymbolLabelList[count].m_nSymbolType; + m_pNewLabelList[count].m_nSymbolSubType1 = m_pSymbolLabelList[count].m_nSymbolSubType1; + m_pNewLabelList[count].m_nSymbolSubType2 = m_pSymbolLabelList[count].m_nSymbolSubType2; + m_pNewLabelList[count].m_nLocationPointer = m_pSymbolLabelList[count].m_nLocationPointer; + m_pNewLabelList[count].m_nNextEntryPointer = m_pSymbolLabelList[count].m_nNextEntryPointer; + } + delete[] m_pSymbolLabelList; + } + m_pSymbolLabelList = m_pNewLabelList; + } + + m_pSymbolLabelList[m_nSymbolLabelList].m_nSymbolType = nSymbolType; + m_pSymbolLabelList[m_nSymbolLabelList].m_nSymbolSubType1 = nSymbolSubType1; + m_pSymbolLabelList[m_nSymbolLabelList].m_nSymbolSubType2 = nSymbolSubType2; + m_pSymbolLabelList[m_nSymbolLabelList].m_nLocationPointer = nLocationPointer; + m_pSymbolLabelList[m_nSymbolLabelList].m_nNextEntryPointer = -1; + + uint32_t nLabelHash = (nSymbolSubType1 & 0x01ff); + if (m_pSymbolLabelStartEntry[nLabelHash] == -1) + { + // First entry in list. + m_pSymbolLabelStartEntry[nLabelHash] = m_nSymbolLabelList; + } + else + { + // Chain the new entry to the tail of the list ... + int32_t nCurrentPtr = m_pSymbolLabelStartEntry[nLabelHash]; + while (m_pSymbolLabelList[nCurrentPtr].m_nNextEntryPointer != -1) + { + nCurrentPtr = m_pSymbolLabelList[nCurrentPtr].m_nNextEntryPointer; + } + m_pSymbolLabelList[nCurrentPtr].m_nNextEntryPointer = m_nSymbolLabelList; + } + + // Advance the symbol label list pointer. + ++m_nSymbolLabelList; + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::AddSymbolToQueryList() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/22/2000 +// Description: Adds information to the symbol query list for examination at +// a later date. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::AddSymbolToQueryList(int32_t nLocationPointer, int32_t nSymbolType, int32_t nSymbolSubType1, int32_t nSymbolSubType2) +{ + if (m_nSymbolQueryListSize == m_nSymbolQueryList) + { + CScriptCompilerSymbolTableEntry *m_pNewQueryList; + + // Create a new list. + + m_nSymbolQueryListSize += 8192; + m_pNewQueryList = new CScriptCompilerSymbolTableEntry[m_nSymbolQueryListSize]; + + if (m_pSymbolQueryList != NULL) + { + for (int32_t count = 0; count < m_nSymbolQueryList; ++count) + { + m_pNewQueryList[count].m_nSymbolType = m_pSymbolQueryList[count].m_nSymbolType; + m_pNewQueryList[count].m_nSymbolSubType1 = m_pSymbolQueryList[count].m_nSymbolSubType1; + m_pNewQueryList[count].m_nSymbolSubType2 = m_pSymbolQueryList[count].m_nSymbolSubType2; + m_pNewQueryList[count].m_nLocationPointer = m_pSymbolQueryList[count].m_nLocationPointer; + m_pNewQueryList[count].m_nNextEntryPointer = m_pSymbolQueryList[count].m_nNextEntryPointer; + } + delete[] m_pSymbolQueryList; + } + m_pSymbolQueryList = m_pNewQueryList; + } + + m_pSymbolQueryList[m_nSymbolQueryList].m_nSymbolType = nSymbolType; + m_pSymbolQueryList[m_nSymbolQueryList].m_nSymbolSubType1 = nSymbolSubType1; + m_pSymbolQueryList[m_nSymbolQueryList].m_nSymbolSubType2 = nSymbolSubType2; + m_pSymbolQueryList[m_nSymbolQueryList].m_nLocationPointer = nLocationPointer; + // m_nNextEntryPointer is not valid for query list. + ++m_nSymbolQueryList; + + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::WalkParseTree() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: This routine will walk the compile tree, generating a postfix +// listing of the nodes (if necessary). +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::WalkParseTree(CScriptParseTreeNode *pNode) +{ + if (pNode != NULL) + { + //INT bPrinted = 0; + + + int nReturnCode; + + ConstantFoldNode(pNode); + nReturnCode = PreVisitGenerateCode(pNode); + + if (nReturnCode == 0) + { + nReturnCode = WalkParseTree(pNode->pLeft); + } + + if (nReturnCode == 0) + { + ConstantFoldNode(pNode); + nReturnCode = InVisitGenerateCode(pNode); + } + + if (nReturnCode == 0) + { + nReturnCode = WalkParseTree(pNode->pRight); + } + + if (nReturnCode == 0) + { + ConstantFoldNode(pNode); + nReturnCode = PostVisitGenerateCode(pNode); + } + + if (nReturnCode > 0) + { + nReturnCode = 0; + } + + // Oh, dear. If there is not enough room, we should probably grow the + // output buffer by a bit! + // [36628] We make this check AFTER the data has been written without + // boundary checks. 200 bytes was not enough to protect from buffer + // overflows, raising to 16K. -virusman 2018/04/13 + if (nReturnCode == 0 && m_nOutputCodeLength >= m_nOutputCodeSize - 16384) + { + m_nOutputCodeSize += CSCRIPTCOMPILER_MAX_CODE_SIZE; + char *pNewArray = new char[m_nOutputCodeSize]; + memcpy(pNewArray,m_pchOutputCode,m_nOutputCodeLength); + delete[] m_pchOutputCode; + m_pchOutputCode = pNewArray; + // return OutputWalkTreeError(STRREF_CSCRIPTCOMPILER_ERROR_SCRIPT_TOO_LARGE,pNode); + } + + return nReturnCode; + } + + // A Null pointer is not an error. + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::StartLineNumberAtBinaryInstruction() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: August 29, 2002 +// Description: This routine will attempt to deal with line number +/////////////////////////////////////////////////////////////////////////////// +void CScriptCompiler::StartLineNumberAtBinaryInstruction(int32_t nFileReference, int32_t nLineNumber, int32_t nBinaryInstruction) +{ + if (m_nCurrentLineNumber != nLineNumber || + m_nCurrentLineNumberFileReference != nFileReference) + { + m_nCurrentLineNumber = nLineNumber; + m_nCurrentLineNumberFileReference = nFileReference; + m_nCurrentLineNumberReferences = 1; + m_nCurrentLineNumberBinaryStartInstruction = nBinaryInstruction; + } + else + { + m_nCurrentLineNumberReferences += 1; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::EndLineNumberAtBinaryInstruction() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: August 29, 2002 +// Description: This routine will attempt to deal with line number +/////////////////////////////////////////////////////////////////////////////// +void CScriptCompiler::EndLineNumberAtBinaryInstruction(int32_t nFileReference, int32_t nLineNumber, int32_t nBinaryInstruction) +{ + // If we've received an end of line for something that we haven't started, + // this is a bug, so let's not write anything into the table. + if (m_nCurrentLineNumber != nLineNumber || + m_nCurrentLineNumberFileReference != nFileReference) + { + m_nCurrentLineNumber = -1; + m_nCurrentLineNumberFileReference = -1; + m_nCurrentLineNumberReferences = 0; + return; + } + + // If you have had multiple references for the same line, just subtract + // one of them away. + if (m_nCurrentLineNumberReferences > 1) + { + m_nCurrentLineNumberReferences -= 1; + return; + } + + // We're at the last reference for a line, so we can write the information + // into the table. + + // Fetch the file name reference. + CExoString *psCurrentLineNumberFileName = m_ppsParseTreeFileNames[m_nCurrentLineNumberFileReference]; + + int32_t nFileNameReference = -1; + for (int32_t nCount=0; nFileNameReference == -1 && nCount < m_nTableFileNames; ++nCount) + { + if (strcmp(m_psTableFileNames[nCount].CStr(),psCurrentLineNumberFileName->CStr()) == 0) + { + nFileNameReference = nCount; + } + } + + if (nFileNameReference == -1) + { + if (m_nTableFileNames >= CSCRIPTCOMPILER_MAX_TABLE_FILENAMES) + { + return; + } + m_psTableFileNames[m_nTableFileNames] = *psCurrentLineNumberFileName; + nFileNameReference = m_nTableFileNames; + ++m_nTableFileNames; + } + + if (m_pnTableInstructionFileReference.size() == m_nLineNumberEntries) + { + int32_t nSize = m_pnTableInstructionFileReference.size() * 2; + if (nSize <= 16) + { + nSize = 16; + } + + m_pnTableInstructionFileReference.resize(nSize); + m_pnTableInstructionLineNumber.resize(nSize); + m_pnTableInstructionBinaryStart.resize(nSize); + m_pnTableInstructionBinaryEnd.resize(nSize); + m_pnTableInstructionBinaryFinal.resize(nSize); + m_pnTableInstructionBinarySortedOrder.resize(nSize); + + } + + // Write all of the information for this line into the table. + m_pnTableInstructionFileReference[m_nLineNumberEntries] = nFileNameReference; + m_pnTableInstructionLineNumber[m_nLineNumberEntries] = nLineNumber; + m_pnTableInstructionBinaryStart[m_nLineNumberEntries] = m_nCurrentLineNumberBinaryStartInstruction; + m_pnTableInstructionBinaryEnd[m_nLineNumberEntries] = nBinaryInstruction; + m_pnTableInstructionBinaryFinal[m_nLineNumberEntries] = FALSE; + m_pnTableInstructionBinarySortedOrder[m_nLineNumberEntries] = -1; + + m_nLineNumberEntries++; + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::AddToSymbolTableVarStack() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: August 21, 2002 +// Description: This routine will add an occupied variable to the symbol +// table. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::AddToSymbolTableVarStack(int32_t nOccupiedVariables, int32_t nStackCurrentDepth, int32_t nGlobalVariableSize) +{ + if (m_nGenerateDebuggerOutput != 0) + { + if (m_pnSymbolTableVarType.size() == m_nSymbolTableVariables) + { + int32_t nSize = m_pnSymbolTableVarType.size() * 2; + if (nSize <= 16) + { + nSize = 16; + } + + m_pnSymbolTableVarType.resize(nSize); + m_psSymbolTableVarName.resize(nSize); + m_psSymbolTableVarStructureName.resize(nSize); + m_pnSymbolTableVarStackLoc.resize(nSize); + m_pnSymbolTableVarBegin.resize(nSize); + m_pnSymbolTableVarEnd.resize(nSize); + m_pnSymbolTableBinaryFinal.resize(nSize); + m_pnSymbolTableBinarySortedOrder.resize(nSize); + } + + m_pnSymbolTableVarType[m_nSymbolTableVariables] = m_pcVarStackList[nOccupiedVariables].m_nVarType; + m_psSymbolTableVarName[m_nSymbolTableVariables] = m_pcVarStackList[nOccupiedVariables].m_psVarName; + if (m_pcVarStackList[nOccupiedVariables].m_nVarType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + m_psSymbolTableVarStructureName[m_nSymbolTableVariables] = (m_pcVarStackList[nOccupiedVariables].m_sVarStructureName); + } + m_pnSymbolTableVarStackLoc[m_nSymbolTableVariables] = nStackCurrentDepth * 4 - nGlobalVariableSize; + m_pnSymbolTableVarBegin[m_nSymbolTableVariables] = m_nOutputCodeLength; + m_pnSymbolTableVarEnd[m_nSymbolTableVariables] = -1; + m_pnSymbolTableBinaryFinal[m_nSymbolTableVariables] = FALSE; + m_pnSymbolTableBinarySortedOrder[m_nSymbolTableVariables] = -1; + m_nSymbolTableVariables += 1; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::RemoveFromSymbolTableVarStack() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: August 21, 2002 +// Description: This routine will add an occupied variable to the symbol +// table. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::RemoveFromSymbolTableVarStack(int32_t nOccupiedVariables, int32_t nStackCurrentDepth, int32_t nGlobalVariableSize) +{ + if (m_nGenerateDebuggerOutput != 0) + { + + int32_t nVarCount = m_nSymbolTableVariables - 1; + while (nVarCount >= 0) + { + BOOL bMatch = TRUE; + if (m_pnSymbolTableVarType[nVarCount] != m_pcVarStackList[nOccupiedVariables].m_nVarType) + { + bMatch = FALSE; + } + if (bMatch == TRUE && + m_psSymbolTableVarName[nVarCount] != m_pcVarStackList[nOccupiedVariables].m_psVarName) + { + bMatch = FALSE; + } + if (bMatch == TRUE && + m_pnSymbolTableVarStackLoc[nVarCount] != nStackCurrentDepth * 4 - nGlobalVariableSize) + { + bMatch = FALSE; + } + + if (bMatch == TRUE && + m_pcVarStackList[nOccupiedVariables].m_nVarType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT && + m_psSymbolTableVarStructureName[nVarCount] != (m_pcVarStackList[nOccupiedVariables].m_sVarStructureName)) + { + bMatch = FALSE; + } + + if (bMatch == TRUE && m_pnSymbolTableVarEnd[nVarCount] == -1) + { + m_pnSymbolTableVarEnd[nVarCount] = m_nOutputCodeLength; + return; + } + + --nVarCount; + } + + ///////////////////////////////////////////////////////////////// + // + // Once we get past this point, we are actually deleting an entry + // that we haven't found in the list ... this is usually a bad + // thing. However, to cover over the problem, we're just going + // to add an additional entry. + // + ///////////////////////////////////////////////////////////////// + + if (m_pnSymbolTableVarType.size() == m_nSymbolTableVariables) + { + int32_t nSize = m_pnSymbolTableVarType.size() * 2; + if (nSize <= 16) + { + nSize = 16; + } + + m_pnSymbolTableVarType.resize(nSize); + m_psSymbolTableVarName.resize(nSize); + m_psSymbolTableVarStructureName.resize(nSize); + m_pnSymbolTableVarStackLoc.resize(nSize); + m_pnSymbolTableVarBegin.resize(nSize); + m_pnSymbolTableVarEnd.resize(nSize); + m_pnSymbolTableBinaryFinal.resize(nSize); + m_pnSymbolTableBinarySortedOrder.resize(nSize); + } + + m_pnSymbolTableVarType[m_nSymbolTableVariables] = m_pcVarStackList[nOccupiedVariables].m_nVarType; + m_psSymbolTableVarName[m_nSymbolTableVariables] = m_pcVarStackList[nOccupiedVariables].m_psVarName; + if (m_pcVarStackList[nOccupiedVariables].m_nVarType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + m_psSymbolTableVarStructureName[m_nSymbolTableVariables] = (m_pcVarStackList[nOccupiedVariables].m_sVarStructureName); + } + m_pnSymbolTableVarStackLoc[m_nSymbolTableVariables] = nStackCurrentDepth * 4 - nGlobalVariableSize; + m_pnSymbolTableVarBegin[m_nSymbolTableVariables] = -1; + m_pnSymbolTableVarEnd[m_nSymbolTableVariables] = m_nOutputCodeLength; + m_pnSymbolTableBinaryFinal[m_nSymbolTableVariables] = FALSE; + m_pnSymbolTableBinarySortedOrder[m_nSymbolTableVariables] = -1; + m_nSymbolTableVariables += 1; + } + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ResolveDebuggingInformation() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: August 29, 2002 +// Description: This routine will take the binary source locations for +// line numbers and variables stored in the .ndb and moves +// them to their correct final location. +/////////////////////////////////////////////////////////////////////////////// +void CScriptCompiler::ResolveDebuggingInformation() +{ + // m_nFinalLineNumberEntries contains the number of entries that + // will eventually be written into the file. Only those entries + // where m_pnTableInstructionBinaryFinal[] == TRUE will be included + // in this total. + m_nFinalLineNumberEntries = 0; + + { + int32_t nCurrentBinarySize = CVIRTUALMACHINE_BINARY_SCRIPT_HEADER; + while (nCurrentBinarySize < m_nFinalBinarySize) + { + BOOL bFoundIdentifier = FALSE; + int32_t nCount2 = m_nMaxPredefinedIdentifierId; + while (bFoundIdentifier == FALSE && nCount2 <= m_nOccupiedIdentifiers) + { + if (nCurrentBinarySize == m_pcIdentifierList[nCount2].m_nBinaryDestinationStart) + { + ResolveDebuggingInformationForIdentifier(nCount2); + nCurrentBinarySize = m_pcIdentifierList[nCount2].m_nBinaryDestinationFinish; + bFoundIdentifier = TRUE; + } + nCount2++; + } + + if (bFoundIdentifier == FALSE) + { + // This is bad, since we have a discontinuity + // in the binary functions. + nCurrentBinarySize = m_nFinalBinarySize; + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ResolveDebuggingInformationForIdentifier() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: August 29, 2002 +// Description: The routine that actually resolves line numbers and +// variables on a per-identifier (function) basis. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::ResolveDebuggingInformationForIdentifier(int32_t nIdentifier) +{ + int32_t nCount; + + // Loop through all of the line number pairs that we have accumulated. + for (nCount = 0; nCount < m_nLineNumberEntries; nCount++) + { + + // Has the entry been resolved? + if (m_pnTableInstructionBinaryFinal[nCount] == FALSE) + { + // Resolve the location of where we will be writing the address + // for the start of the line number we're lookin' for. + int32_t nLineBinaryInstructionStart = m_pnTableInstructionBinaryStart[nCount]; + + if (nLineBinaryInstructionStart >= m_pcIdentifierList[nIdentifier].m_nBinarySourceStart && + nLineBinaryInstructionStart < m_pcIdentifierList[nIdentifier].m_nBinarySourceFinish) + { + // Now, check to see if the query even BELONGS in the final file. + // (The function may not be reachable through a call to main(), + // so we don't need to store information about it!) + // + // If it does belong in the final file, generate its change in + // location and let it be "printed" to the file. + if (m_pcIdentifierList[nIdentifier].m_nBinaryDestinationStart != -1) + { + int32_t nBinaryLocationChange = (m_pcIdentifierList[nIdentifier].m_nBinaryDestinationStart - + m_pcIdentifierList[nIdentifier].m_nBinarySourceStart); + + m_pnTableInstructionBinaryStart[nCount] += nBinaryLocationChange; + m_pnTableInstructionBinaryEnd[nCount] += nBinaryLocationChange; + m_pnTableInstructionBinaryFinal[nCount] = TRUE; + m_pnTableInstructionBinarySortedOrder[m_nFinalLineNumberEntries] = nCount; + m_nFinalLineNumberEntries += 1; + } + } + } + } + + // Loop through all of the symbol table entries we have accumulated. + for (nCount = 0; nCount < m_nSymbolTableVariables; nCount++) + { + // Has the entry been resolved? + if (m_pnSymbolTableBinaryFinal[nCount] == FALSE) + { + int32_t nInstruction = m_pnSymbolTableVarBegin[nCount]; + if (nInstruction == -1) + { + nInstruction = m_pnSymbolTableVarEnd[nCount]; + } + + if (nInstruction >= m_pcIdentifierList[nIdentifier].m_nBinarySourceStart && + nInstruction < m_pcIdentifierList[nIdentifier].m_nBinarySourceFinish) + { + if (m_pcIdentifierList[nIdentifier].m_nBinaryDestinationStart != -1) + { + int32_t nBinaryLocationChange = (m_pcIdentifierList[nIdentifier].m_nBinaryDestinationStart - + m_pcIdentifierList[nIdentifier].m_nBinarySourceStart); + + if (m_pnSymbolTableVarBegin[nCount] != -1) + { + m_pnSymbolTableVarBegin[nCount] += nBinaryLocationChange; + } + + if (m_pnSymbolTableVarEnd[nCount] != -1) + { + m_pnSymbolTableVarEnd[nCount] += nBinaryLocationChange; + } + + m_pnSymbolTableBinaryFinal[nCount] = TRUE; + m_pnSymbolTableBinarySortedOrder[m_nFinalSymbolTableVariables] = nCount; + m_nFinalSymbolTableVariables += 1; + } + } + } + } +} + +CExoString CScriptCompiler::GenerateDebuggerTypeAbbreviation(int32_t nType, CExoString sStructureName) +{ + CExoString sReturnValue = "?"; + + if (nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID || + nType == CSCRIPTCOMPILER_TOKEN_VOID_IDENTIFIER) + { + sReturnValue = "v"; + } + else if (nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT || + nType == CSCRIPTCOMPILER_TOKEN_FLOAT_IDENTIFIER) + { + sReturnValue = "f"; + } + else if (nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT || + nType == CSCRIPTCOMPILER_TOKEN_INTEGER_IDENTIFIER) + { + sReturnValue = "i"; + } + else if (nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT || + nType == CSCRIPTCOMPILER_TOKEN_OBJECT_IDENTIFIER) + { + sReturnValue = "o"; + } + else if (nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING || + nType == CSCRIPTCOMPILER_TOKEN_STRING_IDENTIFIER) + { + sReturnValue = "s"; + } + else if ((nType >= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 && + nType <= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9) || + (nType >= CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE0_IDENTIFIER && + nType <= CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE9_IDENTIFIER)) + { + if (nType >= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 && + nType <= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9) + { + sReturnValue.Format("e%01d",nType - CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0); + } + else + { + sReturnValue.Format("e%01d",nType - CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE0_IDENTIFIER); + } + } + else if (nType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT || + nType == CSCRIPTCOMPILER_TOKEN_STRUCTURE_IDENTIFIER) + { + for (int32_t count = 0; count < m_nMaxStructures; count++) + { + if (m_pcStructList[count].m_psName == sStructureName) + { + sReturnValue.Format("t%04d",count); + } + } + } + + return sReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::WriteOutDebuggerOutput() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: September 3, 2002 +/////////////////////////////////////////////////////////////////////////////// +int32_t CScriptCompiler::WriteDebuggerOutputToFile(CExoString sFileName) +{ + if (m_nGenerateDebuggerOutput != 0) + { + if (m_pchDebuggerCode == NULL) + { + m_nDebuggerCodeSize = CSCRIPTCOMPILER_MAX_DEBUG_OUTPUT_SIZE; + m_pchDebuggerCode = new char[m_nDebuggerCodeSize]; + } + + if (m_pchDebuggerCode == NULL) + { + return STRREF_CSCRIPTCOMPILER_ERROR_UNABLE_TO_OPEN_FILE_FOR_WRITING; + } + + // MGB - February 14, 2003 + // Evaluate the size of the debugger output. If the debugger output is larger + // than the buffer, we should increase the size of the buffer by a bit (double it!) + + { + int32_t nMaxSize = 9 + 40; // NDB Header + Section sizes. + int32_t nMaxTypeNameSize = 5; + int32_t count; + for (count = 0; count < m_nTableFileNames; count++) + { + nMaxSize += m_psTableFileNames[count].GetLength() + 5; + } + for (count = 0; count < m_nMaxStructures; count++) + { + nMaxSize += m_pcStructList[count].m_psName.GetLength() + 6; + int32_t countField; + for (countField = m_pcStructList[count].m_nFieldStart; countField <= m_pcStructList[count].m_nFieldEnd; countField++) + { + // sTypeName can be at most size 5. + nMaxSize += nMaxTypeNameSize + m_pcStructFieldList[countField].m_psVarName.GetLength() + 5; + } + } + for (count = m_nMaxPredefinedIdentifierId; count < m_nOccupiedIdentifiers; count++) + { + nMaxSize += nMaxTypeNameSize + m_pcIdentifierList[count].m_psIdentifier.GetLength() + 26; + int32_t countParams; + for (countParams = 0; countParams < m_pcIdentifierList[count].m_nParameters; countParams++) + { + nMaxSize += nMaxTypeNameSize + 4; + } + } + for (count = 0; count < m_nFinalSymbolTableVariables; count++) + { + int32_t nSTEntry = m_pnSymbolTableBinarySortedOrder[count]; + if (m_pnSymbolTableBinaryFinal[nSTEntry] == TRUE) + { + nMaxSize += nMaxTypeNameSize + m_psSymbolTableVarName[nSTEntry].GetLength() + 31; + } + } + for (count = 0; count < m_nFinalLineNumberEntries; count++) + { + int32_t nLNEntry = m_pnTableInstructionBinarySortedOrder[count]; + if (m_pnTableInstructionBinaryFinal[nLNEntry] == TRUE) + { + nMaxSize += 30; + } + } + + if (nMaxSize >= m_nDebuggerCodeSize) + { + if (nMaxSize < m_nDebuggerCodeSize * 2) + { + m_nDebuggerCodeSize *= 2; + } + else + { + m_nDebuggerCodeSize = nMaxSize * 2; + } + + delete[] m_pchDebuggerCode; + m_pchDebuggerCode = new char[m_nDebuggerCodeSize]; + + if (m_pchDebuggerCode == NULL) + { + return STRREF_CSCRIPTCOMPILER_ERROR_UNABLE_TO_OPEN_FILE_FOR_WRITING; + } + } + } + + + sprintf(m_pchDebuggerCode,"NDB V1.0\n"); + m_nDebuggerCodeLength = 9; + sprintf(m_pchDebuggerCode + m_nDebuggerCodeLength,"%07d %07d %07d %07d %07d\n", + m_nTableFileNames, m_nMaxStructures, + m_nOccupiedIdentifiers - m_nMaxPredefinedIdentifierId, + m_nFinalSymbolTableVariables,m_nFinalLineNumberEntries); + m_nDebuggerCodeLength += 40; + + int32_t count; + + for (count = 0; count < m_nTableFileNames; count++) + { + if (m_psTableFileNames[count] == sFileName) + { + // Capital F indicates the base file. + sprintf(m_pchDebuggerCode + m_nDebuggerCodeLength,"N%02d %s\n",count,m_psTableFileNames[count].CStr()); + } + else + { + sprintf(m_pchDebuggerCode + m_nDebuggerCodeLength,"n%02d %s\n",count,m_psTableFileNames[count].CStr()); + } + m_nDebuggerCodeLength += m_psTableFileNames[count].GetLength() + 5; + } + + for (count = 0; count < m_nMaxStructures; count++) + { + sprintf(m_pchDebuggerCode + m_nDebuggerCodeLength,"s %02d %s\n", + (m_pcStructList[count].m_nFieldEnd - m_pcStructList[count].m_nFieldStart + 1), + m_pcStructList[count].m_psName.CStr()); + m_nDebuggerCodeLength += m_pcStructList[count].m_psName.GetLength() + 6; + + int32_t countField; + for (countField = m_pcStructList[count].m_nFieldStart; countField <= m_pcStructList[count].m_nFieldEnd; countField++) + { + CExoString sTypeName = GenerateDebuggerTypeAbbreviation(m_pcStructFieldList[countField].m_pchType,m_pcStructFieldList[countField].m_psStructureName); + sprintf(m_pchDebuggerCode + m_nDebuggerCodeLength,"sf %s %s\n", + sTypeName.CStr(), m_pcStructFieldList[countField].m_psVarName.CStr()); + m_nDebuggerCodeLength += sTypeName.GetLength() + m_pcStructFieldList[countField].m_psVarName.GetLength() + 5; + } + } + + for (count = m_nMaxPredefinedIdentifierId; count < m_nOccupiedIdentifiers; count++) + { + CExoString sTypeName = GenerateDebuggerTypeAbbreviation(m_pcIdentifierList[count].m_nReturnType,m_pcIdentifierList[count].m_psStructureReturnName); + sprintf(m_pchDebuggerCode + m_nDebuggerCodeLength,"f %08x %08x %03d %s %s\n", + m_pcIdentifierList[count].m_nBinaryDestinationStart, + m_pcIdentifierList[count].m_nBinaryDestinationFinish, + m_pcIdentifierList[count].m_nParameters, + sTypeName.CStr(), m_pcIdentifierList[count].m_psIdentifier.CStr()); + m_nDebuggerCodeLength += sTypeName.GetLength() + m_pcIdentifierList[count].m_psIdentifier.GetLength() + 26; + + int32_t countParams; + for (countParams = 0; countParams < m_pcIdentifierList[count].m_nParameters; countParams++) + { + CExoString sTypeName = (GenerateDebuggerTypeAbbreviation(m_pcIdentifierList[count].m_pchParameters[countParams], + m_pcIdentifierList[count].m_psStructureParameterNames[countParams])); + sprintf(m_pchDebuggerCode + m_nDebuggerCodeLength,"fp %s\n",sTypeName.CStr()); + m_nDebuggerCodeLength += sTypeName.GetLength() + 4; + } + } + + for (count = 0; count < m_nFinalSymbolTableVariables; count++) + { + int32_t nSTEntry = m_pnSymbolTableBinarySortedOrder[count]; + if (m_pnSymbolTableBinaryFinal[nSTEntry] == TRUE) + { + CExoString sTypeName = GenerateDebuggerTypeAbbreviation(m_pnSymbolTableVarType[nSTEntry], + m_psSymbolTableVarStructureName[nSTEntry]); + + sprintf(m_pchDebuggerCode + m_nDebuggerCodeLength,"v %08x %08x %08x %s %s\n", + m_pnSymbolTableVarBegin[nSTEntry], + m_pnSymbolTableVarEnd[nSTEntry], + m_pnSymbolTableVarStackLoc[nSTEntry], + sTypeName.CStr(), m_psSymbolTableVarName[nSTEntry].CStr()); + m_nDebuggerCodeLength += sTypeName.GetLength() + m_psSymbolTableVarName[nSTEntry].GetLength() + 31; + } + } + + for (count = 0; count < m_nFinalLineNumberEntries; count++) + { + int32_t nLNEntry = m_pnTableInstructionBinarySortedOrder[count]; + if (m_pnTableInstructionBinaryFinal[nLNEntry] == TRUE) + { + sprintf(m_pchDebuggerCode + m_nDebuggerCodeLength,"l%02d %07d %08x %08x\n", + m_pnTableInstructionFileReference[nLNEntry],m_pnTableInstructionLineNumber[nLNEntry], + m_pnTableInstructionBinaryStart[nLNEntry],m_pnTableInstructionBinaryEnd[nLNEntry]); + m_nDebuggerCodeLength += 30; + } + } + + // Now that the debugger information has been written into a buffer, + // we can write it out in one operation to disk! + CExoString sModifiedFileName; + sModifiedFileName.Format("%s:%s",m_sOutputAlias.CStr(),sFileName.CStr()); + + const int32_t ret = m_cAPI.ResManWriteToFile( + sModifiedFileName.CStr(), m_nResTypeDebug, + (const uint8_t*) m_pchDebuggerCode, m_nDebuggerCodeLength, false); + + if (ret != 0) + { + return ret; + } + + if (m_bAutomaticCleanUpAfterCompiles == TRUE) + { + CExoString sDirectoryFileName; + + sDirectoryFileName.Format("%s:",m_sOutputAlias.CStr()); + m_cAPI.ResManUpdateResourceDirectory(sDirectoryFileName.CStr()); + + // Delete the Debugger code buffer + delete[] m_pchDebuggerCode; + m_pchDebuggerCode = NULL; + m_nDebuggerCodeSize = 0; + } + + // This must always happen. + m_nDebuggerCodeLength = 0; + } + return 0; +} + +char *CScriptCompiler::InstructionLookback(uint32_t last) +{ + if (last == 0 || last > m_aOutputCodeInstructionBoundaries.size()) + return NULL; + + return &m_pchOutputCode[m_aOutputCodeInstructionBoundaries[m_aOutputCodeInstructionBoundaries.size() - 1 - last]]; +} + +void CScriptCompiler::WriteByteSwap32(char *buffer, int32_t value) +{ + buffer[0] = (char)((value >> 24) & 0xff); + buffer[1] = (char)((value >> 16) & 0xff); + buffer[2] = (char)((value >> 8) & 0xff); + buffer[3] = (char)((value) & 0xff); +} +int32_t CScriptCompiler::ReadByteSwap32(char *buffer) +{ + return ((int32_t)buffer[0] << 24) | ((int32_t)buffer[1] << 16) | ((int32_t)buffer[2] << 8) | (int32_t)buffer[3]; +} + +char *CScriptCompiler::EmitInstruction(uint8_t nOpCode, uint8_t nAuxCode, int32_t nDataSize) +{ + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_OPCODE_LOCATION] = nOpCode; + m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_AUXCODE_LOCATION] = nAuxCode; + + char *ret = &m_pchOutputCode[m_nOutputCodeLength + CVIRTUALMACHINE_EXTRA_DATA_LOCATION]; + + m_nOutputCodeLength += CVIRTUALMACHINE_OPERATION_BASE_SIZE + nDataSize; + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + + return ret; +} + +void CScriptCompiler::EmitModifyStackPointer(int32_t nModifyBy) +{ + if (m_nOptimizationFlags & CSCRIPTCOMPILER_OPTIMIZE_MELD_INSTRUCTIONS) + { + char *last = InstructionLookback(1); + + // Temporarily disabled. Compiler is generating dead MOVSP instructions + // in some cases when returning from a function, and merging a live one + // with a dead one causes issues, unsurprisingly. +#if 0 + // Multiple MODIFY_STACK_POINTER instructions can always be merged into a single one + if (last[CVIRTUALMACHINE_OPCODE_LOCATION] == CVIRTUALMACHINE_OPCODE_MODIFY_STACK_POINTER) + { + int32_t mod = ReadByteSwap32(&last[CVIRTUALMACHINE_EXTRA_DATA_LOCATION]); + mod += nModifyBy; + WriteByteSwap32(&last[CVIRTUALMACHINE_EXTRA_DATA_LOCATION], mod); + return; + } +#endif + // The nwscript construct `int n = 3;` gets compiled into the following: + // RUNSTACK_ADD, TYPE_INTEGER + // CONSTANT, TYPE_INTEGER, 3 + // ASSIGNMENT, TYPE_VOID, -8, 4 + // MODIFY_STACK_POINTER, -4 + // This ends up with just the CONSTI 3 on the top of stack, but the + // dance is necessary as `n = 3` is also an expression which returns 3. + // Then, the last MODSP discards the result of the expression. + // Here, we detect the pattern when it is discarded, and replace the + // whole set of instructions with just CONSTI 3 + if (last[CVIRTUALMACHINE_OPCODE_LOCATION] == CVIRTUALMACHINE_OPCODE_ASSIGNMENT) + { + char *instConstant = InstructionLookback(2); + char *instRunstackAdd = InstructionLookback(3); + + if (instConstant && instConstant[CVIRTUALMACHINE_OPCODE_LOCATION] == CVIRTUALMACHINE_OPCODE_CONSTANT && + instRunstackAdd && instRunstackAdd[CVIRTUALMACHINE_OPCODE_LOCATION] == CVIRTUALMACHINE_OPCODE_RUNSTACK_ADD && + instConstant[CVIRTUALMACHINE_AUXCODE_LOCATION] == instRunstackAdd[CVIRTUALMACHINE_AUXCODE_LOCATION] && + ReadByteSwap32(&last[CVIRTUALMACHINE_EXTRA_DATA_LOCATION]) == -8 + ) + { + // Move the CONST instruction up to where runstack add is.. + const int32_t instConstantSize = (last - instConstant); + memmove(instRunstackAdd, instConstant, instConstantSize); + // Roll back output code length to where the old instConstant started.. + m_nOutputCodeLength = (instRunstackAdd - m_pchOutputCode) + instConstantSize; + // Pop the last two instruction boundaries (CONSTANT, ASSIGNMENT) since those don't exist anymore + m_aOutputCodeInstructionBoundaries.pop_back(); + m_aOutputCodeInstructionBoundaries.pop_back(); + m_aOutputCodeInstructionBoundaries.pop_back(); + m_aOutputCodeInstructionBoundaries.push_back(m_nOutputCodeLength); + return; + } + } + } + + char *buf = EmitInstruction(CVIRTUALMACHINE_OPCODE_MODIFY_STACK_POINTER, 0, 4); + WriteByteSwap32(buf, nModifyBy); +} diff --git a/src/Native Compiler/scriptcompidentspec.cpp b/src/Native Compiler/scriptcompidentspec.cpp new file mode 100644 index 0000000..e1e9d95 --- /dev/null +++ b/src/Native Compiler/scriptcompidentspec.cpp @@ -0,0 +1,907 @@ +// +// SPDX-License-Identifier: GPL-3.0 +// +// This file is part of the NWScript compiler open source release. +// +// The initial source release is licensed under GPL-3.0. +// +// All subsequent changes you submit are required to be licensed under MIT. +// +// However, the project overall will still be GPL-3.0. +// +// The intent is for the base game to be able to pick up changes you explicitly +// submit for inclusion painlessly, while ensuring the overall project source code +// remains available for everyone. +// + +/////////////////////////////////////////////////////////////////////////////// +// BIOWARE CORP. CONFIDENTIAL INFORMATION. // +// COPYRIGHT BIOWARE CORP. ALL RIGHTS RESERVED // +/////////////////////////////////////////////////////////////////////////////// + +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Script Project +//:: +//:: Copyright (c) 2002, BioWare Corp. +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: ScriptCompIdentSpec.cpp +//:: +//:: Implementation of parsing the identifier specification file. +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Created By: Mark Brockington +//:: Created On: October 8, 2002 +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: This file contains "ported" code (used when writing out floating point +//:: numbers on the MacIntosh platforms). +//:: +//:://///////////////////////////////////////////////////////////////////////// + +#include +#include + +// external header files +#include "exobase.h" +#include "scriptcomp.h" + +// internal header files +#include "scriptinternal.h" + + +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Class CScriptCompiler +//:: +//:://///////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::GenerateIdentifierList() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/24/99 +// Description: Takes the output from the lexical analysis to generate a +// list of indentifiers. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::GenerateIdentifierList() +{ + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_ACTION || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_VECTOR || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE1 || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE2 || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE3 || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE4 || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE5 || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE6 || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE7 || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE8 || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9 || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID) + { + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_START_OF_LINE) + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_AFTER_TYPE_DECLARATION; + m_nIdentifierListReturnType = m_nTokenStatus; + return 0; + } + else if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST) + { + // Use the keywords to add the parameter into the list. + + int32_t nValue = m_pcIdentifierList[m_nOccupiedIdentifiers-1].m_nParameters; + + if (nValue == m_pcIdentifierList[m_nOccupiedIdentifiers-1].m_nParameterSpace) + { + int nReturnValue = m_pcIdentifierList[m_nOccupiedIdentifiers-1].ExpandParameterSpace(); + if (nReturnValue < 0) + { + return nReturnValue; + } + } + + m_pcIdentifierList[m_nOccupiedIdentifiers-1].m_pchParameters[nValue] = (char) m_nTokenStatus; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_VECTOR) + { + m_pcIdentifierList[m_nOccupiedIdentifiers-1].m_pchParameters[nValue] = (char) CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT; + m_pcIdentifierList[m_nOccupiedIdentifiers-1].m_psStructureParameterNames[nValue] = "vector"; + } + + m_pcIdentifierList[m_nOccupiedIdentifiers-1].m_pbOptionalParameters[nValue] = FALSE; + m_pcIdentifierList[m_nOccupiedIdentifiers-1].m_nParameters = nValue+1; + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + return nError; + } + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_DEFINE) + { + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_START_OF_LINE) + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_DEFINE_STATEMENT; + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + return nError; + } + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_NUM_STRUCTURES_DEFINITION) + { + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_DEFINE_STATEMENT) + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_DEFINE_NUM_ENGINE_STRUCTURE; + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + return nError; + } + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE_DEFINITION) + { + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_DEFINE_STATEMENT) + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_DEFINE_SINGLE_ENGINE_STRUCTURE; + m_nIdentifierListEngineStructure = (int32_t) (m_pchToken[17] - '0'); + + if (m_nIdentifierListEngineStructure < 0 || m_nIdentifierListEngineStructure >= 10) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + return nError; + } + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + return nError; + } + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_EQUAL) + { + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_AFTER_FUNCTION_IDENTIFIER) + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_BEFORE_CONSTANT_IDENTIFIER; + m_pcIdentifierList[m_nOccupiedIdentifiers-1].m_nIdentifierType = 0; + return 0; + } + else if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST) + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST_CONSTANT; + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + return nError; + } + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_VARIABLE) + { + + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_DEFINE_SINGLE_ENGINE_STRUCTURE) + { + m_pbEngineDefinedStructureValid[m_nIdentifierListEngineStructure] = TRUE; + m_pchToken[m_nTokenCharacters] = 0; + (m_psEngineDefinedStructureName[m_nIdentifierListEngineStructure]).Format("%s",(char *) m_pchToken); + + HashManagerAdd(CSCRIPTCOMPILER_HASH_MANAGER_TYPE_ENGINE_STRUCTURE, m_nIdentifierListEngineStructure); + + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_START_OF_LINE; + return 0; + } + + // There are many identifiers in the list, the only one we + // want to deal with is the name of the function. The names + // of the parameters are irrelevant (hence, the transfer from + // state 1 to state 2. + + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_AFTER_TYPE_DECLARATION) + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_AFTER_FUNCTION_IDENTIFIER; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nIdentifierType = 1; + + if (m_nIdentifierListReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_INTEGER_IDENTIFIER; + } + else if (m_nIdentifierListReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_OBJECT_IDENTIFIER; + } + else if (m_nIdentifierListReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_FLOAT_IDENTIFIER; + } + else if (m_nIdentifierListReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_STRING_IDENTIFIER; + } + else if (m_nIdentifierListReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE0_IDENTIFIER; + } + else if (m_nIdentifierListReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE1) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE1_IDENTIFIER; + } + else if (m_nIdentifierListReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE2) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE2_IDENTIFIER; + } + else if (m_nIdentifierListReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE3) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE3_IDENTIFIER; + } + else if (m_nIdentifierListReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE4) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE4_IDENTIFIER; + } + else if (m_nIdentifierListReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE5) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE5_IDENTIFIER; + } + else if (m_nIdentifierListReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE6) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE6_IDENTIFIER; + } + else if (m_nIdentifierListReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE7) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE7_IDENTIFIER; + } + else if (m_nIdentifierListReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE8) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE8_IDENTIFIER; + } + else if (m_nIdentifierListReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE9_IDENTIFIER; + } + else if (m_nIdentifierListReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_VECTOR) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_STRUCTURE_IDENTIFIER; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_psStructureReturnName = "vector"; + } + else if (m_nIdentifierListReturnType == CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_VOID_IDENTIFIER; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + return nError; + } + + // Copy the token name, MINUS the return type, into the code. + m_pchToken[m_nTokenCharacters] = 0; + (m_pcIdentifierList[m_nOccupiedIdentifiers].m_psIdentifier).Format("%s",(char *) m_pchToken); + (m_pcIdentifierList[m_nOccupiedIdentifiers].m_nIdentifierHash) = HashString(m_pcIdentifierList[m_nOccupiedIdentifiers].m_psIdentifier); + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nIdentifierLength = m_nTokenCharacters; + + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nBinarySourceStart = -1; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nBinarySourceFinish = -1; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nBinaryDestinationStart = -1; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nBinaryDestinationFinish = -1; + HashManagerAdd(CSCRIPTCOMPILER_HASH_MANAGER_TYPE_IDENTIFIER, m_nOccupiedIdentifiers); + + m_nOccupiedIdentifiers++; + if (m_nOccupiedIdentifiers >= CSCRIPTCOMPILER_MAX_IDENTIFIERS) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_IDENTIFIER_LIST_FULL; + return nError; + } + + return 0; + } + else if (m_nIdentifierListState != CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + return nError; + } + + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST) + { + return 0; + } + + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER; + return nError; + + } + + // This one is tough. Bail at the end of the file. + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_EOF) + { + return 0; + } + + // This one is *really* tough. We should signify that we should take + // the negative of the constant when we deal with the next token. + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_MINUS) + { + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_BEFORE_CONSTANT_IDENTIFIER) + { + m_pcIdentifierList[m_nOccupiedIdentifiers-1].m_nIntegerData = -1; + } + // We also store the data here if we are in a parameter list. + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST_CONSTANT) + { + m_pcIdentifierList[m_nOccupiedIdentifiers-1].m_nIntegerData = -1; + } + return 0; + } + + int32_t nCount; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_INTEGER) + { + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_DEFINE_NUM_ENGINE_STRUCTURE) + { + // Fetch the value. + int nValue = 0; + for (nCount = 0; nCount < m_nTokenCharacters; nCount++) + { + nValue = nValue * 10 + (m_pchToken[nCount] - '0'); + } + + if (nValue >= 10) + { + nValue = 10; + } + + if (nValue < 0) + { + nValue = 1; + } + + m_nNumEngineDefinedStructures = nValue; + + if (m_pbEngineDefinedStructureValid) + { + delete[] m_pbEngineDefinedStructureValid; + } + + m_pbEngineDefinedStructureValid = new BOOL[nValue]; + for (int32_t cnt = 0; cnt < nValue; cnt++) + { + m_pbEngineDefinedStructureValid[cnt] = FALSE; + } + + if (m_psEngineDefinedStructureName) + { + delete[] m_psEngineDefinedStructureName; + } + m_psEngineDefinedStructureName = new CExoString[nValue]; + + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_START_OF_LINE; + return 0; + + } + + if (m_bCompileIdentifierConstants == FALSE && + m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_BEFORE_CONSTANT_IDENTIFIER) + { + return 0; + } + + int nLoc = m_nOccupiedIdentifiers - 1; + + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_BEFORE_CONSTANT_IDENTIFIER) + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_AFTER_CONSTANT_IDENTIFIER; + + if(m_pcIdentifierList[nLoc].m_nReturnType!=CSCRIPTCOMPILER_TOKEN_INTEGER_IDENTIFIER || + m_pcIdentifierList[nLoc].m_nIdentifierType != 0) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NON_INTEGER_ID_FOR_INTEGER_CONSTANT; + return nError; + } + } + else if (m_nIdentifierListState != CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST_CONSTANT) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + return nError; + } + + int nSign = 1; + if (m_pcIdentifierList[nLoc].m_nIntegerData == -1) + { + m_pcIdentifierList[nLoc].m_nIntegerData = 0; + nSign = -1; + } + + int nValue = 0; + + // MGB - June 3, 2002 + // Most of the time, the minus sign has already been removed + // from the integer constant. However, if we specify a negative + // constant and THEN use it as a default parameter, it can sneak + // into this function via the back door. This is the ultimate + // final trap for it. + nCount = 0; + if (m_pchToken[nCount] == '-') + { + nSign *= -1; + ++nCount; + } + + for (; nCount < m_nTokenCharacters; nCount++) + { + nValue = nValue * 10 + (m_pchToken[nCount] - '0'); + } + + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_AFTER_CONSTANT_IDENTIFIER) + { + m_pcIdentifierList[nLoc].m_nIntegerData = nSign * nValue; + m_pchToken[m_nTokenCharacters] = 0; + + // MGB - September 14, 2001 - Negative constants will have to be stored + // into the string. + if (nSign == -1) + { + (m_pcIdentifierList[nLoc].m_psStringData).Format("-%s",m_pchToken); + } + else + { + (m_pcIdentifierList[nLoc].m_psStringData).Format("%s",m_pchToken); + } + } + else if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST_CONSTANT) + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST; + int32_t nLoc2 = m_pcIdentifierList[nLoc].m_nParameters - 1; + m_pcIdentifierList[nLoc].m_pbOptionalParameters[nLoc2] = TRUE; + m_pcIdentifierList[nLoc].m_pnOptionalParameterIntegerData[nLoc2] = nSign * nValue; + } + + return 0; + } + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_FLOAT) + { + if (m_bCompileIdentifierConstants == FALSE && m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_BEFORE_CONSTANT_IDENTIFIER) + { + return 0; + } + + int nLoc = m_nOccupiedIdentifiers - 1; + + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_BEFORE_CONSTANT_IDENTIFIER) + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_AFTER_CONSTANT_IDENTIFIER; + if(m_pcIdentifierList[nLoc].m_nReturnType != CSCRIPTCOMPILER_TOKEN_FLOAT_IDENTIFIER || + m_pcIdentifierList[nLoc].m_nIdentifierType != 0) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NON_FLOAT_ID_FOR_FLOAT_CONSTANT; + return nError; + } + } + else if (m_nIdentifierListState != CSCRIPTCOMPILER_IDENT_STATE_IN_VECTOR_PARAMETER && + m_nIdentifierListState != CSCRIPTCOMPILER_IDENT_STATE_IN_VECTOR_CONSTANT && + m_nIdentifierListState != CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST_CONSTANT) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + return nError; + } + + float fSign = 1.0f; + if (m_pcIdentifierList[nLoc].m_nIntegerData == -1) + { + m_pcIdentifierList[nLoc].m_nIntegerData = 0; + fSign = -1.0f; + } + + float fValue = ParseFloatFromTokenString(); + + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_AFTER_CONSTANT_IDENTIFIER) + { + m_pcIdentifierList[nLoc].m_fFloatData = fSign * fValue; + m_pchToken[m_nTokenCharacters] = 0; + + // MGB - September 14, 2001 - Negative constants will have to be stored + // into the string. + if (fSign < 0.0f) + { + (m_pcIdentifierList[nLoc].m_psStringData).Format("-%s",m_pchToken); + } + else + { + (m_pcIdentifierList[nLoc].m_psStringData).Format("%s",m_pchToken); + } + } + else if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST_CONSTANT) + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST; + int32_t nLoc2 = m_pcIdentifierList[nLoc].m_nParameters - 1; + m_pcIdentifierList[nLoc].m_pbOptionalParameters[nLoc2] = TRUE; + m_pcIdentifierList[nLoc].m_pfOptionalParameterFloatData[nLoc2] = fSign * fValue; + } + else if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_IN_VECTOR_CONSTANT) + { + m_pcIdentifierList[nLoc].m_fVectorData[m_nIdentifierListVector] = fSign * fValue; + } + else if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_IN_VECTOR_PARAMETER) + { + int32_t nLoc2 = m_pcIdentifierList[nLoc].m_nParameters - 1; + m_pcIdentifierList[nLoc].m_pbOptionalParameters[nLoc2] = TRUE; + m_pcIdentifierList[nLoc].m_pfOptionalParameterVectorData[nLoc2 * 3 + m_nIdentifierListVector] = fSign * fValue; + } + + return 0; + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_STRING) + { + if (m_bCompileIdentifierConstants == FALSE && m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_BEFORE_CONSTANT_IDENTIFIER) + { + return 0; + } + + int nLoc = m_nOccupiedIdentifiers - 1; + + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_BEFORE_CONSTANT_IDENTIFIER) + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_AFTER_CONSTANT_IDENTIFIER; + + if(m_pcIdentifierList[nLoc].m_nReturnType != CSCRIPTCOMPILER_TOKEN_STRING_IDENTIFIER || + m_pcIdentifierList[nLoc].m_nIdentifierType != 0) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NON_STRING_ID_FOR_STRING_CONSTANT; + return nError; + } + } + else if (m_nIdentifierListState != CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST_CONSTANT) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + return nError; + } + + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_AFTER_CONSTANT_IDENTIFIER) + { + m_pchToken[m_nTokenCharacters] = 0; + (m_pcIdentifierList[nLoc].m_psStringData).Format("%s",m_pchToken); + } + else if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST_CONSTANT) + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST; + int32_t nLoc2 = m_pcIdentifierList[nLoc].m_nParameters - 1; + m_pcIdentifierList[nLoc].m_pbOptionalParameters[nLoc2] = TRUE; + m_pchToken[m_nTokenCharacters] = 0; + (m_pcIdentifierList[nLoc].m_psOptionalParameterStringData[nLoc2]).Format("%s",m_pchToken); + } + return 0; + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT_SELF) + { + if (m_nIdentifierListState != CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST_CONSTANT) + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + return nError; + } + + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST; + int32_t nLoc = m_nOccupiedIdentifiers - 1; + int32_t nLoc2 = m_pcIdentifierList[nLoc].m_nParameters - 1; + m_pcIdentifierList[nLoc].m_pbOptionalParameters[nLoc2] = TRUE; + m_pcIdentifierList[nLoc].m_poidOptionalParameterObjectData[nLoc2] = (OBJECT_ID) 0; + return 0; + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT_INVALID) + { + if (m_nIdentifierListState != CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST_CONSTANT) + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + return nError; + } + + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST; + int32_t nLoc = m_nOccupiedIdentifiers - 1; + int32_t nLoc2 = m_pcIdentifierList[nLoc].m_nParameters - 1; + m_pcIdentifierList[nLoc].m_pbOptionalParameters[nLoc2] = TRUE; + m_pcIdentifierList[nLoc].m_poidOptionalParameterObjectData[nLoc2] = (OBJECT_ID) 1; + return 0; + } + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_LOCATION_INVALID) + { + if (m_nIdentifierListState != CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST_CONSTANT) + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + return nError; + } + + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST; + int32_t nLoc = m_nOccupiedIdentifiers - 1; + int32_t nLoc2 = m_pcIdentifierList[nLoc].m_nParameters - 1; + m_pcIdentifierList[nLoc].m_pbOptionalParameters[nLoc2] = TRUE; + // LOCATION_INVALID is reusing the integer data field + m_pcIdentifierList[nLoc].m_pnOptionalParameterIntegerData[nLoc2] = 0; + return 0; + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_NULL || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_FALSE || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_TRUE || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_OBJECT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_ARRAY || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_STRING) + { + if (m_nIdentifierListState != CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST_CONSTANT) + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + return nError; + } + + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST; + int32_t nLoc = m_nOccupiedIdentifiers - 1; + int32_t nLoc2 = m_pcIdentifierList[nLoc].m_nParameters - 1; + m_pcIdentifierList[nLoc].m_pbOptionalParameters[nLoc2] = TRUE; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_NULL) + m_pcIdentifierList[nLoc].m_psOptionalParameterStringData[nLoc2] = "null"; + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_FALSE) + m_pcIdentifierList[nLoc].m_psOptionalParameterStringData[nLoc2] = "false"; + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_TRUE) + m_pcIdentifierList[nLoc].m_psOptionalParameterStringData[nLoc2] = "true"; + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_OBJECT) + m_pcIdentifierList[nLoc].m_psOptionalParameterStringData[nLoc2] = "{}"; + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_ARRAY) + m_pcIdentifierList[nLoc].m_psOptionalParameterStringData[nLoc2] = "[]"; + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_STRING) + m_pcIdentifierList[nLoc].m_psOptionalParameterStringData[nLoc2] = "\"\""; + else + EXOASSERTNCSTR("missing impl"); + + return 0; + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_SQUARE_BRACKET) + { + if (m_nIdentifierListState != CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST_CONSTANT && + m_nIdentifierListState != CSCRIPTCOMPILER_IDENT_STATE_BEFORE_CONSTANT_IDENTIFIER) + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + return nError; + } + m_nIdentifierListVector = 0; + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_BEFORE_CONSTANT_IDENTIFIER) + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_IN_VECTOR_CONSTANT; + } + else + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_IN_VECTOR_PARAMETER; + } + + int32_t nLoc = m_nOccupiedIdentifiers - 1; + int32_t nLoc2 = m_pcIdentifierList[nLoc].m_nParameters - 1; + m_pcIdentifierList[nLoc].m_pbOptionalParameters[nLoc2] = TRUE; + for (int32_t nLoc3 = 0; nLoc3 < 3; nLoc3++) + { + m_pcIdentifierList[nLoc].m_pfOptionalParameterVectorData[nLoc2 * 3 + nLoc3] = (float) 0.0f; + } + return 0; + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_SQUARE_BRACKET) + { + if (m_nIdentifierListState != CSCRIPTCOMPILER_IDENT_STATE_IN_VECTOR_PARAMETER && + m_nIdentifierListState != CSCRIPTCOMPILER_IDENT_STATE_IN_VECTOR_CONSTANT) + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + return nError; + } + + m_nIdentifierListVector = 0; + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_IN_VECTOR_PARAMETER) + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST; + } + else + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_AFTER_CONSTANT_IDENTIFIER; + } + + return 0; + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + // Semicolon resets the state of the identifier list parsing. + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST) + { + int32_t nLoc = m_nOccupiedIdentifiers - 1; + int32_t nLoc2 = m_pcIdentifierList[nLoc].m_nParameters - 1; + + m_pcIdentifierList[nLoc].m_nIdIdentifier = m_nPredefinedIdentifierOrder; + ++m_nPredefinedIdentifierOrder; + m_pcIdentifierList[nLoc].m_bImplementationInPlace = TRUE; + + + // Find the last non-optional parameter so that we know the size of the list + // of non-optional parameters! + m_pcIdentifierList[nLoc].m_nNonOptionalParameters = 0; + for (int32_t cnt = nLoc2; cnt >= 0 && m_pcIdentifierList[nLoc].m_nNonOptionalParameters == 0; cnt--) + { + if (m_pcIdentifierList[nLoc].m_pbOptionalParameters[cnt] == FALSE) + { + m_pcIdentifierList[nLoc].m_nNonOptionalParameters = cnt+1; + } + } + + } + ++m_nMaxPredefinedIdentifierId; + + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_START_OF_LINE; + return 0; + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_BRACKET) + { + if (m_nIdentifierListState == CSCRIPTCOMPILER_IDENT_STATE_AFTER_FUNCTION_IDENTIFIER) + { + m_nIdentifierListState = CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST; + return 0; + } + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_COMMA) + { + return 0; + } + + return STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST; + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::PrintParseIdentifierFileError() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/24/99 +// Description: This routine will parse the identifier file. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::PrintParseIdentifierFileError(int32_t nParsingError) +{ + CExoString strRes = m_cAPI.TlkResolve(-nParsingError); + + CExoString *psFileName = &(m_pcIncludeFileStack[0].m_sCompiledScriptName); + OutputError(nParsingError,psFileName,m_nLines,strRes); + m_pcIncludeFileStack[0].m_sSourceScript = ""; + return STRREF_CSCRIPTCOMPILER_ERROR_ALREADY_PRINTED; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseIdentifierFile() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/24/99 +// Description: This routine will parse the identifier file. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseIdentifierFile() +{ + + char *pScript; + uint32_t nScriptLength; + uint32_t i; + int32_t ch; + int32_t chNext; + + int32_t nParseCharacterReturn; + + // Initialize(); + + m_nPredefinedIdentifierOrder = 0; + + const char* sTest = m_cAPI.ResManLoadScriptSourceFile(m_sLanguageSource.CStr(), m_nResTypeSource); + if (!sTest) + { + return PrintParseIdentifierFileError(STRREF_CSCRIPTCOMPILER_ERROR_FILE_NOT_FOUND); + } + + m_pcIncludeFileStack[0].m_sSourceScript = sTest; + pScript = m_pcIncludeFileStack[0].m_sSourceScript.CStr(); + nScriptLength = m_pcIncludeFileStack[0].m_sSourceScript.GetLength(); + + if ( nScriptLength < 1 ) + { + ch = -1; + } + else + { + ch = pScript[0]; + } + + if ( nScriptLength < 2 ) + { + chNext = -1; + } + else + { + chNext = pScript[1]; + } + + i = 2; + + while (ch != -1) + { + nParseCharacterReturn = ParseNextCharacter(ch,chNext, pScript + i, nScriptLength - i); + if (nParseCharacterReturn < 0) + { + m_pcIncludeFileStack[0].m_sSourceScript = ""; + return PrintParseIdentifierFileError(nParseCharacterReturn); + } + + while (nParseCharacterReturn >= 0) + { + // Process the location of the next character. + if (ch == '\n') + { + ++m_nLines; + m_nCharacterOnLine = 1; + } + else + { + ++m_nCharacterOnLine; + } + + // Fetch the "next" character and update the status + // of the current character that we are looking at. + ch = chNext; + + if ( i >= nScriptLength ) + { + chNext = -1; + } + else + { + chNext = (unsigned char) pScript[i]; + } + + --nParseCharacterReturn; + + ++i; + } + } + + nParseCharacterReturn = ParseNextCharacter(-1,-1, nullptr, 0); + + m_pcIncludeFileStack[0].m_sSourceScript = ""; + + if (nParseCharacterReturn < 0) + { + return PrintParseIdentifierFileError(nParseCharacterReturn); + } + + return 0; + +} diff --git a/src/Native Compiler/scriptcomplexical.cpp b/src/Native Compiler/scriptcomplexical.cpp new file mode 100644 index 0000000..23482f3 --- /dev/null +++ b/src/Native Compiler/scriptcomplexical.cpp @@ -0,0 +1,1774 @@ +// +// SPDX-License-Identifier: GPL-3.0 +// +// This file is part of the NWScript compiler open source release. +// +// The initial source release is licensed under GPL-3.0. +// +// All subsequent changes you submit are required to be licensed under MIT. +// +// However, the project overall will still be GPL-3.0. +// +// The intent is for the base game to be able to pick up changes you explicitly +// submit for inclusion painlessly, while ensuring the overall project source code +// remains available for everyone. +// + + /////////////////////////////////////////////////////////////////////////////// +// BIOWARE CORP. CONFIDENTIAL INFORMATION. // +// COPYRIGHT BIOWARE CORP. ALL RIGHTS RESERVED // +/////////////////////////////////////////////////////////////////////////////// + +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Script Project +//:: +//:: Copyright (c) 2002, BioWare Corp. +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: ScriptCompLexical.cpp +//:: +//:: Implementation of parsing a stream of characters into tokens that can +//:: either be handed to GenerateIdentifierFile() or GenerateParseTree(). +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Created By: Mark Brockington +//:: Created On: Oct. 8, 2002 +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: This file contains "ported" code (used when writing out floating point +//:: numbers on the MacIntosh platforms). +//:: +//:://///////////////////////////////////////////////////////////////////////// + +#include +#include + +// external header files +#include "exobase.h" +#include "scriptcomp.h" + +// internal header files +#include "scriptinternal.h" + +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Class CScriptCompiler +//:: +//:://///////////////////////////////////////////////////////////////////////// + + + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseFloatFromTokenString() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 10/17/2000 +// Description: Parses a floating point number from the m_pchToken that has +// been read in (i.e. a TOKEN_FLOAT). +/////////////////////////////////////////////////////////////////////////////// + +float CScriptCompiler::ParseFloatFromTokenString() +{ + float fReturnValue = 0.0f; + int32_t nMode = 0; + float fDecimalConstant = 1.0f; + float fSign = 1.0f; + + for (int32_t nCount = 0; nCount < m_nTokenCharacters; nCount++) + { + if (m_pchToken[nCount] == '-' && nCount == 0) + { + fSign = -1.0f; + } + + if (m_pchToken[nCount] >= '0' && + m_pchToken[nCount] <= '9') + { + if (nMode == 0) + { + fReturnValue *= 10.0f; + fReturnValue += (m_pchToken[nCount] - '0'); + } + if (nMode == 1) + { + fDecimalConstant /= 10.0f; + fReturnValue += ((m_pchToken[nCount] - '0') * fDecimalConstant); + } + + } + if (m_pchToken[nCount] == '.') + { + nMode = 1; + } + } + + // If the constant is negative, apply the sign to it. + if (fSign < 0.0f) + { + fReturnValue = fSign * fReturnValue; + } + + return fReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterNumeric() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a numeric character for use by tokens +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterNumeric(int32_t ch) +{ + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_INTEGER; + m_nTokenCharacters = 0; + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_INTEGER || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_HEX_INTEGER || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_FLOAT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_STRING || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_IDENTIFIER) + { + m_pchToken[m_nTokenCharacters] = (char) ch; + ++m_nTokenCharacters; + if (m_nTokenCharacters > CSCRIPTCOMPILER_MAX_TOKEN_LENGTH) + { + return STRREF_CSCRIPTCOMPILER_ERROR_TOKEN_TOO_LONG; + } + + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + return nError; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterPeriod() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a period for use by tokens +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterPeriod(int32_t chNext) +{ + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_FLOAT) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + return nError; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_INTEGER) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_FLOAT; + m_pchToken[m_nTokenCharacters] = '.'; + ++m_nTokenCharacters; + if (m_nTokenCharacters >= CSCRIPTCOMPILER_MAX_TOKEN_LENGTH) + { + return STRREF_CSCRIPTCOMPILER_ERROR_TOKEN_TOO_LONG; + } + + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_IDENTIFIER) + { + // Cut off the current identifier, and then pass the + // period in as a token! + int nReturnValue = HandleIdentifierToken(); + if (nReturnValue >= 0) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_STRUCTURE_PART_SPECIFY; + nReturnValue = HandleToken(); + } + return nReturnValue; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + // If our token starts with a dot, and next one is a digit, it's a float + // in the form of ".42", meaning 0.42 + if (chNext >= '0' && chNext <= '9') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_FLOAT; + m_pchToken[m_nTokenCharacters++] = '0'; + m_pchToken[m_nTokenCharacters++] = '.'; + } + else // Otherwise, assume struct field + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_STRUCTURE_PART_SPECIFY; + return HandleToken(); + } + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + return nError; + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterAlphabet() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a alphabetic character for use by tokens +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterAlphabet(int32_t ch) +{ + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_IDENTIFIER; + m_nTokenCharacters = 0; + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_INTEGER && (ch == 'x' || ch == 'X') && + m_nTokenCharacters == 1 && m_pchToken[0] == '0') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_HEX_INTEGER; + m_pchToken[m_nTokenCharacters] = (char) ch; + ++m_nTokenCharacters; + if (m_nTokenCharacters >= CSCRIPTCOMPILER_MAX_TOKEN_LENGTH) + { + return STRREF_CSCRIPTCOMPILER_ERROR_TOKEN_TOO_LONG; + } + + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_HEX_INTEGER && + ((ch >= 'a' && ch <='f') || + (ch >= 'A' && ch <='F'))) + { + if (ch <= 'F') + { + m_pchToken[m_nTokenCharacters] = (char) (ch + 32); + } + else + { + m_pchToken[m_nTokenCharacters] = (char) ch; + } + ++m_nTokenCharacters; + if (m_nTokenCharacters >= CSCRIPTCOMPILER_MAX_TOKEN_LENGTH) + { + return STRREF_CSCRIPTCOMPILER_ERROR_TOKEN_TOO_LONG; + } + + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_IDENTIFIER || m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_STRING) + { + m_pchToken[m_nTokenCharacters] = (char) ch; + ++m_nTokenCharacters; + if (m_nTokenCharacters >= CSCRIPTCOMPILER_MAX_TOKEN_LENGTH) + { + return STRREF_CSCRIPTCOMPILER_ERROR_TOKEN_TOO_LONG; + } + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + return nError; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterSlash() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a slash for use by tokens +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterSlash(int32_t chNext) +{ + int32_t nReturnValue = 1; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + if (chNext == '*') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_CCOMMENT; + nReturnValue = 1; + } + else if (chNext == '/') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_CPLUSCOMMENT; + nReturnValue = 1; + } + else if (chNext == '=') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_DIVIDE; + int32_t nReturnHandle = HandleToken(); + if (nReturnHandle < 0) + { + return nReturnHandle; + } + nReturnValue = 1; + } + else + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_DIVIDE; + nReturnValue = HandleToken(); + } + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterAmpersand() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles an ampersand for use by tokens +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterAmpersand(int32_t chNext) +{ + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + if (chNext == '&') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_LOGICAL_AND; + int nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + return 1; + } + else if (chNext == '=') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_AND; + int nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + return 1; + } + else + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_BOOLEAN_AND; + return HandleToken(); + } + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + return nError; + } + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterVerticalBar() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a vertical bar for use by tokens +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterVerticalBar(int32_t chNext) +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + if (chNext == '|') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_LOGICAL_OR; + int nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + nReturnValue = 1; + } + else if (chNext == '=') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_OR; + int nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + nReturnValue = 1; + } + else + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_INCLUSIVE_OR; + nReturnValue = HandleToken(); + } + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterAsterisk() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles an asterisk for use by tokens +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterAsterisk(int32_t chNext) +{ + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + if (chNext == '=') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_MULTIPLY; + int nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + return 1; + } + else + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_MULTIPLY; + return HandleToken(); + } + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + return nError; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCommentedOutCharacter() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Determies whether or not the COMMENT token ends. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCommentedOutCharacter(int32_t ch) +{ + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_CPLUSCOMMENT) + { + if (ch == '\n') + { + +#ifndef NWN + // For other projects, this is useful for keeping the scripting + // commands straight. + if (m_bCompileIdentifierList == TRUE) + { + if (m_nTokenCharacters >= 2 && + m_pchToken[0] == '!') + { + m_pchToken[m_nTokenCharacters] = 0; + int32_t nProperFunctionIdentifier = atoi(m_pchToken + 1); + + if (nProperFunctionIdentifier != m_nPredefinedIdentifierOrder) + { + BOOL bAlwaysFalse = FALSE; + EXOASSERTSTR(bAlwaysFalse,"Language Definition File Is Misordered!"); + } + } + } +#endif // !NWN + + TokenInitialize(); + } + else if (m_bCompileIdentifierList == TRUE) + { + +#ifndef NWN + m_pchToken[m_nTokenCharacters] = (char) ch; + ++m_nTokenCharacters; + if (m_nTokenCharacters >= CSCRIPTCOMPILER_MAX_TOKEN_LENGTH) + { + return STRREF_CSCRIPTCOMPILER_ERROR_TOKEN_TOO_LONG; + } +#endif // !NWN + + } + + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_CCOMMENT) + { + // If the last character we have seen is an asterisk, then + // m_nTokenCharacters == 1. Only when we see ch == '/' and + // we see m_nTokenCharacters == 1 should we bother to unset + // CCOMMENT mode. + + if (ch == '*') + { + if (m_nTokenCharacters == 0) + { + m_nTokenCharacters = 1; + } + } + else if (ch == '/') + { + if (m_nTokenCharacters == 1) + { + TokenInitialize(); + } + } + else + { + m_nTokenCharacters = 0; + } + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseStringCharacter() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a character for a TOKEN_STRING token. Need to compile +// stuff like \n into a single character (\n). Wow. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseStringCharacter(int32_t ch, int32_t chNext, char *pScript, int32_t nScriptLength) +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_STRING) + { + if (ch == '\n') + { + return STRREF_CSCRIPTCOMPILER_ERROR_UNTERMINATED_STRING_CONSTANT; + } + else if (ch == '"') + { + return ParseCharacterQuotationMark(); + } + else if (ch == '\\') + { + BOOL escapedChar = true; + // Handle the "\n" type characters in a string constant. + if (chNext == 'n') + { + m_pchToken[m_nTokenCharacters] = '\n'; + } + else if (chNext == '\\') + { + m_pchToken[m_nTokenCharacters] = '\\'; + } + else if (chNext == '"') + { + m_pchToken[m_nTokenCharacters] = '"'; + } + else if (chNext == 'x') + { + // This is bit of a hack; but we can just grab the next characters from + // the source data and return what we ate. + if (nScriptLength < 2) + return STRREF_CSCRIPTCOMPILER_ERROR_UNTERMINATED_STRING_CONSTANT; + const char hex[3] = { pScript[0], pScript[1], '\0'}; + char* ptr = nullptr; + // can never be >byte, we only parse two bytes + m_pchToken[m_nTokenCharacters++] = (char) strtol(hex, &ptr, 16); + return 3; // eat "xXX" + } + else + { + escapedChar = false; + } + + if (escapedChar) + { + ++m_nTokenCharacters; + if (m_nTokenCharacters >= CSCRIPTCOMPILER_MAX_TOKEN_LENGTH) + { + return STRREF_CSCRIPTCOMPILER_ERROR_TOKEN_TOO_LONG; + } + return 1; + } + } + else + { + m_pchToken[m_nTokenCharacters] = (char) ch; + ++m_nTokenCharacters; + if (m_nTokenCharacters >= CSCRIPTCOMPILER_MAX_TOKEN_LENGTH) + { + return STRREF_CSCRIPTCOMPILER_ERROR_TOKEN_TOO_LONG; + } + + return 0; + } + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + + return nReturnValue; +} + +int32_t CScriptCompiler::ParseRawStringCharacter(int32_t ch, int32_t chNext) +{ + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RAW_STRING) + { + if (ch == '"') + { + if (chNext == '"') + { + // "" in a raw string means single " + m_pchToken[m_nTokenCharacters++] = '"'; + if (m_nTokenCharacters >= CSCRIPTCOMPILER_MAX_TOKEN_LENGTH) + { + return STRREF_CSCRIPTCOMPILER_ERROR_TOKEN_TOO_LONG; + } + return 1; // consume 1 more + } + + // Otherwise, terminate string literal. + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_STRING; + return HandleToken(); + } + + m_pchToken[m_nTokenCharacters++] = (char) ch; + if (m_nTokenCharacters >= CSCRIPTCOMPILER_MAX_TOKEN_LENGTH) + { + return STRREF_CSCRIPTCOMPILER_ERROR_TOKEN_TOO_LONG; + } + + return 0; + } + + return STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterQuotationMark() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a quotation mark for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterQuotationMark() +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_STRING; + m_nTokenCharacters = 0; + nReturnValue = 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_STRING) + { + // Quotation Mark terminates the string. + nReturnValue = HandleToken(); + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterHyphen() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a hyphen for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterHyphen(int32_t chNext) +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + if (chNext == '=') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_MINUS; + int nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + nReturnValue = 1; + } + else if (chNext == '-') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_DECREMENT; + int nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + nReturnValue = 1; + } + else + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_MINUS; + nReturnValue = HandleToken(); + } + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterLeftBrace() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a left brace for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterLeftBrace() +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_LEFT_BRACE; + nReturnValue = HandleToken(); + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterRightBrace() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a right brace for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterRightBrace() +{ + int32_t nReturnValue = 0; + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_RIGHT_BRACE; + nReturnValue = HandleToken(); + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterLeftBracket() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a left bracket for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterLeftBracket() +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_LEFT_BRACKET; + nReturnValue = HandleToken(); + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterRightBracket() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a right bracket for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterRightBracket() +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET; + nReturnValue = HandleToken(); + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterLeftSquareBracket() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 10/16/2000 +// Description: Compiles a left bracket for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterLeftSquareBracket() +{ + int32_t nReturnValue = 0; + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_LEFT_SQUARE_BRACKET; + nReturnValue = HandleToken(); + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterRightSquareBracket() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 10/16/2000 +// Description: Compiles a left bracket for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterRightSquareBracket() +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_RIGHT_SQUARE_BRACKET; + nReturnValue = HandleToken(); + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterRightAngle() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a right angle for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterRightAngle(int32_t chNext) +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + if (chNext == '=') + { + // Token is >= + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_COND_GREATER_EQUAL; + int nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + nReturnValue = 1; + } + else if (chNext == '>') + { + // Token is at least >> ... so keep going. + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_SHIFT_RIGHT; + nReturnValue = 0; + } + else + { + // Token is > + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_COND_GREATER_THAN; + nReturnValue = HandleToken(); + } + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SHIFT_RIGHT) + { + + if (chNext == '>') + { + // Token is at least >>> ... so keep going. + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_UNSIGNED_SHIFT_RIGHT; + nReturnValue = 0; + } + else if (chNext == '=') + { + // Token is >>= + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_SHIFT_RIGHT; + int nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + nReturnValue = 1; + } + else + { + // Token is >> + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_SHIFT_RIGHT; + nReturnValue = HandleToken(); + } + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNSIGNED_SHIFT_RIGHT) + { + if (chNext == '=') + { + // Token is >>>= + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_USHIFT_RIGHT; + int nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + nReturnValue = 1; + } + else + { + // Token is >>> + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_UNSIGNED_SHIFT_RIGHT; + nReturnValue = HandleToken(); + } + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterLeftAngle() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a left angle for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterLeftAngle(int32_t chNext) +{ + int32_t nReturnValue = 0; + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + if (chNext == '=') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_COND_LESS_EQUAL; + int nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + nReturnValue = 1; + } + else if (chNext == '<') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_SHIFT_LEFT; + return 0; // DO NOT REMOVE THE NEXT CHARACTER OR CALL HANDLETOKEN(). + // That guarantees we visit the code later on in this function! + } + else + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_COND_LESS_THAN; + nReturnValue = HandleToken(); + } + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SHIFT_LEFT) + { + if (chNext == '=') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_SHIFT_LEFT; + int nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + nReturnValue = 1; + } + else + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_SHIFT_LEFT; + nReturnValue = HandleToken(); + } + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterExclamationPoint() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles an exclamation point for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterExclamationPoint(int32_t chNext) +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + if (chNext == '=') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_COND_NOT_EQUAL; + int nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + nReturnValue = 1; + } + else + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_BOOLEAN_NOT; + nReturnValue = HandleToken(); + } + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterEqualSign() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles an equal sign for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterEqualSign(int32_t chNext) +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + if (chNext == '=') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_COND_EQUAL; + int nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + nReturnValue = 1; + } + else + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_EQUAL; + nReturnValue = HandleToken(); + } + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterPlusSign() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles an plus sign for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterPlusSign(int32_t chNext) +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + if (chNext == '=') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_PLUS; + int nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + nReturnValue = 1; + } + else if (chNext == '+') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_INCREMENT; + int nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + nReturnValue = 1; + } + else + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_PLUS; + nReturnValue = HandleToken(); + } + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterPercentSign() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a percent sign for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterPercentSign(int32_t chNext) +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + if (chNext == '=') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_MODULUS; + int32_t nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + nReturnValue = 1; + } + else + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_MODULUS; + nReturnValue = HandleToken(); + } + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterSemicolon() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a semicolon for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterSemicolon() +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_SEMICOLON; + nReturnValue = HandleToken(); + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterCarat() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a carat for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterCarat(int32_t chNext) +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + if (chNext == '=') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_XOR; + int32_t nHandleReturn = HandleToken(); + if (nHandleReturn < 0) + { + return nHandleReturn; + } + return 1; + } + + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_EXCLUSIVE_OR; + nReturnValue = HandleToken(); + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterTilde() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 08/13/99 +// Description: Compiles a tilde for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterTilde() +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_TILDE; + nReturnValue = HandleToken(); + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterComma() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Compiles a comma for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterComma() +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_COMMA; + nReturnValue = HandleToken(); + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterEllipsis() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/14/2000 +// Description: Compiles an ellipsis for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterEllipsis() +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_IDENTIFIER; + m_nTokenCharacters = 0; + m_pchToken[m_nTokenCharacters] = '#'; + ++m_nTokenCharacters; + if (m_nTokenCharacters >= CSCRIPTCOMPILER_MAX_TOKEN_LENGTH) + { + nReturnValue = STRREF_CSCRIPTCOMPILER_ERROR_TOKEN_TOO_LONG; + } + else + { + nReturnValue = 0; + } + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterQuestionMark() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 02/23/2001 +// Description: Compiles a question mark for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterQuestionMark() +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_QUESTION_MARK; + nReturnValue = HandleToken(); + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseCharacterColon() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 02/23/2001 +// Description: Compiles a colon for use by tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseCharacterColon() +{ + int32_t nReturnValue = 0; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNKNOWN) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_COLON; + nReturnValue = HandleToken(); + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER; + nReturnValue = nError; + } + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::HandleToken() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Determines what to do with a "complete" token. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::HandleToken() +{ + // Do something with the token. + int32_t nReturnValue; + + if (m_bCompileIdentifierList == TRUE) + { + nReturnValue = GenerateIdentifierList(); + } + else + { + nReturnValue = GenerateParseTree(); + } + + if (m_nNextParseTreeFileName >= CSCRIPTCOMPILER_MAX_TABLE_FILENAMES) + { + nReturnValue = STRREF_CSCRIPTCOMPILER_ERROR_INCLUDE_TOO_MANY_LEVELS; + } + + if (nReturnValue < 0) + { + return nReturnValue; + } + + TokenInitialize(); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::TestIdentifierToken() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/09/2001 +// Description: Tests the token and determines what the correct state for +// the node is. (This was removed from HandleIdentifierToken() +// because we need to make use of this during the handling +// of global variable declarations). +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::TestIdentifierToken() +{ + + char cTemp = m_pchToken[m_nTokenCharacters]; + m_pchToken[m_nTokenCharacters] = 0; + int32_t nHashLocation = GetHashEntryByName(m_pchToken); + m_pchToken[m_nTokenCharacters] = cTemp; + + if (nHashLocation == STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER) + { + if (m_pchToken[0] == '#') + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_ELLIPSIS_IN_IDENTIFIER; + return nError; + } + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_VARIABLE; + return 0; + } + + int32_t nIdentifierType = m_pIdentifierHashTable[nHashLocation].m_nIdentifierType; + int32_t nIdentifierIndex = m_pIdentifierHashTable[nHashLocation].m_nIdentifierIndex; + + if (nIdentifierType == CSCRIPTCOMPILER_HASH_MANAGER_TYPE_IDENTIFIER) + { + if (m_pcIdentifierList[nIdentifierIndex].m_nIdentifierType == 1) + { + // We just need to set the right type, based on the "return type" of + // the identifier, to allow the code to compile this properly. + m_nTokenStatus = m_pcIdentifierList[nIdentifierIndex].m_nReturnType; + } + else + { + if (m_pcIdentifierList[nIdentifierIndex].m_nReturnType == CSCRIPTCOMPILER_TOKEN_INTEGER_IDENTIFIER) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_INTEGER; + } + else if (m_pcIdentifierList[nIdentifierIndex].m_nReturnType == CSCRIPTCOMPILER_TOKEN_FLOAT_IDENTIFIER) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_FLOAT; + } + else if (m_pcIdentifierList[nIdentifierIndex].m_nReturnType == CSCRIPTCOMPILER_TOKEN_STRING_IDENTIFIER) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_STRING; + } + // Copy from the "defined" constant to m_pcIdentifierList + int32_t nSize = m_pcIdentifierList[nIdentifierIndex].m_psStringData.GetLength(); + int32_t nCount2; + for (nCount2 = 0; nCount2 < nSize; nCount2++) + { + m_pchToken[nCount2] = ((m_pcIdentifierList[nIdentifierIndex].m_psStringData).CStr())[nCount2]; + } + m_nTokenCharacters = nSize; + } + } + else if (nIdentifierType == CSCRIPTCOMPILER_HASH_MANAGER_TYPE_KEYWORD) + { + m_nTokenStatus = m_pcKeyWords[nIdentifierIndex].GetTokenToTranslate(); + } + else // if (nIdentifierType == CSCRIPTCOMPILER_HASH_MANAGER_TYPE_ENGINE_STRUCTURE) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 + nIdentifierIndex; + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::HandleIdentifierToken() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Deals with identifier tokens. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::HandleIdentifierToken() +{ + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_IDENTIFIER) + { + int32_t nReturnValue = TestIdentifierToken(); + if (nReturnValue != 0) + { + return nReturnValue; + } + } + + return HandleToken(); +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseNextCharacter() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: With a "one-character look ahead" (chNext), determine what +// type of token is expressed by the character in ch and the +// character in chNext. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseNextCharacter(int32_t ch, int32_t chNext, char *pScript, int32_t nScriptLength) +{ + + if (ch == -1) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_EOF; + int nHandleTokenReturn = HandleToken(); + if (nHandleTokenReturn < 0) + { + return nHandleTokenReturn; + } + return 0; + } + + + // Deal with commented out code. + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_CPLUSCOMMENT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_CCOMMENT) + { + // Deal with next character. + return ParseCommentedOutCharacter(ch); + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_STRING) + { + return ParseStringCharacter(ch,chNext, pScript, nScriptLength); + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RAW_STRING) + { + return ParseRawStringCharacter(ch, chNext); + } + + if ((ch == 'r' || ch == 'R') && chNext == '"') + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_RAW_STRING; + m_nTokenCharacters = 0; + return 1; // consume r" + } + + // Handle the tokens associated with integer and real numbers. + // Note that we terminate the current token if we are currently + // building an integer or a real number, and we've received a non + // numeric token. This is VERY important. + + if (ch >= '0' && ch <= '9') + { + return ParseCharacterNumeric(ch); + } + else if (ch == '.') + { + return ParseCharacterPeriod(chNext); + } + else if ((ch == 'x' || ch == 'X') && + m_nTokenCharacters == 1 && + m_pchToken[0] == '0') + { + return ParseCharacterAlphabet(ch); + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_INTEGER || m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_FLOAT) + { + int32_t nCompiledCharacters = 0; + // Compile all suffixes that make sense. + if (ch == 'f') + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_INTEGER) + { + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_FLOAT; + } + nCompiledCharacters = 1; + } + + int nReturnValue = HandleToken(); + if (nReturnValue < 0) + { + return nReturnValue; + } + else if (nCompiledCharacters > 0) + { + return nCompiledCharacters - 1; + } + } + + // For similar reasons, we deal with alphabetic characters and then + // we deal with the abrupt termination of a letter. + + if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_') + { + return ParseCharacterAlphabet(ch); + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_IDENTIFIER) + { + int nReturnValue = HandleIdentifierToken(); + if (nReturnValue < 0) + { + return nReturnValue; + } + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_HEX_INTEGER) + { + int nReturnValue = HandleToken(); + if (nReturnValue < 0) + { + return nReturnValue; + } + } + + if (ch == '/') + { + return ParseCharacterSlash(chNext); + } + else if (ch == '*') + { + return ParseCharacterAsterisk(chNext); + } + else if (ch == '&') + { + return ParseCharacterAmpersand(chNext); + } + else if (ch == '|') + { + return ParseCharacterVerticalBar(chNext); + } + + // Toss out blank space. + if (ch == ' ' || ch == '\n' || ch == '\t') + { + return 0; + } + + if (ch == '"') + { + return ParseCharacterQuotationMark(); + } + if (ch == '-') + { + return ParseCharacterHyphen(chNext); + } + if (ch == '{') + { + return ParseCharacterLeftBrace(); + } + if (ch == '}') + { + return ParseCharacterRightBrace(); + } + if (ch == '(') + { + return ParseCharacterLeftBracket(); + } + if (ch == ')') + { + return ParseCharacterRightBracket(); + } + + if (ch == '[') + { + return ParseCharacterLeftSquareBracket(); + } + if (ch == ']') + { + return ParseCharacterRightSquareBracket(); + } + + if (ch == '<') + { + return ParseCharacterLeftAngle(chNext); + } + if (ch == '>') + { + return ParseCharacterRightAngle(chNext); + } + if (ch == '!') + { + return ParseCharacterExclamationPoint(chNext); + } + if (ch == '=') + { + return ParseCharacterEqualSign(chNext); + } + if (ch == '+') + { + return ParseCharacterPlusSign(chNext); + } + if (ch == '%') + { + return ParseCharacterPercentSign(chNext); + } + if (ch == ';') + { + return ParseCharacterSemicolon(); + } + if (ch == ',') + { + return ParseCharacterComma(); + } + if (ch == '^') + { + return ParseCharacterCarat(chNext); + } + if (ch == '~') + { + return ParseCharacterTilde(); + } + if (ch == '#') + { + return ParseCharacterEllipsis(); + } + if (ch == '?') + { + return ParseCharacterQuestionMark(); + } + if (ch == ':') + { + return ParseCharacterColon(); + } + + return 0; +} diff --git a/src/Native Compiler/scriptcompparsetree.cpp b/src/Native Compiler/scriptcompparsetree.cpp new file mode 100644 index 0000000..3000e70 --- /dev/null +++ b/src/Native Compiler/scriptcompparsetree.cpp @@ -0,0 +1,4609 @@ +// +// SPDX-License-Identifier: GPL-3.0 +// +// This file is part of the NWScript compiler open source release. +// +// The initial source release is licensed under GPL-3.0. +// +// All subsequent changes you submit are required to be licensed under MIT. +// +// However, the project overall will still be GPL-3.0. +// +// The intent is for the base game to be able to pick up changes you explicitly +// submit for inclusion painlessly, while ensuring the overall project source code +// remains available for everyone. +// + +/////////////////////////////////////////////////////////////////////////////// +// BIOWARE CORP. CONFIDENTIAL INFORMATION. // +// COPYRIGHT BIOWARE CORP. ALL RIGHTS RESERVED // +/////////////////////////////////////////////////////////////////////////////// + +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Script Project +//:: +//:: Copyright (c) 2002, BioWare Corp. +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: ScriptCompParseTree.cpp +//:: +//:: Implementation of conversion from scripting language source to a parse +//:: tree that we can use to generate final code. Because this includes the +//:: lexical analysis of tokens, the code to parse the identifier file is +//:: also included within this file. +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Created By: Mark Brockington +//:: Created On: Oct. 8, 2002 +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: This file contains "ported" code (used when writing out floating point +//:: numbers on the MacIntosh platforms). +//:: +//:://///////////////////////////////////////////////////////////////////////// + +#include +#include + +// external header files +#include "exobase.h" +#include "scriptcomp.h" + +// internal header files +#include "scriptinternal.h" + + +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Class CScriptCompiler +//:: +//:://///////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::TokenInitialize() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Sets up parsing routine to be ready to accept a new token. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::TokenInitialize() +{ + m_nTokenStatus = 0; // TOKEN_UNKNOWN; + m_nTokenCharacters = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::PushSRStack() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Code to place another context on to the Shift-Reduce stack. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::PushSRStack(int32_t nState, int32_t nRule, int32_t nTerm, + CScriptParseTreeNode *pCurrentTree) +{ + if (m_nSRStackStates+1 >= m_nSRStackEntries) + { + CScriptCompilerStackEntry *pNewSRStack = new CScriptCompilerStackEntry[m_nSRStackEntries * 2]; + for (int nCount = 0; nCount < m_nSRStackEntries; nCount++) + { + pNewSRStack[nCount].nState = m_pSRStack[nCount].nState; + pNewSRStack[nCount].nRule = m_pSRStack[nCount].nRule; + pNewSRStack[nCount].nTerm = m_pSRStack[nCount].nTerm; + pNewSRStack[nCount].pCurrentTree = m_pSRStack[nCount].pCurrentTree; + pNewSRStack[nCount].pReturnTree = m_pSRStack[nCount].pReturnTree; + } + m_nSRStackEntries *= 2; + delete[] m_pSRStack; + m_pSRStack = pNewSRStack; + } + + ++m_nSRStackStates; + m_pSRStack[m_nSRStackStates].nState = nState; + m_pSRStack[m_nSRStackStates].nRule = nRule; + m_pSRStack[m_nSRStackStates].nTerm = nTerm; + m_pSRStack[m_nSRStackStates].pCurrentTree = pCurrentTree; + m_pSRStack[m_nSRStackStates].pReturnTree = NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::PopSRStack() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Code to remove a context from the Shift-Reduce stack. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::PopSRStack(int32_t *nState, int32_t *nRule, int32_t *nTerm, + CScriptParseTreeNode **pCurrentTree, + CScriptParseTreeNode **pReturnTree ) +{ + + if (m_nSRStackStates < 0) + { + return FALSE; + } + + *nState = m_pSRStack[m_nSRStackStates].nState; + *nRule = m_pSRStack[m_nSRStackStates].nRule; + *nTerm = m_pSRStack[m_nSRStackStates].nTerm; + *pCurrentTree = m_pSRStack[m_nSRStackStates].pCurrentTree; + *pReturnTree = m_pSRStack[m_nSRStackStates].pReturnTree; + --m_nSRStackStates; + + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ModifySRStackReturnTree() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: Code to hand a compile tree pointer to the next stack pointer +// level. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::ModifySRStackReturnTree( + CScriptParseTreeNode *pReturnTree ) +{ + if (m_nSRStackStates >= 0) + { + m_pSRStack[m_nSRStackStates].pReturnTree = pReturnTree; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::GetNewScriptParseTreeNode() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: Nov. 22, 2002 +// Description: Wrapper to create a ScriptParseTreeNode from the blocks that +// we have potentially already created +/////////////////////////////////////////////////////////////////////////////// +CScriptParseTreeNode *CScriptCompiler::GetNewScriptParseTreeNode() +{ + CScriptParseTreeNode *pParseTreeNode; + // New Version + + // If we are at the end of the linked list of node blocks, and we don't + // have any room left in this block (or the block doesn't exist!), + // we should create a new block to use. + if (m_nParseTreeNodeBlockEmptyNodes == -1 && + (m_pCurrentParseTreeNodeBlock == NULL || m_pCurrentParseTreeNodeBlock->m_pNextBlock == NULL)) + { + m_pCurrentParseTreeNodeBlock = new CScriptParseTreeNodeBlock; + m_nParseTreeNodeBlockEmptyNodes = CSCRIPTCOMPILER_PARSETREENODEBLOCK_SIZE - 1; + + // Chain it on to the list of currently allocated blocks. + if (m_pParseTreeNodeBlockTail == NULL) + { + m_pParseTreeNodeBlockHead = m_pCurrentParseTreeNodeBlock; + m_pParseTreeNodeBlockTail = m_pCurrentParseTreeNodeBlock; + } + else + { + m_pParseTreeNodeBlockTail->m_pNextBlock = m_pCurrentParseTreeNodeBlock; + m_pParseTreeNodeBlockTail = m_pCurrentParseTreeNodeBlock; + } + } + + // We are guaranteed that we have a block that exists with nodes in it + // or we have a block that is full that has a connected block that has + // nodes in it! + + if (m_nParseTreeNodeBlockEmptyNodes >= 0) + { + // This block has spots in it ... so we do nothing but let it + // fall through and use the blank spot! + } + else + { + // So our current block doesn't have spots; that's fine ... the next + // one is guaranteed to have spots in it! + m_pCurrentParseTreeNodeBlock = m_pCurrentParseTreeNodeBlock->m_pNextBlock; + m_pCurrentParseTreeNodeBlock->CleanBlockEntries(); + m_nParseTreeNodeBlockEmptyNodes = CSCRIPTCOMPILER_PARSETREENODEBLOCK_SIZE - 1; + } + + // ... and finally, return the connected node! + pParseTreeNode = &(m_pCurrentParseTreeNodeBlock->m_pNodes[m_nParseTreeNodeBlockEmptyNodes]); + --m_nParseTreeNodeBlockEmptyNodes; + + return pParseTreeNode; + + // Old Version + //return new CScriptParseTreeNode(); +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::DeleteScriptParseTreeNode() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: Nov. 22, 2002 +// Description: Wrapper to "deallocate" a CScriptParseTreeNode. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::DeleteScriptParseTreeNode(CScriptParseTreeNode *pParseTreeNode) +{ + // MGB - March 21, 2003 - Big bug in the handling of compiled scripts. + pParseTreeNode->Clean(); + // MGB - March 21, 2003 - End Change. + + // DO NOT DEALLOCATE MEMORY, FOR THE LOVE OF PETE. Let the blocks + // delete themselves. + pParseTreeNode = NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::CreateScriptParseTreeNode() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 02/18/2001 +// Description: Wrapper to create a ScriptParseTreeNode quickly without +// having to change the code in a hundred places. +/////////////////////////////////////////////////////////////////////////////// + +CScriptParseTreeNode *CScriptCompiler::CreateScriptParseTreeNode( + int32_t nNodeOperation, + CScriptParseTreeNode *pNodeLeft, + CScriptParseTreeNode *pNodeRight) +{ + CScriptParseTreeNode *pNewNode = GetNewScriptParseTreeNode(); + pNewNode->nOperation = nNodeOperation; + pNewNode->pLeft = pNodeLeft; + pNewNode->pRight = pNodeRight; + pNewNode->nLine = m_nLines; + pNewNode->nChar = m_nCharacterOnLine; + + if (m_nCompileFileLevel >= 1) + { + if (m_nNextParseTreeFileName != 0) + { + // Check the current entry. + if (m_nCurrentParseTreeFileName >= 0 && + m_nCurrentParseTreeFileName < m_nNextParseTreeFileName) + { + if (*(m_ppsParseTreeFileNames[m_nCurrentParseTreeFileName]) == m_pcIncludeFileStack[m_nCompileFileLevel-1].m_sCompiledScriptName) + { + pNewNode->m_nFileReference = m_nCurrentParseTreeFileName; + return pNewNode; + } + } + + // It's not the current entry, search through all defined values. + int32_t nCount; + for (nCount = 0; nCount < m_nNextParseTreeFileName; nCount++) + { + if (m_ppsParseTreeFileNames[nCount]->CompareNoCase(m_pcIncludeFileStack[m_nCompileFileLevel-1].m_sCompiledScriptName) == TRUE) + { + m_nCurrentParseTreeFileName = nCount; + pNewNode->m_nFileReference = nCount; + return pNewNode; + } + } + } + + // Can't find it in the currently defined values ... make a new one. + int32_t nNewEntry = m_nNextParseTreeFileName; + //(m_nNextParseTreeFileName is 0 ... add the first entry. + pNewNode->m_nFileReference = nNewEntry; + m_ppsParseTreeFileNames[nNewEntry] = new CExoString(m_pcIncludeFileStack[m_nCompileFileLevel-1].m_sCompiledScriptName.CStr()); + ++m_nNextParseTreeFileName; + m_nCurrentParseTreeFileName = nNewEntry; + } + + return pNewNode; + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::DuplicateScriptParseTree() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 02/20/2001 +// Description: Duplicates a parse tree.for use in an assignment. +/////////////////////////////////////////////////////////////////////////////// + +CScriptParseTreeNode *CScriptCompiler::DuplicateScriptParseTree(CScriptParseTreeNode *pNode) +{ + if (pNode == NULL) + { + return NULL; + } + + CScriptParseTreeNode *pNewNode = GetNewScriptParseTreeNode(); + + pNewNode->nOperation = pNode->nOperation; + pNewNode->nIntegerData = pNode->nIntegerData; + pNewNode->nIntegerData2 = pNode->nIntegerData2; + pNewNode->fFloatData = pNode->fFloatData; + pNewNode->fVectorData[0] = pNode->fVectorData[0]; + pNewNode->fVectorData[1] = pNode->fVectorData[1]; + pNewNode->fVectorData[2] = pNode->fVectorData[2]; + pNewNode->nLine = pNode->nLine; + pNewNode->nChar = pNode->nChar; + pNewNode->nType = pNode->nType; + pNewNode->m_nStackPointer= pNode->m_nStackPointer; + pNewNode->m_nFileReference = pNode->m_nFileReference; + + if (pNode->m_psStringData != NULL) + { + pNewNode->m_psStringData = new CExoString(pNode->m_psStringData->CStr()); + } + + + if (pNode->m_psTypeName != NULL) + { + pNewNode->m_psTypeName = new CExoString(pNode->m_psTypeName->CStr()); + } + + pNewNode->pLeft = DuplicateScriptParseTree(pNode->pLeft); + pNewNode->pRight = DuplicateScriptParseTree(pNode->pRight); + + return pNewNode; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::CheckForBadLValue() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 02/20/2001 +// Description: Checks a branch of a tree to determine if this is a +// suitable LValue for an assignment statement. +/////////////////////////////////////////////////////////////////////////////// + +BOOL CScriptCompiler::CheckForBadLValue(CScriptParseTreeNode *pNode) +{ + BOOL bBadLValue = TRUE; + if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_VARIABLE) + { + bBadLValue = FALSE; + } + else if (pNode->nOperation == CSCRIPTCOMPILER_OPERATION_STRUCTURE_PART) + { + CScriptParseTreeNode *pTraceBranch = pNode->pLeft; + while (pTraceBranch != NULL && bBadLValue == TRUE) + { + if (pTraceBranch->nOperation == CSCRIPTCOMPILER_OPERATION_VARIABLE) + { + bBadLValue = FALSE; + } + pTraceBranch = pTraceBranch->pLeft; + } + } + + return bBadLValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::GenerateParseTree() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 07/22/99 +// Description: The main guts of the first pass of the parsing routine. +// This routine takes a token and determines how it should be +// connected to the rest of the tree. This is all done based +// on the 32 case grammar that was determined earlier. Each +// part of the 32-case grammar is listed beside the case +// statement, and this will hopefully make the system as clear +// as mud. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::GenerateParseTree() +{ + int32_t nTopStackState; + int32_t nTopStackRule; + int32_t nTopStackTerm; + CScriptParseTreeNode *pTopStackCurrentNode; + CScriptParseTreeNode *pTopStackReturnNode; + + BOOL bAlwaysTrue = TRUE; + + while (bAlwaysTrue) + { + // Remove the topmost state from the stack. + if (PopSRStack(&nTopStackState, &nTopStackRule, + &nTopStackTerm, &pTopStackCurrentNode, + &pTopStackReturnNode) == FALSE) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_FATAL_COMPILER_ERROR; + return nError; + } + + + // Now, the giant case statement. Based on the State, Rule, Term and Token, + // we either create some new nodes, link the existing nodes to the new nodes, + // push some states on to the stack, install a "return" tree on to the stack + // et cetera. + + switch (nTopStackState) + { + + /////////////////////////////////////////////////////////////////////////////// + // case 35: + // constant: + // (1) integer-constant + // (2) string-constant + // (3) floating-point-constant + // (4) OBJECT_SELF + // (5) OBJECT_INVALID + // (6) [ {{{float_opt} , float_opt} , float_opt} ] (for declaring a vector). + // (7) JSON_* for declaring json default ctor constants. + /////////////////////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_CONSTANT: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + int32_t nCount; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_SQUARE_BRACKET) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONSTANT_VECTOR,NULL,NULL); + pNewNode->fVectorData[0] = 0.0f; + pNewNode->fVectorData[1] = 0.0f; + pNewNode->fVectorData[2] = 0.0f; + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_CONSTANT,6,2,pNewNode); + return 0; + } + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_INTEGER) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER,NULL,NULL); + pNewNode->nIntegerData = 0; + int32_t nSign = 1; + for (nCount = 0; nCount < m_nTokenCharacters; nCount++) + { + // Account for a negative constant from the language definition file. + if (m_pchToken[nCount] == '-' && nCount == 0) + { + nSign = -1; + } + else + { + pNewNode->nIntegerData *= 10; + pNewNode->nIntegerData += (m_pchToken[nCount] - '0'); + } + } + // Apply the negative of the integer, if required. + if (nSign == -1) + { + pNewNode->nIntegerData *= nSign; + } + ModifySRStackReturnTree(pNewNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_HEX_INTEGER) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER,NULL,NULL); + m_pchToken[m_nTokenCharacters] = 0; + pNewNode->nIntegerData = 0; + for (nCount = 2; nCount < m_nTokenCharacters; nCount++) + { + if (m_pchToken[nCount] >= '0' && + m_pchToken[nCount] <= '9') + { + pNewNode->nIntegerData *= 16; + pNewNode->nIntegerData += (m_pchToken[nCount] - '0'); + } + else + { + pNewNode->nIntegerData *= 16; + pNewNode->nIntegerData += 10 + (m_pchToken[nCount] - 'a'); + } + } + ModifySRStackReturnTree(pNewNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_FLOAT) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONSTANT_FLOAT,NULL,NULL); + pNewNode->fFloatData = ParseFloatFromTokenString(); + + ModifySRStackReturnTree(pNewNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_STRING) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONSTANT_STRING,NULL,NULL); + m_pchToken[m_nTokenCharacters] = 0; + pNewNode->m_psStringData = new CExoString(m_pchToken); + ModifySRStackReturnTree(pNewNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT_SELF) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONSTANT_OBJECT,NULL,NULL); + // ??? m_pchToken[m_nTokenCharacters] = 0; + pNewNode->nIntegerData = 0; + ModifySRStackReturnTree(pNewNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT_INVALID) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONSTANT_OBJECT,NULL,NULL); + // ??? m_pchToken[m_nTokenCharacters] = 0; + pNewNode->nIntegerData = 1; + ModifySRStackReturnTree(pNewNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_LOCATION_INVALID) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONSTANT_LOCATION,NULL,NULL); + // ??? m_pchToken[m_nTokenCharacters] = 0; + pNewNode->nIntegerData = 0; + ModifySRStackReturnTree(pNewNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_NULL || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_FALSE || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_TRUE || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_OBJECT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_ARRAY || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_STRING) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONSTANT_JSON,NULL,NULL); + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_NULL) + pNewNode->m_psStringData = new CExoString("null"); + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_FALSE) + pNewNode->m_psStringData = new CExoString("false"); + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_TRUE) + pNewNode->m_psStringData = new CExoString("true"); + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_OBJECT) + pNewNode->m_psStringData = new CExoString("{}"); + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_ARRAY) + pNewNode->m_psStringData = new CExoString("[]"); + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_STRING) + pNewNode->m_psStringData = new CExoString("\"\""); + else + EXOASSERTNCSTR("missing impl"); + + ModifySRStackReturnTree(pNewNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER; + return nError; + } + } + if (nTopStackRule == 6 && nTopStackTerm == 2) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_SQUARE_BRACKET) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_FLOAT) + { + pTopStackCurrentNode->fVectorData[0] = ParseFloatFromTokenString(); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_CONSTANT,6,3,pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_CONSTANT_VECTOR; + return nError; + } + } + if (nTopStackRule == 6 && nTopStackTerm == 3) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_SQUARE_BRACKET) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_COMMA) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_CONSTANT,6,4,pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_CONSTANT_VECTOR; + return nError; + } + } + if (nTopStackRule == 6 && nTopStackTerm == 4) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_FLOAT) + { + pTopStackCurrentNode->fVectorData[1] = ParseFloatFromTokenString(); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_CONSTANT,6,5,pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_CONSTANT_VECTOR; + return nError; + } + } + if (nTopStackRule == 6 && nTopStackTerm == 5) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_SQUARE_BRACKET) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_COMMA) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_CONSTANT,6,6,pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_CONSTANT_VECTOR; + return nError; + } + } + if (nTopStackRule == 6 && nTopStackTerm == 6) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_FLOAT) + { + pTopStackCurrentNode->fVectorData[2] = ParseFloatFromTokenString(); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_CONSTANT,6,7,pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_CONSTANT_VECTOR; + return nError; + } + } + if (nTopStackRule == 6 && nTopStackTerm == 7) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_SQUARE_BRACKET) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_CONSTANT_VECTOR; + return nError; + } + } + + break; + + ////////////////////////////////////////////////////////// + // case 34: + // primary-expression: + // (1) non-void-identifier ( argument-expression-list ) + // (2) constant + // (3) ( expression ) + // (4) variable + // (5) void-identifier ( argument-expression-list ) + // treated as an action argument ONLY! + ///////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_PRIMARY_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_INTEGER || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_HEX_INTEGER || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_FLOAT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_STRING || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT_SELF || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT_INVALID || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_LOCATION_INVALID || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_NULL || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_FALSE || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_TRUE || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_OBJECT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_ARRAY || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_STRING || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_SQUARE_BRACKET) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PRIMARY_EXPRESSION,2,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_CONSTANT,0,0,NULL); + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_VARIABLE) + { + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_VARIABLE,NULL,NULL); + m_pchToken[m_nTokenCharacters] = 0; + pNewNode2->m_psStringData = new CExoString(m_pchToken); + ModifySRStackReturnTree(pNewNode2); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_BRACKET) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PRIMARY_EXPRESSION,3,2,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_EXPRESSION,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_INTEGER_IDENTIFIER || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_FLOAT_IDENTIFIER || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_STRING_IDENTIFIER || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_VOID_IDENTIFIER || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_OBJECT_IDENTIFIER || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_STRUCTURE_IDENTIFIER || + (m_nTokenStatus >= CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE0_IDENTIFIER && + m_nTokenStatus <= CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE9_IDENTIFIER)) + { + // Do somethin' with the identifier. + + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_ACTION_ID,NULL,NULL); + m_pchToken[m_nTokenCharacters] = 0; + pNewNode2->m_psStringData = new CExoString(m_pchToken); + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_ACTION,NULL,pNewNode2); + + if ( m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_VOID_IDENTIFIER ) + { + CScriptParseTreeNode *pNewNode3 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_ACTION_PARAMETER,pNewNode,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PRIMARY_EXPRESSION,5,2,pNewNode3); + } + else + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PRIMARY_EXPRESSION,1,2,pNewNode); + } + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER; + return nError; + } + } + if (nTopStackRule == 1 && nTopStackTerm == 2) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_BRACKET) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PRIMARY_EXPRESSION,1,3,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ARGUMENT_EXPRESSION_LIST,0,0,NULL); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_LEFT_BRACKET_ON_ARG_LIST; + return nError; + } + } + if (nTopStackRule == 1 && nTopStackTerm == 3) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET) + { + pTopStackCurrentNode->pLeft = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_RIGHT_BRACKET_ON_ARG_LIST; + return nError; + } + } + if (nTopStackRule == 5 && nTopStackTerm == 2) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_BRACKET) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PRIMARY_EXPRESSION,5,3,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ARGUMENT_EXPRESSION_LIST,0,0,NULL); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_LEFT_BRACKET_ON_ARG_LIST; + return nError; + } + } + if (nTopStackRule == 5 && nTopStackTerm == 3) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET) + { + pTopStackCurrentNode->pLeft->pLeft = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_RIGHT_BRACKET_ON_ARG_LIST; + return nError; + } + } + if (nTopStackRule == 2 && nTopStackTerm == 1) + { + ModifySRStackReturnTree(pTopStackReturnNode); + } + if (nTopStackRule == 3 && nTopStackTerm == 2) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET) + { + ModifySRStackReturnTree(pTopStackReturnNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER; + return nError; + } + } + break; + + /////////////////////////////////////////// + // case 33: + // post-expression: + // (1) primary-expression + // (2) primary-expression . variable + // (3) primary_expression ++ + // (4) primary_expression -- + ////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_POST_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_POST_EXPRESSION,1,2,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PRIMARY_EXPRESSION,0,0,NULL); + } + if ((nTopStackRule == 1 && nTopStackTerm == 2) || + (nTopStackTerm == 4)) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_STRUCTURE_PART_SPECIFY) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STRUCTURE_PART,NULL,NULL); + if (nTopStackTerm == 4) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + else if (pTopStackCurrentNode != NULL && pTopStackReturnNode == NULL) + { + // This came in when we realized there was a structure part in + // the variable specified in an assignment statement. For the + // love of god, this is getting complicated. + pNewNode->pLeft = pTopStackCurrentNode; + } + else + { + // The standard method of receiving a structure part ... from + // a FREAKIN' return node. (Is that *SO* wrong?) + pNewNode->pLeft = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_POST_EXPRESSION,2,4,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_POST_EXPRESSION,2,3,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_INCREMENT) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_POST_INCREMENT,NULL,NULL); + if (nTopStackTerm == 4) + { + // Adding a post increment to a structure part. + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + else + { + // Adding a post increment to a standalone variable. + pNewNode->pLeft = pTopStackReturnNode; + } + ModifySRStackReturnTree(pNewNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_DECREMENT) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_POST_DECREMENT,NULL,NULL); + if (nTopStackTerm == 4) + { + // Adding a post increment to a structure part. + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + else + { + // Adding a post increment to a standalone variable. + pNewNode->pLeft = pTopStackReturnNode; + } + ModifySRStackReturnTree(pNewNode); + return 0; + } + else + { + // It's not one of our magic three operators? + // Well, then! That doesn't bode well. We have + // to report what we've constructed up the stack. + + if (nTopStackTerm == 4) + { + // We are processing a structure part. + // We have a variable hanging around in pTopStackReturnNode. + // This should be reattached. + pTopStackCurrentNode->pRight = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + else + { + // This is just a standard primary-expression, so + // let's just send it up the stack. + ModifySRStackReturnTree(pTopStackReturnNode); + } + } + } + if (nTopStackRule == 2 && nTopStackTerm == 3) + { + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_VARIABLE,NULL,NULL); + m_pchToken[m_nTokenCharacters] = 0; + pNewNode2->m_psStringData = new CExoString(m_pchToken); + ModifySRStackReturnTree(pNewNode2); + return 0; + } + + break; + + //////////////////////////////////// + // case 32: + // unary-expression: + // (1) - post-expression + // (2) ~ post-expression + // (3) ! post-expression + // (4) ++ post-expression + // (5) -- post_expression + // (6) post-expression + //////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_UNARY_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_MINUS) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_NEGATION,NULL,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_UNARY_EXPRESSION,1,1,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_POST_EXPRESSION,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_TILDE) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_ONES_COMPLEMENT,NULL,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_UNARY_EXPRESSION,2,1,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_POST_EXPRESSION,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_BOOLEAN_NOT) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_BOOLEAN_NOT,NULL,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_UNARY_EXPRESSION,3,1,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_POST_EXPRESSION,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_INCREMENT) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_PRE_INCREMENT,NULL,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_UNARY_EXPRESSION,4,1,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_POST_EXPRESSION,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_DECREMENT) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_PRE_DECREMENT,NULL,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_UNARY_EXPRESSION,5,1,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_POST_EXPRESSION,0,0,NULL); + return 0; + } + else + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_UNARY_EXPRESSION,6,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_POST_EXPRESSION,0,0,NULL); + } + } + if ((nTopStackRule >= 1 && nTopStackRule <= 5) && nTopStackTerm == 1) + { + pTopStackCurrentNode->pLeft = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + if (nTopStackRule == 6 && nTopStackTerm == 1) + { + ModifySRStackReturnTree(pTopStackReturnNode); + } + break; + + ////////////////////////////////////////////////////// + // case 31: + // multiplicative-expression + // (1) unary-expression + // (2) multiplicative-expression * unary-expression + // (3) multiplicative-expression / unary-expression + // (4) multiplicative-expression % unary-expression + ////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_MULTIPLICATIVE_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_MULTIPLICATIVE_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_UNARY_EXPRESSION,0,0,NULL); + } + if ((nTopStackRule == 1 && nTopStackTerm == 1) || + (nTopStackTerm == 3)) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_MULTIPLY) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_MULTIPLY,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_MULTIPLICATIVE_EXPRESSION,2,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_UNARY_EXPRESSION,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_DIVIDE) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_DIVIDE,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_MULTIPLICATIVE_EXPRESSION,3,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_UNARY_EXPRESSION,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_MODULUS) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_MODULUS,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_MULTIPLICATIVE_EXPRESSION,4,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_UNARY_EXPRESSION,0,0,NULL); + return 0; + } + else + { + if (nTopStackTerm == 3) + { + pTopStackCurrentNode->pRight = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + else + { + ModifySRStackReturnTree(pTopStackReturnNode); + } + } + } + break; + + ////////////////////////////////////////////////////////// + // case 30: + // additive-expression + // (1) multiplicative-expression + // (2) additive-expression + multiplicative-expression + // (3) additive-expression - multiplicative-expression + ////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_ADDITIVE_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ADDITIVE_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_MULTIPLICATIVE_EXPRESSION,0,0,NULL); + } + if ((nTopStackRule == 1 && nTopStackTerm == 1) || + (nTopStackTerm == 3)) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_PLUS) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_ADD,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ADDITIVE_EXPRESSION,2,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_MULTIPLICATIVE_EXPRESSION,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_MINUS) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_SUBTRACT,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ADDITIVE_EXPRESSION,3,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_MULTIPLICATIVE_EXPRESSION,0,0,NULL); + return 0; + } + else + { + if (nTopStackTerm == 3) + { + pTopStackCurrentNode->pRight = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + else + { + ModifySRStackReturnTree(pTopStackReturnNode); + } + } + } + break; + + //////////////////////////////////////////////////// + // case 29: + // shift-expression + // (1) additive-expression + // (2) shift-expression >> additive-expression + // (3) shift-expression << additive-expression + // (4) shift-expression >>> additive-expression + //////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_SHIFT_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_SHIFT_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ADDITIVE_EXPRESSION,0,0,NULL); + } + if ((nTopStackRule == 1 && nTopStackTerm == 1) || + (nTopStackTerm == 3)) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SHIFT_LEFT) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_SHIFT_LEFT,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_SHIFT_EXPRESSION,2,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ADDITIVE_EXPRESSION,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SHIFT_RIGHT) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_SHIFT_RIGHT,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_SHIFT_EXPRESSION,3,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ADDITIVE_EXPRESSION,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_UNSIGNED_SHIFT_RIGHT) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_UNSIGNED_SHIFT_RIGHT,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_SHIFT_EXPRESSION,4,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ADDITIVE_EXPRESSION,0,0,NULL); + return 0; + } + else + { + if (nTopStackTerm == 3) + { + pTopStackCurrentNode->pRight = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + else + { + ModifySRStackReturnTree(pTopStackReturnNode); + } + } + } + break; + + //////////////////////////////////////////////////// + // case 28: + // relational-expression: + // (1) shift-expression + // (2) relational-expression >= shift-expression + // (3) relational-expression > shift-expression + // (4) relational-expression < shift-expression + // (5) relational-expression <= shift-expression + //////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_RELATIONAL_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_RELATIONAL_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_SHIFT_EXPRESSION,0,0,NULL); + } + if ((nTopStackRule == 1 && nTopStackTerm == 1) || + (nTopStackTerm == 3)) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_COND_GREATER_EQUAL) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONDITION_GEQ,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_RELATIONAL_EXPRESSION,2,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_SHIFT_EXPRESSION,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_COND_LESS_EQUAL) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONDITION_LEQ,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_RELATIONAL_EXPRESSION,3,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_SHIFT_EXPRESSION,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_COND_LESS_THAN) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONDITION_LT,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_RELATIONAL_EXPRESSION,4,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_SHIFT_EXPRESSION,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_COND_GREATER_THAN) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONDITION_GT,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_RELATIONAL_EXPRESSION,5,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_SHIFT_EXPRESSION,0,0,NULL); + return 0; + } + else + { + if (nTopStackTerm == 3) + { + pTopStackCurrentNode->pRight = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + else + { + ModifySRStackReturnTree(pTopStackReturnNode); + } + } + } + break; + + ///////////////////////////////////////////////////////// + // case 27: + // equality-expression + // (1) relational-expression + // (2) equality-expression == relational-expression + // (3) equality-expression != relational-expression + ///////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_EQUALITY_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_EQUALITY_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_RELATIONAL_EXPRESSION,0,0,NULL); + } + if ((nTopStackRule == 1 && nTopStackTerm == 1) || + (nTopStackTerm == 3)) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_COND_NOT_EQUAL) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONDITION_NOT_EQUAL,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_EQUALITY_EXPRESSION,2,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_RELATIONAL_EXPRESSION,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_COND_EQUAL) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONDITION_EQUAL,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_EQUALITY_EXPRESSION,3,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_RELATIONAL_EXPRESSION,0,0,NULL); + return 0; + } + else + { + if (nTopStackTerm == 3) + { + pTopStackCurrentNode->pRight = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + else + { + ModifySRStackReturnTree(pTopStackReturnNode); + } + } + } + break; + + //////////////////////////////////////////////////////// + // case 26: + // boolean-AND-expression + // (1) equality-expression + // (2) boolean-AND-expression & equailty-expression + //////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_BOOLEAN_AND_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_BOOLEAN_AND_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_EQUALITY_EXPRESSION,0,0,NULL); + } + if ((nTopStackRule == 1 && nTopStackTerm == 1) || + (nTopStackTerm == 3)) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_BOOLEAN_AND) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_BOOLEAN_AND,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_BOOLEAN_AND_EXPRESSION,2,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_EQUALITY_EXPRESSION,0,0,NULL); + return 0; + } + else + { + if (nTopStackTerm == 3) + { + pTopStackCurrentNode->pRight = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + else + { + ModifySRStackReturnTree(pTopStackReturnNode); + } + } + } + break; + + ////////////////////////////////////////////////////////// + // case 25: + // exclusive-OR-expression + // (1) boolean-AND-expression + // (2) exclusive-OR-expression ^ boolean-AND-expression + ////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_EXCLUSIVE_OR_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_EXCLUSIVE_OR_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_BOOLEAN_AND_EXPRESSION,0,0,NULL); + } + if ((nTopStackRule == 1 && nTopStackTerm == 1) || + (nTopStackTerm == 3)) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_EXCLUSIVE_OR) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_EXCLUSIVE_OR,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_EXCLUSIVE_OR_EXPRESSION,2,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_BOOLEAN_AND_EXPRESSION,0,0,NULL); + return 0; + } + else + { + if (nTopStackTerm == 3) + { + pTopStackCurrentNode->pRight = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + else + { + ModifySRStackReturnTree(pTopStackReturnNode); + } + } + } + break; + + ///////////////////////////////////////////////////////////////////////////// + // case 24: + // inclusive-OR-expression: + // (1) exclusive-OR-expression + // (2) inclusive-OR-expression | exclusive-OR-expression + ///////////////////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_INCLUSIVE_OR_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_INCLUSIVE_OR_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_EXCLUSIVE_OR_EXPRESSION,0,0,NULL); + } + if ((nTopStackRule == 1 && nTopStackTerm == 1) || + (nTopStackTerm == 3)) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_INCLUSIVE_OR) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_INCLUSIVE_OR,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_INCLUSIVE_OR_EXPRESSION,2,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_EXCLUSIVE_OR_EXPRESSION,0,0,NULL); + return 0; + } + else + { + if (nTopStackTerm == 3) + { + pTopStackCurrentNode->pRight = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + else + { + ModifySRStackReturnTree(pTopStackReturnNode); + } + } + } + break; + + /////////////////////////////////////////////////////////////// + // case 23: + // logical-AND-expression: + // (1) inclusive-OR-expression + // (2) logical-AND-expression && inclusive-OR-expression + /////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_LOGICAL_AND_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_LOGICAL_AND_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_INCLUSIVE_OR_EXPRESSION,0,0,NULL); + } + if ((nTopStackRule == 1 && nTopStackTerm == 1) || + (nTopStackTerm == 3)) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LOGICAL_AND) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_LOGICAL_AND,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_LOGICAL_AND_EXPRESSION,2,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_INCLUSIVE_OR_EXPRESSION,0,0,NULL); + return 0; + } + else + { + if (nTopStackTerm == 3) + { + pTopStackCurrentNode->pRight = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + else + { + ModifySRStackReturnTree(pTopStackReturnNode); + } + } + } + break; + + ////////////////////////////////////////////////////////////////////////////////// + // case 22: + // logical-OR-expression: + // (1) logical-AND-expression + // (2) logical-OR-expression || logical-AND-expression + ////////////////////////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_LOGICAL_OR_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_LOGICAL_OR_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_LOGICAL_AND_EXPRESSION,0,0,NULL); + } + if ((nTopStackRule == 1 && nTopStackTerm == 1) || + (nTopStackTerm == 3)) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LOGICAL_OR) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_LOGICAL_OR,pTopStackReturnNode,NULL); + if (nTopStackTerm == 3) + { + pNewNode->pLeft = pTopStackCurrentNode; + pNewNode->pLeft->pRight = pTopStackReturnNode; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_LOGICAL_OR_EXPRESSION,2,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_LOGICAL_AND_EXPRESSION,0,0,NULL); + return 0; + } + else + { + if (nTopStackTerm == 3) + { + pTopStackCurrentNode->pRight = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + else + { + ModifySRStackReturnTree(pTopStackReturnNode); + } + } + } + break; + + ////////////////////////////////////////////////////////////////////////////////// + // case 21: + // conditional-expression: + // (1) logical-OR-expression + // (2) logical-OR-expression ? conditional_expression : conditional_expression + ////////////////////////////////////////////////////////////////////////////////// + case CSCRIPTCOMPILER_GRAMMAR_CONDITIONAL_EXPRESSION: + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_CONDITIONAL_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_LOGICAL_OR_EXPRESSION,0,0,NULL); + } + if (nTopStackRule == 1 && nTopStackTerm == 1) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_QUESTION_MARK) + { + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_COND_CONDITION,pTopStackReturnNode,NULL); + CScriptParseTreeNode *pNewNode3 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_COND_CHOICE,NULL,NULL); + CScriptParseTreeNode *pNewNode1 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_COND_BLOCK,pNewNode2,pNewNode3); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_CONDITIONAL_EXPRESSION,2,3,pNewNode1); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_CONDITIONAL_EXPRESSION,0,0,NULL); + return 0; + } + else + { + ModifySRStackReturnTree(pTopStackReturnNode); + } + } + if (nTopStackRule == 2 && nTopStackTerm == 3) + { + if (m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_COLON) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_CONDITIONAL_REQUIRES_SECOND_EXPRESSION; + return nError; + } + pTopStackCurrentNode->pRight->pLeft = pTopStackReturnNode; + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_CONDITIONAL_EXPRESSION,2,5,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_CONDITIONAL_EXPRESSION,0,0,NULL); + return 0; + } + if (nTopStackRule == 2 && nTopStackTerm == 5) + { + pTopStackCurrentNode->pRight->pRight = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + break; + ///////////////////////////////////////////////////////////////////// + // case 20: + // assignment-expression: + // (1) conditional-expression + // (2) variable_lvalue = conditional_expression + // (3) variable_lvalue -= conditional_expression + // (4) variable_lvalue += conditional_expression + // (5) variable_lvalue *= conditional_expression + // (6) variable_lvalue /= conditional_expression + // (7) variable_lvalue %= conditional_expression + // (8) variable_lvalue &= conditional_expression + // (9) variable_lvalue ^= conditional_expression + // (10) variable_lvalue |= conditional_expression + // (11) variable_lvalue <<= conditional_expression + // (12) variable_lvalue >>= conditional_expression + // (13) variable_lvalue >>>= conditional_expression + // variable_lvalue is a variable or a structure part of a variable. + //////////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_ASSIGNMENT_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + // MGB - Feb 11, 2003 - BEGIN CHANGE + // To speed up compiling, what's the point in pushing + // and popping all of this when we know we are on a + // greased pole directly to the unary expression? + // It saves about 2 seconds (in debug mode) by losing + // approximately 20% of all the Push and Pop operations. + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ASSIGNMENT_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_CONDITIONAL_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_LOGICAL_OR_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_LOGICAL_AND_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_INCLUSIVE_OR_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_EXCLUSIVE_OR_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_BOOLEAN_AND_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_EQUALITY_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_RELATIONAL_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_SHIFT_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ADDITIVE_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_MULTIPLICATIVE_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_UNARY_EXPRESSION,0,0,NULL); + // MGB - Feb 11, 2003 - END CHANGE + } + if (nTopStackRule == 1 && nTopStackTerm == 1) + { + if (m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_EQUAL && + m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_MINUS && + m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_PLUS && + m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_MULTIPLY && + m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_DIVIDE && + m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_MODULUS && + m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_AND && + m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_XOR && + m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_OR && + m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_SHIFT_LEFT && + m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_SHIFT_RIGHT && + m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_USHIFT_RIGHT) + + { + ModifySRStackReturnTree(pTopStackReturnNode); + } + else + { + if (CheckForBadLValue(pTopStackReturnNode) == TRUE) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_BAD_LVALUE; + return nError; + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_EQUAL) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_ASSIGNMENT,NULL,pTopStackReturnNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ASSIGNMENT_EXPRESSION,2,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_CONDITIONAL_EXPRESSION,0,0,NULL); + } + else + { + CScriptParseTreeNode *pDupTree = DuplicateScriptParseTree(pTopStackReturnNode); + int32_t nNewTerm = 0; + CScriptParseTreeNode *pNewNode2 = NULL; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_MINUS) + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_SUBTRACT,pDupTree,NULL); + nNewTerm = 3; + } + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_PLUS) + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_ADD,pDupTree,NULL); + nNewTerm = 4; + } + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_MULTIPLY) + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_MULTIPLY,pDupTree,NULL); + nNewTerm = 5; + } + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_DIVIDE) + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_DIVIDE,pDupTree,NULL); + nNewTerm = 6; + } + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_MODULUS) + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_MODULUS,pDupTree,NULL); + nNewTerm = 7; + } + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_AND) + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_BOOLEAN_AND,pDupTree,NULL); + nNewTerm = 8; + } + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_XOR) + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_EXCLUSIVE_OR,pDupTree,NULL); + nNewTerm = 9; + } + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_OR) + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_INCLUSIVE_OR,pDupTree,NULL); + nNewTerm = 10; + } + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_SHIFT_LEFT) + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_SHIFT_LEFT,pDupTree,NULL); + nNewTerm = 11; + } + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_SHIFT_RIGHT) + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_SHIFT_RIGHT,pDupTree,NULL); + nNewTerm = 12; + } + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_USHIFT_RIGHT) + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_UNSIGNED_SHIFT_RIGHT,pDupTree,NULL); + nNewTerm = 13; + } + + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_ASSIGNMENT,pNewNode2,pTopStackReturnNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ASSIGNMENT_EXPRESSION,nNewTerm,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_CONDITIONAL_EXPRESSION,0,0,NULL); + + } + return 0; + } + } + if (nTopStackTerm == 3) + { + + // Mmmm ... creamy goodness. If we've managed to screw up the assignment because this + // is a variable, and there's a left bracket right after it ... then we've probably got + // an undefined identifier and we should do something about it. + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_BRACKET) + { + if (pTopStackReturnNode != NULL) + { + if (pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_VARIABLE) + { + m_sUndefinedIdentifier = *(pTopStackReturnNode->m_psStringData); + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER; + return nError; + } + } + } + + if (nTopStackRule == 2) + { + // For this rule (a plain assignment) attach the operand to the left of the assignment. + pTopStackCurrentNode->pLeft = pTopStackReturnNode; + } + else + { + // For other rules, attach the second operand to the operation instead of the assignment. + pTopStackCurrentNode->pLeft->pRight = pTopStackReturnNode; + } + + ModifySRStackReturnTree(pTopStackCurrentNode); + } + + break; + + //////////////////////////////// + // case 19: + // expression: + // (1) assignment-expression + //////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ASSIGNMENT_EXPRESSION,0,0,NULL); + } + if (nTopStackRule == 1 && nTopStackTerm == 1) + { + ModifySRStackReturnTree(pTopStackReturnNode); + } + break; + + //////////////////////////////////////////////////////////////// + // case 18: + // non-void-expression: + // (1) expression, then ensure there is a single-return-value. + //////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_NON_VOID_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_VOID_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_EXPRESSION,0,0,NULL); + } + if (nTopStackRule == 1 && nTopStackTerm == 1) + { + // MGB - October 29, 2002 + // Here, we would check to see if there is a single-return-value + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_NON_VOID_EXPRESSION,pTopStackReturnNode,NULL); + ModifySRStackReturnTree(pNewNode); + } + break; + + ///////////////////////////////////////////////////////////////////////////// + // case 17: + // boolean-expression: + // (1) non-void-expression, then Ensure there is a single integer returned. + ///////////////////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_BOOLEAN_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_BOOLEAN_EXPRESSION,1,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_VOID_EXPRESSION,0,0,NULL); + } + if (nTopStackRule == 1 && nTopStackTerm == 1) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_INTEGER_EXPRESSION,pTopStackReturnNode,NULL); + ModifySRStackReturnTree(pNewNode); + } + break; + + ///////////////////////////////////////////////////////////////// + // case 16: + // after-argument-expression: + // (1) ) // the end of the list ... hand it to previous level. + // (2) , non-void-expression after-argument-expression + ///////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_AFTER_ARGUMENT_EXPRESSION: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET) + { + ModifySRStackReturnTree(NULL); + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_COMMA) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_AFTER_ARGUMENT_EXPRESSION,2,2,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_VOID_EXPRESSION,0,0,NULL); + return 0; + } + else + { + if (m_pSRStack[m_nSRStackStates].pCurrentTree != NULL) + { + if (m_pSRStack[m_nSRStackStates].pCurrentTree->pRight != NULL) + { + if (m_pSRStack[m_nSRStackStates].pCurrentTree->pRight->nOperation == CSCRIPTCOMPILER_OPERATION_VARIABLE) + { + m_sUndefinedIdentifier = *(m_pSRStack[m_nSRStackStates].pCurrentTree->pRight->m_psStringData); + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER; + return nError; + } + if (m_pSRStack[m_nSRStackStates].pCurrentTree->pRight->pLeft != NULL) + { + if (m_pSRStack[m_nSRStackStates].pCurrentTree->pRight->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_VARIABLE) + { + m_sUndefinedIdentifier = *(m_pSRStack[m_nSRStackStates].pCurrentTree->pRight->pLeft->m_psStringData); + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER; + return nError; + } + + } + } + } + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER; + return nError; + } + } + if (nTopStackRule == 2 && nTopStackTerm == 2) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_ACTION_ARG_LIST,NULL,pTopStackReturnNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_AFTER_ARGUMENT_EXPRESSION,2,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_AFTER_ARGUMENT_EXPRESSION,0,0,NULL); + } + if (nTopStackRule == 2 && nTopStackTerm == 3) + { + pTopStackCurrentNode->pLeft = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + break; + + /////////////////////////////////////////////////////////////////////////////// + // case 15: + // argument-expression-list: + // (1) ) // You see the end of the list ... can pop back to previous level if + // // when you've cross-checked the types of the return values, you see + // // that everything is kosher. + // (2) non-void-expression after-argument-expression + /////////////////////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_ARGUMENT_EXPRESSION_LIST: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACE) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER; + return nError; + } + else if (m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ARGUMENT_EXPRESSION_LIST,2,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_VOID_EXPRESSION,0,0,NULL); + } + } + if (nTopStackRule == 2 && nTopStackTerm == 1) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_ACTION_ARG_LIST,NULL,pTopStackReturnNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ARGUMENT_EXPRESSION_LIST,2,2,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_AFTER_ARGUMENT_EXPRESSION,0,0,NULL); + } + if (nTopStackRule == 2 && nTopStackTerm == 2) + { + pTopStackCurrentNode->pLeft = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + break; + + ////////////////////////////////////////// + // case 14: + // declaration-variable-list-separator + // (1) ; + // (2) , declaration-variable-list + ////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_DECL_VARLIST_SEPARATOR: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_COMMA) + { + // Create a new statement that we can link to. + CScriptParseTreeNode *pNewNode3 = CreateScriptParseTreeNode(0,NULL,NULL); + if (pTopStackCurrentNode != NULL && + pTopStackCurrentNode->pLeft != NULL && + pTopStackCurrentNode->pLeft->pLeft != NULL) + { + pNewNode3->nOperation = pTopStackCurrentNode->pLeft->pLeft->nOperation; + if (pNewNode3->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_STRUCT) + { + pNewNode3->m_psStringData = new CExoString(pTopStackCurrentNode->pLeft->pLeft->m_psStringData->CStr()); + } + } + + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_KEYWORD_DECLARATION,pNewNode3,NULL); + CScriptParseTreeNode *pNewNode1 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT,pNewNode2,NULL); + + // Point back to the new statement ... + if (pTopStackCurrentNode->pRight == NULL) + { + // There is no assignment statement added to the list, so we + // can attach directly to pTopStackCurrentNode->pRight. + pTopStackCurrentNode->pRight = pNewNode1; + // Update the line number. + pTopStackCurrentNode->pRight->nLine = pTopStackCurrentNode->nLine; + } + else + { + // We've also added an assignment statement to the list, so we + // have to attach to pTopStackCurrentNode->pRight->pRight instead. + pTopStackCurrentNode->pRight->pRight = pNewNode1; + // Update the line number. + pTopStackCurrentNode->pRight->pRight->nLine = pTopStackCurrentNode->nLine; + + } + // ... and feed it back to the main routine. + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_DECL_VARLIST,0,0,pNewNode1); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_VARIABLE_LIST; + return nError; + } + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_VARIABLE_LIST; + return nError; + } + break; + + //////////////////////////////////////////////////////////////////////////// + // case 13: + // declaration-variable-list + // (1) variable declaration-variable-list-separator + // (2) variable = non-void-expression declaration-variable-list-separator + //////////////////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_DECL_VARLIST: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_VARIABLE) + { + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_VARIABLE,NULL,NULL); + m_pchToken[m_nTokenCharacters] = 0; + pNewNode2->m_psStringData = new CExoString(m_pchToken); + + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_VARIABLE_LIST,pNewNode2,NULL); + + if (pTopStackCurrentNode != NULL && + pTopStackCurrentNode->pLeft != NULL) + { + pTopStackCurrentNode->pLeft->pRight = pNewNode; + } + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_DECL_VARLIST,1,2,pTopStackCurrentNode); + + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_VARIABLE_LIST; + return nError; + } + } + else if (nTopStackRule == 1 && nTopStackTerm == 2) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_EQUAL) + { + CScriptParseTreeNode *pNewNode0 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_VARIABLE,NULL,NULL); + if (pTopStackCurrentNode != NULL && + pTopStackCurrentNode->pLeft != NULL && + pTopStackCurrentNode->pLeft->pRight != NULL && + pTopStackCurrentNode->pLeft->pRight->pLeft != NULL) + { + pNewNode0->m_psStringData = new CExoString(pTopStackCurrentNode->pLeft->pRight->pLeft->m_psStringData->CStr()); + } + + CScriptParseTreeNode *pNewNode1 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_ASSIGNMENT,NULL,pNewNode0); + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT,pNewNode1,NULL); + pTopStackCurrentNode->pRight = pNewNode2; + pTopStackCurrentNode->pRight->nLine = pTopStackCurrentNode->nLine; + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_DECL_VARLIST,2,2,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_VOID_EXPRESSION,0,0,NULL); + return 0; + } + else + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_DECL_VARLIST_SEPARATOR,0,0,pTopStackCurrentNode); + } + } + else if (nTopStackRule == 2 && nTopStackTerm == 2) + { + pTopStackCurrentNode->pRight->pLeft->pLeft = pTopStackReturnNode; + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_DECL_VARLIST_SEPARATOR,0,0,pTopStackCurrentNode); + } + + break; + + /////////////////////////////////////////////////////////////////////// + // case 12: + // non-void-type-specifier: + // (1) [const] int + // (2) [const] float + // (3) [const] string + // (4) object + // (5) struct variable + // (6) one of the ENGINE_STRUCTURE defined variables passed in from + // the language definition! + // (7) vector -- treated as a structure. + /////////////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_NON_VOID_TYPE_SPECIFIER: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_CONST) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONST_DECLARATION,NULL,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_VOID_TYPE_SPECIFIER,0,0,pNewNode); + return 0; + } + + if (pTopStackCurrentNode != NULL && + m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_KEYWORD_INT && + m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT && + m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_INVALID_TYPE_FOR_CONST_KEYWORD; + return nError; + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_VECTOR) + { + // Treat "vector" as a "struct vector" token. + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_KEYWORD_STRUCT,NULL,NULL); + m_pchToken[m_nTokenCharacters] = 0; + pNewNode->m_psStringData = new CExoString(m_pchToken); + ModifySRStackReturnTree(pNewNode); + return 0; + } + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_KEYWORD_STRUCT,NULL,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_VOID_TYPE_SPECIFIER,5,1,pNewNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT || + (m_nTokenStatus >= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 && + m_nTokenStatus <= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9)) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(0,NULL,NULL); + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + pNewNode->nOperation = CSCRIPTCOMPILER_OPERATION_KEYWORD_INT; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT) + { + pNewNode->nOperation = CSCRIPTCOMPILER_OPERATION_KEYWORD_FLOAT; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING) + { + pNewNode->nOperation = CSCRIPTCOMPILER_OPERATION_KEYWORD_STRING; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT) + { + pNewNode->nOperation = CSCRIPTCOMPILER_OPERATION_KEYWORD_OBJECT; + } + else if (m_nTokenStatus >= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 && m_nTokenStatus <= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9) + { + pNewNode->nOperation = CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE0 + (m_nTokenStatus - CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0); + } + + if (pTopStackCurrentNode != NULL) + { + // We've already checked to see if this is a valid type ... so we only need to + // put the const declaration operation above this type so that we can interpret + // it at the right time. + pTopStackCurrentNode->pLeft = pNewNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + else + { + ModifySRStackReturnTree(pNewNode); + } + + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_INVALID_DECLARATION_TYPE; + return nError; + } + } + if (nTopStackRule == 5 && nTopStackTerm == 1) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_VARIABLE) + { + m_pchToken[m_nTokenCharacters] = 0; + pTopStackCurrentNode->m_psStringData = new CExoString(m_pchToken); + ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_INVALID_DECLARATION_TYPE; + return nError; + } + } + + break; + + //////////////////////////////////////////////////////////////////////// + // case 11: + // any-type-specifier: + // (1) void + // (2) non-void-type-specifier + //////////////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_ANY_TYPE_SPECIFIER: + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_KEYWORD_VOID,NULL,NULL); + ModifySRStackReturnTree(pNewNode); + return 0; + } + else + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_VOID_TYPE_SPECIFIER,0,0,NULL); + } + } + break; + + //////////////////////////////////////////////////////////////////////// + // case 10: + // Within-a-statement: + // (1) { within-a-compound-statement } + // (2) void-returning-identifier(argument-expression-list); + // (3) if (boolean-expression) statement + // (4) if (boolean-expression) statement else statement + // (5) switch ( boolean-expression ) statement + // (6) return non-void-expression(opt) ; + // (7) while (boolean-expression) statement + // (8) do statement while (boolean-expression); + // (9) for (expression_opt; expression_opt; expression_opt) statement + // (10) non-void-type-specifier declaration-list ; + // (11) expression(opt) ; + // (12) default : + // (13) case conditional_expression : + // (14) break ; + // (15) continue ; + ///////////////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_BRACE) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,1,2,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_COMPOUND_STATEMENT,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_RETURN) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_RETURN,NULL,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,6,2,pNewNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_SWITCH) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_SWITCH_BLOCK,NULL,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,5,2,pNewNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_DEFAULT) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_DEFAULT,NULL,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,12,2,pNewNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_CASE) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CASE,NULL,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,13,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_CONDITIONAL_EXPRESSION,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_BREAK) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_BREAK,NULL,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,14,2,pNewNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_CONTINUE) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONTINUE,NULL,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,15,2,pNewNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_VOID_IDENTIFIER) + { + // Do somethin' with the identifier. + + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_ACTION_ID,NULL,NULL); + m_pchToken[m_nTokenCharacters] = 0; + pNewNode2->m_psStringData = new CExoString(m_pchToken); + + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_ACTION,NULL,pNewNode2); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,2,2,pNewNode); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_IF) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,3,2,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_ELSE) + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_ELSE_WITHOUT_CORRESPONDING_IF; + return nError; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_WHILE) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,7,2,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_DO) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,8,2,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_FOR) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,9,2,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_CONST || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_VECTOR || + (m_nTokenStatus >= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 && + m_nTokenStatus <= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9)) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,10,2,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_VOID_TYPE_SPECIFIER,0,0,NULL); + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,11,2,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_EXPRESSION,0,0,NULL); + } + } + if (nTopStackRule == 1 && nTopStackTerm == 2) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACE) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_COMPOUND_STATEMENT,pTopStackReturnNode,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,1,3,pNewNode); + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_END_COMPOUND_STATEMENT; + return nError; + } + } + if (nTopStackRule == 1 && nTopStackTerm == 3) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACE) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else + { + int32_t nError = STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_END_COMPOUND_STATEMENT; + return nError; + } + } + if (nTopStackRule == 2 && nTopStackTerm == 2) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_BRACKET) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,2,3,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ARGUMENT_EXPRESSION_LIST,0,0,NULL); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_LEFT_BRACKET_ON_ARG_LIST; + return nError; + } + } + if (nTopStackRule == 2 && nTopStackTerm == 3) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET) + { + pTopStackCurrentNode->pLeft = pTopStackReturnNode; + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,2,4,pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_RIGHT_BRACKET_ON_ARG_LIST; + return nError; + } + } + if (nTopStackRule == 2 && nTopStackTerm == 4) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_SEMICOLON_AFTER_EXPRESSION; + return nError; + } + } + if (nTopStackRule == 3 && nTopStackTerm == 2) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_BRACKET) + { + CScriptParseTreeNode *pNewNode3 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_IF_CHOICE,NULL,NULL); + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_IF_CONDITION,NULL,NULL); + CScriptParseTreeNode *pNewNode1 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_IF_BLOCK,pNewNode2,pNewNode3); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,3,3,pNewNode1); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_BOOLEAN_EXPRESSION,0,0,NULL); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_LEFT_BRACKET_ON_EXPRESSION; + return nError; + } + } + if (nTopStackRule == 3 && nTopStackTerm == 3) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET) + { + pTopStackCurrentNode->pLeft->pLeft = pTopStackReturnNode; + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,3,5,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,0,0,NULL); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_RIGHT_BRACKET_ON_EXPRESSION; + return nError; + } + } + if (nTopStackRule == 3 && nTopStackTerm == 5) + { + // Link up the "if expression true" statement. + if (pTopStackReturnNode == NULL) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_IF_CONDITION_CANNOT_BE_FOLLOWED_BY_A_NULL_STATEMENT; + return nError; + } + + // MGB - For Script Debugger. + CScriptParseTreeNode *pNewNode2; + + if (pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_COMPOUND_STATEMENT || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_IF_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_WHILE_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_DOWHILE_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_SWITCH_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_FOR_BLOCK ) + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT_NO_DEBUG,pTopStackReturnNode,NULL); + } + else + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT,pTopStackReturnNode,NULL); + pNewNode2->nLine = pTopStackReturnNode->nLine; + } + // MGB - !For Script Debugger + + pTopStackCurrentNode->pRight->pLeft = pNewNode2; + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_ELSE) + { + // MGB - For Script Debugger. + // When we process an else branch, we need a line + // number for the code! + pTopStackCurrentNode->pRight->nLine = m_nLines; + // MGB - !For Script Debugger + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,3,7,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,0,0,NULL); + return 0; + } + else + { + ModifySRStackReturnTree(pTopStackCurrentNode); + } + } + if (nTopStackRule == 3 && nTopStackTerm == 7) + { + if (pTopStackReturnNode == NULL) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_ELSE_CANNOT_BE_FOLLOWED_BY_A_NULL_STATEMENT; + return nError; + } + + // Link up the "if expression false" statement. + CScriptParseTreeNode *pNewNode2; + + if (pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_COMPOUND_STATEMENT || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_IF_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_WHILE_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_DOWHILE_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_SWITCH_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_FOR_BLOCK) + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT_NO_DEBUG,pTopStackReturnNode,NULL); + } + else + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT,pTopStackReturnNode,NULL); + pNewNode2->nLine = pTopStackReturnNode->nLine; + } + + // MGB - August 10, 2001 - Fixed bug where else expression was actually linking + // directly to the returned parse tree without linking the operation_statement in place. + pTopStackCurrentNode->pRight->pRight = pNewNode2; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + if (nTopStackRule == 5 && nTopStackTerm == 2) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_BRACKET) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,5,4,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_BOOLEAN_EXPRESSION,0,0,NULL); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_LEFT_BRACKET_ON_EXPRESSION; + return nError; + } + } + if (nTopStackRule == 5 && nTopStackTerm == 4) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_SWITCH_CONDITION,pTopStackReturnNode,NULL); + pTopStackCurrentNode->pLeft = pNewNode; + // MGB - February 5, 2003 - Removed pTopStackReturnNode from left branch of this + // tree node, since it didn't really make a lot of sense. + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT_NO_DEBUG,NULL,NULL); + pTopStackCurrentNode->pRight = pNewNode2; + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,5,5,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,0,0,NULL); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_RIGHT_BRACKET_ON_EXPRESSION; + return nError; + } + } + if (nTopStackRule == 5 && nTopStackTerm == 5) + { + if (pTopStackReturnNode == NULL) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_SWITCH_CONDITION_CANNOT_BE_FOLLOWED_BY_A_NULL_STATEMENT; + return nError; + } + + pTopStackCurrentNode->pRight->pLeft = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + if (nTopStackRule == 6 && nTopStackTerm == 2) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,6,3,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_VOID_EXPRESSION,0,0,NULL); + } + } + + if (nTopStackRule == 6 && nTopStackTerm == 3) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + pTopStackCurrentNode->pRight = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_RETURN_STATEMENT; + return nError; + } + } + + if (nTopStackRule == 7 && nTopStackTerm == 2) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_BRACKET) + { + CScriptParseTreeNode *pNewNode1 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_WHILE_BLOCK,NULL,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,7,3,pNewNode1); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_BOOLEAN_EXPRESSION,0,0,NULL); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_LEFT_BRACKET_ON_EXPRESSION; + return nError; + } + } + if (nTopStackRule == 7 && nTopStackTerm == 3) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET) + { + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_WHILE_CONDITION,pTopStackReturnNode,NULL); + CScriptParseTreeNode *pNewNode3 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_WHILE_CHOICE,NULL,NULL); + pTopStackCurrentNode->pLeft = pNewNode2; + pTopStackCurrentNode->pRight = pNewNode3; + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,7,5,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,0,0,NULL); + return 0; + } + else + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_BRACKET) + { + if (pTopStackReturnNode != NULL) + { + if (pTopStackReturnNode->pLeft != NULL) + { + if (pTopStackReturnNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_VARIABLE) + { + m_sUndefinedIdentifier = *(pTopStackReturnNode->pLeft->m_psStringData); + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER; + return nError; + } + } + } + } + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_RIGHT_BRACKET_ON_EXPRESSION; + return nError; + } + } + if (nTopStackRule == 7 && nTopStackTerm == 5) + { + // Link up the "if expression true" statement. + CScriptParseTreeNode *pNewNode4 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_WHILE_CONTINUE,NULL,NULL); + CScriptParseTreeNode *pNewNode3 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT_NO_DEBUG,pNewNode4,NULL); + + if (pTopStackReturnNode == NULL) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_WHILE_CONDITION_CANNOT_BE_FOLLOWED_BY_A_NULL_STATEMENT; + return nError; + } + + // MGB - For Script Debugger. + CScriptParseTreeNode *pNewNode2; + + if (pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_COMPOUND_STATEMENT || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_IF_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_WHILE_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_DOWHILE_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_SWITCH_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_FOR_BLOCK) + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT_NO_DEBUG,pTopStackReturnNode,pNewNode3); + } + else + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT,pTopStackReturnNode,pNewNode3); + pNewNode2->nLine = pTopStackReturnNode->nLine; + } + // MGB - !For Script Debugger + + pTopStackCurrentNode->pRight->pLeft = pNewNode2; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + if (nTopStackRule == 8 && nTopStackTerm == 2) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_WHILE) + { + //CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT,pTopStackReturnNode,NULL); + CScriptParseTreeNode *pNewNode2; + + if (pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_COMPOUND_STATEMENT || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_IF_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_WHILE_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_DOWHILE_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_SWITCH_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_FOR_BLOCK ) + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT_NO_DEBUG,pTopStackReturnNode,NULL); + } + else + { + pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT,pTopStackReturnNode,NULL); + pNewNode2->nLine = pTopStackReturnNode->nLine; + } + CScriptParseTreeNode *pNewNode1 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_DOWHILE_CONDITION,NULL,NULL); + CScriptParseTreeNode *pNewNode3 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_DOWHILE_BLOCK,pNewNode2,pNewNode1); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,8,3,pNewNode3); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_WHILE_AFTER_DO_KEYWORD; + return nError; + } + } + if (nTopStackRule == 8 && nTopStackTerm == 3) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_BRACKET) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,8,5,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_BOOLEAN_EXPRESSION,0,0,NULL); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_LEFT_BRACKET_ON_EXPRESSION; + return nError; + } + } + if (nTopStackRule == 8 && nTopStackTerm == 5) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET) + { + pTopStackCurrentNode->pRight->pLeft = pTopStackReturnNode; + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,8,6,pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_RIGHT_BRACKET_ON_EXPRESSION; + return nError; + } + } + if (nTopStackRule == 8 && nTopStackTerm == 6) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_SEMICOLON_AFTER_EXPRESSION; + return nError; + } + } + if (nTopStackRule == 9 && nTopStackTerm == 2) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_BRACKET) + { + CScriptParseTreeNode *pNewNode8 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_WHILE_CONTINUE,NULL,NULL); + CScriptParseTreeNode *pNewNode9 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT,NULL,NULL); + CScriptParseTreeNode *pNewNode1 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT,NULL,NULL); + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT_NO_DEBUG,pNewNode8,pNewNode9); + CScriptParseTreeNode *pNewNode3 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_WHILE_CHOICE,pNewNode1,pNewNode2); + CScriptParseTreeNode *pNewNode4 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_WHILE_CONDITION,NULL,NULL); + CScriptParseTreeNode *pNewNode5 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_WHILE_BLOCK,pNewNode4,pNewNode3); + CScriptParseTreeNode *pNewNode6 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT_NO_DEBUG,pNewNode5,NULL); + CScriptParseTreeNode *pNewNode7 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT,NULL,pNewNode6); + + CScriptParseTreeNode *pNewNode10 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_FOR_BLOCK,pNewNode7,NULL); + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,9,3,pNewNode10); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_LEFT_BRACKET_ON_EXPRESSION; + return nError; + } + } + if (nTopStackRule == 9 && nTopStackTerm == 3) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,9,5,pTopStackCurrentNode); + return 0; + } + else + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,9,4,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_EXPRESSION,0,0,NULL); + } + } + if (nTopStackRule == 9 && nTopStackTerm == 4) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + // Create a node and stick the expression on to it. + pTopStackCurrentNode->pLeft->pLeft = pTopStackReturnNode; + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,9,5,pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_SEMICOLON_AFTER_EXPRESSION; + return nError; + } + } + if (nTopStackRule == 9 && nTopStackTerm == 5) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER,NULL,NULL); + pNewNode->nIntegerData = 1; + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_NON_VOID_EXPRESSION,pNewNode,NULL); + CScriptParseTreeNode *pNewNode3 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_INTEGER_EXPRESSION,pNewNode2,NULL); + + pTopStackCurrentNode->pLeft->pRight->pLeft->pLeft->pLeft = pNewNode3; + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,9,7,pTopStackCurrentNode); + return 0; + } + else + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,9,6,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_BOOLEAN_EXPRESSION,0,0,NULL); + } + } + if (nTopStackRule == 9 && nTopStackTerm == 6) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + // Create a node and stick the expression on to it. + pTopStackCurrentNode->pLeft->pRight->pLeft->pLeft->pLeft = pTopStackReturnNode; + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,9,7,pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_SEMICOLON_AFTER_EXPRESSION; + return nError; + } + } + if (nTopStackRule == 9 && nTopStackTerm == 7) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,9,9,pTopStackCurrentNode); + return 0; + } + else + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,9,8,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_EXPRESSION,0,0,NULL); + } + } + if (nTopStackRule == 9 && nTopStackTerm == 8) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET) + { + pTopStackCurrentNode->pLeft->pRight->pLeft->pRight->pRight->pRight->pLeft = pTopStackReturnNode; + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,9,9,pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_SEMICOLON_AFTER_EXPRESSION; + return nError; + } + } + if (nTopStackRule == 9 && nTopStackTerm == 9) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,9,10,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,0,0,NULL); + } + if (nTopStackRule == 9 && nTopStackTerm == 10) + { + if (pTopStackReturnNode == NULL) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_FOR_STATEMENT_CANNOT_BE_FOLLOWED_BY_A_NULL_STATEMENT; + return nError; + } + + pTopStackCurrentNode->pLeft->pRight->pLeft->pRight->pLeft->pLeft = pTopStackReturnNode; + + if (pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_COMPOUND_STATEMENT || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_IF_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_WHILE_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_DOWHILE_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_SWITCH_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_FOR_BLOCK) + { + pTopStackCurrentNode->pLeft->pRight->pLeft->pRight->pLeft->nOperation = CSCRIPTCOMPILER_OPERATION_STATEMENT_NO_DEBUG; + } + else + { + // It's already a STATEMENT, so set the line correctly. + pTopStackCurrentNode->pLeft->pRight->pLeft->pRight->pLeft->nLine = pTopStackReturnNode->nLine; + } + + ModifySRStackReturnTree(pTopStackCurrentNode); + } + if (nTopStackRule == 10 && nTopStackTerm == 2) + { + // Create a node. + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_KEYWORD_DECLARATION,pTopStackReturnNode,NULL); + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT,pNewNode,NULL); + CScriptParseTreeNode *pNewNode3 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT_LIST,pNewNode2,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,10,3,pNewNode3); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_DECL_VARLIST,0,0,pNewNode2); + } + if (nTopStackRule == 10 && nTopStackTerm == 3) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_SEMICOLON_AFTER_EXPRESSION; + return nError; + } + } + if (nTopStackRule == 11 && nTopStackTerm == 2) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + ModifySRStackReturnTree(pTopStackReturnNode); + return 0; + } + else + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_BRACKET) + { + if (pTopStackReturnNode != NULL) + { + if (pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_VARIABLE) + { + m_sUndefinedIdentifier = *(pTopStackReturnNode->m_psStringData); + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER; + return nError; + } + } + } + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_SEMICOLON_AFTER_EXPRESSION; + return nError; + } + } + if (nTopStackRule == 12 && nTopStackTerm == 2) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_COLON) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_COLON_AFTER_DEFAULT_LABEL; + return nError; + } + } + if (nTopStackRule == 13 && nTopStackTerm == 3) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_COLON) + { + pTopStackCurrentNode->pLeft = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_COLON_AFTER_CASE_LABEL; + return nError; + } + } + if ((nTopStackRule == 14 && nTopStackTerm == 2) || + (nTopStackRule == 15 && nTopStackTerm == 2)) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_SEMICOLON_AFTER_STATEMENT; + return nError; + } + } + + break; + + ///////////////////////////////////////////////////////////// + // case 9: + // Within-a-statement-list + // (1) within-a-statement within-a-statement-list + ///////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_WITHIN_STATEMENT_LIST: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACE || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_EOF) + { + // Jump up to previous level. + ModifySRStackReturnTree(NULL); + } + else + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT,NULL,NULL); + if (pTopStackCurrentNode != NULL) + { + pTopStackCurrentNode->pRight = pNewNode; + } + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_STATEMENT_LIST,1,1,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,0,0,NULL); + } + } + if (nTopStackRule == 1 && nTopStackTerm == 1) + { + pTopStackCurrentNode->pLeft = pTopStackReturnNode; + + // MGB - Stuff - For Debugging! + // IF_BLOCK will take care of its own addressing for conditionals. + if (pTopStackReturnNode != NULL) + { + if (pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_IF_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_SWITCH_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_WHILE_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_DOWHILE_BLOCK || + pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_FOR_BLOCK) + { + pTopStackCurrentNode->nOperation = CSCRIPTCOMPILER_OPERATION_STATEMENT_NO_DEBUG; + } + + } + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_STATEMENT_LIST,1,2,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_STATEMENT_LIST,0,0,pTopStackCurrentNode); + } + if (nTopStackRule == 1 && nTopStackTerm == 2) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + } + break; + + ///////////////////////////////////////////////////////////// + // case 8: + // within-a-compound-statement: + // (1) (right brace returns up higher in the tree) + // (2) Within-a-statement-list + ///////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_WITHIN_COMPOUND_STATEMENT: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACE) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + } + else + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_COMPOUND_STATEMENT,2,1,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_STATEMENT_LIST,0,0,NULL); + } + } + if (nTopStackRule == 2 && nTopStackTerm == 1) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT_LIST,pTopStackReturnNode,NULL); + ModifySRStackReturnTree(pNewNode); + } + break; + + ///////////////////////////////////////////////////////////// + // case 7: + // non-initialized-decl-variable-list-separator + // (1) ; + // (2) , variable non-initialized-decl-variable-list + // + // A semi-colon should pass back up the stack so that the + // rule containing the right brace can compile it. + ////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_VARLIST_SEPARATOR: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_COMMA) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_VARLIST,0,0,pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_VARIABLE_LIST; + return nError; + } + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_VARIABLE_LIST; + return nError; + } + break; + + ////////////////////////////////////////////////////////////////// + // case 6: + // non-initialized-decl-variable-list + // (1) variable non-initialized-decl-variable-list-separator + // + // A semi-colon should pass back up the stack so that the + // rule containing the right brace can compile it. + ////////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_VARLIST: + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_VARIABLE) + { + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_VARIABLE,NULL,NULL); + m_pchToken[m_nTokenCharacters] = 0; + pNewNode2->m_psStringData = new CExoString(m_pchToken); + + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_VARIABLE_LIST,pNewNode2,NULL); + + if (pTopStackCurrentNode != NULL) + { + pTopStackCurrentNode->pRight = pNewNode; + } + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_VARLIST,1,2,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_VARLIST_SEPARATOR,0,0,pNewNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_VARIABLE_LIST; + return nError; + } + } + else if (nTopStackRule == 1 && nTopStackTerm == 2) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_VARIABLE_LIST; + return nError; + } + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////////// + // case 5: + // non-initialized-decl-list: + // (1) + // (2) non-void-type-specifier non-initialized-decl-variable-list ; non-initialized-decl-list + // + // A right brace should pass back up the stack so that the + // rule containing the right brace can compile it. + //////////////////////////////////////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_LIST: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_VECTOR || + (m_nTokenStatus >= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 && + m_nTokenStatus <= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9)) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT,NULL,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_LIST,2,2,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_VOID_TYPE_SPECIFIER,0,0,NULL); + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACE) + { + ModifySRStackReturnTree(pTopStackCurrentNode); + //return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_BAD_TYPE_SPECIFIER; + return nError; + } + } + if (nTopStackRule == 2 && nTopStackTerm == 2) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_KEYWORD_DECLARATION,pTopStackReturnNode,NULL); + pTopStackCurrentNode->pLeft = pNewNode; + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_LIST,2,3,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_VARLIST,0,0,NULL); + } + if (nTopStackRule == 2 && nTopStackTerm == 3) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + // Attach variable list. + pTopStackCurrentNode->pLeft->pRight = pTopStackReturnNode; + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_LIST,2,4,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_LIST,0,0,NULL); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_SEMICOLON_AFTER_EXPRESSION; + return nError; + } + } + if (nTopStackRule == 2 && nTopStackTerm == 4) + { + pTopStackCurrentNode->pRight = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + + break; + + ////////////////////////////////////////////////////////////////////// + // case 4: + // after-function-parameter: + // (1) + // (2) , function-parameter-list + // + // A right bracket should pass back up the stack so that the + // rule containing the right bracket can compile it. + ////////////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_AFTER_FUNCTION_PARAM: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET) + { + ModifySRStackReturnTree(NULL); + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_COMMA) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTION_PARAM_LIST,0,0,NULL); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER; + return nError; + } + } + break; + + ////////////////////////////////////////////////////////////////////// + // case 3: + // function-parameter-list: + // (1) non-void-type-specifier variable after-function-parameter + // (2) non-void-type-specifier variable = constant after-function-parameter + // (3) non-void-type-specifier variable = - constant after-function-parameter + // + // Note: the right bracket should return to the previous rule. + ////////////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_FUNCTION_PARAM_LIST: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTION_PARAM_LIST,1,2,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_VOID_TYPE_SPECIFIER,0,0,NULL); + //return 0; + } + } + if (nTopStackRule == 1 && nTopStackTerm == 2) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_VARIABLE) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_FUNCTION_PARAM_NAME,NULL,pTopStackReturnNode); + m_pchToken[m_nTokenCharacters] = 0; + pNewNode->m_psStringData = new CExoString(m_pchToken); + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTION_PARAM_LIST,1,3,pNewNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_BAD_VARIABLE_NAME; + return nError; + } + } + else if (nTopStackRule == 1 && nTopStackTerm == 3) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_EQUAL) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTION_PARAM_LIST,2,4,pTopStackCurrentNode); + //PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PRIMARY_EXPRESSION,0,0,NULL); + return 0; + } + else + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTION_PARAM_LIST,0,1,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_AFTER_FUNCTION_PARAM,0,0,NULL); + } + } + else if (nTopStackRule == 2 && nTopStackTerm == 4) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_MINUS) + { + // Constants can be negated in the function declaration, so allow them to be negated! + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_NEGATION,NULL,NULL); + pTopStackCurrentNode->pRight->pRight = pNewNode; + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTION_PARAM_LIST,3,6,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PRIMARY_EXPRESSION,0,0,NULL); + return 0; + } + else + { + // This should be a constant, hand it off to parsing by the primary expression rule. + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTION_PARAM_LIST,2,5,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PRIMARY_EXPRESSION,0,0,NULL); + } + } + else if (nTopStackRule == 2 && nTopStackTerm == 5) + { + // Add the constant on to the food chain. + pTopStackCurrentNode->pRight->pRight = pTopStackReturnNode; + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTION_PARAM_LIST,0,1,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_AFTER_FUNCTION_PARAM,0,0,NULL); + } + else if (nTopStackRule == 3 && nTopStackTerm == 6) + { + // Add the constant right after the potential negation. + pTopStackCurrentNode->pRight->pRight->pRight = pTopStackReturnNode; + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTION_PARAM_LIST,0,1,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_AFTER_FUNCTION_PARAM,0,0,NULL); + } + + // 3,0,1 deals with after-function-parameter + if (nTopStackRule == 0 && nTopStackTerm == 1) + { + pTopStackCurrentNode->pLeft = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + break; + + ////////////////////////////////////////////////////////////////////// + // case 2: + // after-program: + // (1) + ////////////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_AFTER_PROGRAM: + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_EOF) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_AFTER_PROGRAM,1,1,pTopStackReturnNode); + ModifySRStackReturnTree(pTopStackReturnNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_AFTER_COMPOUND_STATEMENT_AT_END; + return nError; + } + } + + // This passes the result of the include back to the + // previous thread of execution. All of the functional + // units are linked together. + + if (nTopStackRule == 1 && nTopStackTerm == 1) + { + ModifySRStackReturnTree(pTopStackReturnNode); + } + + break; + + ////////////////////////////////////////////////////////////////////////////////////////// + // case 1: + // functional-unit: + // (1) any-type-specifier variable ( parameter-list ) ; + // (2) any-type-specifier variable ( parameter-list ) { within-a-compound-statement } + // (3) struct-specifier { non-initialized-decl-list } ; + // (4) non-void-type-specifier declaration-list ; + ////////////////////////////////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_FUNCTIONAL_UNIT: + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTIONAL_UNIT,0,2,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_ANY_TYPE_SPECIFIER,0,0,NULL); + } + if (nTopStackRule == 0 && nTopStackTerm == 2) + { + if (pTopStackReturnNode->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_STRUCT && + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_BRACE) + { + // Transfer to rule 3. + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STRUCTURE_DEFINITION,pTopStackReturnNode,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTIONAL_UNIT,3,4,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_LIST,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_VARIABLE || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_INTEGER_IDENTIFIER || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_FLOAT_IDENTIFIER || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_STRING_IDENTIFIER || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_OBJECT_IDENTIFIER || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_STRUCTURE_IDENTIFIER || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_VOID_IDENTIFIER || + (m_nTokenStatus >= CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE0_IDENTIFIER && + m_nTokenStatus <= CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE9_IDENTIFIER)) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_FUNCTION_IDENTIFIER,pTopStackReturnNode,NULL); + m_pchToken[m_nTokenCharacters] = 0; + pNewNode->m_psStringData = new CExoString(m_pchToken); + + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_FUNCTION_DECLARATION,pNewNode,NULL); + CScriptParseTreeNode *pNewNode3 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_FUNCTION,pNewNode2,NULL); + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTIONAL_UNIT,0,3,pNewNode3); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_FUNCTION_DEFINITION_MISSING_NAME; + return nError; + } + } + if (nTopStackRule == 0 && nTopStackTerm == 3) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_BRACKET) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTIONAL_UNIT,0,5,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTION_PARAM_LIST,0,0,NULL); + return 0; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_COMMA || + m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_EQUAL) + { + // Move the code to the appropriate spot where we deal with + // global variables. + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTIONAL_UNIT,4,0,pTopStackCurrentNode); + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_FUNCTION_DEFINITION_MISSING_PARAMETER_LIST; + return nError; + } + + } + + if (nTopStackRule == 0 && nTopStackTerm == 5) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET) + { + pTopStackCurrentNode->pLeft->pRight = pTopStackReturnNode; + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTIONAL_UNIT,0,6,pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_MALFORMED_PARAMETER_LIST; + return nError; + } + + } + + // Here, we finally decide between a function declaration and + // a function implementation! + + if (nTopStackRule == 0 && nTopStackTerm == 6) + { + // At this point we have access to a full declaration of a + // function. Thus, we should make use of this, and write + // the identifier on to the list. + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + int32_t nReturnValue = AddUserDefinedIdentifier(pTopStackCurrentNode, FALSE); + ModifySRStackReturnTree(pTopStackCurrentNode); + return nReturnValue; + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_LEFT_BRACE) + { + int32_t nReturnValue = AddUserDefinedIdentifier(pTopStackCurrentNode, TRUE); + if (nReturnValue < 0) + { + return nReturnValue; + } + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTIONAL_UNIT,2,8,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT,1,2,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_WITHIN_COMPOUND_STATEMENT,0,0,NULL); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER; + return nError; + } + } + + if (nTopStackRule == 2 && nTopStackTerm == 8) + { + pTopStackCurrentNode->pRight = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + + if (nTopStackRule == 3 && nTopStackTerm == 4) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_RIGHT_BRACE) + { + pTopStackCurrentNode->pRight = pTopStackReturnNode; + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTIONAL_UNIT,3,5,pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_BAD_TYPE_SPECIFIER; + return nError; + } + + } + + if (nTopStackRule == 3 && nTopStackTerm == 5) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + AddToGlobalVariableList(pTopStackCurrentNode); + //ModifySRStackReturnTree(pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_SEMICOLON_AFTER_STRUCTURE; + return nError; + } + } + + if (nTopStackRule == 4 && nTopStackTerm == 0) + { + // Break the current tree, and build our own tree based on the fact + // that this is a declaration. + + // Create the nodes for specifying a declaration. + + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_KEYWORD_DECLARATION,NULL,NULL); + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT,pNewNode,NULL); + CScriptParseTreeNode *pNewNode3 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT_LIST,pNewNode2,NULL); + + if (pTopStackCurrentNode->pLeft->pLeft->pLeft->nOperation != CSCRIPTCOMPILER_OPERATION_CONST_DECLARATION) + { + // A variable type is at pTopStackCurrentNode->pLeft->pLeft->pLeft; + // Now, we attach the type declaration from the old branch. + pNewNode->pLeft = pTopStackCurrentNode->pLeft->pLeft->pLeft; + pTopStackCurrentNode->pLeft->pLeft->pLeft = NULL; + + } + else + { + // The const_declaration is at pTopStackCurrentNode->pLeft->pLeft->pLeft; + + // Remove the "const_declaration" operand from the type, and move it + // to the top of the statement list! + + // First attach the "proper" type to the keyword declaration object. + pNewNode->pLeft = pTopStackCurrentNode->pLeft->pLeft->pLeft->pLeft; + // Detach the const_declaration operand from its type. + pTopStackCurrentNode->pLeft->pLeft->pLeft->pLeft = NULL; + // Attach the const_declaration operand between the statement list and + // the first statement node. (Following the left chains ... + // pNewNode3->const_declaration->pNewNode2) + pNewNode3->pLeft = pTopStackCurrentNode->pLeft->pLeft->pLeft; + pNewNode3->pLeft->pLeft = pNewNode2; + } + + // Hand the statement list up the food chain. + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTIONAL_UNIT,4,3,pNewNode3); + + // Do not allow a void parameter. + if (pNewNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_KEYWORD_VOID) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_INVALID_DECLARATION_TYPE; + return nError; + } + + int32_t nStartOfDeclarationLine = pNewNode->pLeft->nLine; + pNewNode3->nLine = nStartOfDeclarationLine; + pNewNode2->nLine = nStartOfDeclarationLine; + + // If we have placed a const_declaration in place, give it the same + // line number, too! + if (pNewNode3->pLeft != pNewNode2) + { + pNewNode3->pLeft->nLine = nStartOfDeclarationLine; + } + + // Now, let's get the variable name from the old tree. + pNewNode->m_psStringData = new CExoString(pTopStackCurrentNode->pLeft->pLeft->m_psStringData->CStr()); + + // Let's verify that this is, in fact, a variable (see rule 13). + int32_t m_nCurrentTokenStatus = m_nTokenStatus; + + m_nTokenStatus = CSCRIPTCOMPILER_TOKEN_IDENTIFIER; + m_nTokenCharacters = pNewNode->m_psStringData->GetLength(); + memcpy(m_pchToken, pNewNode->m_psStringData->CStr(), m_nTokenCharacters); + int32_t nReturnValue = TestIdentifierToken(); + if (nReturnValue != 0) + { + return nReturnValue; + } + + if (m_nTokenStatus != CSCRIPTCOMPILER_TOKEN_VARIABLE) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_VARIABLE_LIST; + return nError; + } + + CScriptParseTreeNode *pNewNode5 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_VARIABLE, NULL, NULL); + m_pchToken[m_nTokenCharacters] = 0; + pNewNode5->m_psStringData = new CExoString(m_pchToken); + + CScriptParseTreeNode *pNewNode6 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_VARIABLE_LIST, pNewNode5, NULL); + + pNewNode->pRight = pNewNode6; + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_DECL_VARLIST,1,2,pNewNode2); + + // Restore the token, and delete the compile tree associated with + // the old branch. + m_nTokenStatus = m_nCurrentTokenStatus; + + DeleteScriptParseTreeNode(pTopStackCurrentNode->pLeft->pLeft); + DeleteScriptParseTreeNode(pTopStackCurrentNode->pLeft); + DeleteScriptParseTreeNode(pTopStackCurrentNode); + } + + if (nTopStackRule == 4 && nTopStackTerm == 3) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_SEMICOLON) + { + // Store the result into the global variable array. + + // If we have global variables, we should be dealing with + // them immediately by adding them to the identifier list. + if (pTopStackCurrentNode->pLeft != NULL && + pTopStackCurrentNode->pLeft->nOperation == CSCRIPTCOMPILER_OPERATION_CONST_DECLARATION) + { + int32_t nReturnValue = GenerateIdentifiersFromConstantVariables(pTopStackCurrentNode->pLeft->pLeft); + DeleteParseTree(FALSE, pTopStackCurrentNode); + if (nReturnValue < 0) + { + return nReturnValue; + } + return 0; + } + + AddToGlobalVariableList(pTopStackCurrentNode); + return 0; + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NO_SEMICOLON_AFTER_EXPRESSION; + return nError; + } + } + + break; + + ///////////////////////////////////////////////////////////////////////// + // case 0: + // program: + // (1) after-program + // (2) functional-unit program + // (3) #include string program + ////////////////////////////////////////////////////////////////////////// + + case CSCRIPTCOMPILER_GRAMMAR_PROGRAM: + + if (nTopStackRule == 0 && nTopStackTerm == 0) + { + if (pTopStackCurrentNode == NULL && m_nCompileFileLevel <= 1) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_AFTER_PROGRAM,0,0,NULL); + } + + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_EOF) + { + ModifySRStackReturnTree(NULL); + + // If we're in an included file ... we actually + // leave the routine here, instead of trying + // to hunt down the TOKEN_EOF in rule 1 ... we + // would have to pillage the entire stack of the + // file that called this routine to get to that + // one! + + if (m_nCompileFileLevel > 1) + { + return 0; + } + } + else if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_KEYWORD_INCLUDE) + { + // Note that this is at this level so that we + // can avoid giving a functional unit + // to an included file. This is the ONLY time + // that we really violate the structure of the + // grammar in this way. + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PROGRAM,3,1,pTopStackCurrentNode); + return 0; + } + else + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PROGRAM,1,2,NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_FUNCTIONAL_UNIT,0,0,NULL); + } + } + + if (nTopStackRule == 1 && nTopStackTerm == 2) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_FUNCTIONAL_UNIT, pTopStackReturnNode, NULL); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PROGRAM,1,3,pNewNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PROGRAM,0,0,pNewNode); + } + if (nTopStackRule == 1 && nTopStackTerm == 3) + { + pTopStackCurrentNode->pRight = pTopStackReturnNode; + ModifySRStackReturnTree(pTopStackCurrentNode); + } + + if (nTopStackRule == 3 && nTopStackTerm == 1) + { + if (m_nTokenStatus == CSCRIPTCOMPILER_TOKEN_STRING) + { + + CExoString sStringToCompile; + m_pchToken[m_nTokenCharacters] = 0; + (sStringToCompile).Format("%s",m_pchToken); + + // First things first, let's check to see if the file has + // already been included. If it has, ignore the directive + // completely! + BOOL bFileAlreadyIncluded = FALSE; + int32_t nCount = 0; + for (nCount = 0; nCount < m_nNextParseTreeFileName; nCount++) + { + if (m_ppsParseTreeFileNames[nCount]->CompareNoCase(sStringToCompile) == TRUE) + { + bFileAlreadyIncluded = TRUE; + } + } + + if (bFileAlreadyIncluded == TRUE) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PROGRAM,0,0,pTopStackCurrentNode); + return 0; + } + + // If we have got to here, the file hasn't already been included, + // and we should prepare to load it in! + + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PROGRAM,3,2,pTopStackCurrentNode); + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PROGRAM,0,0,pTopStackCurrentNode); + + // Want a TokenInitialize() here, since that is the + // state we'll be returning to once we've finished + // parsing this file. + TokenInitialize(); + return CompileFile(sStringToCompile); + } + } + + if (nTopStackRule == 3 && nTopStackTerm == 2) + { + // This is where we have to handle the reacquisition of + // the code from the previous script. pTopStackReturnNode + // points to a series of FUNCTIONAL_UNIT nodes. These + // have to be added to the queue as (0,1,3) units that + // are all detached from one another to allow the code + // for this script to operate correctly. + + CScriptParseTreeNode *pRunnerNode; + CScriptParseTreeNode *pNextNode; + CScriptParseTreeNode *pLastNode; + + pLastNode = NULL; + pRunnerNode = pTopStackReturnNode; + + while (pRunnerNode != NULL) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PROGRAM,1,3,pRunnerNode); + pLastNode = pRunnerNode; + pNextNode = pRunnerNode->pRight; + pRunnerNode->pRight = NULL; + pRunnerNode = pNextNode; + } + + // Now that we've inserted everything in place, set up + // to read the next functional unit! + if (pLastNode == NULL) + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PROGRAM,0,0,pTopStackCurrentNode); + } + else + { + PushSRStack(CSCRIPTCOMPILER_GRAMMAR_PROGRAM,0,0,pLastNode); + } + + } + break; + default: + int nError = STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER; + return nError; + } // case + } + + // To alleviate warnings from Warning Level 4. + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::AddUserDefinedIdentifier() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/17/2000 +// Description: Adds a user-defined identifier that has been compiled into the +// list. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::AddUserDefinedIdentifier(CScriptParseTreeNode *pFunctionDeclaration, BOOL bFunctionImplementation) +{ + + // First, get the identifier name which should be in + // pLeft->pLeft->pcStringData; + (m_pcIdentifierList[m_nOccupiedIdentifiers].m_psIdentifier) = *(pFunctionDeclaration->pLeft->pLeft->m_psStringData); + (m_pcIdentifierList[m_nOccupiedIdentifiers].m_nIdentifierHash) = HashString(*(pFunctionDeclaration->pLeft->pLeft->m_psStringData)); + (m_pcIdentifierList[m_nOccupiedIdentifiers].m_nIdentifierLength) = pFunctionDeclaration->pLeft->pLeft->m_psStringData->GetLength(); + + // Return Type is in pLeft->pLeft->pLeft ... read the + // operation to see the type of variable. + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nIdentifierType = 1; + + int32_t nOperationReturnType = pFunctionDeclaration->pLeft->pLeft->pLeft->nOperation; + + if (nOperationReturnType == CSCRIPTCOMPILER_OPERATION_KEYWORD_INT) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_INTEGER_IDENTIFIER; + } + else if (nOperationReturnType == CSCRIPTCOMPILER_OPERATION_KEYWORD_OBJECT) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_OBJECT_IDENTIFIER; + } + else if (nOperationReturnType == CSCRIPTCOMPILER_OPERATION_KEYWORD_FLOAT) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_FLOAT_IDENTIFIER; + } + else if (nOperationReturnType == CSCRIPTCOMPILER_OPERATION_KEYWORD_STRING) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_STRING_IDENTIFIER; + } + else if (nOperationReturnType == CSCRIPTCOMPILER_OPERATION_KEYWORD_VOID) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_VOID_IDENTIFIER; + } + else if (nOperationReturnType == CSCRIPTCOMPILER_OPERATION_KEYWORD_STRUCT) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_STRUCTURE_IDENTIFIER; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_psStructureReturnName = *(pFunctionDeclaration->pLeft->pLeft->pLeft->m_psStringData); + } + else if (nOperationReturnType >= CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE0 && + nOperationReturnType <= CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE9) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nReturnType = CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE0_IDENTIFIER + (nOperationReturnType - CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE0); + } + + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_FUNCTION_DECLARATION; + return nError; + } + + // User defined functions NEVER have a list here. + + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nIdIdentifier = -1; + + // The list of function types is availble via pLeft->pRight. + // The list of "parameter names" spawns off to the left ... + // This allows us to pull the variables off of the list in + // reverse stack order (i.e. 3rd parameter, 2nd parameter, + // first parameter) when executing this as a linked list! + // Thus, you're going to want to count the number of + // parameter names first, and then trace through them and + // discover the variable names. + + CScriptParseTreeNode *pNode = pFunctionDeclaration->pLeft->pRight; + int32_t nTotalParameters = 0; + + while (pNode != NULL) + { + ++nTotalParameters; + pNode = pNode->pLeft; + } + + while (nTotalParameters > m_pcIdentifierList[m_nOccupiedIdentifiers].m_nParameterSpace) + { + int nReturnValue = m_pcIdentifierList[m_nOccupiedIdentifiers].ExpandParameterSpace(); + if (nReturnValue < 0) + { + return nReturnValue; + } + } + + pNode = pFunctionDeclaration->pLeft->pRight; + int32_t nCurrentParameters = 0; + int32_t nParameterType; + BOOL bOptionalParametersStarted = FALSE; + + // MGB - If a previous declaration and implementation match, an identifier + // gets removed from the stack. Thus, m_nOccupiedIdentifiers should be cleared. + // This is the biggest "remaining" bug, since it prevents a future function from + // compiling. + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nParameters = 0; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nNonOptionalParameters = 0; + + if (pNode != NULL) + { + while (pNode != NULL) + { + nParameterType = pNode->pRight->nOperation; + char nNewParameterType; + + if (nParameterType == CSCRIPTCOMPILER_OPERATION_KEYWORD_INT) + { + nNewParameterType = CSCRIPTCOMPILER_TOKEN_KEYWORD_INT; + } + else if (nParameterType == CSCRIPTCOMPILER_OPERATION_KEYWORD_OBJECT) + { + nNewParameterType = CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT; + } + else if (nParameterType == CSCRIPTCOMPILER_OPERATION_KEYWORD_FLOAT) + { + nNewParameterType = CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT; + } + else if (nParameterType == CSCRIPTCOMPILER_OPERATION_KEYWORD_STRING) + { + nNewParameterType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING; + } + else if (nParameterType == CSCRIPTCOMPILER_OPERATION_KEYWORD_VOID) + { + nNewParameterType = CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID; + } + else if (nParameterType == CSCRIPTCOMPILER_OPERATION_KEYWORD_STRUCT) + { + nNewParameterType = CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT; + } + else if (nParameterType >= CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE0 && + nParameterType <= CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE9) + { + nNewParameterType = (char) (CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 + (nParameterType - CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE0)); + } + else + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_PARSING_FUNCTION_DECLARATION; + return nError; + } + + m_pcIdentifierList[m_nOccupiedIdentifiers].m_pchParameters[nCurrentParameters] = nNewParameterType; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_pbOptionalParameters[nCurrentParameters] = FALSE; + + // If we've started processing optional parameters for this function + // declaration, all further parameters MUST be optional. So sayeth Mark. + + if (bOptionalParametersStarted == TRUE && pNode->pRight->pRight == NULL) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NON_OPTIONAL_PARAMETER_CANNOT_FOLLOW_OPTIONAL_PARAMETER; + return nError; + } + + if (nNewParameterType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_psStructureParameterNames[nCurrentParameters] = *(pNode->pRight->m_psStringData); + if (pNode->pRight->pRight != NULL && *(pNode->pRight->m_psStringData) != "vector") + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_TYPE_DOES_NOT_HAVE_AN_OPTIONAL_PARAMETER; + return nError; + } + if (pNode->pRight->pRight != NULL && *(pNode->pRight->m_psStringData) == "vector") + { + CScriptParseTreeNode *pNodeToDelete = pNode->pRight->pRight; + + if (pNode->pRight->pRight->nOperation != CSCRIPTCOMPILER_OPERATION_CONSTANT_VECTOR) + { + pNode->pRight->pRight = NULL; + DeleteScriptParseTreeNode(pNodeToDelete); + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NON_CONSTANT_IN_FUNCTION_DECLARATION; + return nError; + } + m_pcIdentifierList[m_nOccupiedIdentifiers].m_pbOptionalParameters[nCurrentParameters] = TRUE; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_pfOptionalParameterVectorData[nCurrentParameters * 3] = pNode->pRight->pRight->fVectorData[0]; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_pfOptionalParameterVectorData[nCurrentParameters * 3 + 1] = pNode->pRight->pRight->fVectorData[1]; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_pfOptionalParameterVectorData[nCurrentParameters * 3 + 2] = pNode->pRight->pRight->fVectorData[2]; + bOptionalParametersStarted = TRUE; + pNode->pRight->pRight = NULL; + DeleteScriptParseTreeNode(pNodeToDelete); + + } + } + else if (nNewParameterType == CSCRIPTCOMPILER_TOKEN_KEYWORD_INT) + { + if (pNode->pRight->pRight != NULL) + { + CScriptParseTreeNode *pNodeToDelete = pNode->pRight->pRight; + + // MGB - March 25, 2003 - May have a constant or a constant with a negation attached to + // its right hand branch. This piece of code should be able to handle both for integers. + BOOL bValidConstant = FALSE; + + // Are we a valid constant? + if (pNode->pRight->pRight->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER || + (pNode->pRight->pRight->nOperation == CSCRIPTCOMPILER_OPERATION_NEGATION && + pNode->pRight->pRight->pRight->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER)) + { + bValidConstant = TRUE; + } + + // Delete things if we are not a valid constant, and error out. + if (bValidConstant == FALSE) + { + pNode->pRight->pRight = NULL; + if (pNodeToDelete->pRight != NULL) + { + DeleteScriptParseTreeNode(pNodeToDelete->pRight); + } + pNodeToDelete->pRight = NULL; + DeleteScriptParseTreeNode(pNodeToDelete); + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NON_CONSTANT_IN_FUNCTION_DECLARATION; + return nError; + } + + m_pcIdentifierList[m_nOccupiedIdentifiers].m_pbOptionalParameters[nCurrentParameters] = TRUE; + + if (pNode->pRight->pRight->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER) + { + // Store the fetched value. + m_pcIdentifierList[m_nOccupiedIdentifiers].m_pnOptionalParameterIntegerData[nCurrentParameters] = pNode->pRight->pRight->nIntegerData; + } + else + { + // Negate the fetched value, since we have a negation at the front of the constant. + m_pcIdentifierList[m_nOccupiedIdentifiers].m_pnOptionalParameterIntegerData[nCurrentParameters] = -(pNode->pRight->pRight->pRight->nIntegerData); + } + + bOptionalParametersStarted = TRUE; + + // Delete the appropriate nodes! + pNode->pRight->pRight = NULL; + if (pNodeToDelete->pRight != NULL) + { + DeleteScriptParseTreeNode(pNodeToDelete->pRight); + } + DeleteScriptParseTreeNode(pNodeToDelete); + + } + } + else if (nNewParameterType == CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT) + { + if (pNode->pRight->pRight != NULL) + { + CScriptParseTreeNode *pNodeToDelete = pNode->pRight->pRight; + + if (pNode->pRight->pRight->nOperation != CSCRIPTCOMPILER_OPERATION_CONSTANT_OBJECT) + { + pNode->pRight->pRight = NULL; + DeleteScriptParseTreeNode(pNodeToDelete); + + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NON_CONSTANT_IN_FUNCTION_DECLARATION; + return nError; + } + m_pcIdentifierList[m_nOccupiedIdentifiers].m_pbOptionalParameters[nCurrentParameters] = TRUE; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_poidOptionalParameterObjectData[nCurrentParameters] = pNode->pRight->pRight->nIntegerData; + bOptionalParametersStarted = TRUE; + pNode->pRight->pRight = NULL; + DeleteScriptParseTreeNode(pNodeToDelete); + + + } + + } + else if (nNewParameterType == CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT) + { + if (pNode->pRight->pRight != NULL) + { + CScriptParseTreeNode *pNodeToDelete = pNode->pRight->pRight; + + // MGB - March 25, 2003 - May have a constant or a constant with a negation attached to + // its right hand branch. This piece of code should be able to handle both for floating points. + BOOL bValidConstant = FALSE; + + // Are we a valid constant? + if (pNode->pRight->pRight->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_FLOAT || + (pNode->pRight->pRight->nOperation == CSCRIPTCOMPILER_OPERATION_NEGATION && + pNode->pRight->pRight->pRight->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_FLOAT)) + { + bValidConstant = TRUE; + } + + // Delete things if we are not a valid constant, and error out. + if (bValidConstant == FALSE) + { + pNode->pRight->pRight = NULL; + if (pNodeToDelete->pRight != NULL) + { + DeleteScriptParseTreeNode(pNodeToDelete->pRight); + } + pNodeToDelete->pRight = NULL; + DeleteScriptParseTreeNode(pNodeToDelete); + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NON_CONSTANT_IN_FUNCTION_DECLARATION; + return nError; + } + + m_pcIdentifierList[m_nOccupiedIdentifiers].m_pbOptionalParameters[nCurrentParameters] = TRUE; + + if (pNode->pRight->pRight->nOperation == CSCRIPTCOMPILER_OPERATION_CONSTANT_FLOAT) + { + // Store the fetched value. + m_pcIdentifierList[m_nOccupiedIdentifiers].m_pfOptionalParameterFloatData[nCurrentParameters] = pNode->pRight->pRight->fFloatData; + } + else + { + // Negate the fetched value, since we have a negation at the front of the constant. + m_pcIdentifierList[m_nOccupiedIdentifiers].m_pfOptionalParameterFloatData[nCurrentParameters] = -(pNode->pRight->pRight->pRight->fFloatData); + } + + bOptionalParametersStarted = TRUE; + + // Delete the appropriate nodes! + pNode->pRight->pRight = NULL; + if (pNodeToDelete->pRight != NULL) + { + DeleteScriptParseTreeNode(pNodeToDelete->pRight); + } + DeleteScriptParseTreeNode(pNodeToDelete); + } + } + else if (nNewParameterType == CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING) + { + if (pNode->pRight->pRight != NULL) + { + CScriptParseTreeNode *pNodeToDelete = pNode->pRight->pRight; + + if (pNode->pRight->pRight->nOperation != CSCRIPTCOMPILER_OPERATION_CONSTANT_STRING) + { + pNode->pRight->pRight = NULL; + DeleteScriptParseTreeNode(pNodeToDelete); + + + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NON_CONSTANT_IN_FUNCTION_DECLARATION; + return nError; + } + m_pcIdentifierList[m_nOccupiedIdentifiers].m_pbOptionalParameters[nCurrentParameters] = TRUE; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_psOptionalParameterStringData[nCurrentParameters] = *(pNode->pRight->pRight->m_psStringData); + bOptionalParametersStarted = TRUE; + pNode->pRight->pRight = NULL; + DeleteScriptParseTreeNode(pNodeToDelete); + + + } + } + + else if (nNewParameterType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE2 /* location */) + { + if (pNode->pRight->pRight != NULL) + { + CScriptParseTreeNode *pNodeToDelete = pNode->pRight->pRight; + + if (pNode->pRight->pRight->nOperation != CSCRIPTCOMPILER_OPERATION_CONSTANT_LOCATION) + { + pNode->pRight->pRight = NULL; + DeleteScriptParseTreeNode(pNodeToDelete); + + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NON_CONSTANT_IN_FUNCTION_DECLARATION; + return nError; + } + m_pcIdentifierList[m_nOccupiedIdentifiers].m_pbOptionalParameters[nCurrentParameters] = TRUE; + // LOCATION constants reusing the integer data because i'm lazy. If you ever add more than LOCATION_INVALID + // you will probably have to expand that to carry actual locations. While you're at that, move the + // CScriptLocation out of sharedcore and into include/ somewhere. + m_pcIdentifierList[m_nOccupiedIdentifiers].m_pnOptionalParameterIntegerData[nCurrentParameters] = pNode->pRight->pRight->nIntegerData; + bOptionalParametersStarted = TRUE; + pNode->pRight->pRight = NULL; + DeleteScriptParseTreeNode(pNodeToDelete); + } + } + + else if (nNewParameterType == CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE7 /* json */) + { + if (pNode->pRight->pRight != NULL) + { + CScriptParseTreeNode *pNodeToDelete = pNode->pRight->pRight; + + if (pNode->pRight->pRight->nOperation != CSCRIPTCOMPILER_OPERATION_CONSTANT_JSON) + { + pNode->pRight->pRight = NULL; + DeleteScriptParseTreeNode(pNodeToDelete); + + int nError = STRREF_CSCRIPTCOMPILER_ERROR_NON_CONSTANT_IN_FUNCTION_DECLARATION; + return nError; + } + m_pcIdentifierList[m_nOccupiedIdentifiers].m_pbOptionalParameters[nCurrentParameters] = TRUE; + // Json data is loaded into the string array, same as strings. all json data is kept as raw + // strings while living inside the compiler; only when hitting the VM it attempts to parse. + // (BORLAND is too simple to understand nlohmann::json) + m_pcIdentifierList[m_nOccupiedIdentifiers].m_psOptionalParameterStringData[nCurrentParameters] = *pNode->pRight->pRight->m_psStringData; + bOptionalParametersStarted = TRUE; + pNode->pRight->pRight = NULL; + DeleteScriptParseTreeNode(pNodeToDelete); + } + } + + else if (nNewParameterType == CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID || + (nNewParameterType >= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 && + nNewParameterType <= CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9)) + { + if (pNode->pRight->pRight != NULL) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_TYPE_DOES_NOT_HAVE_AN_OPTIONAL_PARAMETER; + return nError; + } + } + + ++nCurrentParameters; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nParameters = nCurrentParameters; + if (bOptionalParametersStarted == FALSE) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nNonOptionalParameters = nCurrentParameters; + } + + pNode = pNode->pLeft; + + } + } + + // Check to see if the function is already in the list. If + // it is, then we don't need to do anything about this one. + + int32_t nHashLocation = GetHashEntryByName(m_pcIdentifierList[m_nOccupiedIdentifiers].m_psIdentifier.CStr()); + BOOL bFoundIdenticalFunction = FALSE; + + if (nHashLocation != STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER) + { + int32_t nIdentifierType = m_pIdentifierHashTable[nHashLocation].m_nIdentifierType; + int32_t nIdentifierIndex = m_pIdentifierHashTable[nHashLocation].m_nIdentifierIndex; + + if (nIdentifierType == CSCRIPTCOMPILER_HASH_MANAGER_TYPE_IDENTIFIER) + { + if (m_pcIdentifierList[nIdentifierIndex].m_nIdentifierType == 1) + { + if (m_pcIdentifierList[nIdentifierIndex].m_nParameters == m_pcIdentifierList[m_nOccupiedIdentifiers].m_nParameters) + { + BOOL bParameterListDifferent = FALSE; + + int32_t count2; + for (count2 = 0; count2 < m_pcIdentifierList[nIdentifierIndex].m_nParameters; count2++) + { + if ((m_pcIdentifierList[nIdentifierIndex].m_pchParameters[count2] != m_pcIdentifierList[m_nOccupiedIdentifiers].m_pchParameters[count2]) || + (m_pcIdentifierList[nIdentifierIndex].m_pchParameters[count2] == CSCRIPTCOMPILER_TOKEN_STRUCTURE_IDENTIFIER && + m_pcIdentifierList[nIdentifierIndex].m_psStructureParameterNames[count2] != m_pcIdentifierList[m_nOccupiedIdentifiers].m_psStructureParameterNames[count2])) + { + bParameterListDifferent = TRUE; + } + } + + if (bParameterListDifferent == FALSE) + { + bFoundIdenticalFunction = TRUE; + if (bFunctionImplementation == TRUE) + { + if (m_pcIdentifierList[nIdentifierIndex].m_bImplementationInPlace == TRUE) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_DUPLICATE_FUNCTION_IMPLEMENTATION; + // If you don't want the name of the duplicate function. + //return OutputWalkTreeError(nError, NULL); + // If you want the name of the duplicate function, use this. + return OutputIdentifierError(m_pcIdentifierList[nIdentifierIndex].m_psIdentifier,nError,1); + } + else + { + m_pcIdentifierList[nIdentifierIndex].m_bImplementationInPlace = TRUE; + } + } + } + } + + // If the function name is the same, it better be identical, or + // we should gork right here. + if (bFoundIdenticalFunction == FALSE) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_FUNCTION_IMPLEMENTATION_AND_DEFINITION_DIFFER; + return nError; + } + + } + } + } + + + if (bFoundIdenticalFunction == FALSE) + { + m_pcIdentifierList[m_nOccupiedIdentifiers].m_bImplementationInPlace = bFunctionImplementation; + + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nBinarySourceStart = -1; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nBinarySourceFinish = -1; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nBinaryDestinationStart = -1; + m_pcIdentifierList[m_nOccupiedIdentifiers].m_nBinaryDestinationFinish = -1; + + HashManagerAdd(CSCRIPTCOMPILER_HASH_MANAGER_TYPE_IDENTIFIER, m_nOccupiedIdentifiers); + + m_nOccupiedIdentifiers++; + if (m_nOccupiedIdentifiers >= CSCRIPTCOMPILER_MAX_IDENTIFIERS) + { + int nError = STRREF_CSCRIPTCOMPILER_ERROR_IDENTIFIER_LIST_FULL; + return nError; + } + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::AddToGlobalVariableList +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/09/2001 +// Description: Adds the current statement to the global variable compile tree. +/////////////////////////////////////////////////////////////////////////////// +int32_t CScriptCompiler::AddToGlobalVariableList(CScriptParseTreeNode *pGlobalVariableNode) +{ + if (m_pGlobalVariableParseTree == NULL) + { + CScriptParseTreeNode *pNewNode = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_GLOBAL_VARIABLES,NULL,NULL); + m_pGlobalVariableParseTree = pNewNode; + } + + // Create a new "statement" for this declaration. + CScriptParseTreeNode *pNewNode2 = CreateScriptParseTreeNode(CSCRIPTCOMPILER_OPERATION_STATEMENT,pGlobalVariableNode,NULL); + + // This guarantees the connection will be made between the variable + // declaration and the line it is on, not the line the statement + // terminates on. + if (pGlobalVariableNode != NULL) + { + pNewNode2->nLine = pGlobalVariableNode->nLine; + } + + + // Look for the last statement in the list of statements and attach + // this node to them. + CScriptParseTreeNode *pFindLastStatement = m_pGlobalVariableParseTree; + + while (pFindLastStatement->pRight != NULL) + { + pFindLastStatement = pFindLastStatement->pRight; + } + + pFindLastStatement->pRight = pNewNode2; + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::PrintParseSourceError() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/15/2001 +// Description: This routine will generate an error log based on the value +// passed in. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::PrintParseSourceError(int32_t nParsingError) +{ + CExoString strRes = m_cAPI.TlkResolve(-nParsingError); + + CExoString *psFileName = &(m_pcIncludeFileStack[m_nCompileFileLevel-1].m_sCompiledScriptName); + CExoString sErrorText; + + if (nParsingError != STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER) + { + sErrorText.Format("%s",strRes.CStr()); + } + else + { + sErrorText.Format("%s (%s)",strRes.CStr(),m_sUndefinedIdentifier.CStr()); + } + + OutputError(nParsingError,psFileName,m_nLines,sErrorText); + + nParsingError = STRREF_CSCRIPTCOMPILER_ERROR_ALREADY_PRINTED; + + return CleanUpDuringCompile(nParsingError); +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::DeleteCompileStack() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 08/05/99 +// Description: This routine will delete all of the nodes that can be +// accessed from the compiler's run time stack. +/////////////////////////////////////////////////////////////////////////////// + +void CScriptCompiler::DeleteCompileStack() +{ + int32_t i; + for (i=0; i <= m_nSRStackStates; i++) + { + if (m_pSRStack[i].pCurrentTree != NULL) + { + DeleteParseTree(TRUE,m_pSRStack[i].pCurrentTree); + } + if (m_pSRStack[i].pReturnTree != NULL) + { + DeleteParseTree(TRUE,m_pSRStack[i].pReturnTree); + } + } + +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::CleanUpDuringCompile() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/26/2000 +// Description: This routine will delete all of the data associated with the +// current compile that is going on. +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::CleanUpDuringCompile(int32_t nReturnValue) +{ + DeleteCompileStack(); + --m_nCompileFileLevel; + if (m_nCompileFileLevel > 0) + { + ShutdownIncludeFile(m_nCompileFileLevel); + } + DeleteParseTree(FALSE, m_pGlobalVariableParseTree); + m_pGlobalVariableParseTree = NULL; + ClearUserDefinedIdentifiers(); + ClearAllSymbolLists(); + return nReturnValue; +} + +/////////////////////////////////////////////////////////////////////////////// +// CScriptCompiler::ParseSource() +/////////////////////////////////////////////////////////////////////////////// +// Created By: Mark Brockington +// Created On: 01/15/2001 +// Description: This routine will generate a parse tree from the string +// specified in pScript (of length nScriptLength). +/////////////////////////////////////////////////////////////////////////////// + +int32_t CScriptCompiler::ParseSource(char *pScript, int32_t nScriptLength) +{ + + int32_t i; // location in the string + int32_t nParseNextCharReturn; // returned value from ParseNextCharacter + + int32_t ch; // character at location pScript[i] + int32_t chNext; // character at location pScript[i+1]. Yes, I freakin' cheat and + // look ahead a character. Gonna make something out of it, weasel boy? + // I didn't think so. + + if (m_nOccupiedIdentifiers == 0) + { + int32_t nReturnValue = ParseIdentifierFile(); + if (nReturnValue < 0) + { + return nReturnValue; + } + } + + /////////////////////////////////////////////// + // + // Initialize with the first two characters. + // + /////////////////////////////////////////////// + + if ( nScriptLength < 1 ) + { + ch = -1; + } + else + { + ch = pScript[0]; + } + + if ( nScriptLength < 2 ) + { + chNext = -1; + } + else + { + chNext = pScript[1]; + } + + i = 2; + + /////////////////////////////////////////////// + // + // Loop through all remaining characters. + // + /////////////////////////////////////////////// + + while (ch != -1) + { + nParseNextCharReturn = ParseNextCharacter(ch,chNext,pScript + i,nScriptLength - i); + + if (nParseNextCharReturn < 0) + { + return PrintParseSourceError(nParseNextCharReturn); + } + + while (nParseNextCharReturn >= 0) + { + // Process the location of the next character. + if (ch == '\n') + { + ++m_nLines; + m_nCharacterOnLine = 1; + } + else + { + ++m_nCharacterOnLine; + } + + // Fetch the "next" character and update the status + // of the current character that we are looking at. + ch = chNext; + + if ( i >= nScriptLength ) + { + chNext = -1; + } + else + { + chNext = (unsigned char) pScript[i]; + } + + --nParseNextCharReturn; + + ++i; + } + + } + + /////////////////////////////////////////////// + // + // Parse an EOF. + // + /////////////////////////////////////////////// + + nParseNextCharReturn = ParseNextCharacter(-1,-1, nullptr, 0); + + if (nParseNextCharReturn < 0) + { + + return PrintParseSourceError(nParseNextCharReturn); + } + + return 0; +} diff --git a/src/Native Compiler/scripterrors.h b/src/Native Compiler/scripterrors.h new file mode 100644 index 0000000..19cc4f2 --- /dev/null +++ b/src/Native Compiler/scripterrors.h @@ -0,0 +1,175 @@ +// +// SPDX-License-Identifier: GPL-3.0 +// +// This file is part of the NWScript compiler open source release. +// +// The initial source release is licensed under GPL-3.0. +// +// All subsequent changes you submit are required to be licensed under MIT. +// +// However, the project overall will still be GPL-3.0. +// +// The intent is for the base game to be able to pick up changes you explicitly +// submit for inclusion painlessly, while ensuring the overall project source code +// remains available for everyone. +// + +/////////////////////////////////////////////////////////////////////////////// +// BIOWARE CORP. CONFIDENTIAL INFORMATION. // +// COPYRIGHT BIOWARE CORP. ALL RIGHTS RESERVED // +/////////////////////////////////////////////////////////////////////////////// + +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Script Project +//:: +//:: Copyright (c) 2002, BioWare Corp. +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: ScriptErrors.h +//:: +//:: A project-specific header file, specifying error constants that relate +//:: to string references in the game code. +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Created By: Mark Brockington +//:: Created On: Oct. 24, 2002 +//:: +//:://///////////////////////////////////////////////////////////////////////// + +// All error codes in the following file relate to negative values of string references. + +#ifndef __SCRIPTERRORS_H__ +#define __SCRIPTERRORS_H__ + +#define STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_CHARACTER -560 +#define STRREF_CSCRIPTCOMPILER_ERROR_FATAL_COMPILER_ERROR -561 +#define STRREF_CSCRIPTCOMPILER_ERROR_PROGRAM_COMPOUND_STATEMENT_AT_START -562 +#define STRREF_CSCRIPTCOMPILER_ERROR_UNEXPECTED_END_COMPOUND_STATEMENT -563 +#define STRREF_CSCRIPTCOMPILER_ERROR_AFTER_COMPOUND_STATEMENT_AT_END -564 +#define STRREF_CSCRIPTCOMPILER_ERROR_PARSING_VARIABLE_LIST -565 +#define STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_STATE_IN_COMPILER -566 +#define STRREF_CSCRIPTCOMPILER_ERROR_INVALID_DECLARATION_TYPE -567 +#define STRREF_CSCRIPTCOMPILER_ERROR_NO_LEFT_BRACKET_ON_EXPRESSION -568 +#define STRREF_CSCRIPTCOMPILER_ERROR_NO_RIGHT_BRACKET_ON_EXPRESSION -569 +#define STRREF_CSCRIPTCOMPILER_ERROR_BAD_START_OF_STATEMENT -570 +#define STRREF_CSCRIPTCOMPILER_ERROR_NO_LEFT_BRACKET_ON_ARG_LIST -571 +#define STRREF_CSCRIPTCOMPILER_ERROR_NO_RIGHT_BRACKET_ON_ARG_LIST -572 +#define STRREF_CSCRIPTCOMPILER_ERROR_NO_SEMICOLON_AFTER_EXPRESSION -573 +#define STRREF_CSCRIPTCOMPILER_ERROR_PARSING_ASSIGNMENT_STATEMENT -574 +#define STRREF_CSCRIPTCOMPILER_ERROR_BAD_LVALUE -575 +#define STRREF_CSCRIPTCOMPILER_ERROR_BAD_CONSTANT_TYPE -576 +#define STRREF_CSCRIPTCOMPILER_ERROR_IDENTIFIER_LIST_FULL -577 +#define STRREF_CSCRIPTCOMPILER_ERROR_NON_INTEGER_ID_FOR_INTEGER_CONSTANT -578 +#define STRREF_CSCRIPTCOMPILER_ERROR_NON_FLOAT_ID_FOR_FLOAT_CONSTANT -579 +#define STRREF_CSCRIPTCOMPILER_ERROR_NON_STRING_ID_FOR_STRING_CONSTANT -580 +#define STRREF_CSCRIPTCOMPILER_ERROR_VARIABLE_ALREADY_USED_WITHIN_SCOPE -581 +#define STRREF_CSCRIPTCOMPILER_ERROR_VARIABLE_DEFINED_WITHOUT_TYPE -582 +#define STRREF_CSCRIPTCOMPILER_ERROR_INCORRECT_VARIABLE_STATE_LEFT_ON_STACK -583 +#define STRREF_CSCRIPTCOMPILER_ERROR_NON_INTEGER_EXPRESSION_WHERE_INTEGER_REQUIRED -584 +#define STRREF_CSCRIPTCOMPILER_ERROR_VOID_EXPRESSION_WHERE_NON_VOID_REQUIRED -585 +#define STRREF_CSCRIPTCOMPILER_ERROR_INVALID_PARAMETERS_FOR_ASSIGNMENT -586 +#define STRREF_CSCRIPTCOMPILER_ERROR_DECLARATION_DOES_NOT_MATCH_PARAMETERS -587 +#define STRREF_CSCRIPTCOMPILER_ERROR_LOGICAL_OPERATION_HAS_INVALID_OPERANDS -588 +#define STRREF_CSCRIPTCOMPILER_ERROR_EQUALITY_TEST_HAS_INVALID_OPERANDS -589 +#define STRREF_CSCRIPTCOMPILER_ERROR_COMPARISON_TEST_HAS_INVALID_OPERANDS -590 +#define STRREF_CSCRIPTCOMPILER_ERROR_SHIFT_OPERATION_HAS_INVALID_OPERANDS -591 +#define STRREF_CSCRIPTCOMPILER_ERROR_ARITHMETIC_OPERATION_HAS_INVALID_OPERANDS -592 +#define STRREF_CSCRIPTCOMPILER_ERROR_UNKNOWN_OPERATION_IN_SEMANTIC_CHECK -593 +#define STRREF_CSCRIPTCOMPILER_ERROR_SCRIPT_TOO_LARGE -594 +#define STRREF_CSCRIPTCOMPILER_ERROR_RETURN_STATEMENT_HAS_NO_PARAMETERS -595 +#define STRREF_CSCRIPTCOMPILER_ERROR_NO_WHILE_AFTER_DO_KEYWORD -596 +#define STRREF_CSCRIPTCOMPILER_ERROR_FUNCTION_DEFINITION_MISSING_NAME -597 +#define STRREF_CSCRIPTCOMPILER_ERROR_FUNCTION_DEFINITION_MISSING_PARAMETER_LIST -598 +#define STRREF_CSCRIPTCOMPILER_ERROR_MALFORMED_PARAMETER_LIST -599 +#define STRREF_CSCRIPTCOMPILER_ERROR_BAD_TYPE_SPECIFIER -600 +#define STRREF_CSCRIPTCOMPILER_ERROR_NO_SEMICOLON_AFTER_STRUCTURE -601 +#define STRREF_CSCRIPTCOMPILER_ERROR_ELLIPSIS_IN_IDENTIFIER -602 +#define STRREF_CSCRIPTCOMPILER_ERROR_FILE_NOT_FOUND -603 +#define STRREF_CSCRIPTCOMPILER_ERROR_INCLUDE_RECURSIVE -604 +#define STRREF_CSCRIPTCOMPILER_ERROR_INCLUDE_TOO_MANY_LEVELS -605 +#define STRREF_CSCRIPTCOMPILER_ERROR_PARSING_RETURN_STATEMENT -606 +#define STRREF_CSCRIPTCOMPILER_ERROR_PARSING_IDENTIFIER_LIST -607 +#define STRREF_CSCRIPTCOMPILER_ERROR_PARSING_FUNCTION_DECLARATION -608 +#define STRREF_CSCRIPTCOMPILER_ERROR_DUPLICATE_FUNCTION_IMPLEMENTATION -609 +#define STRREF_CSCRIPTCOMPILER_ERROR_TOKEN_TOO_LONG -610 +#define STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_STRUCTURE -611 +#define STRREF_CSCRIPTCOMPILER_ERROR_LEFT_OF_STRUCTURE_PART_NOT_STRUCTURE - 612 +#define STRREF_CSCRIPTCOMPILER_ERROR_RIGHT_OF_STRUCTURE_PART_NOT_FIELD_IN_STRUCTURE -613 +#define STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_FIELD_IN_STRUCTURE -614 +#define STRREF_CSCRIPTCOMPILER_ERROR_STRUCTURE_REDEFINED -615 +#define STRREF_CSCRIPTCOMPILER_ERROR_VARIABLE_USED_TWICE_IN_SAME_STRUCTURE -616 +#define STRREF_CSCRIPTCOMPILER_ERROR_FUNCTION_IMPLEMENTATION_AND_DEFINITION_DIFFER -617 +#define STRREF_CSCRIPTCOMPILER_ERROR_MISMATCHED_TYPES -618 +#define STRREF_CSCRIPTCOMPILER_ERROR_INTEGER_NOT_AT_TOP_OF_STACK -619 +#define STRREF_CSCRIPTCOMPILER_ERROR_RETURN_TYPE_AND_FUNCTION_TYPE_MISMATCHED -620 +#define STRREF_CSCRIPTCOMPILER_ERROR_NOT_ALL_CONTROL_PATHS_RETURN_A_VALUE -621 +#define STRREF_CSCRIPTCOMPILER_ERROR_UNDEFINED_IDENTIFIER -622 +#define STRREF_CSCRIPTCOMPILER_ERROR_NO_FUNCTION_MAIN_IN_SCRIPT -623 +#define STRREF_CSCRIPTCOMPILER_ERROR_FUNCTION_MAIN_MUST_HAVE_VOID_RETURN_VALUE -624 +#define STRREF_CSCRIPTCOMPILER_ERROR_FUNCTION_MAIN_MUST_HAVE_NO_PARAMETERS -625 +#define STRREF_CSCRIPTCOMPILER_ERROR_NON_VOID_FUNCTION_CANNOT_BE_A_STATEMENT -626 +#define STRREF_CSCRIPTCOMPILER_ERROR_BAD_VARIABLE_NAME -627 +#define STRREF_CSCRIPTCOMPILER_ERROR_NON_OPTIONAL_PARAMETER_CANNOT_FOLLOW_OPTIONAL_PARAMETER -628 +#define STRREF_CSCRIPTCOMPILER_ERROR_TYPE_DOES_NOT_HAVE_AN_OPTIONAL_PARAMETER -629 +#define STRREF_CSCRIPTCOMPILER_ERROR_NON_CONSTANT_IN_FUNCTION_DECLARATION -630 +#define STRREF_CSCRIPTCOMPILER_ERROR_PARSING_CONSTANT_VECTOR -631 +#define STRREF_CSCRIPTCOMPILER_ERROR_OPERAND_MUST_BE_AN_INTEGER_LVALUE -1594 +#define STRREF_CSCRIPTCOMPILER_ERROR_CONDITIONAL_REQUIRES_SECOND_EXPRESSION -1595 +#define STRREF_CSCRIPTCOMPILER_ERROR_CONDITIONAL_MUST_HAVE_MATCHING_RETURN_TYPES -1596 +#define STRREF_CSCRIPTCOMPILER_ERROR_MULTIPLE_DEFAULT_STATEMENTS_WITHIN_SWITCH -1597 +#define STRREF_CSCRIPTCOMPILER_ERROR_MULTIPLE_CASE_CONSTANT_STATEMENTS_WITHIN_SWITCH -1598 +#define STRREF_CSCRIPTCOMPILER_ERROR_CASE_PARAMETER_NOT_A_CONSTANT_INTEGER -1599 +#define STRREF_CSCRIPTCOMPILER_ERROR_SWITCH_MUST_EVALUATE_TO_AN_INTEGER -1600 +#define STRREF_CSCRIPTCOMPILER_ERROR_NO_COLON_AFTER_DEFAULT_LABEL -1601 +#define STRREF_CSCRIPTCOMPILER_ERROR_NO_COLON_AFTER_CASE_LABEL -1602 +#define STRREF_CSCRIPTCOMPILER_ERROR_NO_SEMICOLON_AFTER_STATEMENT -1603 +#define STRREF_CSCRIPTCOMPILER_ERROR_BREAK_OUTSIDE_OF_LOOP_OR_CASE_STATEMENT -4834 +#define STRREF_CSCRIPTCOMPILER_ERROR_TOO_MANY_PARAMETERS_ON_FUNCTION -4835 +#define STRREF_CSCRIPTCOMPILER_ERROR_UNABLE_TO_OPEN_FILE_FOR_WRITING -4836 +#define STRREF_CSCRIPTCOMPILER_ERROR_UNTERMINATED_STRING_CONSTANT -4855 +#define STRREF_CSCRIPTCOMPILER_ERROR_NO_FUNCTION_INTSC_IN_SCRIPT -5182 +#define STRREF_CSCRIPTCOMPILER_ERROR_FUNCTION_INTSC_MUST_HAVE_VOID_RETURN_VALUE -5183 +#define STRREF_CSCRIPTCOMPILER_ERROR_FUNCTION_INTSC_MUST_HAVE_NO_PARAMETERS -5184 +#define STRREF_CSCRIPTCOMPILER_ERROR_JUMPING_OVER_DECLARATION_STATEMENTS_CASE_DISALLOWED -6804 +#define STRREF_CSCRIPTCOMPILER_ERROR_JUMPING_OVER_DECLARATION_STATEMENTS_DEFAULT_DISALLOWED -6805 +#define STRREF_CSCRIPTCOMPILER_ERROR_ELSE_WITHOUT_CORRESPONDING_IF -6823 +#define STRREF_CSCRIPTCOMPILER_ERROR_IF_CONDITION_CANNOT_BE_FOLLOWED_BY_A_NULL_STATEMENT -10407 +// New Errors! +#define STRREF_CSCRIPTCOMPILER_ERROR_INVALID_TYPE_FOR_CONST_KEYWORD -3741 +#define STRREF_CSCRIPTCOMPILER_ERROR_CONST_KEYWORD_CANNOT_BE_USED_ON_NON_GLOBAL_VARIABLES -3742 +#define STRREF_CSCRIPTCOMPILER_ERROR_INVALID_VALUE_ASSIGNED_TO_CONSTANT -3752 +#define STRREF_CSCRIPTCOMPILER_ERROR_SWITCH_CONDITION_CANNOT_BE_FOLLOWED_BY_A_NULL_STATEMENT -9081 +#define STRREF_CSCRIPTCOMPILER_ERROR_WHILE_CONDITION_CANNOT_BE_FOLLOWED_BY_A_NULL_STATEMENT -9082 +#define STRREF_CSCRIPTCOMPILER_ERROR_FOR_STATEMENT_CANNOT_BE_FOLLOWED_BY_A_NULL_STATEMENT -9083 +#define STRREF_CSCRIPTCOMPILER_ERROR_CANNOT_INCLUDE_THIS_FILE_TWICE -9155 +#define STRREF_CSCRIPTCOMPILER_ERROR_ELSE_CANNOT_BE_FOLLOWED_BY_A_NULL_STATEMENT -40104 + +#define STRREF_CVIRTUALMACHINE_ERROR_TOO_MANY_INSTRUCTIONS -632 +#define STRREF_CVIRTUALMACHINE_ERROR_TOO_MANY_LEVELS_OF_RECURSION -633 +#define STRREF_CVIRTUALMACHINE_ERROR_FILE_NOT_OPENED -634 +#define STRREF_CVIRTUALMACHINE_ERROR_FILE_NOT_COMPILED_SUCCESSFULLY -635 +#define STRREF_CVIRTUALMACHINE_ERROR_INVALID_AUX_CODE -636 +#define STRREF_CVIRTUALMACHINE_ERROR_NULL_VIRTUAL_MACHINE_NODE -637 +#define STRREF_CVIRTUALMACHINE_ERROR_STACK_OVERFLOW -638 +#define STRREF_CVIRTUALMACHINE_ERROR_STACK_UNDERFLOW -639 +#define STRREF_CVIRTUALMACHINE_ERROR_INVALID_OP_CODE -640 +#define STRREF_CVIRTUALMACHINE_ERROR_INVALID_EXTRA_DATA_ON_OP_CODE -641 +#define STRREF_CVIRTUALMACHINE_ERROR_INVALID_COMMAND -642 +#define STRREF_CVIRTUALMACHINE_ERROR_FAKE_SHORTCUT_LOGICAL_OPERATION -643 +#define STRREF_CVIRTUALMACHINE_ERROR_DIVIDE_BY_ZERO -644 +#define STRREF_CVIRTUALMACHINE_ERROR_FAKE_ABORT_SCRIPT -645 +#define STRREF_CVIRTUALMACHINE_ERROR_IP_OUT_OF_CODE_SEGMENT -646 +#define STRREF_CVIRTUALMACHINE_ERROR_COMMAND_IMPLEMENTER_NOT_SET -647 +#define STRREF_CVIRTUALMACHINE_ERROR_UNKNOWN_TYPE_ON_RUN_TIME_STACK -648 + +// This is not actually a string reference, but a separate error code +// to prevent the same error from being reported inside an include file +// and in the main file that contains the include. So, you don't have to +// put this one into the string table. +// THIS ERROR CAN NOT MATCH ANY OF THE OTHER ERRORS LISTED IN THIS FILE! +#define STRREF_CSCRIPTCOMPILER_ERROR_ALREADY_PRINTED -1 + +#endif // __SCRIPTERRORS_H__ diff --git a/src/Native Compiler/scriptinternal.h b/src/Native Compiler/scriptinternal.h new file mode 100644 index 0000000..fb2f7d3 --- /dev/null +++ b/src/Native Compiler/scriptinternal.h @@ -0,0 +1,772 @@ +// +// SPDX-License-Identifier: GPL-3.0 +// +// This file is part of the NWScript compiler open source release. +// +// The initial source release is licensed under GPL-3.0. +// +// All subsequent changes you submit are required to be licensed under MIT. +// +// However, the project overall will still be GPL-3.0. +// +// The intent is for the base game to be able to pick up changes you explicitly +// submit for inclusion painlessly, while ensuring the overall project source code +// remains available for everyone. +// + +/////////////////////////////////////////////////////////////////////////////// +// BIOWARE CORP. CONFIDENTIAL INFORMATION. // +// COPYRIGHT BIOWARE CORP. ALL RIGHTS RESERVED // +/////////////////////////////////////////////////////////////////////////////// + +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Script Project +//:: +//:: Copyright (c) 2002, BioWare Corp. +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: ScriptInternal.h +//:: +//:: Header file for constants and non-exposed classes used inside Script +//:: project. +//:: +//:://///////////////////////////////////////////////////////////////////////// +//:: +//:: Created By: Mark Brockington +//:: Created On: Oct. 8, 2002 +//:: +//:://///////////////////////////////////////////////////////////////////////// + +#ifndef __SCRIPTINTERNAL_H__ +#define __SCRIPTINTERNAL_H__ + +#define CSCRIPTCOMPILER_MAX_STACK_ENTRIES 1024 +#define CSCRIPTCOMPILER_MAX_OPERATIONS 88 +#define CSCRIPTCOMPILER_MAX_IDENTIFIERS 65536 +#define CSCRIPTCOMPILER_SIZE_IDENTIFIER_HASH_TABLE 131072 // NOTE: This should be larger than MAX_IDENTIFIERS +#define CSCRIPTCOMPILER_MASK_SIZE_IDENTIFIER_HASH_TABLE 0x0001ffff +#define CSCRIPTCOMPILER_MAX_VARIABLES 1024 +#define CSCRIPTCOMPILER_MAX_CODE_SIZE 524288 // 512K. +#define CSCRIPTCOMPILER_MAX_DEBUG_OUTPUT_SIZE 2097152 // 2048K, 1048576 = 1024K. +#define CSCRIPTCOMPILER_MAX_STRUCTURES 256 +#define CSCRIPTCOMPILER_MAX_STRUCTURE_FIELDS 4096 +#define CSCRIPTCOMPILER_MAX_KEYWORDS 42 + +#define CSCRIPTCOMPILER_BINARY_ADDRESS_LENGTH 13 + + +#define CSCRIPTCOMPILER_TOKEN_UNKNOWN 0 +#define CSCRIPTCOMPILER_TOKEN_DIVIDE 1 +#define CSCRIPTCOMPILER_TOKEN_CPLUSCOMMENT 2 +#define CSCRIPTCOMPILER_TOKEN_CCOMMENT 3 +#define CSCRIPTCOMPILER_TOKEN_INTEGER 4 +#define CSCRIPTCOMPILER_TOKEN_FLOAT 5 +#define CSCRIPTCOMPILER_TOKEN_IDENTIFIER 6 +#define CSCRIPTCOMPILER_TOKEN_STRING 7 +#define CSCRIPTCOMPILER_TOKEN_LOGICAL_AND 8 +#define CSCRIPTCOMPILER_TOKEN_LOGICAL_OR 9 +#define CSCRIPTCOMPILER_TOKEN_MINUS 10 +#define CSCRIPTCOMPILER_TOKEN_LEFT_BRACE 11 +#define CSCRIPTCOMPILER_TOKEN_RIGHT_BRACE 12 +#define CSCRIPTCOMPILER_TOKEN_LEFT_BRACKET 13 +#define CSCRIPTCOMPILER_TOKEN_RIGHT_BRACKET 14 +#define CSCRIPTCOMPILER_TOKEN_SEMICOLON 15 +#define CSCRIPTCOMPILER_TOKEN_COMMA 16 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_IF 17 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_ELSE 18 +#define CSCRIPTCOMPILER_TOKEN_EOF 19 +#define CSCRIPTCOMPILER_TOKEN_COND_GREATER_EQUAL 20 +#define CSCRIPTCOMPILER_TOKEN_COND_LESS_EQUAL 21 +#define CSCRIPTCOMPILER_TOKEN_COND_GREATER_THAN 22 +#define CSCRIPTCOMPILER_TOKEN_COND_LESS_THAN 23 +#define CSCRIPTCOMPILER_TOKEN_COND_NOT_EQUAL 24 +#define CSCRIPTCOMPILER_TOKEN_COND_EQUAL 25 +#define CSCRIPTCOMPILER_TOKEN_PLUS 26 +#define CSCRIPTCOMPILER_TOKEN_MODULUS 27 +#define CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_EQUAL 28 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_INT 29 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_FLOAT 30 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_STRING 31 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT 32 +#define CSCRIPTCOMPILER_TOKEN_VARIABLE 33 +#define CSCRIPTCOMPILER_TOKEN_INTEGER_IDENTIFIER 34 +#define CSCRIPTCOMPILER_TOKEN_FLOAT_IDENTIFIER 35 +#define CSCRIPTCOMPILER_TOKEN_STRING_IDENTIFIER 36 +#define CSCRIPTCOMPILER_TOKEN_OBJECT_IDENTIFIER 37 +#define CSCRIPTCOMPILER_TOKEN_VOID_IDENTIFIER 38 +#define CSCRIPTCOMPILER_TOKEN_INCLUSIVE_OR 39 +#define CSCRIPTCOMPILER_TOKEN_EXCLUSIVE_OR 40 +#define CSCRIPTCOMPILER_TOKEN_BOOLEAN_AND 41 +#define CSCRIPTCOMPILER_TOKEN_SHIFT_LEFT 42 +#define CSCRIPTCOMPILER_TOKEN_SHIFT_RIGHT 43 +#define CSCRIPTCOMPILER_TOKEN_MULTIPLY 44 +#define CSCRIPTCOMPILER_TOKEN_HEX_INTEGER 45 +#define CSCRIPTCOMPILER_TOKEN_UNSIGNED_SHIFT_RIGHT 46 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_ACTION 47 +#define CSCRIPTCOMPILER_TOKEN_TILDE 48 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_RETURN 49 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_WHILE 50 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_FOR 51 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_DO 52 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_VOID 53 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_STRUCT 54 +#define CSCRIPTCOMPILER_TOKEN_STRUCTURE_PART_SPECIFY 55 +#define CSCRIPTCOMPILER_TOKEN_STRUCTURE_IDENTIFIER 56 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_INCLUDE 57 +#define CSCRIPTCOMPILER_TOKEN_BOOLEAN_NOT 58 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_VECTOR 59 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_DEFINE 60 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_NUM_STRUCTURES_DEFINITION 61 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE_DEFINITION 62 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE0 63 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE1 64 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE2 65 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE3 66 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE4 67 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE5 68 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE6 69 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE7 70 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE8 71 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_ENGINE_STRUCTURE9 72 +#define CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE0_IDENTIFIER 73 +#define CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE1_IDENTIFIER 74 +#define CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE2_IDENTIFIER 75 +#define CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE3_IDENTIFIER 76 +#define CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE4_IDENTIFIER 77 +#define CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE5_IDENTIFIER 78 +#define CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE6_IDENTIFIER 79 +#define CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE7_IDENTIFIER 80 +#define CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE8_IDENTIFIER 81 +#define CSCRIPTCOMPILER_TOKEN_ENGINE_STRUCTURE9_IDENTIFIER 82 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT_SELF 83 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_OBJECT_INVALID 84 +#define CSCRIPTCOMPILER_TOKEN_VECTOR_IDENTIFIER 85 +#define CSCRIPTCOMPILER_TOKEN_LEFT_SQUARE_BRACKET 86 +#define CSCRIPTCOMPILER_TOKEN_RIGHT_SQUARE_BRACKET 87 +#define CSCRIPTCOMPILER_TOKEN_INCREMENT 88 +#define CSCRIPTCOMPILER_TOKEN_DECREMENT 89 +#define CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_MINUS 90 +#define CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_PLUS 91 +#define CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_MULTIPLY 92 +#define CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_DIVIDE 93 +#define CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_MODULUS 94 +#define CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_AND 95 +#define CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_XOR 96 +#define CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_OR 97 +#define CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_SHIFT_LEFT 98 +#define CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_SHIFT_RIGHT 99 +#define CSCRIPTCOMPILER_TOKEN_ASSIGNMENT_USHIFT_RIGHT 100 +#define CSCRIPTCOMPILER_TOKEN_QUESTION_MARK 101 +#define CSCRIPTCOMPILER_TOKEN_COLON 102 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_CASE 103 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_BREAK 104 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_SWITCH 105 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_DEFAULT 106 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_CONTINUE 107 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_CONST 108 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_NULL 109 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_FALSE 110 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_TRUE 111 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_OBJECT 112 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_ARRAY 113 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_JSON_STRING 114 +#define CSCRIPTCOMPILER_TOKEN_KEYWORD_LOCATION_INVALID 115 +#define CSCRIPTCOMPILER_TOKEN_RAW_STRING 116 + +const char *TokenKeywordToString(int nTokenKeyword); + +#define CSCRIPTCOMPILER_GRAMMAR_PROGRAM 0 +#define CSCRIPTCOMPILER_GRAMMAR_FUNCTIONAL_UNIT 1 +#define CSCRIPTCOMPILER_GRAMMAR_AFTER_PROGRAM 2 +#define CSCRIPTCOMPILER_GRAMMAR_FUNCTION_PARAM_LIST 3 +#define CSCRIPTCOMPILER_GRAMMAR_AFTER_FUNCTION_PARAM 4 +#define CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_LIST 5 +#define CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_VARLIST 6 +#define CSCRIPTCOMPILER_GRAMMAR_NON_INIT_DECL_VARLIST_SEPARATOR 7 +#define CSCRIPTCOMPILER_GRAMMAR_WITHIN_COMPOUND_STATEMENT 8 +#define CSCRIPTCOMPILER_GRAMMAR_WITHIN_STATEMENT_LIST 9 +#define CSCRIPTCOMPILER_GRAMMAR_WITHIN_A_STATEMENT 10 +#define CSCRIPTCOMPILER_GRAMMAR_ANY_TYPE_SPECIFIER 11 +#define CSCRIPTCOMPILER_GRAMMAR_NON_VOID_TYPE_SPECIFIER 12 +#define CSCRIPTCOMPILER_GRAMMAR_DECL_VARLIST 13 +#define CSCRIPTCOMPILER_GRAMMAR_DECL_VARLIST_SEPARATOR 14 +#define CSCRIPTCOMPILER_GRAMMAR_ARGUMENT_EXPRESSION_LIST 15 +#define CSCRIPTCOMPILER_GRAMMAR_AFTER_ARGUMENT_EXPRESSION 16 +#define CSCRIPTCOMPILER_GRAMMAR_BOOLEAN_EXPRESSION 17 +#define CSCRIPTCOMPILER_GRAMMAR_NON_VOID_EXPRESSION 18 +#define CSCRIPTCOMPILER_GRAMMAR_EXPRESSION 19 +#define CSCRIPTCOMPILER_GRAMMAR_ASSIGNMENT_EXPRESSION 20 +#define CSCRIPTCOMPILER_GRAMMAR_CONDITIONAL_EXPRESSION 21 +#define CSCRIPTCOMPILER_GRAMMAR_LOGICAL_OR_EXPRESSION 22 +#define CSCRIPTCOMPILER_GRAMMAR_LOGICAL_AND_EXPRESSION 23 +#define CSCRIPTCOMPILER_GRAMMAR_INCLUSIVE_OR_EXPRESSION 24 +#define CSCRIPTCOMPILER_GRAMMAR_EXCLUSIVE_OR_EXPRESSION 25 +#define CSCRIPTCOMPILER_GRAMMAR_BOOLEAN_AND_EXPRESSION 26 +#define CSCRIPTCOMPILER_GRAMMAR_EQUALITY_EXPRESSION 27 +#define CSCRIPTCOMPILER_GRAMMAR_RELATIONAL_EXPRESSION 28 +#define CSCRIPTCOMPILER_GRAMMAR_SHIFT_EXPRESSION 29 +#define CSCRIPTCOMPILER_GRAMMAR_ADDITIVE_EXPRESSION 30 +#define CSCRIPTCOMPILER_GRAMMAR_MULTIPLICATIVE_EXPRESSION 31 +#define CSCRIPTCOMPILER_GRAMMAR_UNARY_EXPRESSION 32 +#define CSCRIPTCOMPILER_GRAMMAR_POST_EXPRESSION 33 +#define CSCRIPTCOMPILER_GRAMMAR_PRIMARY_EXPRESSION 34 +#define CSCRIPTCOMPILER_GRAMMAR_CONSTANT 35 + +const char *GrammarToString(int nGrammar); + +#define CSCRIPTCOMPILER_OPERATION_COMPOUND_STATEMENT 0 +#define CSCRIPTCOMPILER_OPERATION_STATEMENT 1 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_DECLARATION 2 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_INT 3 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_FLOAT 4 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_STRING 5 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_OBJECT 6 +#define CSCRIPTCOMPILER_OPERATION_VARIABLE_LIST 7 +#define CSCRIPTCOMPILER_OPERATION_VARIABLE 8 +#define CSCRIPTCOMPILER_OPERATION_STATEMENT_LIST 9 +#define CSCRIPTCOMPILER_OPERATION_IF_BLOCK 10 +#define CSCRIPTCOMPILER_OPERATION_IF_CHOICE 11 +#define CSCRIPTCOMPILER_OPERATION_IF_CONDITION 12 +#define CSCRIPTCOMPILER_OPERATION_ACTION 13 +#define CSCRIPTCOMPILER_OPERATION_ACTION_ID 14 +#define CSCRIPTCOMPILER_OPERATION_ASSIGNMENT 15 +#define CSCRIPTCOMPILER_OPERATION_ACTION_ARG_LIST 16 +#define CSCRIPTCOMPILER_OPERATION_CONSTANT_INTEGER 17 +#define CSCRIPTCOMPILER_OPERATION_CONSTANT_FLOAT 18 +#define CSCRIPTCOMPILER_OPERATION_CONSTANT_STRING 19 +#define CSCRIPTCOMPILER_OPERATION_INTEGER_EXPRESSION 20 +#define CSCRIPTCOMPILER_OPERATION_NON_VOID_EXPRESSION 21 +#define CSCRIPTCOMPILER_OPERATION_LOGICAL_OR 22 +#define CSCRIPTCOMPILER_OPERATION_LOGICAL_AND 23 +#define CSCRIPTCOMPILER_OPERATION_INCLUSIVE_OR 24 +#define CSCRIPTCOMPILER_OPERATION_EXCLUSIVE_OR 25 +#define CSCRIPTCOMPILER_OPERATION_BOOLEAN_AND 26 +#define CSCRIPTCOMPILER_OPERATION_CONDITION_EQUAL 27 +#define CSCRIPTCOMPILER_OPERATION_CONDITION_NOT_EQUAL 28 +#define CSCRIPTCOMPILER_OPERATION_CONDITION_GEQ 29 +#define CSCRIPTCOMPILER_OPERATION_CONDITION_GT 30 +#define CSCRIPTCOMPILER_OPERATION_CONDITION_LT 31 +#define CSCRIPTCOMPILER_OPERATION_CONDITION_LEQ 32 +#define CSCRIPTCOMPILER_OPERATION_SHIFT_LEFT 33 +#define CSCRIPTCOMPILER_OPERATION_SHIFT_RIGHT 34 +#define CSCRIPTCOMPILER_OPERATION_ADD 35 +#define CSCRIPTCOMPILER_OPERATION_SUBTRACT 36 +#define CSCRIPTCOMPILER_OPERATION_MULTIPLY 37 +#define CSCRIPTCOMPILER_OPERATION_DIVIDE 38 +#define CSCRIPTCOMPILER_OPERATION_MODULUS 39 +#define CSCRIPTCOMPILER_OPERATION_NEGATION 40 +#define CSCRIPTCOMPILER_OPERATION_ACTION_PARAMETER 41 +#define CSCRIPTCOMPILER_OPERATION_UNSIGNED_SHIFT_RIGHT 42 +#define CSCRIPTCOMPILER_OPERATION_STRUCTURE_PART 43 +#define CSCRIPTCOMPILER_OPERATION_ONES_COMPLEMENT 44 +#define CSCRIPTCOMPILER_OPERATION_WHILE_BLOCK 45 +#define CSCRIPTCOMPILER_OPERATION_WHILE_CHOICE 46 +#define CSCRIPTCOMPILER_OPERATION_WHILE_CONDITION 47 +#define CSCRIPTCOMPILER_OPERATION_DOWHILE_BLOCK 48 +#define CSCRIPTCOMPILER_OPERATION_DOWHILE_CONDITION 49 +#define CSCRIPTCOMPILER_OPERATION_FUNCTIONAL_UNIT 50 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_STRUCT 51 +#define CSCRIPTCOMPILER_OPERATION_STRUCTURE_DEFINITION 52 +#define CSCRIPTCOMPILER_OPERATION_FUNCTION_IDENTIFIER 53 +#define CSCRIPTCOMPILER_OPERATION_FUNCTION_DECLARATION 54 +#define CSCRIPTCOMPILER_OPERATION_FUNCTION 55 +#define CSCRIPTCOMPILER_OPERATION_FUNCTION_PARAM_NAME 56 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_VOID 57 +#define CSCRIPTCOMPILER_OPERATION_RETURN 58 +#define CSCRIPTCOMPILER_OPERATION_BOOLEAN_NOT 59 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE0 60 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE1 61 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE2 62 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE3 63 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE4 64 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE5 65 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE6 66 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE7 67 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE8 68 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_ENGINE_STRUCTURE9 69 +#define CSCRIPTCOMPILER_OPERATION_CONSTANT_OBJECT 70 +#define CSCRIPTCOMPILER_OPERATION_KEYWORD_VECTOR 71 +#define CSCRIPTCOMPILER_OPERATION_CONSTANT_VECTOR 72 +#define CSCRIPTCOMPILER_OPERATION_GLOBAL_VARIABLES 73 +#define CSCRIPTCOMPILER_OPERATION_POST_INCREMENT 74 +#define CSCRIPTCOMPILER_OPERATION_POST_DECREMENT 75 +#define CSCRIPTCOMPILER_OPERATION_PRE_INCREMENT 76 +#define CSCRIPTCOMPILER_OPERATION_PRE_DECREMENT 77 +#define CSCRIPTCOMPILER_OPERATION_COND_BLOCK 78 +#define CSCRIPTCOMPILER_OPERATION_COND_CHOICE 79 +#define CSCRIPTCOMPILER_OPERATION_COND_CONDITION 80 +#define CSCRIPTCOMPILER_OPERATION_SWITCH_BLOCK 81 +#define CSCRIPTCOMPILER_OPERATION_SWITCH_CONDITION 82 +#define CSCRIPTCOMPILER_OPERATION_DEFAULT 83 +#define CSCRIPTCOMPILER_OPERATION_CASE 84 +#define CSCRIPTCOMPILER_OPERATION_BREAK 85 +#define CSCRIPTCOMPILER_OPERATION_CONTINUE 86 +#define CSCRIPTCOMPILER_OPERATION_WHILE_CONTINUE 87 +#define CSCRIPTCOMPILER_OPERATION_STATEMENT_NO_DEBUG 88 +#define CSCRIPTCOMPILER_OPERATION_FOR_BLOCK 89 +#define CSCRIPTCOMPILER_OPERATION_CONST_DECLARATION 90 +#define CSCRIPTCOMPILER_OPERATION_CONSTANT_JSON 91 +#define CSCRIPTCOMPILER_OPERATION_CONSTANT_LOCATION 92 + +const char *OperationToString(int nOperation); + +#define CSCRIPTCOMPILER_IDENT_STATE_START_OF_LINE 0 +#define CSCRIPTCOMPILER_IDENT_STATE_AFTER_TYPE_DECLARATION 1 +#define CSCRIPTCOMPILER_IDENT_STATE_AFTER_FUNCTION_IDENTIFIER 2 +#define CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST 3 +#define CSCRIPTCOMPILER_IDENT_STATE_BEFORE_CONSTANT_IDENTIFIER 4 +#define CSCRIPTCOMPILER_IDENT_STATE_AFTER_CONSTANT_IDENTIFIER 5 +#define CSCRIPTCOMPILER_IDENT_STATE_DEFINE_STATEMENT 6 +#define CSCRIPTCOMPILER_IDENT_STATE_DEFINE_NUM_ENGINE_STRUCTURE 7 +#define CSCRIPTCOMPILER_IDENT_STATE_DEFINE_SINGLE_ENGINE_STRUCTURE 8 +#define CSCRIPTCOMPILER_IDENT_STATE_IN_VECTOR_PARAMETER 9 +#define CSCRIPTCOMPILER_IDENT_STATE_IN_VECTOR_CONSTANT 10 +#define CSCRIPTCOMPILER_IDENT_STATE_IN_PARAMETER_LIST_CONSTANT 11 + +#define CVIRTUALMACHINE_MAX_RECURSION_LEVELS 8 +// niv, 17 mar 2017, #28735: now configured in ini +// #define CVIRTUALMACHINE_MAX_INSTRUCT_EXECUTE 131072 //8192 +#define CVIRTUALMACHINE_MAX_RUNTIME_VARS 128 +#define CVIRTUALMACHINE_MAX_SUBROUTINES 128 + +#define CVIRTUALMACHINE_BINARY_SCRIPT_HEADER 13 // 9 for the NCS crud, and +// 4 for the size of the file. + +// Format of the byte-encoded data. +#define CVIRTUALMACHINE_OPERATION_BASE_SIZE 2 +#define CVIRTUALMACHINE_OPCODE_LOCATION 0 +#define CVIRTUALMACHINE_AUXCODE_LOCATION 1 +#define CVIRTUALMACHINE_EXTRA_DATA_LOCATION 2 + + +#define CVIRTUALMACHINE_OPCODE_ASSIGNMENT 0x01 +#define CVIRTUALMACHINE_OPCODE_RUNSTACK_ADD 0x02 +#define CVIRTUALMACHINE_OPCODE_RUNSTACK_COPY 0x03 +#define CVIRTUALMACHINE_OPCODE_CONSTANT 0x04 +#define CVIRTUALMACHINE_OPCODE_EXECUTE_COMMAND 0x05 +#define CVIRTUALMACHINE_OPCODE_LOGICAL_AND 0x06 +#define CVIRTUALMACHINE_OPCODE_LOGICAL_OR 0x07 +#define CVIRTUALMACHINE_OPCODE_INCLUSIVE_OR 0x08 +#define CVIRTUALMACHINE_OPCODE_EXCLUSIVE_OR 0x09 +#define CVIRTUALMACHINE_OPCODE_BOOLEAN_AND 0x0a +#define CVIRTUALMACHINE_OPCODE_EQUAL 0x0b +#define CVIRTUALMACHINE_OPCODE_NOT_EQUAL 0x0c +#define CVIRTUALMACHINE_OPCODE_GEQ 0x0d +#define CVIRTUALMACHINE_OPCODE_GT 0x0e +#define CVIRTUALMACHINE_OPCODE_LT 0x0f +#define CVIRTUALMACHINE_OPCODE_LEQ 0x10 +#define CVIRTUALMACHINE_OPCODE_SHIFT_LEFT 0x11 +#define CVIRTUALMACHINE_OPCODE_SHIFT_RIGHT 0x12 +#define CVIRTUALMACHINE_OPCODE_USHIFT_RIGHT 0x13 +#define CVIRTUALMACHINE_OPCODE_ADD 0x14 +#define CVIRTUALMACHINE_OPCODE_SUB 0x15 +#define CVIRTUALMACHINE_OPCODE_MUL 0x16 +#define CVIRTUALMACHINE_OPCODE_DIV 0x17 +#define CVIRTUALMACHINE_OPCODE_MODULUS 0x18 +#define CVIRTUALMACHINE_OPCODE_NEGATION 0x19 +#define CVIRTUALMACHINE_OPCODE_ONES_COMPLEMENT 0x1a +// New Instructions +#define CVIRTUALMACHINE_OPCODE_MODIFY_STACK_POINTER 0x1b // move the stack pointer arbitrarily +// to delete useless objects. +#define CVIRTUALMACHINE_OPCODE_STORE_IP 0x1c // store the instruction pointer for +// use when saving action. +#define CVIRTUALMACHINE_OPCODE_JMP 0x1d // jump unconditionally +#define CVIRTUALMACHINE_OPCODE_JSR 0x1e // jump to a subroutine +#define CVIRTUALMACHINE_OPCODE_JZ 0x1f // jump if top integer on stack is 0 +#define CVIRTUALMACHINE_OPCODE_RET 0x20 // return from a JSR. +#define CVIRTUALMACHINE_OPCODE_DE_STRUCT 0x21 // structure part. +#define CVIRTUALMACHINE_OPCODE_BOOLEAN_NOT 0x22 +#define CVIRTUALMACHINE_OPCODE_DECREMENT 0x23 +#define CVIRTUALMACHINE_OPCODE_INCREMENT 0x24 +#define CVIRTUALMACHINE_OPCODE_JNZ 0x25 +#define CVIRTUALMACHINE_OPCODE_ASSIGNMENT_BASE 0x26 +#define CVIRTUALMACHINE_OPCODE_RUNSTACK_COPY_BASE 0x27 +#define CVIRTUALMACHINE_OPCODE_DECREMENT_BASE 0x28 +#define CVIRTUALMACHINE_OPCODE_INCREMENT_BASE 0x29 +#define CVIRTUALMACHINE_OPCODE_SAVE_BASE_POINTER 0x2A +#define CVIRTUALMACHINE_OPCODE_RESTORE_BASE_POINTER 0x2B +#define CVIRTUALMACHINE_OPCODE_STORE_STATE 0x2C // An extended version of Store_IP, it +// also saves the stack information that +// we need to keep stack sizes down in +// the save games. +#define CVIRTUALMACHINE_OPCODE_NO_OPERATION 0x2D + +// For OPCODE_LOOP, determine when to evaluate the operation. +#define CVIRTUALMACHINE_AUXCODE_EVAL_INPLACE 0x70 +#define CVIRTUALMACHINE_AUXCODE_EVAL_POSTPLACE 0x71 + +#define CVIRTUALMACHINE_AUXCODE_TYPE_VOID 0x01 +#define CVIRTUALMACHINE_AUXCODE_TYPE_COMMAND 0x02 +#define CVIRTUALMACHINE_AUXCODE_TYPE_INTEGER 0x03 +#define CVIRTUALMACHINE_AUXCODE_TYPE_FLOAT 0x04 +#define CVIRTUALMACHINE_AUXCODE_TYPE_STRING 0x05 +#define CVIRTUALMACHINE_AUXCODE_TYPE_OBJECT 0x06 +#define CVIRTUALMACHINE_AUXCODE_TYPE_ENGST0 0x10 +#define CVIRTUALMACHINE_AUXCODE_TYPE_ENGST1 0x11 +#define CVIRTUALMACHINE_AUXCODE_TYPE_ENGST2 0x12 // 8193.35 (CONST): uint32_t 0 = LOCATION_INVALID +#define CVIRTUALMACHINE_AUXCODE_TYPE_ENGST3 0x13 +#define CVIRTUALMACHINE_AUXCODE_TYPE_ENGST4 0x14 +#define CVIRTUALMACHINE_AUXCODE_TYPE_ENGST5 0x15 +#define CVIRTUALMACHINE_AUXCODE_TYPE_ENGST6 0x16 +#define CVIRTUALMACHINE_AUXCODE_TYPE_ENGST7 0x17 // 8193.35 (CONST): uint16_t size + string +#define CVIRTUALMACHINE_AUXCODE_TYPE_ENGST8 0x18 +#define CVIRTUALMACHINE_AUXCODE_TYPE_ENGST9 0x19 + +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_INTEGER_INTEGER 0x20 +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_FLOAT_FLOAT 0x21 +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_OBJECT_OBJECT 0x22 +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_STRING_STRING 0x23 +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_STRUCT_STRUCT 0x24 +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_INTEGER_FLOAT 0x25 +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_FLOAT_INTEGER 0x26 +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_ENGST0_ENGST0 0x30 +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_ENGST1_ENGST1 0x31 +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_ENGST2_ENGST2 0x32 +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_ENGST3_ENGST3 0x33 +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_ENGST4_ENGST4 0x34 +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_ENGST5_ENGST5 0x35 +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_ENGST6_ENGST6 0x36 +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_ENGST7_ENGST7 0x37 +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_ENGST8_ENGST8 0x38 +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_ENGST9_ENGST9 0x39 +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_VECTOR_VECTOR 0x3a +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_VECTOR_FLOAT 0x3b +#define CVIRTUALMACHINE_AUXCODE_TYPETYPE_FLOAT_VECTOR 0x3c + +// stuff for saving out ScriptSituations and Stacks +#define CVIRTUALMACHINE_GFF_CODESIZE "CodeSize" +#define CVIRTUALMACHINE_GFF_CODE "Code" +#define CVIRTUALMACHINE_GFF_INSTRUCTIONPTR "InstructionPtr" +#define CVIRTUALMACHINE_GFF_SECONDARYPTR "SecondaryPtr" + +#define CVIRTUALMACHINE_GFF_NDBSIZE "NDBSize" +#define CVIRTUALMACHINE_GFF_NDB "NDB" + +#define CVIRTUALMACHINE_GFF_NAME "Name" +#define CVIRTUALMACHINE_GFF_SCRIPTCHUNK "ScriptChunk" +#define CVIRTUALMACHINE_GFF_SCRIPTEVENTID "ScriptEventID" +#define CVIRTUALMACHINE_GFF_STACKSIZE "StackSize" +#define CVIRTUALMACHINE_GFF_STACK "Stack" + +#define CVIRTUALMACHINE_GFF_STACKTYPE 0 + +#define CVIRTUALMACHINESTACK_BASEPOINTER "BasePointer" +#define CVIRTUALMACHINESTACK_STACKPOINTER "StackPointer" +#define CVIRTUALMACHINESTACK_TOTALSIZE "TotalSize" + +#define CVIRTUALMACHINESTACK_STACKLIST "Stack" +#define CVIRTUALMACHINESTACK_STACKTYPE "Type" +#define CVIRTUALMACHINESTACK_STACKVALUE "Value" + +#define CVIRTUALMACHINE_GFF_JMPLIST "JmpList" +#define CVIRTUALMACHINE_GFF_JMP_LABEL "Label" +#define CVIRTUALMACHINE_GFF_JMP_VM_INSTPTR "VMInstPtr" +#define CVIRTUALMACHINE_GFF_JMP_INSTPTR "InstPtr" +#define CVIRTUALMACHINE_GFF_JMP_STACKPTR "StackPtr" +#define CVIRTUALMACHINE_GFF_JMP_INSTPTRLEVEL "InstPtrLevel" +#define CVIRTUALMACHINE_GFF_JMP_FROMJMP "FromJmp" +#define CVIRTUALMACHINE_GFF_JMP_RETVAL "RetVal" + +#define CVIRTUALMACHINE_MAX_MESSAGE_SIZE 4096 + +class CScriptParseTreeNode +{ + +public: + int32_t nOperation; + CExoString *m_psStringData; + int32_t nIntegerData; + int32_t nIntegerData2; + int32_t nIntegerData3; + int32_t nIntegerData4; + float fFloatData; + float fVectorData[3]; + // json is reusing m_psStringData + int32_t m_nFileReference; + int32_t nLine; + int32_t nChar; + CScriptParseTreeNode *pLeft; + CScriptParseTreeNode *pRight; + int32_t nType; + CExoString *m_psTypeName; + /* int32_t m_nNodeLocation; ???? */ + int32_t m_nStackPointer; + + CScriptParseTreeNode() { m_psStringData = NULL; m_psTypeName = NULL; Clean(); } + + void Clean() + { + if (m_psStringData != NULL) + { + delete m_psStringData; + m_psStringData = NULL; + } + if (m_psTypeName != NULL) + { + delete m_psTypeName; + m_psTypeName = NULL; + } + + nOperation = 0; + nIntegerData = 0; + nIntegerData2 = 0; + nIntegerData3 = 0; + nIntegerData4 = 0; + fFloatData = 0.0f; + pLeft = NULL ; + pRight = NULL; + fVectorData[0] = 0.0f; + fVectorData[1] = 0.0f; + fVectorData[2] = 0.0f; + m_nFileReference = -1; + nLine = 0; + nChar = 0; + nType = 0; + m_nStackPointer = 0; + } + + ~CScriptParseTreeNode() + { + if (m_psStringData != NULL) + { + delete m_psStringData; + m_psStringData = NULL; + } + if (m_psTypeName != NULL) + { + delete m_psTypeName; + m_psTypeName = NULL; + } + } + + void DebugDump(const char *prefix = "", FILE *out = NULL) + { + if (!out) out = stdout; + fprintf(out, "%s[%p] (pLeft=%p, pRight=%p)\n", prefix, this, pLeft, pRight); + fprintf(out, " Operation: %s\n", OperationToString(nOperation)); + fprintf(out, " Type: %s\n", TokenKeywordToString(nType)); + fprintf(out, " IntegerData: %d %d %d %d\n", nIntegerData, nIntegerData2, nIntegerData3, nIntegerData4); + fprintf(out, " FloatData: %f %f %f %f\n", fFloatData, fVectorData[0], fVectorData[1], fVectorData[2]); + fprintf(out, " StringData: \"%s\"\n", m_psStringData ? m_psStringData->CStr() : ""); + fprintf(out, " TypeName: \"%s\"\n", m_psTypeName ? m_psTypeName->CStr() : ""); + fprintf(out, " File/Line/Char/SP: %d %d %d %d\n", m_nFileReference, nLine, nChar, m_nStackPointer); + } +}; + +#define CSCRIPTCOMPILER_PARSETREENODEBLOCK_SIZE 4096 + +class CScriptParseTreeNodeBlock +{ +public: + CScriptParseTreeNode m_pNodes[CSCRIPTCOMPILER_PARSETREENODEBLOCK_SIZE]; + CScriptParseTreeNodeBlock *m_pNextBlock; + + CScriptParseTreeNodeBlock() + { + m_pNextBlock = NULL; + CleanBlockEntries(); + } + + void CleanBlockEntries() + { + uint32_t nCount; + for (nCount = 0; nCount < CSCRIPTCOMPILER_PARSETREENODEBLOCK_SIZE; ++nCount) + { + m_pNodes[nCount].Clean(); + } + } +}; + +class CScriptCompilerStackEntry +{ +public: + int32_t nState; + int32_t nRule; + int32_t nTerm; + CScriptParseTreeNode *pCurrentTree; + CScriptParseTreeNode *pReturnTree; +}; + +class CScriptCompilerKeyWordEntry +{ +public: + CScriptCompilerKeyWordEntry() + { + m_sAlphanumericName = ""; + m_nHashValue = 0; + m_nNameLength = 0; + m_nTokenToTranslate = 0; + } + + void Add(CExoString sName, int32_t nHashValue, int32_t nTokenToTranslate) + { + EXOASSERT(m_nHashValue == 0); + m_sAlphanumericName = sName; + m_nHashValue = nHashValue; + m_nNameLength = sName.GetLength(); + m_nTokenToTranslate = nTokenToTranslate; + } + + inline CExoString *GetPointerToName() { return &m_sAlphanumericName; } + inline char *GetAlphanumericName() { return m_sAlphanumericName.CStr(); } + inline uint32_t GetHash() { return m_nHashValue; } + inline uint32_t GetLength() { return m_nNameLength; } + inline int32_t GetTokenToTranslate() { return m_nTokenToTranslate; } + +private: + CExoString m_sAlphanumericName; + uint32_t m_nHashValue; + uint32_t m_nNameLength; + int32_t m_nTokenToTranslate; +}; + +#define CSCRIPTCOMPILER_HASH_MANAGER_TYPE_UNKNOWN 0 +#define CSCRIPTCOMPILER_HASH_MANAGER_TYPE_IDENTIFIER 1 +#define CSCRIPTCOMPILER_HASH_MANAGER_TYPE_KEYWORD 2 +#define CSCRIPTCOMPILER_HASH_MANAGER_TYPE_ENGINE_STRUCTURE 3 + +class CScriptCompilerIdentifierHashTableEntry +{ +public: + CScriptCompilerIdentifierHashTableEntry() + { + m_nHashValue = 0; + m_nIdentifierType = CSCRIPTCOMPILER_HASH_MANAGER_TYPE_UNKNOWN; + m_nIdentifierIndex = 0; + } + + uint32_t m_nHashValue; + uint32_t m_nIdentifierType; + uint32_t m_nIdentifierIndex; +}; + +class CScriptCompilerIdListEntry +{ +public: + CExoString m_psIdentifier; + uint32_t m_nIdentifierLength; + uint32_t m_nIdentifierHash; + int32_t m_nIdentifierType; + int32_t m_nReturnType; + int32_t m_bImplementationInPlace; + CExoString m_psStructureReturnName; + //INT m_nIdentifierOrder; + + // For constants ... + CExoString m_psStringData; + int32_t m_nIntegerData; + float m_fFloatData; + float m_fVectorData[3]; + + // For identifiers .. + int32_t m_nIdIdentifier; + int32_t m_nParameters; + int32_t m_nNonOptionalParameters; + + int32_t m_nParameterSpace; + + // For each parameter ... + char *m_pchParameters; + CExoString *m_psStructureParameterNames; + // For the optional part of each parameter ... + BOOL *m_pbOptionalParameters; + int32_t *m_pnOptionalParameterIntegerData; + float *m_pfOptionalParameterFloatData; + CExoString *m_psOptionalParameterStringData; + OBJECT_ID *m_poidOptionalParameterObjectData; + float *m_pfOptionalParameterVectorData; + // json is reusing string data + + // For user-defined identifiers + int32_t m_nBinarySourceStart; + int32_t m_nBinarySourceFinish; + int32_t m_nBinaryDestinationStart; + int32_t m_nBinaryDestinationFinish; + + CScriptCompilerIdListEntry(); + ~CScriptCompilerIdListEntry(); + int32_t ExpandParameterSpace(); +}; + +class CScriptCompilerVarStackEntry +{ +public: + CExoString m_psVarName; + int32_t m_nVarType; + int32_t m_nVarLevel; + int32_t m_nVarRunTimeLocation; + CExoString m_sVarStructureName; + + CScriptCompilerVarStackEntry() + { + m_nVarType = 0; + m_nVarLevel = 0; + m_nVarRunTimeLocation = 0; + } + +}; + +class CScriptCompilerStructureEntry +{ +public: + CExoString m_psName; + int32_t m_nFieldStart; + int32_t m_nFieldEnd; + int32_t m_nByteSize; + + CScriptCompilerStructureEntry() + { + m_nFieldStart = 0; + m_nFieldEnd = 0; + m_nByteSize = 0; + } +}; + +class CScriptCompilerStructureFieldEntry +{ +public: + uint8_t m_pchType; + CExoString m_psStructureName; + CExoString m_psVarName; + int32_t m_nLocation; + + CScriptCompilerStructureFieldEntry() + { + m_pchType = 0; + m_nLocation = 0; + } + +}; + +#define CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_UNKNOWN 0 +#define CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_FUNCTION_ENTRY 1 +#define CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_FUNCTION_EXIT 2 +#define CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_BREAK 3 +#define CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_CONTINUE 4 +#define CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_SWITCH_CASE 5 +#define CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_SWITCH_DEFAULT 6 + +class CScriptCompilerSymbolTableEntry +{ +public: + uint32_t m_nSymbolType; + uint32_t m_nSymbolSubType1; + uint32_t m_nSymbolSubType2; + int32_t m_nLocationPointer; + int32_t m_nNextEntryPointer; + + CScriptCompilerSymbolTableEntry() + { + m_nSymbolType = CSCRIPTCOMPILER_SYMBOL_TABLE_ENTRY_TYPE_UNKNOWN; + m_nSymbolSubType1 = 0; + m_nSymbolSubType2 = 0; + m_nLocationPointer = 0; + m_nNextEntryPointer = -1; + } +}; + + + +#endif // __SCRIPTINTERNAL_H__ diff --git a/src/Native Compiler/xxhash.c b/src/Native Compiler/xxhash.c new file mode 100644 index 0000000..ff28749 --- /dev/null +++ b/src/Native Compiler/xxhash.c @@ -0,0 +1,1030 @@ +/* +* xxHash - Fast Hash algorithm +* Copyright (C) 2012-2016, Yann Collet +* +* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * 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. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "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 COPYRIGHT +* OWNER OR CONTRIBUTORS 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. +* +* You can contact the author at : +* - xxHash homepage: http://www.xxhash.com +* - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + + +/* ************************************* +* Tuning parameters +***************************************/ +/*!XXH_FORCE_MEMORY_ACCESS : + * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. + * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. + * The below switch allow to select different access method for improved performance. + * Method 0 (default) : use `memcpy()`. Safe and portable. + * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). + * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. + * Method 2 : direct access. This method doesn't depend on compiler but violate C standard. + * It can generate buggy code on targets which do not support unaligned memory accesses. + * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) + * See http://stackoverflow.com/a/32095106/646947 for details. + * Prefer these methods in priority order (0 > 1 > 2) + */ +#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ +# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) +# define XXH_FORCE_MEMORY_ACCESS 2 +# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || \ + (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \ + || defined(__ARM_ARCH_7S__) )) +# define XXH_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +/*!XXH_ACCEPT_NULL_INPUT_POINTER : + * If input pointer is NULL, xxHash default behavior is to dereference it, triggering a segfault. + * When this macro is enabled, xxHash actively checks input for null pointer. + * It it is, result for null input pointers is the same as a null-length input. + */ +#ifndef XXH_ACCEPT_NULL_INPUT_POINTER /* can be defined externally */ +# define XXH_ACCEPT_NULL_INPUT_POINTER 0 +#endif + +/*!XXH_FORCE_NATIVE_FORMAT : + * By default, xxHash library provides endian-independent Hash values, based on little-endian convention. + * Results are therefore identical for little-endian and big-endian CPU. + * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. + * Should endian-independence be of no importance for your application, you may set the #define below to 1, + * to improve speed for Big-endian CPU. + * This option has no impact on Little_Endian CPU. + */ +#ifndef XXH_FORCE_NATIVE_FORMAT /* can be defined externally */ +# define XXH_FORCE_NATIVE_FORMAT 0 +#endif + +/*!XXH_FORCE_ALIGN_CHECK : + * This is a minor performance trick, only useful with lots of very small keys. + * It means : check for aligned/unaligned input. + * The check costs one initial branch per hash; + * set it to 0 when the input is guaranteed to be aligned, + * or when alignment doesn't matter for performance. + */ +#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ +# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) +# define XXH_FORCE_ALIGN_CHECK 0 +# else +# define XXH_FORCE_ALIGN_CHECK 1 +# endif +#endif + + +/* ************************************* +* Includes & Memory related functions +***************************************/ +/*! Modify the local functions below should you wish to use some other memory routines +* for malloc(), free() */ +#include +static void* XXH_malloc(size_t s) { return malloc(s); } +static void XXH_free (void* p) { free(p); } +/*! and for memcpy() */ +#include +static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } + +#include /* assert */ + +#define XXH_STATIC_LINKING_ONLY +#include "xxhash.h" + + +/* ************************************* +* Compiler Specific Options +***************************************/ +#ifdef _MSC_VER /* Visual Studio */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# define FORCE_INLINE static __forceinline +#else +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif +# else +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ +#endif + + +/* ************************************* +* Basic Types +***************************************/ +#ifndef MEM_MODULE +# if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; +# else + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; +# endif +#endif + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U32 u32; } __attribute__((packed)) unalign; +static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ +static U32 XXH_read32(const void* memPtr) +{ + U32 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + + +/* **************************************** +* Compiler-specific Functions and Macros +******************************************/ +#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */ +#if defined(_MSC_VER) +# define XXH_rotl32(x,r) _rotl(x,r) +# define XXH_rotl64(x,r) _rotl64(x,r) +#else +# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r))) +# define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r))) +#endif + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap32 _byteswap_ulong +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap32 __builtin_bswap32 +#else +static U32 XXH_swap32 (U32 x) +{ + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} +#endif + + +/* ************************************* +* Architecture Macros +***************************************/ +typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; + +/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */ +#ifndef XXH_CPU_LITTLE_ENDIAN +static int XXH_isLittleEndian(void) +{ + const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ + return one.c[0]; +} +# define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian() +#endif + + +/* *************************** +* Memory reads +*****************************/ +typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; + +FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); + else + return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr); +} + +FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian) +{ + return XXH_readLE32_align(ptr, endian, XXH_unaligned); +} + +static U32 XXH_readBE32(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); +} + + +/* ************************************* +* Macros +***************************************/ +#define XXH_STATIC_ASSERT(c) { enum { XXH_sa = 1/(int)(!!(c)) }; } /* use after variable declarations */ +XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } + + +/* ******************************************************************* +* 32-bit hash functions +*********************************************************************/ +static const U32 PRIME32_1 = 2654435761U; +static const U32 PRIME32_2 = 2246822519U; +static const U32 PRIME32_3 = 3266489917U; +static const U32 PRIME32_4 = 668265263U; +static const U32 PRIME32_5 = 374761393U; + +static U32 XXH32_round(U32 seed, U32 input) +{ + seed += input * PRIME32_2; + seed = XXH_rotl32(seed, 13); + seed *= PRIME32_1; + return seed; +} + +/* mix all bits */ +static U32 XXH32_avalanche(U32 h32) +{ + h32 ^= h32 >> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + return(h32); +} + +#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align) + +static U32 +XXH32_finalize(U32 h32, const void* ptr, size_t len, + XXH_endianess endian, XXH_alignment align) + +{ + const BYTE* p = (const BYTE*)ptr; + +#define PROCESS1 \ + h32 += (*p++) * PRIME32_5; \ + h32 = XXH_rotl32(h32, 11) * PRIME32_1 ; + +#define PROCESS4 \ + h32 += XXH_get32bits(p) * PRIME32_3; \ + p+=4; \ + h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; + + switch(len&15) /* or switch(bEnd - p) */ + { + case 12: PROCESS4; + /* fallthrough */ + case 8: PROCESS4; + /* fallthrough */ + case 4: PROCESS4; + return XXH32_avalanche(h32); + + case 13: PROCESS4; + /* fallthrough */ + case 9: PROCESS4; + /* fallthrough */ + case 5: PROCESS4; + PROCESS1; + return XXH32_avalanche(h32); + + case 14: PROCESS4; + /* fallthrough */ + case 10: PROCESS4; + /* fallthrough */ + case 6: PROCESS4; + PROCESS1; + PROCESS1; + return XXH32_avalanche(h32); + + case 15: PROCESS4; + /* fallthrough */ + case 11: PROCESS4; + /* fallthrough */ + case 7: PROCESS4; + /* fallthrough */ + case 3: PROCESS1; + /* fallthrough */ + case 2: PROCESS1; + /* fallthrough */ + case 1: PROCESS1; + /* fallthrough */ + case 0: return XXH32_avalanche(h32); + } + assert(0); + return h32; /* reaching this point is deemed impossible */ +} + + +FORCE_INLINE U32 +XXH32_endian_align(const void* input, size_t len, U32 seed, + XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U32 h32; + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)16; + } +#endif + + if (len>=16) { + const BYTE* const limit = bEnd - 15; + U32 v1 = seed + PRIME32_1 + PRIME32_2; + U32 v2 = seed + PRIME32_2; + U32 v3 = seed + 0; + U32 v4 = seed - PRIME32_1; + + do { + v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4; + v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4; + v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4; + v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4; + } while (p < limit); + + h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); + } else { + h32 = seed + PRIME32_5; + } + + h32 += (U32)len; + + return XXH32_finalize(h32, p, len&15, endian, align); +} + + +XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH32_state_t state; + XXH32_reset(&state, seed); + XXH32_update(&state, input, len); + return XXH32_digest(&state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } } + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + + + +/*====== Hash streaming ======*/ + +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) +{ + return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed) +{ + XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME32_1 + PRIME32_2; + state.v2 = seed + PRIME32_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME32_1; + /* do not write into reserved, planned to be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; +} + + +FORCE_INLINE XXH_errorcode +XXH32_update_endian(XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian) +{ + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + { const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + + state->total_len_32 += (unsigned)len; + state->large_len |= (len>=16) | (state->total_len_32>=16); + + if (state->memsize + len < 16) { /* fill in tmp buffer */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len); + state->memsize += (unsigned)len; + return XXH_OK; + } + + if (state->memsize) { /* some data left from previous update */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize); + { const U32* p32 = state->mem32; + state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++; + state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++; + state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++; + state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian)); + } + p += 16-state->memsize; + state->memsize = 0; + } + + if (p <= bEnd-16) { + const BYTE* const limit = bEnd - 16; + U32 v1 = state->v1; + U32 v2 = state->v2; + U32 v3 = state->v3; + U32 v4 = state->v4; + + do { + v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4; + v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4; + v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4; + v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem32, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + } + + return XXH_OK; +} + + +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH32_update_endian(state_in, input, len, XXH_bigEndian); +} + + +FORCE_INLINE U32 +XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian) +{ + U32 h32; + + if (state->large_len) { + h32 = XXH_rotl32(state->v1, 1) + + XXH_rotl32(state->v2, 7) + + XXH_rotl32(state->v3, 12) + + XXH_rotl32(state->v4, 18); + } else { + h32 = state->v3 /* == seed */ + PRIME32_5; + } + + h32 += state->total_len_32; + + return XXH32_finalize(h32, state->mem32, state->memsize, endian, XXH_aligned); +} + + +XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_digest_endian(state_in, XXH_littleEndian); + else + return XXH32_digest_endian(state_in, XXH_bigEndian); +} + + +/*====== Canonical representation ======*/ + +/*! Default XXH result types are basic unsigned 32 and 64 bits. +* The canonical representation follows human-readable write convention, aka big-endian (large digits first). +* These functions allow transformation of hash result into and from its canonical format. +* This way, hash values can be written into a file or buffer, remaining comparable across different systems. +*/ + +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) +{ + return XXH_readBE32(src); +} + + +#ifndef XXH_NO_LONG_LONG + +/* ******************************************************************* +* 64-bit hash functions +*********************************************************************/ + +/*====== Memory access ======*/ + +#ifndef MEM_MODULE +# define MEM_MODULE +# if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint64_t U64; +# else + /* if compiler doesn't support unsigned long long, replace by another 64-bit type */ + typedef unsigned long long U64; +# endif +#endif + + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign64; +static U64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ + +static U64 XXH_read64(const void* memPtr) +{ + U64 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap64 _byteswap_uint64 +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap64 __builtin_bswap64 +#else +static U64 XXH_swap64 (U64 x) +{ + return ((x << 56) & 0xff00000000000000ULL) | + ((x << 40) & 0x00ff000000000000ULL) | + ((x << 24) & 0x0000ff0000000000ULL) | + ((x << 8) & 0x000000ff00000000ULL) | + ((x >> 8) & 0x00000000ff000000ULL) | + ((x >> 24) & 0x0000000000ff0000ULL) | + ((x >> 40) & 0x000000000000ff00ULL) | + ((x >> 56) & 0x00000000000000ffULL); +} +#endif + +FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); + else + return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr); +} + +FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian) +{ + return XXH_readLE64_align(ptr, endian, XXH_unaligned); +} + +static U64 XXH_readBE64(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); +} + + +/*====== xxh64 ======*/ + +static const U64 PRIME64_1 = 11400714785074694791ULL; +static const U64 PRIME64_2 = 14029467366897019727ULL; +static const U64 PRIME64_3 = 1609587929392839161ULL; +static const U64 PRIME64_4 = 9650029242287828579ULL; +static const U64 PRIME64_5 = 2870177450012600261ULL; + +static U64 XXH64_round(U64 acc, U64 input) +{ + acc += input * PRIME64_2; + acc = XXH_rotl64(acc, 31); + acc *= PRIME64_1; + return acc; +} + +static U64 XXH64_mergeRound(U64 acc, U64 val) +{ + val = XXH64_round(0, val); + acc ^= val; + acc = acc * PRIME64_1 + PRIME64_4; + return acc; +} + +static U64 XXH64_avalanche(U64 h64) +{ + h64 ^= h64 >> 33; + h64 *= PRIME64_2; + h64 ^= h64 >> 29; + h64 *= PRIME64_3; + h64 ^= h64 >> 32; + return h64; +} + + +#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align) + +static U64 +XXH64_finalize(U64 h64, const void* ptr, size_t len, + XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)ptr; + +#define PROCESS1_64 \ + h64 ^= (*p++) * PRIME64_5; \ + h64 = XXH_rotl64(h64, 11) * PRIME64_1; + +#define PROCESS4_64 \ + h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1; \ + p+=4; \ + h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; + +#define PROCESS8_64 { \ + U64 const k1 = XXH64_round(0, XXH_get64bits(p)); \ + p+=8; \ + h64 ^= k1; \ + h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; \ +} + + switch(len&31) { + case 24: PROCESS8_64; + /* fallthrough */ + case 16: PROCESS8_64; + /* fallthrough */ + case 8: PROCESS8_64; + return XXH64_avalanche(h64); + + case 28: PROCESS8_64; + /* fallthrough */ + case 20: PROCESS8_64; + /* fallthrough */ + case 12: PROCESS8_64; + /* fallthrough */ + case 4: PROCESS4_64; + return XXH64_avalanche(h64); + + case 25: PROCESS8_64; + /* fallthrough */ + case 17: PROCESS8_64; + /* fallthrough */ + case 9: PROCESS8_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 29: PROCESS8_64; + /* fallthrough */ + case 21: PROCESS8_64; + /* fallthrough */ + case 13: PROCESS8_64; + /* fallthrough */ + case 5: PROCESS4_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 26: PROCESS8_64; + /* fallthrough */ + case 18: PROCESS8_64; + /* fallthrough */ + case 10: PROCESS8_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 30: PROCESS8_64; + /* fallthrough */ + case 22: PROCESS8_64; + /* fallthrough */ + case 14: PROCESS8_64; + /* fallthrough */ + case 6: PROCESS4_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 27: PROCESS8_64; + /* fallthrough */ + case 19: PROCESS8_64; + /* fallthrough */ + case 11: PROCESS8_64; + PROCESS1_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 31: PROCESS8_64; + /* fallthrough */ + case 23: PROCESS8_64; + /* fallthrough */ + case 15: PROCESS8_64; + /* fallthrough */ + case 7: PROCESS4_64; + /* fallthrough */ + case 3: PROCESS1_64; + /* fallthrough */ + case 2: PROCESS1_64; + /* fallthrough */ + case 1: PROCESS1_64; + /* fallthrough */ + case 0: return XXH64_avalanche(h64); + } + + /* impossible to reach */ + assert(0); + return 0; /* unreachable, but some compilers complain without it */ +} + +FORCE_INLINE U64 +XXH64_endian_align(const void* input, size_t len, U64 seed, + XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U64 h64; + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)32; + } +#endif + + if (len>=32) { + const BYTE* const limit = bEnd - 32; + U64 v1 = seed + PRIME64_1 + PRIME64_2; + U64 v2 = seed + PRIME64_2; + U64 v3 = seed + 0; + U64 v4 = seed - PRIME64_1; + + do { + v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8; + v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8; + v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8; + v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8; + } while (p<=limit); + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + + } else { + h64 = seed + PRIME64_5; + } + + h64 += (U64) len; + + return XXH64_finalize(h64, p, len, endian, align); +} + + +XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH64_state_t state; + XXH64_reset(&state, seed); + XXH64_update(&state, input, len); + return XXH64_digest(&state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */ + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } } + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + +/*====== Hash Streaming ======*/ + +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void) +{ + return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed) +{ + XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME64_1 + PRIME64_2; + state.v2 = seed + PRIME64_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME64_1; + /* do not write into reserved, planned to be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; +} + +FORCE_INLINE XXH_errorcode +XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian) +{ + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + { const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + + state->total_len += len; + + if (state->memsize + len < 32) { /* fill in tmp buffer */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len); + state->memsize += (U32)len; + return XXH_OK; + } + + if (state->memsize) { /* tmp buffer is full */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize); + state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian)); + state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian)); + state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian)); + state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian)); + p += 32-state->memsize; + state->memsize = 0; + } + + if (p+32 <= bEnd) { + const BYTE* const limit = bEnd - 32; + U64 v1 = state->v1; + U64 v2 = state->v2; + U64 v3 = state->v3; + U64 v4 = state->v4; + + do { + v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8; + v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8; + v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8; + v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem64, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + } + + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH64_update_endian(state_in, input, len, XXH_bigEndian); +} + +FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian) +{ + U64 h64; + + if (state->total_len >= 32) { + U64 const v1 = state->v1; + U64 const v2 = state->v2; + U64 const v3 = state->v3; + U64 const v4 = state->v4; + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + } else { + h64 = state->v3 /*seed*/ + PRIME64_5; + } + + h64 += (U64) state->total_len; + + return XXH64_finalize(h64, state->mem64, (size_t)state->total_len, endian, XXH_aligned); +} + +XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_digest_endian(state_in, XXH_littleEndian); + else + return XXH64_digest_endian(state_in, XXH_bigEndian); +} + + +/*====== Canonical representation ======*/ + +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) +{ + return XXH_readBE64(src); +} + +#endif /* XXH_NO_LONG_LONG */ diff --git a/src/Native Compiler/xxhash.h b/src/Native Compiler/xxhash.h new file mode 100644 index 0000000..d6bad94 --- /dev/null +++ b/src/Native Compiler/xxhash.h @@ -0,0 +1,328 @@ +/* + xxHash - Extremely Fast Hash algorithm + Header File + Copyright (C) 2012-2016, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "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 COPYRIGHT + OWNER OR CONTRIBUTORS 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. + + You can contact the author at : + - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + +/* Notice extracted from xxHash homepage : + +xxHash is an extremely fast Hash algorithm, running at RAM speed limits. +It also successfully passes all tests from the SMHasher suite. + +Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) + +Name Speed Q.Score Author +xxHash 5.4 GB/s 10 +CrapWow 3.2 GB/s 2 Andrew +MumurHash 3a 2.7 GB/s 10 Austin Appleby +SpookyHash 2.0 GB/s 10 Bob Jenkins +SBox 1.4 GB/s 9 Bret Mulvey +Lookup3 1.2 GB/s 9 Bob Jenkins +SuperFastHash 1.2 GB/s 1 Paul Hsieh +CityHash64 1.05 GB/s 10 Pike & Alakuijala +FNV 0.55 GB/s 5 Fowler, Noll, Vo +CRC32 0.43 GB/s 9 +MD5-32 0.33 GB/s 10 Ronald L. Rivest +SHA1-32 0.28 GB/s 10 + +Q.Score is a measure of quality of the hash function. +It depends on successfully passing SMHasher test set. +10 is a perfect score. + +A 64-bit version, named XXH64, is available since r35. +It offers much better speed, but for 64-bit applications only. +Name Speed on 64 bits Speed on 32 bits +XXH64 13.8 GB/s 1.9 GB/s +XXH32 6.8 GB/s 6.0 GB/s +*/ + +#ifndef XXHASH_H_5627135585666179 +#define XXHASH_H_5627135585666179 1 + +#if defined (__cplusplus) +extern "C" { +#endif + + +/* **************************** +* Definitions +******************************/ +#include /* size_t */ +typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; + + +/* **************************** + * API modifier + ******************************/ +/** XXH_INLINE_ALL (and XXH_PRIVATE_API) + * This is useful to include xxhash functions in `static` mode + * in order to inline them, and remove their symbol from the public list. + * Inlining can offer dramatic performance improvement on small keys. + * Methodology : + * #define XXH_INLINE_ALL + * #include "xxhash.h" + * `xxhash.c` is automatically included. + * It's not useful to compile and link it as a separate module. + */ +#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) +# ifndef XXH_STATIC_LINKING_ONLY +# define XXH_STATIC_LINKING_ONLY +# endif +# if defined(__GNUC__) +# define XXH_PUBLIC_API static __inline __attribute__((unused)) +# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# define XXH_PUBLIC_API static inline +# elif defined(_MSC_VER) +# define XXH_PUBLIC_API static __inline +# else + /* this version may generate warnings for unused static functions */ +# define XXH_PUBLIC_API static +# endif +#else +# define XXH_PUBLIC_API /* do nothing */ +#endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */ + +/*! XXH_NAMESPACE, aka Namespace Emulation : + * + * If you want to include _and expose_ xxHash functions from within your own library, + * but also want to avoid symbol collisions with other libraries which may also include xxHash, + * + * you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library + * with the value of XXH_NAMESPACE (therefore, avoid NULL and numeric values). + * + * Note that no change is required within the calling program as long as it includes `xxhash.h` : + * regular symbol name will be automatically translated by this header. + */ +#ifdef XXH_NAMESPACE +# define XXH_CAT(A,B) A##B +# define XXH_NAME2(A,B) XXH_CAT(A,B) +# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber) +# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32) +# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState) +# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState) +# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset) +# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update) +# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest) +# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState) +# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash) +# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical) +# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64) +# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState) +# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState) +# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset) +# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update) +# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest) +# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState) +# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash) +# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical) +#endif + + +/* ************************************* +* Version +***************************************/ +#define XXH_VERSION_MAJOR 0 +#define XXH_VERSION_MINOR 6 +#define XXH_VERSION_RELEASE 5 +#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE) +XXH_PUBLIC_API unsigned XXH_versionNumber (void); + + +/*-********************************************************************** +* 32-bit hash +************************************************************************/ +typedef unsigned int XXH32_hash_t; + +/*! XXH32() : + Calculate the 32-bit hash of sequence "length" bytes stored at memory address "input". + The memory between input & input+length must be valid (allocated and read-accessible). + "seed" can be used to alter the result predictably. + Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s */ +XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed); + +/*====== Streaming ======*/ +typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned int seed); +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); + +/* + * Streaming functions generate the xxHash of an input provided in multiple segments. + * Note that, for small input, they are slower than single-call functions, due to state management. + * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized. + * + * XXH state must first be allocated, using XXH*_createState() . + * + * Start a new hash by initializing state with a seed, using XXH*_reset(). + * + * Then, feed the hash state by calling XXH*_update() as many times as necessary. + * The function returns an error code, with 0 meaning OK, and any other value meaning there is an error. + * + * Finally, a hash value can be produced anytime, by using XXH*_digest(). + * This function returns the nn-bits hash as an int or long long. + * + * It's still possible to continue inserting input into the hash state after a digest, + * and generate some new hashes later on, by calling again XXH*_digest(). + * + * When done, free XXH state space if it was allocated dynamically. + */ + +/*====== Canonical representation ======*/ + +typedef struct { unsigned char digest[4]; } XXH32_canonical_t; +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); + +/* Default result type for XXH functions are primitive unsigned 32 and 64 bits. + * The canonical representation uses human-readable write convention, aka big-endian (large digits first). + * These functions allow transformation of hash result into and from its canonical format. + * This way, hash values can be written into a file / memory, and remain comparable on different systems and programs. + */ + + +#ifndef XXH_NO_LONG_LONG +/*-********************************************************************** +* 64-bit hash +************************************************************************/ +typedef unsigned long long XXH64_hash_t; + +/*! XXH64() : + Calculate the 64-bit hash of sequence of length "len" stored at memory address "input". + "seed" can be used to alter the result predictably. + This function runs faster on 64-bit systems, but slower on 32-bit systems (see benchmark). +*/ +XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed); + +/*====== Streaming ======*/ +typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed); +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr); + +/*====== Canonical representation ======*/ +typedef struct { unsigned char digest[8]; } XXH64_canonical_t; +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash); +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src); +#endif /* XXH_NO_LONG_LONG */ + + + +#ifdef XXH_STATIC_LINKING_ONLY + +/* ================================================================================================ + This section contains declarations which are not guaranteed to remain stable. + They may change in future versions, becoming incompatible with a different version of the library. + These declarations should only be used with static linking. + Never use them in association with dynamic linking ! +=================================================================================================== */ + +/* These definitions are only present to allow + * static allocation of XXH state, on stack or in a struct for example. + * Never **ever** use members directly. */ + +#if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + +struct XXH32_state_s { + uint32_t total_len_32; + uint32_t large_len; + uint32_t v1; + uint32_t v2; + uint32_t v3; + uint32_t v4; + uint32_t mem32[4]; + uint32_t memsize; + uint32_t reserved; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH32_state_t */ + +struct XXH64_state_s { + uint64_t total_len; + uint64_t v1; + uint64_t v2; + uint64_t v3; + uint64_t v4; + uint64_t mem64[4]; + uint32_t memsize; + uint32_t reserved[2]; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH64_state_t */ + +# else + +struct XXH32_state_s { + unsigned total_len_32; + unsigned large_len; + unsigned v1; + unsigned v2; + unsigned v3; + unsigned v4; + unsigned mem32[4]; + unsigned memsize; + unsigned reserved; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH32_state_t */ + +# ifndef XXH_NO_LONG_LONG /* remove 64-bit support */ +struct XXH64_state_s { + unsigned long long total_len; + unsigned long long v1; + unsigned long long v2; + unsigned long long v3; + unsigned long long v4; + unsigned long long mem64[4]; + unsigned memsize; + unsigned reserved[2]; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH64_state_t */ +# endif + +# endif + + +#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) +# include "xxhash.c" /* include xxhash function bodies as `static`, for inlining */ +#endif + +#endif /* XXH_STATIC_LINKING_ONLY */ + + +#if defined (__cplusplus) +} +#endif + +#endif /* XXHASH_H_5627135585666179 */ From 7e1e0eeca000b3e12f0acb0f502f42136fa9f3ab Mon Sep 17 00:00:00 2001 From: Leonardo Silva <99574879+Leonard-The-Wise@users.noreply.github.com> Date: Wed, 26 Jul 2023 04:25:40 -0300 Subject: [PATCH 2/6] Initial migration to new compiler engine. Still a LOT of crashes. --- NWScript-Npp/NWScript-Npp.vcxproj | 55 +++- src/Common.cpp | 30 ++ src/Common.h | 6 + src/NWScriptCompiler.cpp | 4 +- src/NWScriptCompiler.h | 2 +- src/NWScriptCompilerV2.cpp | 425 ++++++++++++++++++++++++- src/NWScriptCompilerV2.h | 86 ++++- src/Native Compiler/exostring.cpp | 3 +- src/Native Compiler/exotypes.h | 6 +- src/Native Compiler/scriptcompcore.cpp | 3 +- src/PluginMain.cpp | 6 +- src/PluginMain.h | 6 +- src/ProjectVersion.h | 4 +- 13 files changed, 597 insertions(+), 39 deletions(-) diff --git a/NWScript-Npp/NWScript-Npp.vcxproj b/NWScript-Npp/NWScript-Npp.vcxproj index 49f8507..a74b359 100644 --- a/NWScript-Npp/NWScript-Npp.vcxproj +++ b/NWScript-Npp/NWScript-Npp.vcxproj @@ -122,7 +122,7 @@ Level3 NOMINMAX;PCRE2_STATIC;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_WIN32;_DEBUG;_USRDLL;DLL_EXPORTS;%(PreprocessorDefinitions) $(ProjectDir)..\src\;$(ProjectDir)..\src\Lexers;$(ProjectDir)..\src\Lexers\Lexlib;$(ProjectDir)..\src\Lexers\Scintilla;$(ProjectDir)..\src\Plugin Controls;$(ProjectDir)..\src\Plugin Interface;$(ProjectDir)..\src\Notepad Controls;$(ProjectDir)..\src\Utils;$(SolutionDir)lib\NscLib\Exports;$(SolutionDir)lib\lunasvg\include;$(ProjectDir)..\src\DarkMode - stdcpplatest + stdcpp20 NoListing Use pch.h @@ -148,7 +148,7 @@ true NOMINMAX;PCRE2_STATIC;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_WIN32;NDEBUG;_USRDLL;DLL_EXPORTS;%(PreprocessorDefinitions) $(ProjectDir)..\src\;$(ProjectDir)..\src\Lexers;$(ProjectDir)..\src\Lexers\Lexlib;$(ProjectDir)..\src\Lexers\Scintilla;$(ProjectDir)..\src\Plugin Controls;$(ProjectDir)..\src\Plugin Interface;$(ProjectDir)..\src\Notepad Controls;$(ProjectDir)..\src\Utils;$(SolutionDir)lib\NscLib\Exports;$(SolutionDir)lib\lunasvg\include;$(ProjectDir)..\src\DarkMode - stdcpplatest + stdcpp20 NoListing Use pch.h @@ -200,7 +200,7 @@ Level3 NOMINMAX;PCRE2_STATIC;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN64;_WIN64;WIN32;_WIN32;NDEBUG;_USRDLL;DLL_EXPORTS;%(PreprocessorDefinitions) $(ProjectDir)..\src\;$(ProjectDir)..\src\Lexers;$(ProjectDir)..\src\Lexers\Lexlib;$(ProjectDir)..\src\Lexers\Scintilla;$(ProjectDir)..\src\Plugin Controls;$(ProjectDir)..\src\Plugin Interface;$(ProjectDir)..\src\Notepad Controls;$(ProjectDir)..\src\Utils;$(SolutionDir)lib\NscLib\Exports;$(SolutionDir)lib\lunasvg\include;$(ProjectDir)..\src\DarkMode - stdcpplatest + stdcpp20 NoListing Use pch.h @@ -335,13 +335,48 @@ - - - - - - - + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + diff --git a/src/Common.cpp b/src/Common.cpp index 40c0a1a..55bc602 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -897,6 +897,26 @@ namespace NWScriptPluginCommons { return true; } + char* fileToBufferC(const generic_string& filePath, size_t* pBufferSize) + { + std::ifstream fileReadStream; + + fileReadStream.open(filePath.c_str(), std::ios::in | std::ios::binary); + if (!fileReadStream.is_open()) + { + return NULL; + } + + fileReadStream.seekg(0, std::ios::end); + *pBufferSize = fileReadStream.tellg(); + char* pRetValue = new char[*pBufferSize]; + fileReadStream.seekg(0, std::ios::beg); + fileReadStream.read(pRetValue, *pBufferSize); + fileReadStream.close(); + + return pRetValue; + } + // Saves a string buffer into a raw file bool bufferToFile(const generic_string& filePath, const std::string& sContents) { @@ -912,6 +932,16 @@ namespace NWScriptPluginCommons { return true; } + // Removes the file extension from name + std::string removeFileExtension(const std::string& filename) + { + size_t lastDotPos = filename.find_last_of("."); + if (lastDotPos != std::string::npos) { + return filename.substr(0, lastDotPos); + } + return filename; + } + // Writes a pseudo-batch file to store Notepad++ executable to be called by ShellExecute // with a delay (So it can restart seeamlessly). bool writePseudoBatchExecute(const generic_string& path, const generic_string& executePath) diff --git a/src/Common.h b/src/Common.h index 11251ff..acdd09d 100644 --- a/src/Common.h +++ b/src/Common.h @@ -183,9 +183,15 @@ namespace NWScriptPluginCommons { // Loads a raw file into a string buffer bool fileToBuffer(const generic_string& filePath, std::string& sContents); + // Loads a raw file into a char* buffer. pBufferSize returns the number of read bytes + char* fileToBufferC(const generic_string& filePath, size_t* pBufferSize); + // Saves a string buffer into a raw file bool bufferToFile(const generic_string& filePath, const std::string& sContents); + // Removes the file extension from name + std::string removeFileExtension(const std::string& filename); + // Checks for a path's write permission. // returns false if not successful (eg: file/path doesn't exist), else returns true and fills outFilePermission enums bool checkWritePermission(const generic_string& sPath, PathWritePermission& outPathPermission); diff --git a/src/NWScriptCompiler.cpp b/src/NWScriptCompiler.cpp index b6d5da1..8bcd67c 100644 --- a/src/NWScriptCompiler.cpp +++ b/src/NWScriptCompiler.cpp @@ -199,7 +199,7 @@ void NWScriptCompiler::processFile(bool fromMemory, char* fileContents) else { _logger.log("Disassembling binary: " + _sourcePath.string(), LogType::ConsoleMessage); - bSuccess = disassembleBinary(inFileContents, fileResType, fileResRef); + bSuccess = disassemblyBinary(inFileContents, fileResType, fileResRef); } notifyCaller(bSuccess); @@ -313,7 +313,7 @@ bool NWScriptCompiler::compileScript(std::string& fileContents, return true; } -bool NWScriptCompiler::disassembleBinary(std::string& fileContents, +bool NWScriptCompiler::disassemblyBinary(std::string& fileContents, const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef) { std::string generatedCode; diff --git a/src/NWScriptCompiler.h b/src/NWScriptCompiler.h index 32fb485..c33e378 100644 --- a/src/NWScriptCompiler.h +++ b/src/NWScriptCompiler.h @@ -167,7 +167,7 @@ namespace NWScriptPlugin const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef); // Disassemble a binary file into a pcode assembly text format - bool disassembleBinary(std::string& fileContents, + bool disassemblyBinary(std::string& fileContents, const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef); // Dependencies files and views diff --git a/src/NWScriptCompilerV2.cpp b/src/NWScriptCompilerV2.cpp index 0bb0a6b..692a9f7 100644 --- a/src/NWScriptCompilerV2.cpp +++ b/src/NWScriptCompilerV2.cpp @@ -15,6 +15,7 @@ #include "VersionInfoEx.h" using namespace NWScriptPlugin; + typedef NWScriptLogger::LogType LogType; #define DEPENDENCYHEADER " \ @@ -35,6 +36,123 @@ typedef jpcre2::select pcre2; static pcre2::Regex assemblyLine(FORMATDISASMREGEX, PCRE2_MULTILINE, jpcre2::JIT_COMPILE); static pcre2::Regex dependencyParse(DEPENDENCYPARSEREGEX, 0, jpcre2::JIT_COMPILE); +// This new global resource manager pointer is required for new compiler. +// It will only point to the compiler's instance ResourceManager +// Since it needs a global resource, we also make a global pointer to the script compiler itself +static ResourceManager* g_ResourceManager = nullptr; +static NWScriptCompilerV2* g_NWScriptCompilerV2 = nullptr; + +static std::map CompileErrorTlk = { + {560, "Unexpected character"}, + {561, "Fatal compiler error"}, + {562, "Program compound statement at start"}, + {563, "Unexpected end compound statement"}, + {564, "After compound statement at end"}, + {565, "Parsing variable list"}, + {566, "Unknown state in compiler"}, + {567, "Invalid declaration type"}, + {568, "No left bracket on expression"}, + {569, "No right bracket on expression"}, + {570, "Bad start of statement"}, + {571, "No left bracket on arg list"}, + {572, "No right bracket on arg list"}, + {573, "No semicolon after expression"}, + {574, "Parsing assignment statement"}, + {575, "Bad lvalue"}, + {576, "Bad constant type"}, + {577, "Identifier list full"}, + {578, "Non integer id for integer constant"}, + {579, "Non float id for float constant"}, + {580, "Non string id for string constant"}, + {581, "Variable already used within scope"}, + {582, "Variable defined without type"}, + {583, "Incorrect variable state left on stack"}, + {584, "Non integer expression where integer required"}, + {585, "Void expression where non void required"}, + {586, "Invalid parameters for assignment"}, + {587, "Declaration does not match parameters"}, + {588, "Logical operation has invalid operands"}, + {589, "Equality test has invalid operands"}, + {590, "Comparison test has invalid operands"}, + {591, "Shift operation has invalid operands"}, + {592, "Arithmetic operation has invalid operands"}, + {593, "Unknown operation in semantic check"}, + {594, "Script too large"}, + {595, "Return statement has no parameters"}, + {596, "No while after do keyword"}, + {597, "Function definition missing name"}, + {598, "Function definition missing parameter list"}, + {599, "Malformed parameter list"}, + {600, "Bad type specifier"}, + {601, "No semicolon after structure"}, + {602, "Ellipsis in identifier"}, + {603, "File not found"}, + {604, "Include recursive"}, + {605, "Include too many levels"}, + {606, "Parsing return statement"}, + {607, "Parsing identifier list"}, + {608, "Parsing function declaration"}, + {609, "Duplicate function implementation"}, + {610, "Token too long"}, + {611, "Undefined structure"}, + {612, "Left of structure part not structure"}, + {613, "Right of structure part not field in structure"}, + {614, "Undefined field in structure"}, + {615, "Structure redefined"}, + {616, "Variable used twice in same structure"}, + {617, "Function implementation and definition differ"}, + {618, "Mismatched types"}, + {619, "Integer not at top of stack"}, + {620, "Return type and function type mismatched"}, + {621, "Not all control paths return a value"}, + {622, "Undefined identifier"}, + {623, "No function main in script"}, + {624, "Function main must have void return value"}, + {625, "Function main must have no parameters"}, + {626, "Non void function cannot be a statement"}, + {627, "Bad variable name"}, + {628, "Non optional parameter cannot follow optional parameter"}, + {629, "Type does not have an optional parameter"}, + {630, "Non constant in function declaration"}, + {631, "Parsing constant vector"}, + {1594, "Operand must be an integer lvalue"}, + {1595, "Conditional requires second expression"}, + {1596, "Conditional must have matching return types"}, + {1597, "Multiple default statements within switch"}, + {1598, "Multiple case constant statements within switch"}, + {1599, "Case parameter not a constant integer"}, + {1600, "Switch must evaluate to an integer"}, + {1601, "No colon after default label"}, + {1602, "No colon after case label"}, + {1603, "No semicolon after statement"}, + {4834, "Break outside of loop or case statement"}, + {4835, "Too many parameters on function"}, + {4836, "Unable to open file for writing"}, + {4855, "Unterminated string constant"}, + {5182, "No function intsc in script"}, + {5183, "Function intsc must have void return value"}, + {5184, "Function intsc must have no parameters"}, + {6804, "Jumping over declaration statements case disallowed"}, + {6805, "Jumping over declaration statements default disallowed"}, + {6823, "Else without corresponding if"}, + {3741, "Invalid type for const keyword"}, + {3742, "Const keyword cannot be used on non global variables"}, + {3752, "Invalid value assigned to constant"}, + {9081, "Switch condition cannot be followed by a null statement"}, + {9082, "While condition cannot be followed by a null statement"}, + {9083, "For statement cannot be followed by a null statement"}, + {9155, "Cannot include this file twice"}, + {10407, "If condition cannot be followed by a null statement"}, + {40104, "Else cannot be followed by a null statement"} +}; + + +NWScriptCompilerV2::NWScriptCompilerV2() : + _resourceManager(nullptr), _settings(nullptr), _compiler(nullptr) +{ + g_NWScriptCompilerV2 = this; +} + bool NWScriptCompilerV2::initialize() { // Critical path, initialize resources @@ -147,12 +265,25 @@ void NWScriptCompilerV2::processFile(bool fromMemory, char* fileContents) _includePaths.push_back(properDirNameA(wstr2str(s)) + "\\"); } + // Set global resource variable to current resource manager + g_ResourceManager = _resourceManager.get(); + + // Create compiler. Points functions of API to our own. + CScriptCompilerAPI cAPI; + cAPI.ResManLoadScriptSourceFile = ResManLoadScriptSourceFile; + cAPI.ResManUpdateResourceDirectory = ResManUpdateResourceDirectory; + cAPI.ResManWriteToFile = ResManWriteToFile; + cAPI.TlkResolve = TlkResolve; + + _compilerV2 = std::make_unique(NWN::ResNSS, NWN::ResNCS, NWN::ResNDB, cAPI); + // Create our compiler/disassembler _compiler = std::make_unique(*_resourceManager, _settings->useNonBiowareExtenstions); _compiler->NscSetLogger(&_logger); _compiler->NscSetIncludePaths(_includePaths); _compiler->NscSetCompilerErrorPrefix(SCRIPTERRORPREFIX); _compiler->NscSetResourceCacheEnabled(true); + } // Acquire information about NWN Resource Type of the file. Warning of ignored result is incorrect. @@ -190,22 +321,81 @@ void NWScriptCompilerV2::processFile(bool fromMemory, char* fileContents) bool bSuccess = false; if (_compilerMode == 0) { + // Use old library to fetch preprocessor and make dependencies, since the new Beamdog's compiler don't support them if (_fetchPreprocessorOnly) + { _logger.log("Fetching preprocessor output for: " + _sourcePath.string(), LogType::ConsoleMessage); - else + bSuccess = compileScript(inFileContents, fileResType, fileResRef); + } + + if (_makeDependencyView) + { + _logger.log("Making dependency view for: " + _sourcePath.string(), LogType::ConsoleMessage); + bSuccess = compileScript(inFileContents, fileResType, fileResRef); + } + + // Use new library for compiling to support NWScript latest features + if (!_fetchPreprocessorOnly && !_makeDependencyView) + { _logger.log("Compiling script: " + _sourcePath.string(), LogType::ConsoleMessage); - bSuccess = compileScript(inFileContents, fileResType, fileResRef); + bSuccess = compileScriptV2(inFileContents, fileResType, fileResRef); + } + } else { _logger.log("Disassembling binary: " + _sourcePath.string(), LogType::ConsoleMessage); - bSuccess = disassembleBinary(inFileContents, fileResType, fileResRef); + bSuccess = disassemblyBinary(inFileContents, fileResType, fileResRef); } notifyCaller(bSuccess); } +bool NWScriptCompilerV2::compileScriptV2(std::string& fileContents, + const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef) +{ + // Setup compiler according to user's preferences + _compilerV2->SetGenerateDebuggerOutput(_settings->generateSymbols); + uint32_t optimizationFlags = _settings->generateSymbols ? CSCRIPTCOMPILER_OPTIMIZE_NOTHING : + _settings->optimizeScript ? CSCRIPTCOMPILER_OPTIMIZE_EVERYTHING : CSCRIPTCOMPILER_OPTIMIZE_NOTHING; + _compilerV2->SetOptimizationFlags(optimizationFlags); + _compilerV2->SetCompileConditionalOrMain(true); + _compilerV2->SetIdentifierSpecification("nwscript"); + _compilerV2->SetOutputAlias(""); + + // Compile memory allocated file + NativeCompileResult ret; + + ret.code = _compilerV2->CompileFile(_sourcePath.string()); + + //ret.code = _compilerV2->CompileScriptChunk(fileContents, false); + + // Sometimes, CompileFile returns 1 or -1; in which case the error sould be in CapturedError. + // Forward from there. + if (ret.code == 1 || ret.code == -1) + { + ret.code = _compilerV2->GetCapturedErrorStrRef(); + assert(ret.code != 0); + if (ret.code == 0) + { + ret.code = STRREF_CSCRIPTCOMPILER_ERROR_FATAL_COMPILER_ERROR*-1; + _logger.log(CompileErrorTlk[ret.code], + LogType::Critical, std::string("NSC0") + std::to_string(ret.code)); + + return false; + } + } + + ret.str = ret.code ? _compilerV2->GetCapturedError()->CStr() : (char*)""; + + if (ret.code) + _logger.log(ret.str, LogType::Error); + + return (ret.code == 0); +} + + bool NWScriptCompilerV2::compileScript(std::string& fileContents, const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef) { @@ -313,7 +503,7 @@ bool NWScriptCompilerV2::compileScript(std::string& fileContents, return true; } -bool NWScriptCompilerV2::disassembleBinary(std::string& fileContents, +bool NWScriptCompilerV2::disassemblyBinary(std::string& fileContents, const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef) { std::string generatedCode; @@ -457,4 +647,229 @@ bool NWScriptCompilerV2::MakeDependenciesFile(const std::set& depen } return true; -} \ No newline at end of file +} + + +bool CacheResource(const char* ResFileContents, UINT32 ResFileLength, bool Allocated, + const NWN::ResRef32& ResRef, NWN::ResType ResType, const std::string& sLocation) +{ + try + { + ResourceCacheKey Key; + ResourceCacheEntry Entry; + bool Inserted; + + Key.ResRef = ResRef; + Key.ResType = ResType; + + Entry.Allocated = Allocated; + Entry.Contents = (char*)ResFileContents; + Entry.Size = ResFileLength; + Entry.Location = sLocation; + + Inserted = g_NWScriptCompilerV2->getResourceCache().insert(ResourceCache::value_type(Key, Entry)).second; + + assert(Inserted == true); + } + catch (std::exception) + { + return false; + } + + return true; +} + +// Dummy function. Interface to new compiler - we do nothing here. +BOOL NWScriptPlugin::ResManUpdateResourceDirectory(const char* sAlias) +{ + return false; +} + +// Intercepts ResManWriteToFile from CScriptCompiler API. +int32_t NWScriptPlugin::ResManWriteToFile(const char* sFileName, RESTYPE nResType, const uint8_t* pData, size_t nSize, bool bBinary) +{ + + // Decides which type of file to write depending on ResType. + + std::string dataRef; + dataRef.assign(reinterpret_cast(pData), nSize); + + generic_string outputPath = str2wstr(g_NWScriptCompilerV2->getDestinationDirectory().string() + + "\\" + fs::path(sFileName).stem().string() + + "." + g_ResourceManager->ResTypeToExt(nResType) + ); + + if (!bufferToFile(outputPath, dataRef)) + { + g_NWScriptCompilerV2->logger().log("", LogType::ConsoleMessage); + + switch (nResType) + { + case NWN::ResNCS: + g_NWScriptCompilerV2->logger().log(TEXT("Unable to write compiled output file: ") + outputPath, LogType::Critical, TEXT("NSC2005")); + break; + case NWN::ResNDB: + g_NWScriptCompilerV2->logger().log(TEXT("Unable to write generated symbols output file: ") + outputPath, LogType::Critical, TEXT("NSC2006")); + break; + } + + g_NWScriptCompilerV2->logger().log("", LogType::ConsoleMessage); + return -1; + } + + return 0; +} + +const char* NWScriptPlugin::ResManLoadScriptSourceFile(const char* sFileName, RESTYPE nResType) +{ + + // Try to find resource on cache first. + NWN::ResRef32 ResRef; + ResourceCacheKey CacheKey; + + + std::string sFileNameStem = fs::path(sFileName).stem().string(); + + try + { + ResRef = g_ResourceManager->ResRef32FromStr(sFileNameStem); + } + catch (std::exception) + { + return NULL; + } + + CacheKey.ResRef = ResRef; + CacheKey.ResType = (NWN::ResType)nResType; + + ResourceCache::const_iterator it = g_NWScriptCompilerV2->getResourceCache().find(CacheKey); + + if (it != g_NWScriptCompilerV2->getResourceCache().end()) + { + return it->second.Contents; + } + + size_t fileSize = 0; + char* fileContents = NULL; + + // Not found: try search include paths first. + for (auto it = g_NWScriptCompilerV2->includePaths().begin(); it != g_NWScriptCompilerV2->includePaths().end(); ++it) + { + std::string Str(*it); +#ifdef _WINDOWS + if (Str.back() != '\\') + Str += "\\"; +#else + if (Str.back() != '/') + Str += "/"; +#endif + Str += sFileNameStem; + Str += "."; + Str += g_ResourceManager->ResTypeToExt(nResType); + + fileSize = 0; + fileContents = fileToBufferC(str2wstr(Str), &fileSize); + if (fileSize > 0) + { + // #BUG: Discover why the last byte is always garbage + fileContents[fileSize] = 0; + + // Cache loaded file for future reference + g_NWScriptCompilerV2->logger().WriteText("INFO: Loaded File -> %s\n", Str.c_str()); + std::string res = *it + sFileNameStem + "." + g_ResourceManager->ResTypeToExt(nResType); + + CacheResource(fileContents, fileSize, true, ResRef, (NWN::ResType)nResType, res); + + return fileContents; + } + } + + // Not found: try opening via resource files + ResourceManager::FileHandle Handle = g_ResourceManager->OpenFile(ResRef, nResType); + + if (Handle == ResourceManager::INVALID_FILE) + return NULL; + + // Read entire file upfront + fileSize = g_ResourceManager->GetEncapsulatedFileSize(Handle); + + size_t BytesLeft = fileSize; + size_t Offset = 0; + size_t Read = 0; + + try + { + if (fileSize != 0) + { + fileContents = new char[fileSize]; + + if (fileContents == NULL) + { + g_NWScriptCompilerV2->logger().log("Critical failure: Memory allocation for resource [" + std::string(sFileName) + "] failed.", LogType::Critical, "NSC2101"); + throw std::bad_alloc(); + } + + while (BytesLeft) + { + if (!g_ResourceManager->ReadEncapsulatedFile(Handle, Offset, BytesLeft, &Read, &fileContents[Offset])) + { + g_NWScriptCompilerV2->logger().log("Critical failure: ReadEncapsulatedFile did not succeeded.", LogType::Critical, "NSC2101"); + throw std::runtime_error("Critical failure: ReadEncapsulatedFile did not succeeded"); + } + + if (Read == 0) + { + g_NWScriptCompilerV2->logger().log("Critical failure: read 0 bytes from resource file [" + std::string(sFileName) + "]", LogType::Critical, "NSC2102"); + throw std::runtime_error("Critical failure: read 0 bytes from resource file"); + } + + Offset += Read; + BytesLeft -= Read; + } + } + else + { + fileContents = NULL; + } + } + catch (std::exception) + { + if (fileContents) + delete[] fileContents; + + g_ResourceManager->CloseFile(Handle); + return NULL; + } + + std::string res = ""; + std::string AccessorName; + try + { + g_ResourceManager->GetResourceAccessorName(Handle, AccessorName); + res = AccessorName + "/" + sFileNameStem + "." + g_ResourceManager->ResTypeToExt(nResType); + } + catch (std::exception) {} + + // Show includes always. Filter in script plugin + g_NWScriptCompilerV2->logger().WriteText("INFO: Loaded file from game's resources -> %s\n", res.c_str()); + + // Closes file and appends results to cache + g_ResourceManager->CloseFile(Handle); + + // # BUG: Discover why last byte is always trash + if (fileContents != NULL) + fileContents[fileSize] = 0; + + CacheResource(fileContents, fileSize, true, ResRef, (NWN::ResType)nResType, res); + + return fileContents; +} + +const char* NWScriptPlugin::TlkResolve(STRREF strRef) +{ + if (CompileErrorTlk.contains(strRef)) + return CompileErrorTlk[strRef].c_str(); + else + return (std::string("Unknown error code -> ") + std::to_string(strRef)).c_str(); +} + diff --git a/src/NWScriptCompilerV2.h b/src/NWScriptCompilerV2.h index 515f95e..e4c63a5 100644 --- a/src/NWScriptCompilerV2.h +++ b/src/NWScriptCompilerV2.h @@ -10,8 +10,9 @@ #include #include -#include "Native Compiler/scriptcomp.h" // New oficial compiler provided by Beamdog itself. -#include "Nsc.h" // Here we are using NscLib only for the game's resource manager +#include "Native Compiler/exobase.h" // New oficial compiler provided by Beamdog itself. +#include "Native Compiler/scriptcomp.h" // +#include "Nsc.h" // Here we are using NscLib for older features like preprocessor and make dependency #include "Common.h" #include "Settings.h" @@ -19,12 +20,53 @@ namespace NWScriptPlugin { + + // Copied from NscCompiler.cpp -> Resource Cache structures + struct ResourceCacheKey + { + NWN::ResRef32 ResRef; + NWN::ResType ResType; + + inline bool operator < (const ResourceCacheKey& other) const + { + return (ResType < other.ResType) || + (memcmp(&ResRef, &other.ResRef, sizeof(ResRef)) < 0); + } + + inline bool operator == (const ResourceCacheKey& other) const + { + return (ResType == other.ResType) && + (memcmp(&ResRef, &other.ResRef, sizeof(ResRef)) == 0); + } + }; + + struct ResourceCacheEntry + { + bool Allocated; + char* Contents; + UINT32 Size; + std::string Location; + }; + + typedef std::map ResourceCache; + + struct NativeCompileResult + { + int32_t code; + char* str; // static buffer + }; + + // Function pointers to resolve new compiler Resource API requirements + static BOOL ResManUpdateResourceDirectory(const char* sAlias); + static int32_t ResManWriteToFile(const char* sFileName, RESTYPE nResType, const uint8_t* pData, size_t nSize, bool bBinary); + static const char* ResManLoadScriptSourceFile(const char* sFileName, RESTYPE nResType); + static const char* TlkResolve(STRREF strRef); + class NWScriptCompilerV2 final { public: - NWScriptCompilerV2() : - _resourceManager(nullptr), _settings(nullptr), _compiler(nullptr) {} + NWScriptCompilerV2(); bool isInitialized() { return _resourceManager != nullptr; @@ -41,6 +83,7 @@ namespace NWScriptPlugin // Reset compiler state void reset() { _resourceManager = nullptr; + _compilerV2 = nullptr; _compiler = nullptr; _includePaths.clear(); _fetchPreprocessorOnly = false; @@ -50,6 +93,13 @@ namespace NWScriptPlugin setMode(0); _processingEndCallback = nullptr; clearLog(); + + // Free memory from Resource Cache to avoid memory leaks +// for (auto it = _ResourceCache.begin(); it != _ResourceCache.end(); ++it) +// if (it->second.Contents != NULL) +// delete it->second.Contents; + + _ResourceCache.clear(); } // Sets destination to a VALID and existing directory (or else get an error) @@ -113,15 +163,15 @@ namespace NWScriptPlugin _makeDependencyView = false; } - int getMode() const { + inline int getMode() const { return _compilerMode; } - bool isViewDependencies() const { + inline bool isViewDependencies() const { return _makeDependencyView; } - bool isFetchPreprocessorOnly() const { + inline bool isFetchPreprocessorOnly() const { return _fetchPreprocessorOnly; } @@ -129,18 +179,31 @@ namespace NWScriptPlugin return _logger; } + std::vector& includePaths() { + return _includePaths; + } + // Returns if an output path is required for operation - bool isOutputDirRequired() { + inline bool isOutputDirRequired() { return !(_fetchPreprocessorOnly || _makeDependencyView); } + inline ResourceCache& getResourceCache() { + return _ResourceCache; + } + void processFile(bool fromMemory, char* fileContents); private: + std::unique_ptr _resourceManager; + ResourceCache _ResourceCache; + std::unique_ptr _compilerV2; + + // # TODO: Remove old compiler references std::unique_ptr _compiler; - + bool _fetchPreprocessorOnly = false; bool _makeDependencyView = false; int _compilerMode = 0; @@ -168,8 +231,11 @@ namespace NWScriptPlugin bool compileScript(std::string& fileContents, const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef); + bool compileScriptV2(std::string& fileContents, + const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef); + // Disassemble a binary file into a pcode assembly text format - bool disassembleBinary(std::string& fileContents, + bool disassemblyBinary(std::string& fileContents, const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef); // Dependencies files and views diff --git a/src/Native Compiler/exostring.cpp b/src/Native Compiler/exostring.cpp index 9387445..6666c7d 100644 --- a/src/Native Compiler/exostring.cpp +++ b/src/Native Compiler/exostring.cpp @@ -1421,7 +1421,8 @@ CExoString CExoString::AsTAG() const newBuffer[nIndex] = '\0'; newStr = newBuffer; -#ifdef _DEBUG // 01/27/03 - PLR - Need to debug this properly! + // PATCH:: Modified _DEBUG macro +#ifdef _DEBUG_EXOSTRING // 01/27/03 - PLR - Need to debug this properly! if ( newStr != m_sString ) { LOGSET("TAG CHANGE ===> (%s) -> (%s)\n", m_sString, newStr.CStr())LOGFLUSH diff --git a/src/Native Compiler/exotypes.h b/src/Native Compiler/exotypes.h index c675130..a20ff55 100644 --- a/src/Native Compiler/exotypes.h +++ b/src/Native Compiler/exotypes.h @@ -21,7 +21,7 @@ // WIN32 don't clobber std::min/max. // This is not what we need in our life. -#define NOMINMAX +//#define NOMINMAX #include #include @@ -63,6 +63,9 @@ typedef uint16_t RESTYPE; #define NWN_max(a,b) (std::max)(a,b) #define NWN_min(a,b) (std::min)(a,b) +// PATCH: __cplusplus macro is defined as 1999 still by Microsoft. We disable here. +#ifdef DONT_USE_CPLUSPLUC + // This would be in in c++17. #if __cplusplus < 201500 namespace std @@ -87,6 +90,7 @@ namespace std }; #endif +#endif #if ((defined __linux__) || (defined __APPLE__)) #define stricmp strcasecmp #define strnicmp strncasecmp diff --git a/src/Native Compiler/scriptcompcore.cpp b/src/Native Compiler/scriptcompcore.cpp index 69e959d..01d9989 100644 --- a/src/Native Compiler/scriptcompcore.cpp +++ b/src/Native Compiler/scriptcompcore.cpp @@ -1416,7 +1416,8 @@ int32_t CScriptCompiler::CompileScriptConditional(const CExoString &sScriptCondi int32_t CScriptCompiler::GetCompiledScriptCode(char **ppnCode, int32_t *pnCodeSize) { - *pnCodeSize = m_nOutputCodeSize; + // PATCH: This function seems to be returning the wrong values + *pnCodeSize = m_nOutputCodeLength; //m_nOutputCodeSize; *ppnCode = m_pchOutputCode; return 0; diff --git a/src/PluginMain.cpp b/src/PluginMain.cpp index 407154c..65720fe 100644 --- a/src/PluginMain.cpp +++ b/src/PluginMain.cpp @@ -2571,7 +2571,7 @@ void Plugin::DoCompileOrDisasm(generic_string filePath, bool fromCurrentScintill // Process script. _compiler.setSourceFilePath(scriptPath); #ifdef USE_THREADS - std::thread tProcessor(&NWScriptCompiler::processFile, &_compiler, fromCurrentScintilla, &_tempFileContents[0]); + std::thread tProcessor(&NWScriptCompilerV2::processFile, &_compiler, fromCurrentScintilla, &_tempFileContents[0]); tProcessor.detach(); #else _compiler.processFile(fromCurrentScintilla, &_tempFileContents[0]); @@ -2600,7 +2600,7 @@ void Plugin::BuildFilesList() // Receives notifications when a "Compile" menu command ends void Plugin::CompileEndingCallback(HRESULT decision) { - NWScriptCompiler& compiler = Instance().Compiler(); + NWScriptCompilerV2& compiler = Instance().Compiler(); // Clear any content of temporary stash if exists Instance()._tempFileContents.clear(); @@ -2640,7 +2640,7 @@ void Plugin::CompileEndingCallback(HRESULT decision) // Receives notifications when a "Disassemble" menu command ends void Plugin::DisassembleEndingCallback(HRESULT decision) { - NWScriptCompiler& compiler = Instance().Compiler(); + NWScriptCompilerV2& compiler = Instance().Compiler(); // Unlock controls to compiler log window Instance()._loggerWindow->LockControls(false); diff --git a/src/PluginMain.h b/src/PluginMain.h index a58b21f..34d96b0 100644 --- a/src/PluginMain.h +++ b/src/PluginMain.h @@ -18,7 +18,7 @@ #include "LineIndentor.h" #include "Settings.h" #include "NWScriptParser.h" -#include "NWScriptCompiler.h" +#include "NWScriptCompilerV2.h" #include "AboutDialog.h" #include "LoggerDialog.h" @@ -99,7 +99,7 @@ namespace NWScriptPlugin { // Retrieve's Plugin's LineIndentor Object LineIndentor& Indentor() { return _indentor; } // Retrieve the Compiler Object - NWScriptCompiler& Compiler() { return _compiler; }; + NWScriptCompilerV2& Compiler() { return _compiler; }; // Retrieve's Plugin's Module Handle HMODULE DllHModule() const { return _dllHModule; } // Retrieves Notepad++ HWND @@ -327,7 +327,7 @@ namespace NWScriptPlugin { NotepadLexer _notepadCurrentLexer; PluginMessenger _messageInstance; LineIndentor _indentor; - NWScriptCompiler _compiler; + NWScriptCompilerV2 _compiler; tTbData _dockingData; // needs persistent info for docking data HICON _dockingIcon; // needs persistent info for docking data generic_string _dockingTitle; // needs persistent info for docking data diff --git a/src/ProjectVersion.h b/src/ProjectVersion.h index 0108781..c12bd3d 100644 --- a/src/ProjectVersion.h +++ b/src/ProjectVersion.h @@ -22,9 +22,9 @@ #define stringify(a) stringify_(a) #define VERSION_MAJOR 1 -#define VERSION_MINOR 1 +#define VERSION_MINOR 2 #define VERSION_PATCH 0 -#define VERSION_BUILD 2150 +#define VERSION_BUILD 2300 #define VERSION_STRING stringify(VERSION_MAJOR) "." stringify(VERSION_MINOR) "." \ stringify(VERSION_PATCH) "." stringify(VERSION_BUILD) From 24daa4ebfaab364b831563b7f3f3069ba3e54f05 Mon Sep 17 00:00:00 2001 From: Leonardo Silva <99574879+Leonard-The-Wise@users.noreply.github.com> Date: Wed, 26 Jul 2023 14:08:55 -0300 Subject: [PATCH 3/6] - Fixed some serious HEAP violations with the new compiler; - Cleaned up the house a little bit - Added log support for new compiler API. - Compiling and DISASM now functions properly again. :) --- src/Common.cpp | 38 ++-- src/Common.h | 5 +- src/NWScriptCompilerV2.cpp | 292 +++++++++++++++------------ src/NWScriptCompilerV2.h | 12 +- src/NWScriptLogger.cpp | 16 ++ src/Plugin Controls/LoggerDialog.cpp | 4 +- src/PluginMain.cpp | 2 +- 7 files changed, 220 insertions(+), 149 deletions(-) diff --git a/src/Common.cpp b/src/Common.cpp index 55bc602..72b9cdd 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -152,6 +152,14 @@ namespace NWScriptPluginCommons { return output; } + // Makes a string to lower case + std::string toLowerCase(const std::string& str) + { + std::string result = str; + std::transform(result.begin(), result.end(), result.begin(), ::tolower); + return result; + } + // Opens a file dialog bool openFileDialog(HWND hOwnerWnd, std::vector& outSelectedFiles, const TCHAR* sFilters, const generic_string& lastOpenedFolder, bool multiFile) @@ -897,24 +905,26 @@ namespace NWScriptPluginCommons { return true; } - char* fileToBufferC(const generic_string& filePath, size_t* pBufferSize) + char* fileToNullTermBuffer(const generic_string& filePath, size_t* pBufferSize) { - std::ifstream fileReadStream; + std::ifstream file(filePath, std::ios::binary | std::ios::ate); - fileReadStream.open(filePath.c_str(), std::ios::in | std::ios::binary); - if (!fileReadStream.is_open()) - { - return NULL; - } + if (!file.is_open()) + return nullptr; - fileReadStream.seekg(0, std::ios::end); - *pBufferSize = fileReadStream.tellg(); - char* pRetValue = new char[*pBufferSize]; - fileReadStream.seekg(0, std::ios::beg); - fileReadStream.read(pRetValue, *pBufferSize); - fileReadStream.close(); + std::streampos fileSize = file.tellg(); + *pBufferSize = fileSize; + + char* buffer = new char[*pBufferSize + 1]; + + file.seekg(0, std::ios::beg); + file.read(buffer, fileSize); + file.close(); + + buffer[fileSize] = 0; - return pRetValue; + *pBufferSize = fileSize; + return buffer; } // Saves a string buffer into a raw file diff --git a/src/Common.h b/src/Common.h index acdd09d..0a26efc 100644 --- a/src/Common.h +++ b/src/Common.h @@ -114,6 +114,9 @@ namespace NWScriptPluginCommons { // Returns a new string replacing all input string %VARIABLES% variables with the associated string map(%VARIABLE%, %VALUE%) std::wstring replaceStringsW(const std::wstring& input, std::map& replaceStrings); + // Makes a string to lower case + std::string toLowerCase(const std::string& str); + // Opens a file dialog bool openFileDialog(HWND hOwnerWnd, std::vector& outSelectedFiles, const TCHAR* sFilters = TEXT("All files (*.*)\0*.*"), @@ -184,7 +187,7 @@ namespace NWScriptPluginCommons { bool fileToBuffer(const generic_string& filePath, std::string& sContents); // Loads a raw file into a char* buffer. pBufferSize returns the number of read bytes - char* fileToBufferC(const generic_string& filePath, size_t* pBufferSize); + char* fileToNullTermBuffer(const generic_string& filePath, size_t* pBufferSize); // Saves a string buffer into a raw file bool bufferToFile(const generic_string& filePath, const std::string& sContents); diff --git a/src/NWScriptCompilerV2.cpp b/src/NWScriptCompilerV2.cpp index 692a9f7..583aec0 100644 --- a/src/NWScriptCompilerV2.cpp +++ b/src/NWScriptCompilerV2.cpp @@ -43,109 +43,122 @@ static ResourceManager* g_ResourceManager = nullptr; static NWScriptCompilerV2* g_NWScriptCompilerV2 = nullptr; static std::map CompileErrorTlk = { - {560, "Unexpected character"}, - {561, "Fatal compiler error"}, - {562, "Program compound statement at start"}, - {563, "Unexpected end compound statement"}, - {564, "After compound statement at end"}, - {565, "Parsing variable list"}, - {566, "Unknown state in compiler"}, - {567, "Invalid declaration type"}, - {568, "No left bracket on expression"}, - {569, "No right bracket on expression"}, - {570, "Bad start of statement"}, - {571, "No left bracket on arg list"}, - {572, "No right bracket on arg list"}, - {573, "No semicolon after expression"}, - {574, "Parsing assignment statement"}, - {575, "Bad lvalue"}, - {576, "Bad constant type"}, - {577, "Identifier list full"}, - {578, "Non integer id for integer constant"}, - {579, "Non float id for float constant"}, - {580, "Non string id for string constant"}, - {581, "Variable already used within scope"}, - {582, "Variable defined without type"}, - {583, "Incorrect variable state left on stack"}, - {584, "Non integer expression where integer required"}, - {585, "Void expression where non void required"}, - {586, "Invalid parameters for assignment"}, - {587, "Declaration does not match parameters"}, - {588, "Logical operation has invalid operands"}, - {589, "Equality test has invalid operands"}, - {590, "Comparison test has invalid operands"}, - {591, "Shift operation has invalid operands"}, - {592, "Arithmetic operation has invalid operands"}, - {593, "Unknown operation in semantic check"}, - {594, "Script too large"}, - {595, "Return statement has no parameters"}, - {596, "No while after do keyword"}, - {597, "Function definition missing name"}, - {598, "Function definition missing parameter list"}, - {599, "Malformed parameter list"}, - {600, "Bad type specifier"}, - {601, "No semicolon after structure"}, - {602, "Ellipsis in identifier"}, - {603, "File not found"}, - {604, "Include recursive"}, - {605, "Include too many levels"}, - {606, "Parsing return statement"}, - {607, "Parsing identifier list"}, - {608, "Parsing function declaration"}, - {609, "Duplicate function implementation"}, - {610, "Token too long"}, - {611, "Undefined structure"}, - {612, "Left of structure part not structure"}, - {613, "Right of structure part not field in structure"}, - {614, "Undefined field in structure"}, - {615, "Structure redefined"}, - {616, "Variable used twice in same structure"}, - {617, "Function implementation and definition differ"}, - {618, "Mismatched types"}, - {619, "Integer not at top of stack"}, - {620, "Return type and function type mismatched"}, - {621, "Not all control paths return a value"}, - {622, "Undefined identifier"}, - {623, "No function main in script"}, - {624, "Function main must have void return value"}, - {625, "Function main must have no parameters"}, - {626, "Non void function cannot be a statement"}, - {627, "Bad variable name"}, - {628, "Non optional parameter cannot follow optional parameter"}, - {629, "Type does not have an optional parameter"}, - {630, "Non constant in function declaration"}, - {631, "Parsing constant vector"}, - {1594, "Operand must be an integer lvalue"}, - {1595, "Conditional requires second expression"}, - {1596, "Conditional must have matching return types"}, - {1597, "Multiple default statements within switch"}, - {1598, "Multiple case constant statements within switch"}, - {1599, "Case parameter not a constant integer"}, - {1600, "Switch must evaluate to an integer"}, - {1601, "No colon after default label"}, - {1602, "No colon after case label"}, - {1603, "No semicolon after statement"}, - {4834, "Break outside of loop or case statement"}, - {4835, "Too many parameters on function"}, - {4836, "Unable to open file for writing"}, - {4855, "Unterminated string constant"}, - {5182, "No function intsc in script"}, - {5183, "Function intsc must have void return value"}, - {5184, "Function intsc must have no parameters"}, - {6804, "Jumping over declaration statements case disallowed"}, - {6805, "Jumping over declaration statements default disallowed"}, - {6823, "Else without corresponding if"}, - {3741, "Invalid type for const keyword"}, - {3742, "Const keyword cannot be used on non global variables"}, - {3752, "Invalid value assigned to constant"}, - {9081, "Switch condition cannot be followed by a null statement"}, - {9082, "While condition cannot be followed by a null statement"}, - {9083, "For statement cannot be followed by a null statement"}, - {9155, "Cannot include this file twice"}, + {560, "Error: Unexpected character"}, + {561, "Error: Fatal compiler error"}, + {562, "Error: Program compound statement at start"}, + {563, "Error: Unexpected end compound statement"}, + {564, "Error: After compound statement at end"}, + {565, "Error: Parsing variable list"}, + {566, "Error: Unknown state in compiler"}, + {567, "Error: Invalid declaration type"}, + {568, "Error: No left bracket on expression"}, + {569, "Error: No right bracket on expression"}, + {570, "Error: Bad start of statement"}, + {571, "Error: No left bracket on arg list"}, + {572, "Error: No right bracket on arg list"}, + {573, "Error: No semicolon after expression"}, + {574, "Error: Parsing assignment statement"}, + {575, "Error: Bad lvalue"}, + {576, "Error: Bad constant type"}, + {577, "Error: Identifier list full"}, + {578, "Error: Non integer id for integer constant"}, + {579, "Error: Non float id for float constant"}, + {580, "Error: Non string id for string constant"}, + {581, "Error: Variable already used within scope"}, + {582, "Error: Variable defined without type"}, + {583, "Error: Incorrect variable state left on stack"}, + {584, "Error: Non integer expression where integer required"}, + {585, "Error: Void expression where non void required"}, + {586, "Error: Invalid parameters for assignment"}, + {587, "Error: Declaration does not match parameters"}, + {588, "Error: Logical operation has invalid operands"}, + {589, "Error: Equality test has invalid operands"}, + {590, "Error: Comparison test has invalid operands"}, + {591, "Error: Shift operation has invalid operands"}, + {592, "Error: Arithmetic operation has invalid operands"}, + {593, "Error: Unknown operation in semantic check"}, + {594, "Error: Script too large"}, + {595, "Error: Return statement has no parameters"}, + {596, "Error: No while after do keyword"}, + {597, "Error: Function definition missing name"}, + {598, "Error: Function definition missing parameter list"}, + {599, "Error: Malformed parameter list"}, + {600, "Error: Bad type specifier"}, + {601, "Error: No semicolon after structure"}, + {602, "Error: Ellipsis in identifier"}, + {603, "Error: File not found"}, + {604, "Error: Include recursive"}, + {605, "Error: Include too many levels"}, + {606, "Error: Parsing return statement"}, + {607, "Error: Parsing identifier list"}, + {608, "Error: Parsing function declaration"}, + {609, "Error: Duplicate function implementation"}, + {610, "Error: Token too long"}, + {611, "Error: Undefined structure"}, + {612, "Error: Left of structure part not structure"}, + {613, "Error: Right of structure part not field in structure"}, + {614, "Error: Undefined field in structure"}, + {615, "Error: Structure redefined"}, + {616, "Error: Variable used twice in same structure"}, + {617, "Error: Function implementation and definition differ"}, + {618, "Error: Mismatched types"}, + {619, "Error: Integer not at top of stack"}, + {620, "Error: Return type and function type mismatched"}, + {621, "Error: Not all control paths return a value"}, + {622, "Error: Undefined identifier"}, + {623, "Error: No function main in script"}, + {624, "Error: Function main must have void return value"}, + {625, "Error: Function main must have no parameters"}, + {626, "Error: Non void function cannot be a statement"}, + {627, "Error: Bad variable name"}, + {628, "Error: Non optional parameter cannot follow optional parameter"}, + {629, "Error: Type does not have an optional parameter"}, + {630, "Error: Non constant in function declaration"}, + {631, "Error: Parsing constant vector"}, + {1594, "Error: Operand must be an integer lvalue"}, + {1595, "Error: Conditional requires second expression"}, + {1596, "Error: Conditional must have matching return types"}, + {1597, "Error: Multiple default statements within switch"}, + {1598, "Error: Multiple case constant statements within switch"}, + {1599, "Error: Case parameter not a constant integer"}, + {1600, "Error: Switch must evaluate to an integer"}, + {1601, "Error: No colon after default label"}, + {1602, "Error: No colon after case label"}, + {1603, "Error: No semicolon after statement"}, + {4834, "Error: Break outside of loop or case statement"}, + {4835, "Error: Too many parameters on function"}, + {4836, "Error: Unable to open file for writing"}, + {4855, "Error: Unterminated string constant"}, + {5182, "Error: No function intsc in script"}, + {5183, "Error: Function intsc must have void return value"}, + {5184, "Error: Function intsc must have no parameters"}, + {6804, "Error: Jumping over declaration statements case disallowed"}, + {6805, "Error: Jumping over declaration statements default disallowed"}, + {6823, "Error: Else without corresponding if"}, + {3741, "Error: Invalid type for const keyword"}, + {3742, "Error: Const keyword cannot be used on non global variables"}, + {3752, "Error: Invalid value assigned to constant"}, + {9081, "Error: Switch condition cannot be followed by a null statement"}, + {9082, "Error: While condition cannot be followed by a null statement"}, + {9083, "Error: For statement cannot be followed by a null statement"}, + {9155, "Error: Cannot include this file twice"}, {10407, "If condition cannot be followed by a null statement"}, {40104, "Else cannot be followed by a null statement"} }; +// Now we define some plugin-exclusive compiler message codes +#define NSC2001_RESOURCE_MANAGER_INITIALIZE_FAIL "NSC2001" +#define NSC2002_OPEN_FILE_FAIL "NSC2002" +#define NSC2003_RESERVED "NSC2003" +#define NSC2004_UNKNOWN_COMPILE_ERROR "NSC2004" +#define NSC2005_COULD_NOT_WRITE_COMPILED_FILE "NSC2005" +#define NSC2006_COULD_NOT_GENERATE_SYMBOL_FILE "NSC2006" +#define NSC2007_DISASSEMBLY_COMPILER_INIT_FAIL "NSC2007" +#define NSC2008_COULD_NOT_WRITE_DISASSEMBLY_FILE "NSC2008" +#define NSC2009_COULD_NOT_WRITE_DEPENDENCY_FILE "NSC2009" +#define NSC2010_CANT_COMPILE_NWSCRIPT_NSS "NSC2010" +#define NSC2011_INCLUDE_FILE_IGNORED "NSC2010" + NWScriptCompilerV2::NWScriptCompilerV2() : _resourceManager(nullptr), _settings(nullptr), _compiler(nullptr) @@ -162,7 +175,7 @@ bool NWScriptCompilerV2::initialize() { } catch (std::runtime_error& e) { - _logger.log("Failed to initialize the resources manager: " + std::string(e.what()), LogType::Critical, "NSC2001"); + _logger.log("Failed to initialize the resources manager: " + std::string(e.what()), LogType::Critical, NSC2001_RESOURCE_MANAGER_INITIALIZE_FAIL); return false; } @@ -225,7 +238,8 @@ void NWScriptCompilerV2::processFile(bool fromMemory, char* fileContents) if (_stricmp(_sourcePath.filename().string().c_str(), "nwscript.nss") == 0 && _compilerMode == 0) { _logger.log("Compiling script: " + _sourcePath.string(), LogType::ConsoleMessage); - _logger.log("Error: you can't explicitly compile any script named \"nwscript.nss\", this name is reserved for the main engine.", LogType::Critical, "NSC2010"); + _logger.log("Error: you can't explicitly compile any script named \"nwscript.nss\", this name is reserved for the main engine.", + LogType::Critical, NSC2010_CANT_COMPILE_NWSCRIPT_NSS); _logger.log("File ignored: " + _sourcePath.string() , LogType::Info); notifyCaller(false); return; @@ -300,7 +314,7 @@ void NWScriptCompilerV2::processFile(bool fromMemory, char* fileContents) { if (!fileToBuffer(_sourcePath.c_str(), inFileContents)) { - _logger.log("Could not load the specified file: " + wstr2str(_sourcePath), LogType::Critical, "NSC2002"); + _logger.log("Could not load the specified file: " + wstr2str(_sourcePath), LogType::Critical, NSC2002_OPEN_FILE_FAIL); notifyCaller(false); return; } @@ -360,7 +374,7 @@ bool NWScriptCompilerV2::compileScriptV2(std::string& fileContents, uint32_t optimizationFlags = _settings->generateSymbols ? CSCRIPTCOMPILER_OPTIMIZE_NOTHING : _settings->optimizeScript ? CSCRIPTCOMPILER_OPTIMIZE_EVERYTHING : CSCRIPTCOMPILER_OPTIMIZE_NOTHING; _compilerV2->SetOptimizationFlags(optimizationFlags); - _compilerV2->SetCompileConditionalOrMain(true); + _compilerV2->SetCompileConditionalOrMain(1); _compilerV2->SetIdentifierSpecification("nwscript"); _compilerV2->SetOutputAlias(""); @@ -381,16 +395,40 @@ bool NWScriptCompilerV2::compileScriptV2(std::string& fileContents, { ret.code = STRREF_CSCRIPTCOMPILER_ERROR_FATAL_COMPILER_ERROR*-1; _logger.log(CompileErrorTlk[ret.code], - LogType::Critical, std::string("NSC0") + std::to_string(ret.code)); + LogType::Critical, std::string("NSC") + std::to_string(ret.code)); return false; } } - ret.str = ret.code ? _compilerV2->GetCapturedError()->CStr() : (char*)""; - + // Resolve error messages if apply if (ret.code) - _logger.log(ret.str, LogType::Error); + { + ret.str = ret.code ? _compilerV2->GetCapturedError()->CStr() : (char*)""; + + std::string srcFileName = _sourcePath.stem().string(); + std::string srcFileExt = _sourcePath.extension().string(); + srcFileExt.erase(0, 1); // Delete the . before the extension. + + // Pre-process some known errors that are a bit different from the others (don't have line numbers, etc) + switch (abs(ret.code)) + { + case 561: + case 594: + _logger.log(TlkResolve(ret.code), LogType::Error, "NSC" + ret.code, srcFileName, srcFileExt, "-"); + break; + + // This is about include files. Downgrade to warning... + case 623: + _logger.log("File [" + _sourcePath.filename().string() + "] appears to be an include file - no void main() or StartingConditional() inside. Ignored.", + LogType::Warning, NSC2011_INCLUDE_FILE_IGNORED, srcFileName, srcFileExt, "-"); + ret.code = 0; + break; + default: + _logger.WriteText(ret.str); + break; + } + } return (ret.code == 0); } @@ -457,7 +495,7 @@ bool NWScriptCompilerV2::compileScript(std::string& fileContents, default: _logger.log("", LogType::ConsoleMessage); - _logger.log("Unknown status code", LogType::Critical, "NSC2004"); + _logger.log("Unknown status code", LogType::Critical, NSC2004_UNKNOWN_COMPILE_ERROR); _logger.log("", LogType::ConsoleMessage); return false; } @@ -476,7 +514,7 @@ bool NWScriptCompilerV2::compileScript(std::string& fileContents, if (!bufferToFile(outputPath, dataRef)) { _logger.log("", LogType::ConsoleMessage); - _logger.log(TEXT("Could not write compiled output file: ") + outputPath, LogType::Critical, TEXT("NSC2005")); + _logger.log(TEXT("Could not write compiled output file: ") + outputPath, LogType::Critical, TEXT(NSC2005_COULD_NOT_WRITE_COMPILED_FILE)); _logger.log("", LogType::ConsoleMessage); return false; } @@ -490,7 +528,7 @@ bool NWScriptCompilerV2::compileScript(std::string& fileContents, if (!bufferToFile(outputPath, dataRef)) { _logger.log("", LogType::ConsoleMessage); - _logger.log(TEXT("Could not write generated symbols output file: ") + outputPath, LogType::Critical, TEXT("NSC2006")); + _logger.log(TEXT("Could not write generated symbols output file: ") + outputPath, LogType::Critical, TEXT(NSC2006_COULD_NOT_GENERATE_SYMBOL_FILE)); _logger.log("", LogType::ConsoleMessage); return false; } @@ -515,7 +553,7 @@ bool NWScriptCompilerV2::disassemblyBinary(std::string& fileContents, if (generatedCode == "DISASSEMBLY ERROR: COMPILER INITIALIZATION FAILED!") { _logger.log("", LogType::ConsoleMessage); - _logger.log("Disassembler - Compiler Initialization failed!", LogType::Critical, "NSC2007"); + _logger.log("Disassembler - Compiler Initialization failed!", LogType::Critical, NSC2007_DISASSEMBLY_COMPILER_INIT_FAIL); _logger.log("", LogType::ConsoleMessage); return false; } @@ -534,7 +572,7 @@ bool NWScriptCompilerV2::disassemblyBinary(std::string& fileContents, if (!bufferToFile(outputPath, formatedCode.str())) { _logger.log("", LogType::ConsoleMessage); - _logger.log(TEXT("Could not write disassembled output file: ") + outputPath, LogType::Critical, TEXT("NSC2008")); + _logger.log(TEXT("Could not write disassembled output file: ") + outputPath, LogType::Critical, TEXT(NSC2008_COULD_NOT_WRITE_DISASSEMBLY_FILE)); _logger.log("", LogType::ConsoleMessage); return false; } @@ -640,7 +678,7 @@ bool NWScriptCompilerV2::MakeDependenciesFile(const std::set& depen if (!bufferToFile(outputPath, sdependencies.str())) { _logger.log("", LogType::ConsoleMessage); - _logger.log(TEXT("Could not write dependency file: ") + outputPath, LogType::Critical, TEXT("NSC2009")); + _logger.log(TEXT("Could not write dependency file: ") + outputPath, LogType::Critical, TEXT(NSC2009_COULD_NOT_WRITE_DEPENDENCY_FILE)); _logger.log("", LogType::ConsoleMessage); return false; } @@ -706,10 +744,10 @@ int32_t NWScriptPlugin::ResManWriteToFile(const char* sFileName, RESTYPE nResTyp switch (nResType) { case NWN::ResNCS: - g_NWScriptCompilerV2->logger().log(TEXT("Unable to write compiled output file: ") + outputPath, LogType::Critical, TEXT("NSC2005")); + g_NWScriptCompilerV2->logger().log(TEXT("Unable to write compiled output file: ") + outputPath, LogType::Critical, TEXT(NSC2005_COULD_NOT_WRITE_COMPILED_FILE)); break; case NWN::ResNDB: - g_NWScriptCompilerV2->logger().log(TEXT("Unable to write generated symbols output file: ") + outputPath, LogType::Critical, TEXT("NSC2006")); + g_NWScriptCompilerV2->logger().log(TEXT("Unable to write generated symbols output file: ") + outputPath, LogType::Critical, TEXT(NSC2006_COULD_NOT_GENERATE_SYMBOL_FILE)); break; } @@ -732,7 +770,7 @@ const char* NWScriptPlugin::ResManLoadScriptSourceFile(const char* sFileName, RE try { - ResRef = g_ResourceManager->ResRef32FromStr(sFileNameStem); + ResRef = g_ResourceManager->ResRef32FromStr(toLowerCase(sFileNameStem)); } catch (std::exception) { @@ -768,18 +806,18 @@ const char* NWScriptPlugin::ResManLoadScriptSourceFile(const char* sFileName, RE Str += g_ResourceManager->ResTypeToExt(nResType); fileSize = 0; - fileContents = fileToBufferC(str2wstr(Str), &fileSize); + fileContents = fileToNullTermBuffer(str2wstr(Str), &fileSize); if (fileSize > 0) { - // #BUG: Discover why the last byte is always garbage - fileContents[fileSize] = 0; - // Cache loaded file for future reference - g_NWScriptCompilerV2->logger().WriteText("INFO: Loaded File -> %s\n", Str.c_str()); std::string res = *it + sFileNameStem + "." + g_ResourceManager->ResTypeToExt(nResType); - CacheResource(fileContents, fileSize, true, ResRef, (NWN::ResType)nResType, res); + // If not the current compiling file being loaded (eg: loading an include), logs to console + std::string srcStem = g_NWScriptCompilerV2->getSourceFilePath().stem().string(); + if (strcmp(toLowerCase(sFileNameStem).c_str(), toLowerCase(srcStem).c_str()) != 0) + g_NWScriptCompilerV2->logger().WriteText("INFO: Loaded File from disk path -> %s\n", Str.c_str()); + return fileContents; } } @@ -801,7 +839,7 @@ const char* NWScriptPlugin::ResManLoadScriptSourceFile(const char* sFileName, RE { if (fileSize != 0) { - fileContents = new char[fileSize]; + fileContents = new char[fileSize+1]; // Need a bigger buffer for NULL-Terminated string if (fileContents == NULL) { @@ -826,6 +864,8 @@ const char* NWScriptPlugin::ResManLoadScriptSourceFile(const char* sFileName, RE Offset += Read; BytesLeft -= Read; } + + fileContents[fileSize] = 0; } else { @@ -856,10 +896,6 @@ const char* NWScriptPlugin::ResManLoadScriptSourceFile(const char* sFileName, RE // Closes file and appends results to cache g_ResourceManager->CloseFile(Handle); - // # BUG: Discover why last byte is always trash - if (fileContents != NULL) - fileContents[fileSize] = 0; - CacheResource(fileContents, fileSize, true, ResRef, (NWN::ResType)nResType, res); return fileContents; @@ -870,6 +906,6 @@ const char* NWScriptPlugin::TlkResolve(STRREF strRef) if (CompileErrorTlk.contains(strRef)) return CompileErrorTlk[strRef].c_str(); else - return (std::string("Unknown error code -> ") + std::to_string(strRef)).c_str(); + return "Error: Unknown error code"; } diff --git a/src/NWScriptCompilerV2.h b/src/NWScriptCompilerV2.h index e4c63a5..7780c80 100644 --- a/src/NWScriptCompilerV2.h +++ b/src/NWScriptCompilerV2.h @@ -95,9 +95,15 @@ namespace NWScriptPlugin clearLog(); // Free memory from Resource Cache to avoid memory leaks -// for (auto it = _ResourceCache.begin(); it != _ResourceCache.end(); ++it) -// if (it->second.Contents != NULL) -// delete it->second.Contents; + for (auto& entry : _ResourceCache) + { + if (entry.second.Allocated && entry.second.Contents) + { + delete[] entry.second.Contents; + entry.second.Contents = nullptr; + entry.second.Allocated = false; + } + } _ResourceCache.clear(); } diff --git a/src/NWScriptLogger.cpp b/src/NWScriptLogger.cpp index 3cee65b..60179a2 100644 --- a/src/NWScriptLogger.cpp +++ b/src/NWScriptLogger.cpp @@ -13,6 +13,7 @@ #define PREPROCESSORPARSE R"((?:[\w\s\\.\-\(\):]+)(?:[\w\s\\.\-\(\):]+)\((?:\d+)\):\s(?:\w+):\s(?:NSC6022): Preprocessed: (.*))" #define COMPILERREGEX R"((?[\w\s\\.\-\(\):]+)\.(?[\w\s\\.\-\(\):]+)\((?\d+)\):\s(?\w+):\s(?NSC\d+):\s(?.+))" +#define COMPILERREGEXV2 R"((?[\w\s\\.\-\(\):]+)\.(?[\w\s\\.\-\(\):]+)\((?\d+)\):\s(?\w+):\s(?.+)\s(?NSC\d+))" #define GENERALMESSAGE R"(\s*(?WARNING|ERROR|INFO)\s*:\s*(?.+))" #define INCLUDESPARSEREGEX R"( ShowIncludes: Handled resource ([^\/]+)\/([^\\\n]+))" @@ -20,11 +21,13 @@ typedef jpcre2::select pcre2; static const pcre2::Regex preprocessorRegex(PREPROCESSORPARSE, 0, jpcre2::JIT_COMPILE); static const pcre2::Regex fileParsingMessageRegex(COMPILERREGEX, 0, jpcre2::JIT_COMPILE); +static const pcre2::Regex fileParsingMessageRegexV2(COMPILERREGEXV2, 0, jpcre2::JIT_COMPILE); static const pcre2::Regex generalMessageRegex(GENERALMESSAGE, PCRE2_CASELESS, jpcre2::JIT_COMPILE); static const pcre2::Regex includesRegex(INCLUDESPARSEREGEX, 0, jpcre2::JIT_COMPILE); static pcre2::RegexMatch preprocessorParsing(&preprocessorRegex); static pcre2::RegexMatch fileParsingMessage(&fileParsingMessageRegex); +static pcre2::RegexMatch fileParsingMessageV2(&fileParsingMessageRegexV2); static pcre2::RegexMatch generalMessage(&generalMessageRegex); static pcre2::RegexMatch includeFile(&includesRegex); @@ -92,6 +95,19 @@ void NWScriptLogger::WriteTextV(const char* fmt, va_list ap) return; } + + // Parsing messages from new Beamdog's compiler + fileParsingMessageV2.setNamedSubstringVector(&namedGroup); + fileParsingMessageV2.setSubject(buf); + matches = fileParsingMessageV2.match(); + if (matches) + { + log(namedGroup[0]["message"], + (namedGroup[0]["type"] == "Error") ? LogType::Error : (namedGroup[0]["type"] == "Warning") ? LogType::Warning : LogType::Info, + namedGroup[0]["code"], namedGroup[0]["fileName"], namedGroup[0]["fileExt"], namedGroup[0]["lineNumber"]); + + return; + } // Try again if nothing got first for general-purpose library messages generalMessage.setNamedSubstringVector(&namedGroup); diff --git a/src/Plugin Controls/LoggerDialog.cpp b/src/Plugin Controls/LoggerDialog.cpp index 7f9d370..73de6b7 100644 --- a/src/Plugin Controls/LoggerDialog.cpp +++ b/src/Plugin Controls/LoggerDialog.cpp @@ -309,10 +309,10 @@ intptr_t LoggerDialog::childrenDlgProc(UINT message, WPARAM wParam, LPARAM lPara // Dispatch to Plugin for processing. generic_string fileName = r.fileName.empty() ? TEXT("") : r.fileName + TEXT(".") + r.fileExt; - int lineNumber = r.lineNumber.empty() ? -1 : stoi(r.lineNumber); + int lineNumber = r.lineNumber.empty() || r.lineNumber[0] == '-' ? -1 : stoi(r.lineNumber); // Only dispatch messages with valid line numbers - if (navigateToFileCallback && lineNumber > -1) + if (navigateToFileCallback && (lineNumber > -1 || !fileName.empty())) { // HACK: To correct the file navigation issue, we store the current lineNumber being passed // to navigateToFileCallback, so the timer on it can refer back to it. diff --git a/src/PluginMain.cpp b/src/PluginMain.cpp index 65720fe..76919c0 100644 --- a/src/PluginMain.cpp +++ b/src/PluginMain.cpp @@ -2846,7 +2846,7 @@ void Plugin::NavigateToCode(const generic_string& fileName, size_t lineNum, cons // Navigate to line // HACK: For some reason, the normal operation won't work if the file doesn't have the focus. // So we schedule the execution to happen assynchronously with the smallest possible time frame. - if (success) + if (success && lineNum > -1) { SetTimer(Instance().NotepadHwnd(), NAGIVATECALLBACKTIMER, USER_TIMER_MINIMUM, (TIMERPROC)RunScheduledReposition); } From 2bda017a9484aad6a0efe215a489a0a04a62b754 Mon Sep 17 00:00:00 2001 From: Leonardo Silva <99574879+Leonard-The-Wise@users.noreply.github.com> Date: Thu, 27 Jul 2023 00:59:07 -0300 Subject: [PATCH 4/6] - Added settings to choose between native compiler and legacy; - Added help file on compilers; - Optimized some project files; --- Media/CompilerEngine-Dark.rtf | Bin 0 -> 2617 bytes Media/CompilerEngine.rtf | Bin 0 -> 2366 bytes NWScript-Npp/NWScript-Npp.vcxproj | 10 +- NWScript-Npp/NWScript-Npp.vcxproj.filters | 132 +-- src/NWScriptCompiler.cpp | 519 +++++++++- src/NWScriptCompiler.h | 91 +- src/NWScriptCompilerV2.cpp | 911 ------------------ src/NWScriptCompilerV2.h | 251 ----- src/NWScriptLogger.cpp | 28 +- src/Native Compiler/scriptcompcore.cpp | 3 +- .../CompilerSettingsDialog.cpp | 69 +- src/Plugin Controls/PluginControls.rc | 62 +- src/Plugin Controls/PluginControlsRC.h | 17 +- src/{ => Plugin Controls}/WarningDialog.cpp | 0 src/{ => Plugin Controls}/WarningDialog.h | 0 src/Plugin Controls/WhatIsThisDialog.cpp | 221 +++++ src/Plugin Controls/WhatIsThisDialog.h | 48 + src/PluginMain.cpp | 54 +- src/PluginMain.h | 6 +- src/Settings.cpp | 2 + src/Settings.h | 1 + 21 files changed, 1114 insertions(+), 1311 deletions(-) create mode 100644 Media/CompilerEngine-Dark.rtf create mode 100644 Media/CompilerEngine.rtf delete mode 100644 src/NWScriptCompilerV2.cpp delete mode 100644 src/NWScriptCompilerV2.h rename src/{ => Plugin Controls}/WarningDialog.cpp (100%) rename src/{ => Plugin Controls}/WarningDialog.h (100%) create mode 100644 src/Plugin Controls/WhatIsThisDialog.cpp create mode 100644 src/Plugin Controls/WhatIsThisDialog.h diff --git a/Media/CompilerEngine-Dark.rtf b/Media/CompilerEngine-Dark.rtf new file mode 100644 index 0000000000000000000000000000000000000000..e455e4b8a6dc7452e03d2f32f839c1a642068df5 GIT binary patch literal 2617 zcmcImU2oeq6y0-y{)hY4tw>x+cCvn8D4Kq3!CJ3q3KRqQLP?axnIct^ied!*?>m>0 zvwGby^kG3@i(i-Lo_o)cZ-tLdE~E`QeN_D>FU|^4tES0>b%U;4*UQKgM%qoDt_va?yC#N&63*I_!~Y6AoK# zzKq~(a7Ko-y8LQY&PVt6@7;>Z8RvnBo(Zq&VwH)FSIXw+g(%ISR_AFxf0EDhc;Io5fempC_+H!%5zS#D4j5n?t!oOW`VRS#O-HKjUK=zQod+++a z7&{duAgT;G?9<=_%G^o^!BzK^L>s4v$zxK7dc0Wvi|5V9Ou z6U?a_HtmA0v;`4IDSn}FFxRAWxU!M9+L7xciZxa5 zt=dRT9r}Y>#63-sWjw^K1e+I)yjLWL8m(*=CzTps3l2s|;>I8|QV z%>0F&jq(Od=o@qmDCC_E*W(C?KT2q=(BUIl~Wrk#Xqa@gYHCJ2i2}gJbH7mU9 zv@$ijRAXTIu+m?CfB*W!+spN@)W+C{r;i@l(HeDwx2#qqcsb+hQ({(02VsGr_5 za%f`@-$M`|OuYTJqhc`ZaIo6_o>8S2a1w6H{Ts~eV3Rbr={i(zb$QpV|AU*&elzvO z2r-n3`vP*V*+@V%QcA8ki++|B6b&=G ze9#fv=6X^DdcI{RDpe)ftPa+F6Fq!IMV@GsjDtrn0MP8^km!q9o``^d&UUuk+Hq)B vCQsh^`YRn|2&yX$OTSst3D#ySht$lFew#3Kyh^~w!3$>ZOznc7CExuC6QZ4d literal 0 HcmV?d00001 diff --git a/Media/CompilerEngine.rtf b/Media/CompilerEngine.rtf new file mode 100644 index 0000000000000000000000000000000000000000..a4e371d13ff5208d3020227e3b45733c541ee195 GIT binary patch literal 2366 zcmb_dU2ohr5bSe+{D*x@8=Nna?(D`fiXdNYjV9M1E)W#p3ni^i%u%F5QkD;f|9gj& z&gnHlfdVxQ;Y2NWhqF7wTj4^J3u(MckE-A1#c3gGZkkLOJE+Qby$o?dOS8?h_1WQf z!}0A_G}eSrYW&DV<45JaXnOa#5KYxe=Q(8bN@`U)b#aHFTr|#h(tIPRLySy8*ld*k zdj@N~)zYW0tFOMwdF$Q72e+cK+BzVj3*or_@my>jv&oBdQR;#7r{`%so0hY*n0>ue zoSvq|Vm(LZXGMx#pIxLfKI}F7mO0$g#=1`G2+rr!vaFTaQl8$6`}ZQfukPPh7k4Sq zW3gq!P6q4fgQ{Ati;VJYnJtUrtjLk{vFJJ(+V@U$wetOI28-S3WPfRDHX&cHGrAMk zipQNY^;!&7P7fc5Ubl8z~~ z?3vhXm0|xpW_YC{l#z|>*$CM{n08<_yf_i7KRn0Y(dlKaV z719w&Pts800R;d$qu+ZrCs%fG6;mAlh?n93OSexj;ta>rE$K4E>Yeog0Xx=vlm&Ak z!kUWHsI;co1R8q0gf=15GB7qlA!f^-0688Z@;}HeQ zw(XLxlmQV(M*?Zegb7F_5Giy#{DG}S1Ux2Gtwh&Tg}Eo4Maf2*YEQNgXx7xdx4e~D zI?M;P$a|7VP0SoM=qfl>1$t*|KI#WSKu3YUOuC9HUK0R3O%z+Y>n+d|a4%oI1}pGk<<> zg1o^e%nhan6vjx4^4J36#bGQrjOiXD@6#!1qqc4E&*+W86jhjsH?@j{9-j*CUA4!w zJk8Hn%hl;o+>(CA@l$h@0Tb|>B&5S&CeSn1ggxpEr|mUM&;!@1c5x^y-NNJ*>pI$2 zqv3=;-2VFK`!^rnUT%J)HiX_kd-BALM)M7>7qi5XpTM^GX4$v>^Pqy}pWa8*5XN1+ zGIZ^{4!8eps~B{&x9kpo>DEb#J@%b%d*i{u4&F%fY;Jt@R+aZ7isb9#bpMNu%|Uvo*l*N8qT1q^!|EvV#5MwdEx_Dygw6b^BrVHpO8IRK#9#G%a>vo4VV|M9f5*VZ - @@ -272,6 +271,8 @@ + + @@ -291,7 +292,6 @@ - @@ -380,7 +380,6 @@ - @@ -397,6 +396,8 @@ + + ..\src;%(AdditionalIncludeDirectories) @@ -428,13 +429,14 @@ NotUsing - + + diff --git a/NWScript-Npp/NWScript-Npp.vcxproj.filters b/NWScript-Npp/NWScript-Npp.vcxproj.filters index 4737c52..b2a3a84 100644 --- a/NWScript-Npp/NWScript-Npp.vcxproj.filters +++ b/NWScript-Npp/NWScript-Npp.vcxproj.filters @@ -129,9 +129,6 @@ Utils - - Plugin Dialogs - Resource Files @@ -161,7 +158,6 @@ Utils - Plugin Dialogs @@ -232,7 +228,13 @@ Native Compiler - + + + Plugin Dialogs + + + Plugin Dialogs + @@ -266,9 +268,6 @@ Utils - - Plugin Dialogs - Plugin Dialogs @@ -286,7 +285,6 @@ Plugin Dialogs - Plugin Dialogs @@ -348,7 +346,13 @@ Native Compiler - + + + Plugin Dialogs + + + Plugin Dialogs + @@ -368,95 +372,101 @@ Media - - Resource Files - - - Resource Files - Resource Files - Resource Files + Media + + + Media + + + Media - Resource Files + Media - Resource Files + Media - Resource Files + Media - Resource Files + Media - Resource Files + Media + + + Media - Resource Files + Media - Resource Files + Media + + + Media + + + Media - Resource Files + Media - Resource Files + Media + + + Media + + + Media + + + Media + + + Media - Resource Files + Media - Resource Files + Media - Resource Files + Media - Resource Files + Media - Resource Files + Media - Resource Files + Media - Resource Files + Media - Resource Files + Media - Resource Files + Media - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files + Media - - Resource Files + + Media - - Resource Files + + Media @@ -471,17 +481,17 @@ Media - - Resource Files - - Resource Files + Media - Resource Files + Media + + + Media - Resource Files + Media diff --git a/src/NWScriptCompiler.cpp b/src/NWScriptCompiler.cpp index 8bcd67c..c4770b7 100644 --- a/src/NWScriptCompiler.cpp +++ b/src/NWScriptCompiler.cpp @@ -15,6 +15,7 @@ #include "VersionInfoEx.h" using namespace NWScriptPlugin; + typedef NWScriptLogger::LogType LogType; #define DEPENDENCYHEADER " \ @@ -35,6 +36,136 @@ typedef jpcre2::select pcre2; static pcre2::Regex assemblyLine(FORMATDISASMREGEX, PCRE2_MULTILINE, jpcre2::JIT_COMPILE); static pcre2::Regex dependencyParse(DEPENDENCYPARSEREGEX, 0, jpcre2::JIT_COMPILE); +// This new global resource manager pointer is required for new compiler. +// It will only point to the compiler's instance ResourceManager +// Since it needs a global resource, we also make a global pointer to the script compiler itself +static ResourceManager* g_ResourceManager = nullptr; +static NWScriptCompiler* g_NWScriptCompilerV2 = nullptr; + +static std::map CompileErrorTlk = { + {560, "Error: Unexpected character"}, + {561, "Error: Fatal compiler error"}, + {562, "Error: Program compound statement at start"}, + {563, "Error: Unexpected end compound statement"}, + {564, "Error: After compound statement at end"}, + {565, "Error: Parsing variable list"}, + {566, "Error: Unknown state in compiler"}, + {567, "Error: Invalid declaration type"}, + {568, "Error: No left bracket on expression"}, + {569, "Error: No right bracket on expression"}, + {570, "Error: Bad start of statement"}, + {571, "Error: No left bracket on arg list"}, + {572, "Error: No right bracket on arg list"}, + {573, "Error: No semicolon after expression"}, + {574, "Error: Parsing assignment statement"}, + {575, "Error: Bad lvalue"}, + {576, "Error: Bad constant type"}, + {577, "Error: Identifier list full"}, + {578, "Error: Non integer id for integer constant"}, + {579, "Error: Non float id for float constant"}, + {580, "Error: Non string id for string constant"}, + {581, "Error: Variable already used within scope"}, + {582, "Error: Variable defined without type"}, + {583, "Error: Incorrect variable state left on stack"}, + {584, "Error: Non integer expression where integer required"}, + {585, "Error: Void expression where non void required"}, + {586, "Error: Invalid parameters for assignment"}, + {587, "Error: Declaration does not match parameters"}, + {588, "Error: Logical operation has invalid operands"}, + {589, "Error: Equality test has invalid operands"}, + {590, "Error: Comparison test has invalid operands"}, + {591, "Error: Shift operation has invalid operands"}, + {592, "Error: Arithmetic operation has invalid operands"}, + {593, "Error: Unknown operation in semantic check"}, + {594, "Error: Script too large"}, + {595, "Error: Return statement has no parameters"}, + {596, "Error: No while after do keyword"}, + {597, "Error: Function definition missing name"}, + {598, "Error: Function definition missing parameter list"}, + {599, "Error: Malformed parameter list"}, + {600, "Error: Bad type specifier"}, + {601, "Error: No semicolon after structure"}, + {602, "Error: Ellipsis in identifier"}, + {603, "Error: File not found"}, + {604, "Error: Include recursive"}, + {605, "Error: Include too many levels"}, + {606, "Error: Parsing return statement"}, + {607, "Error: Parsing identifier list"}, + {608, "Error: Parsing function declaration"}, + {609, "Error: Duplicate function implementation"}, + {610, "Error: Token too long"}, + {611, "Error: Undefined structure"}, + {612, "Error: Left of structure part not structure"}, + {613, "Error: Right of structure part not field in structure"}, + {614, "Error: Undefined field in structure"}, + {615, "Error: Structure redefined"}, + {616, "Error: Variable used twice in same structure"}, + {617, "Error: Function implementation and definition differ"}, + {618, "Error: Mismatched types"}, + {619, "Error: Integer not at top of stack"}, + {620, "Error: Return type and function type mismatched"}, + {621, "Error: Not all control paths return a value"}, + {622, "Error: Undefined identifier"}, + {623, "Error: No function main in script"}, + {624, "Error: Function main must have void return value"}, + {625, "Error: Function main must have no parameters"}, + {626, "Error: Non void function cannot be a statement"}, + {627, "Error: Bad variable name"}, + {628, "Error: Non optional parameter cannot follow optional parameter"}, + {629, "Error: Type does not have an optional parameter"}, + {630, "Error: Non constant in function declaration"}, + {631, "Error: Parsing constant vector"}, + {1594, "Error: Operand must be an integer lvalue"}, + {1595, "Error: Conditional requires second expression"}, + {1596, "Error: Conditional must have matching return types"}, + {1597, "Error: Multiple default statements within switch"}, + {1598, "Error: Multiple case constant statements within switch"}, + {1599, "Error: Case parameter not a constant integer"}, + {1600, "Error: Switch must evaluate to an integer"}, + {1601, "Error: No colon after default label"}, + {1602, "Error: No colon after case label"}, + {1603, "Error: No semicolon after statement"}, + {4834, "Error: Break outside of loop or case statement"}, + {4835, "Error: Too many parameters on function"}, + {4836, "Error: Unable to open file for writing"}, + {4855, "Error: Unterminated string constant"}, + {5182, "Error: No function intsc in script"}, + {5183, "Error: Function intsc must have void return value"}, + {5184, "Error: Function intsc must have no parameters"}, + {6804, "Error: Jumping over declaration statements case disallowed"}, + {6805, "Error: Jumping over declaration statements default disallowed"}, + {6823, "Error: Else without corresponding if"}, + {3741, "Error: Invalid type for const keyword"}, + {3742, "Error: Const keyword cannot be used on non global variables"}, + {3752, "Error: Invalid value assigned to constant"}, + {9081, "Error: Switch condition cannot be followed by a null statement"}, + {9082, "Error: While condition cannot be followed by a null statement"}, + {9083, "Error: For statement cannot be followed by a null statement"}, + {9155, "Error: Cannot include this file twice"}, + {10407, "If condition cannot be followed by a null statement"}, + {40104, "Else cannot be followed by a null statement"} +}; + +// Now we define some plugin-exclusive compiler message codes +#define NSC2001_RESOURCE_MANAGER_INITIALIZE_FAIL "NSC2001" +#define NSC2002_OPEN_FILE_FAIL "NSC2002" +#define NSC2003_RESERVED "NSC2003" +#define NSC2004_UNKNOWN_COMPILE_ERROR "NSC2004" +#define NSC2005_COULD_NOT_WRITE_COMPILED_FILE "NSC2005" +#define NSC2006_COULD_NOT_GENERATE_SYMBOL_FILE "NSC2006" +#define NSC2007_DISASSEMBLY_COMPILER_INIT_FAIL "NSC2007" +#define NSC2008_COULD_NOT_WRITE_DISASSEMBLY_FILE "NSC2008" +#define NSC2009_COULD_NOT_WRITE_DEPENDENCY_FILE "NSC2009" +#define NSC2010_CANT_COMPILE_NWSCRIPT_NSS "NSC2010" +#define NSC2011_INCLUDE_FILE_IGNORED "NSC2010" + + +NWScriptCompiler::NWScriptCompiler() : + _resourceManager(nullptr), _settings(nullptr), _compilerLegacy(nullptr) +{ + g_NWScriptCompilerV2 = this; +} + bool NWScriptCompiler::initialize() { // Critical path, initialize resources @@ -44,7 +175,7 @@ bool NWScriptCompiler::initialize() { } catch (std::runtime_error& e) { - _logger.log("Failed to initialize the resources manager: " + std::string(e.what()), LogType::Critical, "NSC2001"); + _logger.log("Failed to initialize the resources manager: " + std::string(e.what()), LogType::Critical, NSC2001_RESOURCE_MANAGER_INITIALIZE_FAIL); return false; } @@ -53,6 +184,33 @@ bool NWScriptCompiler::initialize() { return true; } +void NWScriptCompiler::reset() { + _resourceManager = nullptr; + _compilerNative = nullptr; + _compilerLegacy = nullptr; + _includePaths.clear(); + _fetchPreprocessorOnly = false; + _makeDependencyView = false; + _sourcePath = ""; + _destDir = ""; + setMode(0); + _processingEndCallback = nullptr; + clearLog(); + + // Free memory from Resource Cache + for (auto& entry : _ResourceCache) + { + if (entry.second.Allocated && entry.second.Contents) + { + delete[] entry.second.Contents; + entry.second.Contents = nullptr; + entry.second.Allocated = false; + } + } + + _ResourceCache.clear(); +} + bool NWScriptCompiler::loadScriptResources() { ResourceManager::ModuleLoadParams LoadParams; @@ -107,7 +265,8 @@ void NWScriptCompiler::processFile(bool fromMemory, char* fileContents) if (_stricmp(_sourcePath.filename().string().c_str(), "nwscript.nss") == 0 && _compilerMode == 0) { _logger.log("Compiling script: " + _sourcePath.string(), LogType::ConsoleMessage); - _logger.log("Error: you can't explicitly compile any script named \"nwscript.nss\", this name is reserved for the main engine.", LogType::Critical, "NSC2010"); + _logger.log("Error: you can't explicitly compile any script named \"nwscript.nss\", this name is reserved for the main engine.", + LogType::Critical, NSC2010_CANT_COMPILE_NWSCRIPT_NSS); _logger.log("File ignored: " + _sourcePath.string() , LogType::Info); notifyCaller(false); return; @@ -147,12 +306,24 @@ void NWScriptCompiler::processFile(bool fromMemory, char* fileContents) _includePaths.push_back(properDirNameA(wstr2str(s)) + "\\"); } + // Set global resource variable to current resource manager + g_ResourceManager = _resourceManager.get(); + + // Create compiler. Points functions of API to our own. + CScriptCompilerAPI cAPI; + cAPI.ResManLoadScriptSourceFile = ResManLoadScriptSourceFile; + cAPI.ResManUpdateResourceDirectory = ResManUpdateResourceDirectory; + cAPI.ResManWriteToFile = ResManWriteToFile; + cAPI.TlkResolve = TlkResolve; + + _compilerNative = std::make_unique(NWN::ResNSS, NWN::ResNCS, NWN::ResNDB, cAPI); + // Create our compiler/disassembler - _compiler = std::make_unique(*_resourceManager, _settings->useNonBiowareExtenstions); - _compiler->NscSetLogger(&_logger); - _compiler->NscSetIncludePaths(_includePaths); - _compiler->NscSetCompilerErrorPrefix(SCRIPTERRORPREFIX); - _compiler->NscSetResourceCacheEnabled(true); + _compilerLegacy = std::make_unique(*_resourceManager, _settings->useNonBiowareExtenstions); + _compilerLegacy->NscSetLogger(&_logger); + _compilerLegacy->NscSetIncludePaths(_includePaths); + _compilerLegacy->NscSetCompilerErrorPrefix(SCRIPTERRORPREFIX); + _compilerLegacy->NscSetResourceCacheEnabled(true); } // Acquire information about NWN Resource Type of the file. Warning of ignored result is incorrect. @@ -169,7 +340,7 @@ void NWScriptCompiler::processFile(bool fromMemory, char* fileContents) { if (!fileToBuffer(_sourcePath.c_str(), inFileContents)) { - _logger.log("Could not load the specified file: " + wstr2str(_sourcePath), LogType::Critical, "NSC2002"); + _logger.log("Could not load the specified file: " + wstr2str(_sourcePath), LogType::Critical, NSC2002_OPEN_FILE_FAIL); notifyCaller(false); return; } @@ -190,11 +361,28 @@ void NWScriptCompiler::processFile(bool fromMemory, char* fileContents) bool bSuccess = false; if (_compilerMode == 0) { + // Use old library to fetch preprocessor and make dependencies, since the new Beamdog's compiler don't support them if (_fetchPreprocessorOnly) + { _logger.log("Fetching preprocessor output for: " + _sourcePath.string(), LogType::ConsoleMessage); - else + bSuccess = compileScriptLegacy(inFileContents, fileResType, fileResRef); + } + + if (_makeDependencyView) + { + _logger.log("Making dependency view for: " + _sourcePath.string(), LogType::ConsoleMessage); + bSuccess = compileScriptLegacy(inFileContents, fileResType, fileResRef); + } + + // Use new library for compiling to support NWScript latest features + if (!_fetchPreprocessorOnly && !_makeDependencyView) + { _logger.log("Compiling script: " + _sourcePath.string(), LogType::ConsoleMessage); - bSuccess = compileScript(inFileContents, fileResType, fileResRef); + if (_settings->compilerEngine == 0) + bSuccess = compileScriptNative(inFileContents, fileResType, fileResRef); + else + bSuccess = compileScriptLegacy(inFileContents, fileResType, fileResRef); + } } else { @@ -206,7 +394,75 @@ void NWScriptCompiler::processFile(bool fromMemory, char* fileContents) } -bool NWScriptCompiler::compileScript(std::string& fileContents, +bool NWScriptCompiler::compileScriptNative(std::string& fileContents, + const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef) +{ + // Setup compiler according to user's preferences + _compilerNative->SetGenerateDebuggerOutput(_settings->generateSymbols); + uint32_t optimizationFlags = _settings->generateSymbols ? CSCRIPTCOMPILER_OPTIMIZE_NOTHING : + _settings->optimizeScript ? CSCRIPTCOMPILER_OPTIMIZE_EVERYTHING : CSCRIPTCOMPILER_OPTIMIZE_NOTHING; + _compilerNative->SetOptimizationFlags(optimizationFlags); + _compilerNative->SetCompileConditionalOrMain(1); + _compilerNative->SetIdentifierSpecification("nwscript"); + _compilerNative->SetOutputAlias(""); + + // Compile memory allocated file + NativeCompileResult ret; + + ret.code = _compilerNative->CompileFile(_sourcePath.string()); + + //ret.code = _compilerV2->CompileScriptChunk(fileContents, false); + + // Sometimes, CompileFile returns 1 or -1; in which case the error sould be in CapturedError. + // Forward from there. + if (ret.code == 1 || ret.code == -1) + { + ret.code = _compilerNative->GetCapturedErrorStrRef(); + assert(ret.code != 0); + if (ret.code == 0) + { + ret.code = STRREF_CSCRIPTCOMPILER_ERROR_FATAL_COMPILER_ERROR*-1; + _logger.log(CompileErrorTlk[ret.code], + LogType::Critical, std::string("NSC") + std::to_string(ret.code)); + + return false; + } + } + + // Resolve error messages if apply + if (ret.code) + { + ret.str = ret.code ? _compilerNative->GetCapturedError()->CStr() : (char*)""; + + std::string srcFileName = _sourcePath.stem().string(); + std::string srcFileExt = _sourcePath.extension().string(); + srcFileExt.erase(0, 1); // Delete the . before the extension. + + // Pre-process some known errors that are a bit different from the others (don't have line numbers, etc) + switch (abs(ret.code)) + { + case 561: + case 594: + _logger.log(TlkResolve(ret.code), LogType::Error, "NSC" + ret.code, srcFileName, srcFileExt, "-"); + break; + + // This is about include files. Downgrade to warning... + case 623: + _logger.log("File [" + _sourcePath.filename().string() + "] appears to be an include file - no void main() or StartingConditional() inside. Ignored.", + LogType::Warning, NSC2011_INCLUDE_FILE_IGNORED, srcFileName, srcFileExt, "-"); + ret.code = 0; + break; + default: + _logger.WriteText(ret.str); + break; + } + } + + return (ret.code == 0); +} + + +bool NWScriptCompiler::compileScriptLegacy(std::string& fileContents, const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef) { // We always ignore include files. And for our project, the compiler ALWAYS @@ -243,7 +499,7 @@ bool NWScriptCompiler::compileScript(std::string& fileContents, swutil::ByteVec debugSymbols; std::set fileDependencies; - NscResult result = _compiler->NscCompileScript(fileResRef, fileContents.c_str(), fileContents.size(), _settings->compileVersion, + NscResult result = _compilerLegacy->NscCompileScript(fileResRef, fileContents.c_str(), fileContents.size(), _settings->compileVersion, bOptimize, bIgnoreIncludes, &_logger, compilerFlags, generatedCode, debugSymbols, fileDependencies, _settings->generateSymbols); switch (result) @@ -267,7 +523,7 @@ bool NWScriptCompiler::compileScript(std::string& fileContents, default: _logger.log("", LogType::ConsoleMessage); - _logger.log("Unknown status code", LogType::Critical, "NSC2004"); + _logger.log("Unknown status code", LogType::Critical, NSC2004_UNKNOWN_COMPILE_ERROR); _logger.log("", LogType::ConsoleMessage); return false; } @@ -286,7 +542,7 @@ bool NWScriptCompiler::compileScript(std::string& fileContents, if (!bufferToFile(outputPath, dataRef)) { _logger.log("", LogType::ConsoleMessage); - _logger.log(TEXT("Could not write compiled output file: ") + outputPath, LogType::Critical, TEXT("NSC2005")); + _logger.log(TEXT("Could not write compiled output file: ") + outputPath, LogType::Critical, TEXT(NSC2005_COULD_NOT_WRITE_COMPILED_FILE)); _logger.log("", LogType::ConsoleMessage); return false; } @@ -300,7 +556,7 @@ bool NWScriptCompiler::compileScript(std::string& fileContents, if (!bufferToFile(outputPath, dataRef)) { _logger.log("", LogType::ConsoleMessage); - _logger.log(TEXT("Could not write generated symbols output file: ") + outputPath, LogType::Critical, TEXT("NSC2006")); + _logger.log(TEXT("Could not write generated symbols output file: ") + outputPath, LogType::Critical, TEXT(NSC2006_COULD_NOT_GENERATE_SYMBOL_FILE)); _logger.log("", LogType::ConsoleMessage); return false; } @@ -319,13 +575,13 @@ bool NWScriptCompiler::disassemblyBinary(std::string& fileContents, std::string generatedCode; // Main disassemble step. - _compiler->NscDisassembleScript(fileContents.c_str(), fileContents.size(), generatedCode); + _compilerLegacy->NscDisassembleScript(fileContents.c_str(), fileContents.size(), generatedCode); // This is the way the library returns errors to us on that routine... :D if (generatedCode == "DISASSEMBLY ERROR: COMPILER INITIALIZATION FAILED!") { _logger.log("", LogType::ConsoleMessage); - _logger.log("Disassembler - Compiler Initialization failed!", LogType::Critical, "NSC2007"); + _logger.log("Disassembler - Compiler Initialization failed!", LogType::Critical, NSC2007_DISASSEMBLY_COMPILER_INIT_FAIL); _logger.log("", LogType::ConsoleMessage); return false; } @@ -344,7 +600,7 @@ bool NWScriptCompiler::disassemblyBinary(std::string& fileContents, if (!bufferToFile(outputPath, formatedCode.str())) { _logger.log("", LogType::ConsoleMessage); - _logger.log(TEXT("Could not write disassembled output file: ") + outputPath, LogType::Critical, TEXT("NSC2008")); + _logger.log(TEXT("Could not write disassembled output file: ") + outputPath, LogType::Critical, TEXT(NSC2008_COULD_NOT_WRITE_DISASSEMBLY_FILE)); _logger.log("", LogType::ConsoleMessage); return false; } @@ -450,11 +706,234 @@ bool NWScriptCompiler::MakeDependenciesFile(const std::set& depende if (!bufferToFile(outputPath, sdependencies.str())) { _logger.log("", LogType::ConsoleMessage); - _logger.log(TEXT("Could not write dependency file: ") + outputPath, LogType::Critical, TEXT("NSC2009")); + _logger.log(TEXT("Could not write dependency file: ") + outputPath, LogType::Critical, TEXT(NSC2009_COULD_NOT_WRITE_DEPENDENCY_FILE)); _logger.log("", LogType::ConsoleMessage); return false; } } return true; -} \ No newline at end of file +} + + +bool CacheResource(const char* ResFileContents, UINT32 ResFileLength, bool Allocated, + const NWN::ResRef32& ResRef, NWN::ResType ResType, const std::string& sLocation) +{ + try + { + ResourceCacheKey Key; + ResourceCacheEntry Entry; + bool Inserted; + + Key.ResRef = ResRef; + Key.ResType = ResType; + + Entry.Allocated = Allocated; + Entry.Contents = (char*)ResFileContents; + Entry.Size = ResFileLength; + Entry.Location = sLocation; + + Inserted = g_NWScriptCompilerV2->getResourceCache().insert(ResourceCache::value_type(Key, Entry)).second; + + assert(Inserted == true); + } + catch (std::exception) + { + return false; + } + + return true; +} + +// Dummy function. Interface to new compiler - we do nothing here. +BOOL NWScriptPlugin::ResManUpdateResourceDirectory(const char* sAlias) +{ + return false; +} + +// Intercepts ResManWriteToFile from CScriptCompiler API. +int32_t NWScriptPlugin::ResManWriteToFile(const char* sFileName, RESTYPE nResType, const uint8_t* pData, size_t nSize, bool bBinary) +{ + + // Decides which type of file to write depending on ResType. + + std::string dataRef; + dataRef.assign(reinterpret_cast(pData), nSize); + + generic_string outputPath = str2wstr(g_NWScriptCompilerV2->getDestinationDirectory().string() + + "\\" + fs::path(sFileName).stem().string() + + "." + g_ResourceManager->ResTypeToExt(nResType) + ); + + if (!bufferToFile(outputPath, dataRef)) + { + g_NWScriptCompilerV2->logger().log("", LogType::ConsoleMessage); + + switch (nResType) + { + case NWN::ResNCS: + g_NWScriptCompilerV2->logger().log(TEXT("Unable to write compiled output file: ") + outputPath, LogType::Critical, TEXT(NSC2005_COULD_NOT_WRITE_COMPILED_FILE)); + break; + case NWN::ResNDB: + g_NWScriptCompilerV2->logger().log(TEXT("Unable to write generated symbols output file: ") + outputPath, LogType::Critical, TEXT(NSC2006_COULD_NOT_GENERATE_SYMBOL_FILE)); + break; + } + + g_NWScriptCompilerV2->logger().log("", LogType::ConsoleMessage); + return -1; + } + + return 0; +} + +const char* NWScriptPlugin::ResManLoadScriptSourceFile(const char* sFileName, RESTYPE nResType) +{ + + // Try to find resource on cache first. + NWN::ResRef32 ResRef; + ResourceCacheKey CacheKey; + + + std::string sFileNameStem = fs::path(sFileName).stem().string(); + + try + { + ResRef = g_ResourceManager->ResRef32FromStr(toLowerCase(sFileNameStem)); + } + catch (std::exception) + { + return NULL; + } + + CacheKey.ResRef = ResRef; + CacheKey.ResType = (NWN::ResType)nResType; + + ResourceCache::const_iterator it = g_NWScriptCompilerV2->getResourceCache().find(CacheKey); + + if (it != g_NWScriptCompilerV2->getResourceCache().end()) + { + return it->second.Contents; + } + + size_t fileSize = 0; + char* fileContents = NULL; + + // Not found: try search include paths first. + for (auto it = g_NWScriptCompilerV2->includePaths().begin(); it != g_NWScriptCompilerV2->includePaths().end(); ++it) + { + std::string Str(*it); +#ifdef _WINDOWS + if (Str.back() != '\\') + Str += "\\"; +#else + if (Str.back() != '/') + Str += "/"; +#endif + Str += sFileNameStem; + Str += "."; + Str += g_ResourceManager->ResTypeToExt(nResType); + + fileSize = 0; + fileContents = fileToNullTermBuffer(str2wstr(Str), &fileSize); + if (fileSize > 0) + { + // Cache loaded file for future reference + std::string res = *it + sFileNameStem + "." + g_ResourceManager->ResTypeToExt(nResType); + CacheResource(fileContents, fileSize, true, ResRef, (NWN::ResType)nResType, res); + + // If not the current compiling file being loaded (eg: loading an include), logs to console + std::string srcStem = g_NWScriptCompilerV2->getSourceFilePath().stem().string(); + if (strcmp(toLowerCase(sFileNameStem).c_str(), toLowerCase(srcStem).c_str()) != 0) + g_NWScriptCompilerV2->logger().WriteText("INFO: Loaded File from disk path -> %s\n", Str.c_str()); + + return fileContents; + } + } + + // Not found: try opening via resource files + ResourceManager::FileHandle Handle = g_ResourceManager->OpenFile(ResRef, nResType); + + if (Handle == ResourceManager::INVALID_FILE) + return NULL; + + // Read entire file upfront + fileSize = g_ResourceManager->GetEncapsulatedFileSize(Handle); + + size_t BytesLeft = fileSize; + size_t Offset = 0; + size_t Read = 0; + + try + { + if (fileSize != 0) + { + fileContents = new char[fileSize+1]; // Need a bigger buffer for NULL-Terminated string + + if (fileContents == NULL) + { + g_NWScriptCompilerV2->logger().log("Critical failure: Memory allocation for resource [" + std::string(sFileName) + "] failed.", LogType::Critical, "NSC2101"); + throw std::bad_alloc(); + } + + while (BytesLeft) + { + if (!g_ResourceManager->ReadEncapsulatedFile(Handle, Offset, BytesLeft, &Read, &fileContents[Offset])) + { + g_NWScriptCompilerV2->logger().log("Critical failure: ReadEncapsulatedFile did not succeeded.", LogType::Critical, "NSC2101"); + throw std::runtime_error("Critical failure: ReadEncapsulatedFile did not succeeded"); + } + + if (Read == 0) + { + g_NWScriptCompilerV2->logger().log("Critical failure: read 0 bytes from resource file [" + std::string(sFileName) + "]", LogType::Critical, "NSC2102"); + throw std::runtime_error("Critical failure: read 0 bytes from resource file"); + } + + Offset += Read; + BytesLeft -= Read; + } + + fileContents[fileSize] = 0; + } + else + { + fileContents = NULL; + } + } + catch (std::exception) + { + if (fileContents) + delete[] fileContents; + + g_ResourceManager->CloseFile(Handle); + return NULL; + } + + std::string res = ""; + std::string AccessorName; + try + { + g_ResourceManager->GetResourceAccessorName(Handle, AccessorName); + res = AccessorName + "/" + sFileNameStem + "." + g_ResourceManager->ResTypeToExt(nResType); + } + catch (std::exception) {} + + // Show includes always. Filter in script plugin + g_NWScriptCompilerV2->logger().WriteText("INFO: Loaded file from game's resources -> %s\n", res.c_str()); + + // Closes file and appends results to cache + g_ResourceManager->CloseFile(Handle); + + CacheResource(fileContents, fileSize, true, ResRef, (NWN::ResType)nResType, res); + + return fileContents; +} + +const char* NWScriptPlugin::TlkResolve(STRREF strRef) +{ + if (CompileErrorTlk.contains(strRef)) + return CompileErrorTlk[strRef].c_str(); + else + return "Error: Unknown error code"; +} + diff --git a/src/NWScriptCompiler.h b/src/NWScriptCompiler.h index c33e378..4b54fed 100644 --- a/src/NWScriptCompiler.h +++ b/src/NWScriptCompiler.h @@ -10,7 +10,9 @@ #include #include -#include "Nsc.h" +#include "Native Compiler/exobase.h" // New oficial compiler provided by Beamdog itself. +#include "Native Compiler/scriptcomp.h" // +#include "Nsc.h" // Here we are using NscLib for older features like preprocessor and make dependency #include "Common.h" #include "Settings.h" @@ -18,12 +20,53 @@ namespace NWScriptPlugin { + + // Copied from NscCompiler.cpp -> Resource Cache structures + struct ResourceCacheKey + { + NWN::ResRef32 ResRef; + NWN::ResType ResType; + + inline bool operator < (const ResourceCacheKey& other) const + { + return (ResType < other.ResType) || + (memcmp(&ResRef, &other.ResRef, sizeof(ResRef)) < 0); + } + + inline bool operator == (const ResourceCacheKey& other) const + { + return (ResType == other.ResType) && + (memcmp(&ResRef, &other.ResRef, sizeof(ResRef)) == 0); + } + }; + + struct ResourceCacheEntry + { + bool Allocated; + char* Contents; + UINT32 Size; + std::string Location; + }; + + typedef std::map ResourceCache; + + struct NativeCompileResult + { + int32_t code; + char* str; // static buffer + }; + + // Function pointers to resolve new compiler Resource API requirements + static BOOL ResManUpdateResourceDirectory(const char* sAlias); + static int32_t ResManWriteToFile(const char* sFileName, RESTYPE nResType, const uint8_t* pData, size_t nSize, bool bBinary); + static const char* ResManLoadScriptSourceFile(const char* sFileName, RESTYPE nResType); + static const char* TlkResolve(STRREF strRef); + class NWScriptCompiler final { public: - NWScriptCompiler() : - _resourceManager(nullptr), _settings(nullptr), _compiler(nullptr) {} + NWScriptCompiler(); bool isInitialized() { return _resourceManager != nullptr; @@ -38,18 +81,7 @@ namespace NWScriptPlugin bool initialize(); // Reset compiler state - void reset() { - _resourceManager = nullptr; - _compiler = nullptr; - _includePaths.clear(); - _fetchPreprocessorOnly = false; - _makeDependencyView = false; - _sourcePath = ""; - _destDir = ""; - setMode(0); - _processingEndCallback = nullptr; - clearLog(); - } + void reset(); // Sets destination to a VALID and existing directory (or else get an error) void setDestinationDirectory(fs::path dest) { @@ -112,15 +144,15 @@ namespace NWScriptPlugin _makeDependencyView = false; } - int getMode() const { + inline int getMode() const { return _compilerMode; } - bool isViewDependencies() const { + inline bool isViewDependencies() const { return _makeDependencyView; } - bool isFetchPreprocessorOnly() const { + inline bool isFetchPreprocessorOnly() const { return _fetchPreprocessorOnly; } @@ -128,17 +160,31 @@ namespace NWScriptPlugin return _logger; } + std::vector& includePaths() { + return _includePaths; + } + // Returns if an output path is required for operation - bool isOutputDirRequired() { + inline bool isOutputDirRequired() { return !(_fetchPreprocessorOnly || _makeDependencyView); } + inline ResourceCache& getResourceCache() { + return _ResourceCache; + } + void processFile(bool fromMemory, char* fileContents); private: + std::unique_ptr _resourceManager; - std::unique_ptr _compiler; + ResourceCache _ResourceCache; + std::unique_ptr _compilerNative; + + // # TODO: Remove old compiler references + std::unique_ptr _compilerLegacy; + bool _fetchPreprocessorOnly = false; bool _makeDependencyView = false; int _compilerMode = 0; @@ -163,7 +209,10 @@ namespace NWScriptPlugin bool loadScriptResources(); // Compile a plain text script into binary format - bool compileScript(std::string& fileContents, + bool compileScriptLegacy(std::string& fileContents, + const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef); + + bool compileScriptNative(std::string& fileContents, const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef); // Disassemble a binary file into a pcode assembly text format diff --git a/src/NWScriptCompilerV2.cpp b/src/NWScriptCompilerV2.cpp deleted file mode 100644 index 583aec0..0000000 --- a/src/NWScriptCompilerV2.cpp +++ /dev/null @@ -1,911 +0,0 @@ -/** @file NWScriptCompiler.cpp - * Invokes various functions from NscLib compiler/interpreter library. - * - **/ - // Copyright (C) 2022 - Leonardo Silva - // The License.txt file describes the conditions under which this software may be distributed. - - -#include "pch.h" -//#include -//#include "jpcre2.h" - -#include "Utf8_16.h" -#include "NWScriptCompilerV2.h" -#include "VersionInfoEx.h" - -using namespace NWScriptPlugin; - -typedef NWScriptLogger::LogType LogType; - -#define DEPENDENCYHEADER " \ -/*************************************************************************************** \r\n\ - * Dependency files descriptor for \"%DEPENDENCYFILE%\"\r\n\ - * Generated by NWScript Tools for Notepad++ (%VERSION%)\r\n\ - *\r\n\ - * Generation date: %GENERATIONDATE%\r\n\ - ***************************************************************************************/\r\n\ -\r\n\ -" - -#define SCRIPTERRORPREFIX "Error" -#define FORMATDISASMREGEX R"(.+)" -#define DEPENDENCYPARSEREGEX R"(([^\/]+)\/([^\\\n]+))" - -typedef jpcre2::select pcre2; -static pcre2::Regex assemblyLine(FORMATDISASMREGEX, PCRE2_MULTILINE, jpcre2::JIT_COMPILE); -static pcre2::Regex dependencyParse(DEPENDENCYPARSEREGEX, 0, jpcre2::JIT_COMPILE); - -// This new global resource manager pointer is required for new compiler. -// It will only point to the compiler's instance ResourceManager -// Since it needs a global resource, we also make a global pointer to the script compiler itself -static ResourceManager* g_ResourceManager = nullptr; -static NWScriptCompilerV2* g_NWScriptCompilerV2 = nullptr; - -static std::map CompileErrorTlk = { - {560, "Error: Unexpected character"}, - {561, "Error: Fatal compiler error"}, - {562, "Error: Program compound statement at start"}, - {563, "Error: Unexpected end compound statement"}, - {564, "Error: After compound statement at end"}, - {565, "Error: Parsing variable list"}, - {566, "Error: Unknown state in compiler"}, - {567, "Error: Invalid declaration type"}, - {568, "Error: No left bracket on expression"}, - {569, "Error: No right bracket on expression"}, - {570, "Error: Bad start of statement"}, - {571, "Error: No left bracket on arg list"}, - {572, "Error: No right bracket on arg list"}, - {573, "Error: No semicolon after expression"}, - {574, "Error: Parsing assignment statement"}, - {575, "Error: Bad lvalue"}, - {576, "Error: Bad constant type"}, - {577, "Error: Identifier list full"}, - {578, "Error: Non integer id for integer constant"}, - {579, "Error: Non float id for float constant"}, - {580, "Error: Non string id for string constant"}, - {581, "Error: Variable already used within scope"}, - {582, "Error: Variable defined without type"}, - {583, "Error: Incorrect variable state left on stack"}, - {584, "Error: Non integer expression where integer required"}, - {585, "Error: Void expression where non void required"}, - {586, "Error: Invalid parameters for assignment"}, - {587, "Error: Declaration does not match parameters"}, - {588, "Error: Logical operation has invalid operands"}, - {589, "Error: Equality test has invalid operands"}, - {590, "Error: Comparison test has invalid operands"}, - {591, "Error: Shift operation has invalid operands"}, - {592, "Error: Arithmetic operation has invalid operands"}, - {593, "Error: Unknown operation in semantic check"}, - {594, "Error: Script too large"}, - {595, "Error: Return statement has no parameters"}, - {596, "Error: No while after do keyword"}, - {597, "Error: Function definition missing name"}, - {598, "Error: Function definition missing parameter list"}, - {599, "Error: Malformed parameter list"}, - {600, "Error: Bad type specifier"}, - {601, "Error: No semicolon after structure"}, - {602, "Error: Ellipsis in identifier"}, - {603, "Error: File not found"}, - {604, "Error: Include recursive"}, - {605, "Error: Include too many levels"}, - {606, "Error: Parsing return statement"}, - {607, "Error: Parsing identifier list"}, - {608, "Error: Parsing function declaration"}, - {609, "Error: Duplicate function implementation"}, - {610, "Error: Token too long"}, - {611, "Error: Undefined structure"}, - {612, "Error: Left of structure part not structure"}, - {613, "Error: Right of structure part not field in structure"}, - {614, "Error: Undefined field in structure"}, - {615, "Error: Structure redefined"}, - {616, "Error: Variable used twice in same structure"}, - {617, "Error: Function implementation and definition differ"}, - {618, "Error: Mismatched types"}, - {619, "Error: Integer not at top of stack"}, - {620, "Error: Return type and function type mismatched"}, - {621, "Error: Not all control paths return a value"}, - {622, "Error: Undefined identifier"}, - {623, "Error: No function main in script"}, - {624, "Error: Function main must have void return value"}, - {625, "Error: Function main must have no parameters"}, - {626, "Error: Non void function cannot be a statement"}, - {627, "Error: Bad variable name"}, - {628, "Error: Non optional parameter cannot follow optional parameter"}, - {629, "Error: Type does not have an optional parameter"}, - {630, "Error: Non constant in function declaration"}, - {631, "Error: Parsing constant vector"}, - {1594, "Error: Operand must be an integer lvalue"}, - {1595, "Error: Conditional requires second expression"}, - {1596, "Error: Conditional must have matching return types"}, - {1597, "Error: Multiple default statements within switch"}, - {1598, "Error: Multiple case constant statements within switch"}, - {1599, "Error: Case parameter not a constant integer"}, - {1600, "Error: Switch must evaluate to an integer"}, - {1601, "Error: No colon after default label"}, - {1602, "Error: No colon after case label"}, - {1603, "Error: No semicolon after statement"}, - {4834, "Error: Break outside of loop or case statement"}, - {4835, "Error: Too many parameters on function"}, - {4836, "Error: Unable to open file for writing"}, - {4855, "Error: Unterminated string constant"}, - {5182, "Error: No function intsc in script"}, - {5183, "Error: Function intsc must have void return value"}, - {5184, "Error: Function intsc must have no parameters"}, - {6804, "Error: Jumping over declaration statements case disallowed"}, - {6805, "Error: Jumping over declaration statements default disallowed"}, - {6823, "Error: Else without corresponding if"}, - {3741, "Error: Invalid type for const keyword"}, - {3742, "Error: Const keyword cannot be used on non global variables"}, - {3752, "Error: Invalid value assigned to constant"}, - {9081, "Error: Switch condition cannot be followed by a null statement"}, - {9082, "Error: While condition cannot be followed by a null statement"}, - {9083, "Error: For statement cannot be followed by a null statement"}, - {9155, "Error: Cannot include this file twice"}, - {10407, "If condition cannot be followed by a null statement"}, - {40104, "Else cannot be followed by a null statement"} -}; - -// Now we define some plugin-exclusive compiler message codes -#define NSC2001_RESOURCE_MANAGER_INITIALIZE_FAIL "NSC2001" -#define NSC2002_OPEN_FILE_FAIL "NSC2002" -#define NSC2003_RESERVED "NSC2003" -#define NSC2004_UNKNOWN_COMPILE_ERROR "NSC2004" -#define NSC2005_COULD_NOT_WRITE_COMPILED_FILE "NSC2005" -#define NSC2006_COULD_NOT_GENERATE_SYMBOL_FILE "NSC2006" -#define NSC2007_DISASSEMBLY_COMPILER_INIT_FAIL "NSC2007" -#define NSC2008_COULD_NOT_WRITE_DISASSEMBLY_FILE "NSC2008" -#define NSC2009_COULD_NOT_WRITE_DEPENDENCY_FILE "NSC2009" -#define NSC2010_CANT_COMPILE_NWSCRIPT_NSS "NSC2010" -#define NSC2011_INCLUDE_FILE_IGNORED "NSC2010" - - -NWScriptCompilerV2::NWScriptCompilerV2() : - _resourceManager(nullptr), _settings(nullptr), _compiler(nullptr) -{ - g_NWScriptCompilerV2 = this; -} - -bool NWScriptCompilerV2::initialize() { - - // Critical path, initialize resources - try - { - _resourceManager = std::make_unique(&_logger); - } - catch (std::runtime_error& e) - { - _logger.log("Failed to initialize the resources manager: " + std::string(e.what()), LogType::Critical, NSC2001_RESOURCE_MANAGER_INITIALIZE_FAIL); - return false; - } - - NWNHome = getNwnHomePath(_settings->compileVersion); - - return true; -} - -bool NWScriptCompilerV2::loadScriptResources() -{ - ResourceManager::ModuleLoadParams LoadParams; - ResourceManager::StringVec KeyFiles; - - ZeroMemory(&LoadParams, sizeof(LoadParams)); - - LoadParams.SearchOrder = ResourceManager::ModSearch_PrefDirectory; - LoadParams.ResManFlags = ResourceManager::ResManFlagNoGranny2; - LoadParams.ResManFlags |= ResourceManager::ResManFlagErf16; - - if (_settings->compileVersion == 174) - { -#ifdef _WINDOWS - KeyFiles.push_back("data\\nwn_base"); -#else - KeyFiles.emplace_back("data/nwn_base"); -#endif // _WINDOWS - } - else - { - KeyFiles.emplace_back("xp3"); - KeyFiles.emplace_back("xp2patch"); - KeyFiles.emplace_back("xp2"); - KeyFiles.emplace_back("xp1"); - KeyFiles.emplace_back("chitin"); - } - - LoadParams.KeyFiles = &KeyFiles; - LoadParams.ResManFlags |= ResourceManager::ResManFlagBaseResourcesOnly; - - // Legacy code is using ASCII string names. We convert here. Also, many exceptions thrown inside those classes to deal with. - std::string InstallDir = _settings->getChosenInstallDir() + "\\"; - try { - _resourceManager->LoadScriptResources(wstr2str(NWNHome), InstallDir, &LoadParams); - } - catch(...) { - //_resourceManager is writting to the log messages here, so we just return false. - return false; - } - - return true; -} - -void NWScriptCompilerV2::processFile(bool fromMemory, char* fileContents) -{ - NWN::ResType fileResType; - NWN::ResRef32 fileResRef; - std::string inFileContents; - - // First check: safeguard from trying to recompile nwscript.nss - if (_stricmp(_sourcePath.filename().string().c_str(), "nwscript.nss") == 0 && _compilerMode == 0) - { - _logger.log("Compiling script: " + _sourcePath.string(), LogType::ConsoleMessage); - _logger.log("Error: you can't explicitly compile any script named \"nwscript.nss\", this name is reserved for the main engine.", - LogType::Critical, NSC2010_CANT_COMPILE_NWSCRIPT_NSS); - _logger.log("File ignored: " + _sourcePath.string() , LogType::Info); - notifyCaller(false); - return; - } - - // Initialize the compiler if not already - if (!isInitialized()) - { - _logger.log("Initializing compiler...", LogType::ConsoleMessage); - _logger.log("", LogType::ConsoleMessage); - - if (!initialize()) - { - notifyCaller(false); - return; - } - - // Start building up search paths. - _includePaths.push_back(wstr2str(_sourcePath.parent_path())); - - if (!_settings->ignoreInstallPaths) - { - if (!loadScriptResources()) - { - _logger.log("Could not load script resources on installation path: " + _settings->getChosenInstallDir(), LogType::Warning); - } - - if (_settings->compileVersion == 174) - { - std::string overrideDir = _settings->getChosenInstallDir() + "\\ovr\\"; - _includePaths.push_back(overrideDir); - } - } - - for (generic_string s : _settings->getIncludeDirsV()) - { - _includePaths.push_back(properDirNameA(wstr2str(s)) + "\\"); - } - - // Set global resource variable to current resource manager - g_ResourceManager = _resourceManager.get(); - - // Create compiler. Points functions of API to our own. - CScriptCompilerAPI cAPI; - cAPI.ResManLoadScriptSourceFile = ResManLoadScriptSourceFile; - cAPI.ResManUpdateResourceDirectory = ResManUpdateResourceDirectory; - cAPI.ResManWriteToFile = ResManWriteToFile; - cAPI.TlkResolve = TlkResolve; - - _compilerV2 = std::make_unique(NWN::ResNSS, NWN::ResNCS, NWN::ResNDB, cAPI); - - // Create our compiler/disassembler - _compiler = std::make_unique(*_resourceManager, _settings->useNonBiowareExtenstions); - _compiler->NscSetLogger(&_logger); - _compiler->NscSetIncludePaths(_includePaths); - _compiler->NscSetCompilerErrorPrefix(SCRIPTERRORPREFIX); - _compiler->NscSetResourceCacheEnabled(true); - - } - - // Acquire information about NWN Resource Type of the file. Warning of ignored result is incorrect. -#pragma warning (push) -#pragma warning (disable : 6031) - fileResType = _resourceManager->ExtToResType(wstr2str(_sourcePath).c_str()); - fileResRef = _resourceManager->ResRef32FromStr(wstr2str(_sourcePath.stem()).c_str()); -#pragma warning (pop) - - // Load file from disk if not from memory - if (fromMemory) - inFileContents = fileContents; - else - { - if (!fileToBuffer(_sourcePath.c_str(), inFileContents)) - { - _logger.log("Could not load the specified file: " + wstr2str(_sourcePath), LogType::Critical, NSC2002_OPEN_FILE_FAIL); - notifyCaller(false); - return; - } - } - - // Determines file encoding. Only a minimal sample is used here since - // we are not interested in capturing UTF-8 multibyte-like strings, only UTF-16 types. - constexpr const int blockSize = IS_TEXT_UNICODE_STATISTICS; - Utf8_16_Read utfConverter; - int encoding = utfConverter.determineEncoding((unsigned char*)inFileContents.c_str(), (blockSize > inFileContents.size()) ? inFileContents.size() : blockSize); - if (encoding == uni16BE || encoding == uni16LE || encoding == uni16BE_NoBOM || encoding == uni16LE_NoBOM) - { - std::ignore = utfConverter.convert(inFileContents.data(), inFileContents.size()); - inFileContents.assign(utfConverter.getNewBuf(), utfConverter.getNewSize()); - } - - // Execute the process - bool bSuccess = false; - if (_compilerMode == 0) - { - // Use old library to fetch preprocessor and make dependencies, since the new Beamdog's compiler don't support them - if (_fetchPreprocessorOnly) - { - _logger.log("Fetching preprocessor output for: " + _sourcePath.string(), LogType::ConsoleMessage); - bSuccess = compileScript(inFileContents, fileResType, fileResRef); - } - - if (_makeDependencyView) - { - _logger.log("Making dependency view for: " + _sourcePath.string(), LogType::ConsoleMessage); - bSuccess = compileScript(inFileContents, fileResType, fileResRef); - } - - // Use new library for compiling to support NWScript latest features - if (!_fetchPreprocessorOnly && !_makeDependencyView) - { - _logger.log("Compiling script: " + _sourcePath.string(), LogType::ConsoleMessage); - bSuccess = compileScriptV2(inFileContents, fileResType, fileResRef); - } - - } - else - { - _logger.log("Disassembling binary: " + _sourcePath.string(), LogType::ConsoleMessage); - bSuccess = disassemblyBinary(inFileContents, fileResType, fileResRef); - } - - notifyCaller(bSuccess); -} - - -bool NWScriptCompilerV2::compileScriptV2(std::string& fileContents, - const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef) -{ - // Setup compiler according to user's preferences - _compilerV2->SetGenerateDebuggerOutput(_settings->generateSymbols); - uint32_t optimizationFlags = _settings->generateSymbols ? CSCRIPTCOMPILER_OPTIMIZE_NOTHING : - _settings->optimizeScript ? CSCRIPTCOMPILER_OPTIMIZE_EVERYTHING : CSCRIPTCOMPILER_OPTIMIZE_NOTHING; - _compilerV2->SetOptimizationFlags(optimizationFlags); - _compilerV2->SetCompileConditionalOrMain(1); - _compilerV2->SetIdentifierSpecification("nwscript"); - _compilerV2->SetOutputAlias(""); - - // Compile memory allocated file - NativeCompileResult ret; - - ret.code = _compilerV2->CompileFile(_sourcePath.string()); - - //ret.code = _compilerV2->CompileScriptChunk(fileContents, false); - - // Sometimes, CompileFile returns 1 or -1; in which case the error sould be in CapturedError. - // Forward from there. - if (ret.code == 1 || ret.code == -1) - { - ret.code = _compilerV2->GetCapturedErrorStrRef(); - assert(ret.code != 0); - if (ret.code == 0) - { - ret.code = STRREF_CSCRIPTCOMPILER_ERROR_FATAL_COMPILER_ERROR*-1; - _logger.log(CompileErrorTlk[ret.code], - LogType::Critical, std::string("NSC") + std::to_string(ret.code)); - - return false; - } - } - - // Resolve error messages if apply - if (ret.code) - { - ret.str = ret.code ? _compilerV2->GetCapturedError()->CStr() : (char*)""; - - std::string srcFileName = _sourcePath.stem().string(); - std::string srcFileExt = _sourcePath.extension().string(); - srcFileExt.erase(0, 1); // Delete the . before the extension. - - // Pre-process some known errors that are a bit different from the others (don't have line numbers, etc) - switch (abs(ret.code)) - { - case 561: - case 594: - _logger.log(TlkResolve(ret.code), LogType::Error, "NSC" + ret.code, srcFileName, srcFileExt, "-"); - break; - - // This is about include files. Downgrade to warning... - case 623: - _logger.log("File [" + _sourcePath.filename().string() + "] appears to be an include file - no void main() or StartingConditional() inside. Ignored.", - LogType::Warning, NSC2011_INCLUDE_FILE_IGNORED, srcFileName, srcFileExt, "-"); - ret.code = 0; - break; - default: - _logger.WriteText(ret.str); - break; - } - } - - return (ret.code == 0); -} - - -bool NWScriptCompilerV2::compileScript(std::string& fileContents, - const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef) -{ - // We always ignore include files. And for our project, the compiler ALWAYS - // return include dependencies, since message filters are done in a higher application layer. - bool bIgnoreIncludes = true; - bool bOptimize = _settings->optimizeScript; - UINT32 compilerFlags = _settings->compilerFlags; - compilerFlags |= NscCompilerFlag_ShowIncludes; - - // Disable processing overhead for preprocessor messages.. - // Also, since warnings are the type of return, we don't want to suppress them here, no matter what. - if (_fetchPreprocessorOnly) - { - compilerFlags &= ~NscCompilerFlag_GenerateMakeDeps; - bOptimize = false; - compilerFlags &= ~NscCompilerFlag_SuppressWarnings; - compilerFlags |= NscCompilerFlag_ShowPreprocessed; - } - - // Here we are solely worried about creating a human-readable dependencies view - if (_makeDependencyView) - { - compilerFlags |= NscCompilerFlag_GenerateMakeDeps; - compilerFlags |= NscCompilerFlag_SuppressWarnings; - bOptimize = false; - } - - // HACK: Need to know if this will ever be used on this project (we already have a disassembly option, this one generates PCODE while compiling also). - //compilerFlags |= NscCompilerFlag_DumpPCode; - - // Main compilation step - std::string dataRef; // Buffer to file is generic and requires a std::string - swutil::ByteVec generatedCode; - swutil::ByteVec debugSymbols; - std::set fileDependencies; - - NscResult result = _compiler->NscCompileScript(fileResRef, fileContents.c_str(), fileContents.size(), _settings->compileVersion, - bOptimize, bIgnoreIncludes, &_logger, compilerFlags, generatedCode, debugSymbols, fileDependencies, _settings->generateSymbols); - - switch (result) - { - case NscResult_Failure: - { - _logger.log("", LogType::ConsoleMessage); - _logger.log("Compilation aborted with errors.", LogType::ConsoleMessage); - _logger.log("", LogType::ConsoleMessage); - return false; - } - - case NscResult_Include: - { - _logger.log(_sourcePath.filename().string() + " is an include file, ignored.", LogType::ConsoleMessage); - return true; - } - - case NscResult_Success: - break; - - default: - _logger.log("", LogType::ConsoleMessage); - _logger.log("Unknown status code", LogType::Critical, NSC2004_UNKNOWN_COMPILE_ERROR); - _logger.log("", LogType::ConsoleMessage); - return false; - } - - // If we are only to fetch preprocessor code, we're done here (since the _logger takes care of that inside the Compile function) - if (_fetchPreprocessorOnly) - return true; - - // If we are to create human-readable dependencies, return that - if (_makeDependencyView) - return MakeDependenciesView(fileDependencies); - - // Now save code data - generic_string outputPath = str2wstr(_destDir.string() + "\\" + _sourcePath.stem().string() + compiledScriptSuffix); - dataRef.assign(reinterpret_cast(&generatedCode[0]), generatedCode.size()); - if (!bufferToFile(outputPath, dataRef)) - { - _logger.log("", LogType::ConsoleMessage); - _logger.log(TEXT("Could not write compiled output file: ") + outputPath, LogType::Critical, TEXT(NSC2005_COULD_NOT_WRITE_COMPILED_FILE)); - _logger.log("", LogType::ConsoleMessage); - return false; - } - - // Save debug symbols if apply - if (_settings->generateSymbols) - { - dataRef.clear(); - outputPath = str2wstr(_destDir.string() + "\\" + _sourcePath.stem().string() + debugSymbolsFileSuffix); - dataRef.assign(reinterpret_cast(&debugSymbols[0]), debugSymbols.size()); - if (!bufferToFile(outputPath, dataRef)) - { - _logger.log("", LogType::ConsoleMessage); - _logger.log(TEXT("Could not write generated symbols output file: ") + outputPath, LogType::Critical, TEXT(NSC2006_COULD_NOT_GENERATE_SYMBOL_FILE)); - _logger.log("", LogType::ConsoleMessage); - return false; - } - } - - // And file dependencies if apply - if (_settings->compilerFlags & NscCompilerFlag_GenerateMakeDeps) - return MakeDependenciesFile(fileDependencies); - - return true; -} - -bool NWScriptCompilerV2::disassemblyBinary(std::string& fileContents, - const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef) -{ - std::string generatedCode; - - // Main disassemble step. - _compiler->NscDisassembleScript(fileContents.c_str(), fileContents.size(), generatedCode); - - // This is the way the library returns errors to us on that routine... :D - if (generatedCode == "DISASSEMBLY ERROR: COMPILER INITIALIZATION FAILED!") - { - _logger.log("", LogType::ConsoleMessage); - _logger.log("Disassembler - Compiler Initialization failed!", LogType::Critical, NSC2007_DISASSEMBLY_COMPILER_INIT_FAIL); - _logger.log("", LogType::ConsoleMessage); - return false; - } - - // Save file, but first, we remove extra carriage returns the library is generating... - generic_string outputPath = str2wstr(_destDir.string() + "\\" + _sourcePath.stem().string() + disassembledScriptSuffix); - - std::stringstream formatedCode; - pcre2::VecNum matches; - pcre2::RegexMatch fileMatcher(&assemblyLine); - size_t lineCount = fileMatcher.setSubject(generatedCode).setModifier("gm").setNumberedSubstringVector(&matches).match(); - - for (size_t i = 0; i < lineCount; i++) - formatedCode << matches[i][0]; - - if (!bufferToFile(outputPath, formatedCode.str())) - { - _logger.log("", LogType::ConsoleMessage); - _logger.log(TEXT("Could not write disassembled output file: ") + outputPath, LogType::Critical, TEXT(NSC2008_COULD_NOT_WRITE_DISASSEMBLY_FILE)); - _logger.log("", LogType::ConsoleMessage); - return false; - } - - return true; -} - -bool NWScriptCompilerV2::MakeDependenciesView(const std::set& dependencies) -{ - // Generate some timestamp headers - char timestamp[128]; time_t currTime; struct tm currTimeP; - time(&currTime); - errno_t error = localtime_s(&currTimeP, &currTime); - strftime(timestamp, 64, "%B %d, %Y - %R", &currTimeP); - - // Get version from module's binary file - VersionInfoEx versionInfo = VersionInfoEx::getLocalVersion(); - std::stringstream sVersion = {}; - sVersion << "version " << versionInfo.shortString().c_str() << " - build " << versionInfo.build(); - - std::map variablesMap; - - variablesMap.insert({ "%DEPENDENCYFILE%", _sourcePath.filename().string() }); - variablesMap.insert({ "%VERSION%", sVersion.str() }); - variablesMap.insert({ "%GENERATIONDATE%", timestamp }); - - // Input header information - std::stringstream sdependencies; - sdependencies << replaceStringsA(DEPENDENCYHEADER, variablesMap); - - // Main dependencies - sdependencies << " 1) Main file relation (compiled script -> script)" << "\r\n\r\n"; - sdependencies << " Source Directory: " + _sourcePath.parent_path().string() << "\r\n"; - sdependencies << " Destination Directory: " + _destDir.string() << "\r\n"; - sdependencies << " " + _sourcePath.stem().string() << compiledScriptSuffix << - " <- is generated from -> " << _sourcePath.stem().string() << textScriptSuffix << "\r\n\r\n"; - - // Additional dependencies - if (!dependencies.empty()) - { - sdependencies << " 2) Dependencies of script source: " << _sourcePath.stem().string() << textScriptSuffix << "\r\n\r\n"; - - pcre2::VecNum matches; - pcre2::RegexMatch dependencyParser(&dependencyParse); - dependencyParser.setNumberedSubstringVector(&matches); - - // Get first path in dependencies for comparisons. - auto it = dependencies.begin(); - int count = dependencyParser.setSubject(*it).match(); - fs::path currentPath = matches[0][1]; - fs::path comparePath; - - // For each different path, we write the topic information of that folder and then enumerate the files - int topicNumber = 1; - bool bTopicWritten = false; - for (auto& dependency : dependencies) - { - count = dependencyParser.setSubject(dependency).match(); - comparePath = matches[0][1]; // first capture group = directory name. - if (currentPath != comparePath) - { - sdependencies << "\r\n"; - currentPath = comparePath; - bTopicWritten = false; - topicNumber++; - } - - if (!bTopicWritten) - { - sdependencies << " 2." << topicNumber << ") Dependencies from: " << currentPath.string() << "\r\n\r\n"; - bTopicWritten = true; - } - - sdependencies << " -> " << matches[0][2] << "\r\n"; // Second capture group = filename - } - - sdependencies << "\r\n\r\n"; - sdependencies << "------------------[ END OF FILE DEPENDENCIES ]------------------" << "\r\n\r\n"; - - _logger.setProcessorString(sdependencies.str()); - } - - return true; -} - -bool NWScriptCompilerV2::MakeDependenciesFile(const std::set& dependencies) -{ - - // Additional dependencies - if (!dependencies.empty()) - { - std::stringstream sdependencies; - - sdependencies << _sourcePath.stem() << compiledScriptSuffix << ": " << _sourcePath.stem() << textScriptSuffix; - - for (auto& dep : dependencies) - sdependencies << " \\\n " << dep.c_str(); - - for (auto& dep : dependencies) - sdependencies << "\n" << dep.c_str() << "\n"; - - generic_string outputPath = str2wstr(_destDir.string() + "\\" + _sourcePath.stem().string() + dependencyFileSuffix); - if (!bufferToFile(outputPath, sdependencies.str())) - { - _logger.log("", LogType::ConsoleMessage); - _logger.log(TEXT("Could not write dependency file: ") + outputPath, LogType::Critical, TEXT(NSC2009_COULD_NOT_WRITE_DEPENDENCY_FILE)); - _logger.log("", LogType::ConsoleMessage); - return false; - } - } - - return true; -} - - -bool CacheResource(const char* ResFileContents, UINT32 ResFileLength, bool Allocated, - const NWN::ResRef32& ResRef, NWN::ResType ResType, const std::string& sLocation) -{ - try - { - ResourceCacheKey Key; - ResourceCacheEntry Entry; - bool Inserted; - - Key.ResRef = ResRef; - Key.ResType = ResType; - - Entry.Allocated = Allocated; - Entry.Contents = (char*)ResFileContents; - Entry.Size = ResFileLength; - Entry.Location = sLocation; - - Inserted = g_NWScriptCompilerV2->getResourceCache().insert(ResourceCache::value_type(Key, Entry)).second; - - assert(Inserted == true); - } - catch (std::exception) - { - return false; - } - - return true; -} - -// Dummy function. Interface to new compiler - we do nothing here. -BOOL NWScriptPlugin::ResManUpdateResourceDirectory(const char* sAlias) -{ - return false; -} - -// Intercepts ResManWriteToFile from CScriptCompiler API. -int32_t NWScriptPlugin::ResManWriteToFile(const char* sFileName, RESTYPE nResType, const uint8_t* pData, size_t nSize, bool bBinary) -{ - - // Decides which type of file to write depending on ResType. - - std::string dataRef; - dataRef.assign(reinterpret_cast(pData), nSize); - - generic_string outputPath = str2wstr(g_NWScriptCompilerV2->getDestinationDirectory().string() - + "\\" + fs::path(sFileName).stem().string() - + "." + g_ResourceManager->ResTypeToExt(nResType) - ); - - if (!bufferToFile(outputPath, dataRef)) - { - g_NWScriptCompilerV2->logger().log("", LogType::ConsoleMessage); - - switch (nResType) - { - case NWN::ResNCS: - g_NWScriptCompilerV2->logger().log(TEXT("Unable to write compiled output file: ") + outputPath, LogType::Critical, TEXT(NSC2005_COULD_NOT_WRITE_COMPILED_FILE)); - break; - case NWN::ResNDB: - g_NWScriptCompilerV2->logger().log(TEXT("Unable to write generated symbols output file: ") + outputPath, LogType::Critical, TEXT(NSC2006_COULD_NOT_GENERATE_SYMBOL_FILE)); - break; - } - - g_NWScriptCompilerV2->logger().log("", LogType::ConsoleMessage); - return -1; - } - - return 0; -} - -const char* NWScriptPlugin::ResManLoadScriptSourceFile(const char* sFileName, RESTYPE nResType) -{ - - // Try to find resource on cache first. - NWN::ResRef32 ResRef; - ResourceCacheKey CacheKey; - - - std::string sFileNameStem = fs::path(sFileName).stem().string(); - - try - { - ResRef = g_ResourceManager->ResRef32FromStr(toLowerCase(sFileNameStem)); - } - catch (std::exception) - { - return NULL; - } - - CacheKey.ResRef = ResRef; - CacheKey.ResType = (NWN::ResType)nResType; - - ResourceCache::const_iterator it = g_NWScriptCompilerV2->getResourceCache().find(CacheKey); - - if (it != g_NWScriptCompilerV2->getResourceCache().end()) - { - return it->second.Contents; - } - - size_t fileSize = 0; - char* fileContents = NULL; - - // Not found: try search include paths first. - for (auto it = g_NWScriptCompilerV2->includePaths().begin(); it != g_NWScriptCompilerV2->includePaths().end(); ++it) - { - std::string Str(*it); -#ifdef _WINDOWS - if (Str.back() != '\\') - Str += "\\"; -#else - if (Str.back() != '/') - Str += "/"; -#endif - Str += sFileNameStem; - Str += "."; - Str += g_ResourceManager->ResTypeToExt(nResType); - - fileSize = 0; - fileContents = fileToNullTermBuffer(str2wstr(Str), &fileSize); - if (fileSize > 0) - { - // Cache loaded file for future reference - std::string res = *it + sFileNameStem + "." + g_ResourceManager->ResTypeToExt(nResType); - CacheResource(fileContents, fileSize, true, ResRef, (NWN::ResType)nResType, res); - - // If not the current compiling file being loaded (eg: loading an include), logs to console - std::string srcStem = g_NWScriptCompilerV2->getSourceFilePath().stem().string(); - if (strcmp(toLowerCase(sFileNameStem).c_str(), toLowerCase(srcStem).c_str()) != 0) - g_NWScriptCompilerV2->logger().WriteText("INFO: Loaded File from disk path -> %s\n", Str.c_str()); - - return fileContents; - } - } - - // Not found: try opening via resource files - ResourceManager::FileHandle Handle = g_ResourceManager->OpenFile(ResRef, nResType); - - if (Handle == ResourceManager::INVALID_FILE) - return NULL; - - // Read entire file upfront - fileSize = g_ResourceManager->GetEncapsulatedFileSize(Handle); - - size_t BytesLeft = fileSize; - size_t Offset = 0; - size_t Read = 0; - - try - { - if (fileSize != 0) - { - fileContents = new char[fileSize+1]; // Need a bigger buffer for NULL-Terminated string - - if (fileContents == NULL) - { - g_NWScriptCompilerV2->logger().log("Critical failure: Memory allocation for resource [" + std::string(sFileName) + "] failed.", LogType::Critical, "NSC2101"); - throw std::bad_alloc(); - } - - while (BytesLeft) - { - if (!g_ResourceManager->ReadEncapsulatedFile(Handle, Offset, BytesLeft, &Read, &fileContents[Offset])) - { - g_NWScriptCompilerV2->logger().log("Critical failure: ReadEncapsulatedFile did not succeeded.", LogType::Critical, "NSC2101"); - throw std::runtime_error("Critical failure: ReadEncapsulatedFile did not succeeded"); - } - - if (Read == 0) - { - g_NWScriptCompilerV2->logger().log("Critical failure: read 0 bytes from resource file [" + std::string(sFileName) + "]", LogType::Critical, "NSC2102"); - throw std::runtime_error("Critical failure: read 0 bytes from resource file"); - } - - Offset += Read; - BytesLeft -= Read; - } - - fileContents[fileSize] = 0; - } - else - { - fileContents = NULL; - } - } - catch (std::exception) - { - if (fileContents) - delete[] fileContents; - - g_ResourceManager->CloseFile(Handle); - return NULL; - } - - std::string res = ""; - std::string AccessorName; - try - { - g_ResourceManager->GetResourceAccessorName(Handle, AccessorName); - res = AccessorName + "/" + sFileNameStem + "." + g_ResourceManager->ResTypeToExt(nResType); - } - catch (std::exception) {} - - // Show includes always. Filter in script plugin - g_NWScriptCompilerV2->logger().WriteText("INFO: Loaded file from game's resources -> %s\n", res.c_str()); - - // Closes file and appends results to cache - g_ResourceManager->CloseFile(Handle); - - CacheResource(fileContents, fileSize, true, ResRef, (NWN::ResType)nResType, res); - - return fileContents; -} - -const char* NWScriptPlugin::TlkResolve(STRREF strRef) -{ - if (CompileErrorTlk.contains(strRef)) - return CompileErrorTlk[strRef].c_str(); - else - return "Error: Unknown error code"; -} - diff --git a/src/NWScriptCompilerV2.h b/src/NWScriptCompilerV2.h deleted file mode 100644 index 7780c80..0000000 --- a/src/NWScriptCompilerV2.h +++ /dev/null @@ -1,251 +0,0 @@ -/** @file NWScriptCompiler.cpp - * Invokes various functions from NscLib compiler/interpreter library. - * - **/ - // Copyright (C) 2022 - Leonardo Silva - // The License.txt file describes the conditions under which this software may be distributed. - -#pragma once - -#include -#include - -#include "Native Compiler/exobase.h" // New oficial compiler provided by Beamdog itself. -#include "Native Compiler/scriptcomp.h" // -#include "Nsc.h" // Here we are using NscLib for older features like preprocessor and make dependency -#include "Common.h" - -#include "Settings.h" -#include "NWScriptLogger.h" - -namespace NWScriptPlugin -{ - - // Copied from NscCompiler.cpp -> Resource Cache structures - struct ResourceCacheKey - { - NWN::ResRef32 ResRef; - NWN::ResType ResType; - - inline bool operator < (const ResourceCacheKey& other) const - { - return (ResType < other.ResType) || - (memcmp(&ResRef, &other.ResRef, sizeof(ResRef)) < 0); - } - - inline bool operator == (const ResourceCacheKey& other) const - { - return (ResType == other.ResType) && - (memcmp(&ResRef, &other.ResRef, sizeof(ResRef)) == 0); - } - }; - - struct ResourceCacheEntry - { - bool Allocated; - char* Contents; - UINT32 Size; - std::string Location; - }; - - typedef std::map ResourceCache; - - struct NativeCompileResult - { - int32_t code; - char* str; // static buffer - }; - - // Function pointers to resolve new compiler Resource API requirements - static BOOL ResManUpdateResourceDirectory(const char* sAlias); - static int32_t ResManWriteToFile(const char* sFileName, RESTYPE nResType, const uint8_t* pData, size_t nSize, bool bBinary); - static const char* ResManLoadScriptSourceFile(const char* sFileName, RESTYPE nResType); - static const char* TlkResolve(STRREF strRef); - - class NWScriptCompilerV2 final - { - public: - - NWScriptCompilerV2(); - - bool isInitialized() { - return _resourceManager != nullptr; - } - - // Append user settings - void appendSettings(Settings* settings) { - _settings = settings; - } - - // Initialize resource manager - bool initialize(); - - // Reset compiler state - void reset() { - _resourceManager = nullptr; - _compilerV2 = nullptr; - _compiler = nullptr; - _includePaths.clear(); - _fetchPreprocessorOnly = false; - _makeDependencyView = false; - _sourcePath = ""; - _destDir = ""; - setMode(0); - _processingEndCallback = nullptr; - clearLog(); - - // Free memory from Resource Cache to avoid memory leaks - for (auto& entry : _ResourceCache) - { - if (entry.second.Allocated && entry.second.Contents) - { - delete[] entry.second.Contents; - entry.second.Contents = nullptr; - entry.second.Allocated = false; - } - } - - _ResourceCache.clear(); - } - - // Sets destination to a VALID and existing directory (or else get an error) - void setDestinationDirectory(fs::path dest) { - if (!isValidDirectory(str2wstr(dest.string()).c_str())) - throw; - _destDir = dest; - } - - // Sets source path to a VALID and existing file path (or else get an error) - void setSourceFilePath(fs::path source) { - if (!PathFileExists(source.c_str())) - throw; - _sourcePath = source; - } - - // Returns the current set Destination Directory - fs::path getDestinationDirectory() { - return _destDir; - } - - // Returns the current set Source File path - fs::path getSourceFilePath() { - return _sourcePath; - } - - // Set function callback for calling after finishing processing file - void setProcessingEndCallback(void (*processingEndCallback)(HRESULT returnCode)) - { - _processingEndCallback = processingEndCallback; - } - - // Sets function callback for receiving logger messages - void setLoggerMessageCallback(void (*MessageCallback)(const NWScriptLogger::CompilerMessage&)) { - _logger.setMessageCallback(MessageCallback); - } - - // Only write dependencies view to the logger - void setViewDependencies() { - setMode(0); - _makeDependencyView = true; - } - - // Fetchs only preprocessor's output - void setFetchPreprocessorOnly() { - setMode(0); - _fetchPreprocessorOnly = true; - } - - // Clears the log - void clearLog() { - _logger.clear(); - } - - // Sets compiler mode: 0 = compile, 1 = disassemble - void setMode(int compilerMode) { - if (compilerMode < 0 || compilerMode > 1) - throw; - _compilerMode = compilerMode; - _fetchPreprocessorOnly = false; - _makeDependencyView = false; - } - - inline int getMode() const { - return _compilerMode; - } - - inline bool isViewDependencies() const { - return _makeDependencyView; - } - - inline bool isFetchPreprocessorOnly() const { - return _fetchPreprocessorOnly; - } - - NWScriptLogger& logger() { - return _logger; - } - - std::vector& includePaths() { - return _includePaths; - } - - // Returns if an output path is required for operation - inline bool isOutputDirRequired() { - return !(_fetchPreprocessorOnly || _makeDependencyView); - } - - inline ResourceCache& getResourceCache() { - return _ResourceCache; - } - - - void processFile(bool fromMemory, char* fileContents); - - private: - - std::unique_ptr _resourceManager; - ResourceCache _ResourceCache; - std::unique_ptr _compilerV2; - - // # TODO: Remove old compiler references - std::unique_ptr _compiler; - - bool _fetchPreprocessorOnly = false; - bool _makeDependencyView = false; - int _compilerMode = 0; - void (*_processingEndCallback)(HRESULT returnCode) = nullptr; - - generic_string NWNHome; - std::vector _includePaths; - fs::path _sourcePath; - fs::path _destDir; - - Settings* _settings; - - NWScriptLogger _logger; - - // Notify Caller of processing results - void notifyCaller(bool success) { - if (_processingEndCallback) - _processingEndCallback(static_cast((int)success)); - } - - // Load Base script resources - bool loadScriptResources(); - - // Compile a plain text script into binary format - bool compileScript(std::string& fileContents, - const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef); - - bool compileScriptV2(std::string& fileContents, - const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef); - - // Disassemble a binary file into a pcode assembly text format - bool disassemblyBinary(std::string& fileContents, - const NWN::ResType& fileResType, const NWN::ResRef32& fileResRef); - - // Dependencies files and views - bool MakeDependenciesView(const std::set& dependencies); - bool MakeDependenciesFile(const std::set& dependencies); - }; -} \ No newline at end of file diff --git a/src/NWScriptLogger.cpp b/src/NWScriptLogger.cpp index 60179a2..669de68 100644 --- a/src/NWScriptLogger.cpp +++ b/src/NWScriptLogger.cpp @@ -12,22 +12,22 @@ #include "NWScriptLogger.h" #define PREPROCESSORPARSE R"((?:[\w\s\\.\-\(\):]+)(?:[\w\s\\.\-\(\):]+)\((?:\d+)\):\s(?:\w+):\s(?:NSC6022): Preprocessed: (.*))" -#define COMPILERREGEX R"((?[\w\s\\.\-\(\):]+)\.(?[\w\s\\.\-\(\):]+)\((?\d+)\):\s(?\w+):\s(?NSC\d+):\s(?.+))" -#define COMPILERREGEXV2 R"((?[\w\s\\.\-\(\):]+)\.(?[\w\s\\.\-\(\):]+)\((?\d+)\):\s(?\w+):\s(?.+)\s(?NSC\d+))" +#define COMPILERREGEXNATIVE R"((?[\w\s\\.\-\(\):]+)\.(?[\w\s\\.\-\(\):]+)\((?\d+)\):\s(?\w+):\s(?.+)\s(?NSC\d+))" +#define COMPILERREGEXLEGACY R"((?[\w\s\\.\-\(\):]+)\.(?[\w\s\\.\-\(\):]+)\((?\d+)\):\s(?\w+):\s(?NSC\d+):\s(?.+))" #define GENERALMESSAGE R"(\s*(?WARNING|ERROR|INFO)\s*:\s*(?.+))" #define INCLUDESPARSEREGEX R"( ShowIncludes: Handled resource ([^\/]+)\/([^\\\n]+))" typedef jpcre2::select pcre2; static const pcre2::Regex preprocessorRegex(PREPROCESSORPARSE, 0, jpcre2::JIT_COMPILE); -static const pcre2::Regex fileParsingMessageRegex(COMPILERREGEX, 0, jpcre2::JIT_COMPILE); -static const pcre2::Regex fileParsingMessageRegexV2(COMPILERREGEXV2, 0, jpcre2::JIT_COMPILE); +static const pcre2::Regex fileParsingMessageRegexNative(COMPILERREGEXNATIVE, 0, jpcre2::JIT_COMPILE); +static const pcre2::Regex fileParsingMessageRegexLegacy(COMPILERREGEXLEGACY, 0, jpcre2::JIT_COMPILE); static const pcre2::Regex generalMessageRegex(GENERALMESSAGE, PCRE2_CASELESS, jpcre2::JIT_COMPILE); static const pcre2::Regex includesRegex(INCLUDESPARSEREGEX, 0, jpcre2::JIT_COMPILE); static pcre2::RegexMatch preprocessorParsing(&preprocessorRegex); -static pcre2::RegexMatch fileParsingMessage(&fileParsingMessageRegex); -static pcre2::RegexMatch fileParsingMessageV2(&fileParsingMessageRegexV2); +static pcre2::RegexMatch fileParsingMessageNative(&fileParsingMessageRegexNative); +static pcre2::RegexMatch fileParsingMessageLegacy(&fileParsingMessageRegexLegacy); static pcre2::RegexMatch generalMessage(&generalMessageRegex); static pcre2::RegexMatch includeFile(&includesRegex); @@ -84,9 +84,11 @@ void NWScriptLogger::WriteTextV(const char* fmt, va_list ap) } // Check for compiler file parsing messages - fileParsingMessage.setNamedSubstringVector(&namedGroup); - fileParsingMessage.setSubject(buf); - matches = fileParsingMessage.match(); + + // Parsing messages from new Beamdog's compiler + fileParsingMessageNative.setNamedSubstringVector(&namedGroup); + fileParsingMessageNative.setSubject(buf); + matches = fileParsingMessageNative.match(); if (matches) { log(namedGroup[0]["message"], @@ -96,10 +98,10 @@ void NWScriptLogger::WriteTextV(const char* fmt, va_list ap) return; } - // Parsing messages from new Beamdog's compiler - fileParsingMessageV2.setNamedSubstringVector(&namedGroup); - fileParsingMessageV2.setSubject(buf); - matches = fileParsingMessageV2.match(); + // Parsing messages from Legacy Compiler + fileParsingMessageLegacy.setNamedSubstringVector(&namedGroup); + fileParsingMessageLegacy.setSubject(buf); + matches = fileParsingMessageLegacy.match(); if (matches) { log(namedGroup[0]["message"], diff --git a/src/Native Compiler/scriptcompcore.cpp b/src/Native Compiler/scriptcompcore.cpp index 01d9989..69e959d 100644 --- a/src/Native Compiler/scriptcompcore.cpp +++ b/src/Native Compiler/scriptcompcore.cpp @@ -1416,8 +1416,7 @@ int32_t CScriptCompiler::CompileScriptConditional(const CExoString &sScriptCondi int32_t CScriptCompiler::GetCompiledScriptCode(char **ppnCode, int32_t *pnCodeSize) { - // PATCH: This function seems to be returning the wrong values - *pnCodeSize = m_nOutputCodeLength; //m_nOutputCodeSize; + *pnCodeSize = m_nOutputCodeSize; *ppnCode = m_pchOutputCode; return 0; diff --git a/src/Plugin Controls/CompilerSettingsDialog.cpp b/src/Plugin Controls/CompilerSettingsDialog.cpp index 893206d..691dc0e 100644 --- a/src/Plugin Controls/CompilerSettingsDialog.cpp +++ b/src/Plugin Controls/CompilerSettingsDialog.cpp @@ -13,6 +13,7 @@ #include "Nsc.h" #include "CompilerSettingsDialog.h" +#include "WhatIsThisDialog.h" #include "PluginControlsRC.h" #include "PluginDarkMode.h" @@ -20,6 +21,8 @@ using namespace NWScriptPlugin; +static WhatIsThisDialog whatIsThisDialog; + intptr_t CALLBACK CompilerSettingsDialog::run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) { switch (message) @@ -64,10 +67,23 @@ intptr_t CALLBACK CompilerSettingsDialog::run_dlgProc(UINT message, WPARAM wPara ListBox_AddString(GetDlgItem(_hSelf, IDC_LSTADDPATH), s.c_str()); } + if (myset.compilerEngine == 0) + { + ::CheckRadioButton(_hSelf, IDC_USEBEAMDOGCOMPILER, IDC_USELEGACYCOMPILER, IDC_USEBEAMDOGCOMPILER); + EnableWindow(GetDlgItem(_hSelf, IDC_CHKCOMPSTRICTMODE), false); + EnableWindow(GetDlgItem(_hSelf, IDC_CHKNONBIOWAREXTENSIONS), false); + EnableWindow(GetDlgItem(_hSelf, IDC_CHKCOMPMAKEFILE), false); + EnableWindow(GetDlgItem(_hSelf, IDC_CHKCOMPDISABLESLASHPARSE), false); + EnableWindow(GetDlgItem(_hSelf, IDC_CBOTARGETVERSION), false); + EnableWindow(GetDlgItem(_hSelf, IDC_LBLTARGETVERSION), false); + } + else + ::CheckRadioButton(_hSelf, IDC_USEBEAMDOGCOMPILER, IDC_USELEGACYCOMPILER, IDC_USELEGACYCOMPILER); + CheckDlgButton(_hSelf, IDC_CHKCOMPOPTIMIZE, myset.optimizeScript); - CheckDlgButton(_hSelf, IDC_CHKNONBIOWAREXTENSIONS, myset.useNonBiowareExtenstions); CheckDlgButton(_hSelf, IDC_CHKCOMPNDBSYMBOLS, myset.generateSymbols); CheckDlgButton(_hSelf, IDC_CHKCOMPSTRICTMODE, myset.compilerFlags & NscCompilerFlag_StrictModeEnabled); + CheckDlgButton(_hSelf, IDC_CHKNONBIOWAREXTENSIONS, myset.useNonBiowareExtenstions); CheckDlgButton(_hSelf, IDC_CHKCOMPMAKEFILE, myset.compilerFlags & NscCompilerFlag_GenerateMakeDeps); CheckDlgButton(_hSelf, IDC_CHKCOMPDISABLESLASHPARSE, myset.compilerFlags & NscCompilerFlag_DisableDoubleQuote); @@ -151,6 +167,28 @@ intptr_t CALLBACK CompilerSettingsDialog::run_dlgProc(UINT message, WPARAM wPara return FALSE; } + case IDC_USEBEAMDOGCOMPILER: + { + EnableWindow(GetDlgItem(_hSelf, IDC_CHKCOMPSTRICTMODE), false); + EnableWindow(GetDlgItem(_hSelf, IDC_CHKNONBIOWAREXTENSIONS), false); + EnableWindow(GetDlgItem(_hSelf, IDC_CHKCOMPMAKEFILE), false); + EnableWindow(GetDlgItem(_hSelf, IDC_CHKCOMPDISABLESLASHPARSE), false); + EnableWindow(GetDlgItem(_hSelf, IDC_CBOTARGETVERSION), false); + EnableWindow(GetDlgItem(_hSelf, IDC_LBLTARGETVERSION), false); + return FALSE; + } + + case IDC_USELEGACYCOMPILER: + { + EnableWindow(GetDlgItem(_hSelf, IDC_CHKCOMPSTRICTMODE), true); + EnableWindow(GetDlgItem(_hSelf, IDC_CHKNONBIOWAREXTENSIONS), true); + EnableWindow(GetDlgItem(_hSelf, IDC_CHKCOMPMAKEFILE), true); + EnableWindow(GetDlgItem(_hSelf, IDC_CHKCOMPDISABLESLASHPARSE), true); + EnableWindow(GetDlgItem(_hSelf, IDC_CBOTARGETVERSION), true); + EnableWindow(GetDlgItem(_hSelf, IDC_LBLTARGETVERSION), true); + return FALSE; + } + case IDC_BTADDPATH: { TCHAR path[MAX_PATH] = { 0 }; @@ -241,6 +279,33 @@ intptr_t CALLBACK CompilerSettingsDialog::run_dlgProc(UINT message, WPARAM wPara } break; } + + case WM_NOTIFY: + { + switch (wParam) + { + case IDC_LNKWHATISTHIS: + { + NMHDR* nmhdr = reinterpret_cast(lParam); + switch (nmhdr->code) + { + case NM_CLICK: + case NM_RETURN: + + if (!whatIsThisDialog.isCreated()) + whatIsThisDialog.init(_hInst, _hSelf); + + if (!whatIsThisDialog.isVisible()) + whatIsThisDialog.showDialog(); + + return TRUE; + } + break; + } + } + break; + } + } // Signals done processing messages @@ -312,6 +377,8 @@ bool CompilerSettingsDialog::keepSettings() myset.ignoreInstallPaths = IsDlgButtonChecked(_hSelf, IDC_CHKIGNOREINSTALLPATHS); + myset.compilerEngine = (IsDlgButtonChecked(_hSelf, IDC_USEBEAMDOGCOMPILER) ? 0 : 1); + // If user has unsaved input in additional folders, save for him. GetDlgItemText(_hSelf, IDC_TXTADDPATH, tempBuffer, std::size(tempBuffer)); if (tempBuffer[0] > 0) diff --git a/src/Plugin Controls/PluginControls.rc b/src/Plugin Controls/PluginControls.rc index dfafabb..144fc61 100644 --- a/src/Plugin Controls/PluginControls.rc +++ b/src/Plugin Controls/PluginControls.rc @@ -114,17 +114,17 @@ FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "&OK",IDOK,143,293,50,14 PUSHBUTTON "&Cancel",IDCANCEL,254,293,50,14 - GROUPBOX "Neverwinter Nights Installation Folders (for loading nwscript.nss and other game resources)",IDC_STATIC,7,7,411,90 - LTEXT "Neverwinter Nights 1 (or Enhanced Edition)",IDC_STATIC,87,20,293,8 - CONTROL "Use this",IDC_USENWN1,"Button",BS_AUTORADIOBUTTON | WS_GROUP,32,33,41,10 - CONTROL "Use this",IDC_USENWN2,"Button",BS_AUTORADIOBUTTON,32,63,41,10 - EDITTEXT IDC_TXTNWN1INSTALL,86,31,294,14,ES_AUTOHSCROLL - PUSHBUTTON "...",IDC_BTNWN1INSTALL,386,31,20,14 - LTEXT "Neverwinter Nights 2",IDC_STATIC,87,50,291,8 - EDITTEXT IDC_TXTNWN2INSTALL,85,60,294,14,ES_AUTOHSCROLL - PUSHBUTTON "...",IDC_BTNWN2INSTALL,385,60,20,14 + GROUPBOX "Neverwinter Nights Installation Folders",IDC_STATIC,7,7,284,90 + LTEXT "Neverwinter Nights 1 (or Enhanced Edition)",IDC_STATIC,58,20,174,8 + CONTROL "Use this",IDC_USENWN1,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,15,33,41,10 + CONTROL "Use this",IDC_USENWN2,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,15,63,41,10 + EDITTEXT IDC_TXTNWN1INSTALL,57,31,195,14,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_BTNWN1INSTALL,261,31,20,14 + LTEXT "Neverwinter Nights 2",IDC_STATIC,58,50,173,8 + EDITTEXT IDC_TXTNWN2INSTALL,57,60,195,14,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_BTNWN2INSTALL,261,60,20,14 CONTROL "Ignore installation paths.",IDC_CHKIGNOREINSTALLPATHS, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,88,82,96,10 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,57,82,96,10 GROUPBOX "Additional Include Paths",IDC_STATIC,7,104,224,137 EDITTEXT IDC_TXTADDPATH,12,119,186,14,ES_AUTOHSCROLL PUSHBUTTON "...",IDC_BTSEARCHPATH,204,119,20,14 @@ -134,20 +134,25 @@ BEGIN GROUPBOX "Compiler Options",IDC_STATIC,248,104,170,137 CONTROL "Optimize script",IDC_CHKCOMPOPTIMIZE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,263,120,63,10 CONTROL "Enable non-Bioware's extensions",IDC_CHKNONBIOWAREXTENSIONS, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,263,136,121,10 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,263,168,121,10 CONTROL "Generate (.ndb) debug symbols files",IDC_CHKCOMPNDBSYMBOLS, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,263,153,132,10 - CONTROL "Enable strict mode",IDC_CHKCOMPSTRICTMODE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,263,169,75,10 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,263,136,132,10 + CONTROL "Enable strict mode",IDC_CHKCOMPSTRICTMODE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,263,185,75,10 CONTROL "Create makefile (.d) dependencies files",IDC_CHKCOMPMAKEFILE, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,263,185,141,10 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,263,152,141,10 CONTROL "Disable parsing of \\"" and \\\\ tokens",IDC_CHKCOMPDISABLESLASHPARSE, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,263,201,135,10 COMBOBOX IDC_CBOTARGETVERSION,262,219,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Target game version",IDC_STATIC,317,220,67,8 + LTEXT "Target game version",IDC_LBLTARGETVERSION,317,220,67,8 GROUPBOX "Output Directory",IDC_STATIC,7,246,411,40 CONTROL "Same of the script file",IDC_CHKOUTPUTDIR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,21,264,85,10 EDITTEXT IDC_TXTOUTPUTDIR,115,261,263,14,ES_AUTOHSCROLL PUSHBUTTON "...",IDC_BTOUTPUTDIR,383,261,20,14 + GROUPBOX "Compiler Engine",IDC_STATIC,300,7,118,90 + CONTROL "Beamdog's Native Compiler",IDC_USEBEAMDOGCOMPILER, + "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,310,33,102,10 + CONTROL "Legacy NscLib Compiler",IDC_USELEGACYCOMPILER,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,310,61,90,10 + CONTROL "What is this?",IDC_LNKWHATISTHIS,"SysLink",0x0,334,81,41,11 END IDD_BATCHPROCESS DIALOGEX 0, 0, 409, 171 @@ -231,6 +236,16 @@ BEGIN CONTROL "",IDC_BTFILTERINFO,"Button",BS_AUTOCHECKBOX | BS_ICON | BS_PUSHLIKE,396,5,17,14 END +IDD_WHATISTHIS DIALOGEX 0, 0, 347, 195 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_WINDOWEDGE | WS_EX_APPWINDOW +CAPTION "What is this?" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,147,174,50,14 + CONTROL "",IDC_TXTHELP,"RichEdit50W",WS_BORDER | WS_VSCROLL | WS_TABSTOP | 0x804,7,7,333,158 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -323,6 +338,14 @@ BEGIN TOPMARGIN, 2 BOTTOMMARGIN, 123 END + + IDD_WHATISTHIS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 340 + TOPMARGIN, 7 + BOTTOMMARGIN, 188 + END END #endif // APSTUDIO_INVOKED @@ -387,6 +410,11 @@ BEGIN 0 END +IDD_WHATISTHIS AFX_DIALOG_LAYOUT +BEGIN + 0 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -469,6 +497,10 @@ IDR_ABOUTDOC RTF "..\\..\\Media\\OnlineHelp.rtf" IDR_ABOUTDOCDARK RTF "..\\..\\Media\\OnlineHelp-Dark.rtf" +IDR_COMPILERENGINE RTF "..\\..\\Media\\CompilerEngine.rtf" + +IDR_COMPILERENGINEDARK RTF "..\\..\\Media\\CompilerEngine-Dark.rtf" + ///////////////////////////////////////////////////////////////////////////// // diff --git a/src/Plugin Controls/PluginControlsRC.h b/src/Plugin Controls/PluginControlsRC.h index 3ed68c2..823c7cd 100644 --- a/src/Plugin Controls/PluginControlsRC.h +++ b/src/Plugin Controls/PluginControlsRC.h @@ -1,6 +1,6 @@ //{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by PluginControls.rc +// Arquivo de inclusão gerado pelo Microsoft Visual C++. +// Usado por PluginControls.rc // #define IDD_ABOUT 102 #define IDB_HEREBEDRAGONS 103 @@ -46,6 +46,9 @@ #define IDI_REPAIR 187 #define IDB_NWSCRIPTLOGO_SIMPLIFIED 188 #define IDI_APPLICATIONACCESS 189 +#define IDD_WHATISTHIS 190 +#define IDR_COMPILERENGINE 192 +#define IDR_COMPILERENGINEDARK 193 #define IDC_LNKHOMEPAGE 1000 #define IDC_TXTABOUT 1001 #define IDC_LBLPLUGINNAME 1002 @@ -115,6 +118,12 @@ #define IDC_PCTNWSCRIPTFILELOGOBOX 1079 #define IDC_PCTABOUTLOGOBOX 1080 #define IDC_PCTFILEACCESSLOGOBOX 1081 +#define IDC_USEBEAMDOGCOMPILER 1082 +#define IDC_USELEGACYCOMPILER 1083 +#define IDC_WHATISTHIS 1084 +#define IDC_LNKWHATISTHIS 1084 +#define IDC_LBLTARGETVERSION 1085 +#define IDC_TXTHELP 1086 #define IDC_STATIC -1 #define IDC_HEREBEDRAGONS -1 #define IDC_LBLSOLUTION -1 @@ -123,9 +132,9 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 190 +#define _APS_NEXT_RESOURCE_VALUE 194 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1082 +#define _APS_NEXT_CONTROL_VALUE 1087 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/src/WarningDialog.cpp b/src/Plugin Controls/WarningDialog.cpp similarity index 100% rename from src/WarningDialog.cpp rename to src/Plugin Controls/WarningDialog.cpp diff --git a/src/WarningDialog.h b/src/Plugin Controls/WarningDialog.h similarity index 100% rename from src/WarningDialog.h rename to src/Plugin Controls/WarningDialog.h diff --git a/src/Plugin Controls/WhatIsThisDialog.cpp b/src/Plugin Controls/WhatIsThisDialog.cpp new file mode 100644 index 0000000..39cace2 --- /dev/null +++ b/src/Plugin Controls/WhatIsThisDialog.cpp @@ -0,0 +1,221 @@ +/** @file ProcessFilesDialog.cpp + * Process Files dialog box + * + **/ + // Copyright (C) 2022 - Leonardo Silva + // The License.txt file describes the conditions under which this software may be distributed. + +#include "pch.h" +//#include +//#include +//#include + +#include "PluginMain.h" +#include "WhatIsThisDialog.h" + +#include "PluginControlsRC.h" +#include "PluginDarkMode.h" + + +using namespace NWScriptPlugin; + +BEGIN_ANCHOR_MAP(WhatIsThisDialog) + ANCHOR_MAP_ADDGLOBALSIZERESTRICTION(mainWindowSize) + ANCHOR_MAP_ENTRY(_hSelf, IDC_TXTHELP, ANF_ALL) + ANCHOR_MAP_ENTRY(_hSelf, IDOK, ANF_BOTTOM) + ANCHOR_MAP_ADDSIZERESTRICTION(_hSelf, IDC_TXTHELP, txtHelpSize) +END_ANCHOR_MAP(_hSelf) + + + +DWORD CALLBACK EditStreamCallback2(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, PLONG pcb) +{ + IStream* pstm = (IStream*)dwCookie; + DWORD fail = FAILED(pstm->Read(lpBuff, cb, (ULONG*)pcb)); + return fail; +} + +void WhatIsThisDialog::LoadHelpTextEditor(int resourceID) +{ + _currentDocumentID = resourceID; + + // Load resource + auto hResource = FindResourceW(_hInst, MAKEINTRESOURCE(_currentDocumentID), L"RTF"); + HGLOBAL hMemory = NULL; + LPVOID ptr = NULL; + size_t _size = 0; + + if (hResource) + { + _size = SizeofResource(_hInst, hResource); + hMemory = LoadResource(_hInst, hResource); + + if (hMemory) + ptr = LockResource(hMemory); + } + + // Copy image bytes into a real hglobal memory handle + hMemory = ::GlobalAlloc(GHND, _size); + if (hMemory) + { + void* pBuffer = ::GlobalLock(hMemory); + if (pBuffer && ptr) + memcpy(pBuffer, ptr, _size); + } + + // Make a raw string of resources + std::string rawText; + + if (ptr) + rawText.assign((char*)ptr, _size); + + generic_string rawTextW = str2wstr(rawText); + + // Free memory allocated for resource + if (hMemory) + GlobalFree(hMemory); + + // Now, redo the allocated space with the new size (with replaced texts) and copy modified text again + hMemory = ::GlobalAlloc(GHND, rawTextW.size()); + if (hMemory) + { + void* pBuffer = ::GlobalLock(hMemory); + if (pBuffer) + memcpy(pBuffer, wstr2str(rawTextW).c_str(), rawTextW.size()); + } + + // Create stream on hMemory + IStream* pStream = nullptr; + HRESULT hr = CreateStreamOnHGlobal(hMemory, TRUE, &pStream); + if (SUCCEEDED(hr)) + { + // Set paramenters + EDITSTREAM es = { (DWORD_PTR)pStream, 0, EditStreamCallback2 }; + HWND editControl = GetDlgItem(_hSelf, IDC_TXTHELP); + SendMessage(editControl, EM_EXLIMITTEXT, 0, -1); + SendMessage(editControl, EM_SETEVENTMASK, 0, ENM_LINK); + //SendMessage(editControl, EM_SETOLECALLBACK, 0, reinterpret_cast(&_whatIsThisOleCallback)); + + // Load Document + SendMessage(editControl, EM_SETREADONLY, 0, 0); // Set readonly off or images may not load properly + SendMessage(editControl, EM_STREAMIN, SF_RTF, reinterpret_cast(&es)); + SendMessage(editControl, EM_SETREADONLY, 1, 0); + + // Retrieve raw buffer from TXTABOUT for replace strings and later use with hyperlink clicks + GETTEXTLENGTHEX tl = { GTL_NUMCHARS, 1200 }; + _helpText.resize(SendMessage(editControl, EM_GETTEXTLENGTHEX, reinterpret_cast(&tl), 0) + 1); + GETTEXTEX tex = { (DWORD)_helpText.size() * sizeof(TCHAR), GT_RAWTEXT, 1200, NULL, NULL }; + SendMessage(editControl, EM_GETTEXTEX, reinterpret_cast(&tex), reinterpret_cast(_helpText.data())); + + if (PluginDarkMode::isEnabled()) + SendMessage(editControl, EM_SETBKGNDCOLOR, 0, PluginDarkMode::getSofterBackgroundColor()); + } + + // Free memory allocated for resource again + if (hMemory) + GlobalFree(hMemory); + +} + +intptr_t CALLBACK WhatIsThisDialog::run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + { + _dpiManager.resizeControl(_hSelf); + _dpiManager.resizeChildren(_hSelf, true); + + mainWindowSize = { {_dpiManager.scaleX(537), _dpiManager.scaleY(356)} }; + txtHelpSize = { { _dpiManager.scaleX(500), _dpiManager.scaleY(257) } }; + + // Make window zoomable + LONG extStyle = GetWindowLong(GetDlgItem(_hSelf, IDC_TXTHELP), GWL_EXSTYLE); + extStyle |= ES_EX_ZOOMABLE; + SetWindowLong(GetDlgItem(_hSelf, IDC_TXTHELP), GWL_EXSTYLE, extStyle); + + PluginDarkMode::autoSetupWindowAndChildren(_hSelf); + + LoadHelpTextEditor(PluginDarkMode::isEnabled() ? IDR_COMPILERENGINEDARK : IDR_COMPILERENGINE); + + // Setup control anchors + InitAnchors(); + + return TRUE; + } + + case WM_COMMAND: + { + switch (wParam) + { + case IDOK: + case IDCANCEL: + display(false); + destroy(); + return TRUE; + } + break; + } + + case WM_SIZE: + ANCHOR_MAP_HANDLESIZERS(); + + case WM_GETMINMAXINFO: + ANCHOR_MAP_HANDLERESTRICTORS(wParam, lParam); + + case WM_NOTIFY: + { + switch (wParam) + { + + case IDC_TXTHELP: + { + NMHDR* nmhdr = reinterpret_cast(lParam); + switch (nmhdr->code) + { + case EN_LINK: + { + ENLINK* enLinkInfo = (ENLINK*)lParam; + if (enLinkInfo->msg == WM_SETCURSOR) + { + SetCursor(LoadCursor(NULL, IDC_HAND)); + } + + if (enLinkInfo->msg == WM_LBUTTONUP) + { + LaunchHyperlink(*enLinkInfo); + } + + return TRUE; + } + } + break; + } + } + break; + } + + + } + + // Signals done processing messages + return FALSE; +} + +void WhatIsThisDialog::LaunchHyperlink(const ENLINK& link) +{ + // Get text range from raw string + generic_string url = _helpText.substr(link.chrg.cpMin, link.chrg.cpMax - link.chrg.cpMin); + ShellExecute(NULL, L"open", url.c_str(), NULL, NULL, SW_SHOW); +} + + +void WhatIsThisDialog::showDialog() +{ + // Create from resource + if (!isCreated()) + create(IDD_WHATISTHIS); + + //Show and centralize + goToCenter(); +} diff --git a/src/Plugin Controls/WhatIsThisDialog.h b/src/Plugin Controls/WhatIsThisDialog.h new file mode 100644 index 0000000..e683284 --- /dev/null +++ b/src/Plugin Controls/WhatIsThisDialog.h @@ -0,0 +1,48 @@ +/** @file ProcessFilesDialog.h + * Process Files dialog Box + * + **/ + // Copyright (C) 2022 - Leonardo Silva + // The License.txt file describes the conditions under which this software may be distributed. + +#pragma once +#include "StaticDialog.h" +#include "AnchorMap.h" + +namespace NWScriptPlugin { + + class WhatIsThisDialog : public StaticDialog + { + public: + WhatIsThisDialog() = default; + + void showDialog(); + + void setInterruptFlag(std::atomic& interruptFlagVariable) { + _interruptFlagVariable = &interruptFlagVariable; + } + + void lockWindow(bool toLock) { + EnableWindow(_hSelf, !toLock); + } + + protected: + virtual intptr_t CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam); + std::atomic* _interruptFlagVariable; + + private: + + DECLARE_ANCHOR_MAP() + + // Anchoring and size restriction informations + RECTSIZER mainWindowSize = {}; + RECTSIZER txtHelpSize = {}; + + generic_string _helpText; + + int _currentDocumentID = 0; + + void LoadHelpTextEditor(int resourceID); + void LaunchHyperlink(const ENLINK& link); + }; +} diff --git a/src/PluginMain.cpp b/src/PluginMain.cpp index 76919c0..0551c83 100644 --- a/src/PluginMain.cpp +++ b/src/PluginMain.cpp @@ -610,6 +610,18 @@ void Plugin::ProcessMessagesSci(SCNotification* notifyCode) // Initially disable run last batch (until the user runs a batch in session) EnablePluginMenuItem(PLUGINMENU_RUNLASTBATCH, false); + // Check whether to enable legacy options based on script engine chosen + if (Settings().compilerEngine == 0) + { + EnablePluginMenuItem(PLUGINMENU_FETCHPREPROCESSORTEXT, false); + EnablePluginMenuItem(PLUGINMENU_VIEWSCRIPTDEPENDENCIES, false); + } + else + { + EnablePluginMenuItem(PLUGINMENU_FETCHPREPROCESSORTEXT, true); + EnablePluginMenuItem(PLUGINMENU_VIEWSCRIPTDEPENDENCIES, true); + } + // Detects Dark Mode support. Can be done by messaging Notepad++ for newer versions // Or if the messages aren't supported (on a previous version), checks the installation on Notepad++ config.xml. // Note: Dark Mode is different from Dark Theme - 1st is for the plugin GUI, the second is for NWScript file lexing. @@ -1201,8 +1213,26 @@ void Plugin::LockPluginMenu(bool toLock) EnablePluginMenuItem(PLUGINMENU_DISASSEMBLESCRIPT, !toLock); EnablePluginMenuItem(PLUGINMENU_BATCHPROCESSING, !toLock); - EnablePluginMenuItem(PLUGINMENU_FETCHPREPROCESSORTEXT, !toLock); - EnablePluginMenuItem(PLUGINMENU_VIEWSCRIPTDEPENDENCIES, !toLock); + // These depend also on engine settings + if (!toLock) + { + if (Settings().compilerEngine == 0) + { + EnablePluginMenuItem(PLUGINMENU_FETCHPREPROCESSORTEXT, false); + EnablePluginMenuItem(PLUGINMENU_VIEWSCRIPTDEPENDENCIES, false); + } + else + { + EnablePluginMenuItem(PLUGINMENU_FETCHPREPROCESSORTEXT, true); + EnablePluginMenuItem(PLUGINMENU_VIEWSCRIPTDEPENDENCIES, true); + } + } + else + { + EnablePluginMenuItem(PLUGINMENU_FETCHPREPROCESSORTEXT, false); + EnablePluginMenuItem(PLUGINMENU_VIEWSCRIPTDEPENDENCIES, false); + } + EnablePluginMenuItem(PLUGINMENU_SHOWCONSOLE, !toLock); EnablePluginMenuItem(PLUGINMENU_SETTINGS, !toLock); EnablePluginMenuItem(PLUGINMENU_USERPREFERENCES, !toLock); @@ -2571,7 +2601,7 @@ void Plugin::DoCompileOrDisasm(generic_string filePath, bool fromCurrentScintill // Process script. _compiler.setSourceFilePath(scriptPath); #ifdef USE_THREADS - std::thread tProcessor(&NWScriptCompilerV2::processFile, &_compiler, fromCurrentScintilla, &_tempFileContents[0]); + std::thread tProcessor(&NWScriptCompiler::processFile, &_compiler, fromCurrentScintilla, &_tempFileContents[0]); tProcessor.detach(); #else _compiler.processFile(fromCurrentScintilla, &_tempFileContents[0]); @@ -2600,7 +2630,7 @@ void Plugin::BuildFilesList() // Receives notifications when a "Compile" menu command ends void Plugin::CompileEndingCallback(HRESULT decision) { - NWScriptCompilerV2& compiler = Instance().Compiler(); + NWScriptCompiler& compiler = Instance().Compiler(); // Clear any content of temporary stash if exists Instance()._tempFileContents.clear(); @@ -2640,7 +2670,7 @@ void Plugin::CompileEndingCallback(HRESULT decision) // Receives notifications when a "Disassemble" menu command ends void Plugin::DisassembleEndingCallback(HRESULT decision) { - NWScriptCompilerV2& compiler = Instance().Compiler(); + NWScriptCompiler& compiler = Instance().Compiler(); // Unlock controls to compiler log window Instance()._loggerWindow->LockControls(false); @@ -3085,6 +3115,20 @@ PLUGINCOMMAND Plugin::CompilerSettings() compilerSettings.init(Instance().DllHModule(), Instance().NotepadHwnd()); compilerSettings.appendSettings(&Instance()._settings); compilerSettings.doDialog(); + + // Disable or enable some menu options depending which engine was chosen + Plugin& inst = Instance(); + + if (inst.Settings().compilerEngine == 0) + { + inst.EnablePluginMenuItem(PLUGINMENU_FETCHPREPROCESSORTEXT, false); + inst.EnablePluginMenuItem(PLUGINMENU_VIEWSCRIPTDEPENDENCIES, false); + } + else + { + inst.EnablePluginMenuItem(PLUGINMENU_FETCHPREPROCESSORTEXT, true); + inst.EnablePluginMenuItem(PLUGINMENU_VIEWSCRIPTDEPENDENCIES, true); + } } // Opens the user's preferences. diff --git a/src/PluginMain.h b/src/PluginMain.h index 34d96b0..a58b21f 100644 --- a/src/PluginMain.h +++ b/src/PluginMain.h @@ -18,7 +18,7 @@ #include "LineIndentor.h" #include "Settings.h" #include "NWScriptParser.h" -#include "NWScriptCompilerV2.h" +#include "NWScriptCompiler.h" #include "AboutDialog.h" #include "LoggerDialog.h" @@ -99,7 +99,7 @@ namespace NWScriptPlugin { // Retrieve's Plugin's LineIndentor Object LineIndentor& Indentor() { return _indentor; } // Retrieve the Compiler Object - NWScriptCompilerV2& Compiler() { return _compiler; }; + NWScriptCompiler& Compiler() { return _compiler; }; // Retrieve's Plugin's Module Handle HMODULE DllHModule() const { return _dllHModule; } // Retrieves Notepad++ HWND @@ -327,7 +327,7 @@ namespace NWScriptPlugin { NotepadLexer _notepadCurrentLexer; PluginMessenger _messageInstance; LineIndentor _indentor; - NWScriptCompilerV2 _compiler; + NWScriptCompiler _compiler; tTbData _dockingData; // needs persistent info for docking data HICON _dockingIcon; // needs persistent info for docking data generic_string _dockingTitle; // needs persistent info for docking data diff --git a/src/Settings.cpp b/src/Settings.cpp index b2197bf..8d7059b 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -52,6 +52,7 @@ void Settings::Load() neverwinterTwoInstallDir = properDirNameW(GetString(TEXT("Compiler Settings"), TEXT("neverwinterTwoInstallDir"))); ignoreInstallPaths = GetBoolean(TEXT("Compiler Settings"), TEXT("ignoreInstallPaths")); additionalIncludeDirs = GetString(TEXT("Compiler Settings"), TEXT("additionalIncludeDirs")); + compilerEngine = GetNumber(TEXT("Compiler Settings"), TEXT("compilerEngine")); compilerFlags = GetNumber(TEXT("Compiler Settings"), TEXT("compilerFlags")); optimizeScript = GetBoolean(TEXT("Compiler Settings"), TEXT("optimizeScript")); useNonBiowareExtenstions = GetBoolean(TEXT("Compiler Settings"), TEXT("useNonBiowareExtenstions")); @@ -173,6 +174,7 @@ void Settings::Save() SetString(TEXT("Compiler Settings"), TEXT("neverwinterTwoInstallDir"), neverwinterTwoInstallDir); SetBoolean(TEXT("Compiler Settings"), TEXT("ignoreInstallPaths"), ignoreInstallPaths); SetString(TEXT("Compiler Settings"), TEXT("additionalIncludeDirs"), additionalIncludeDirs); + SetNumber(TEXT("Compiler Settings"), TEXT("compilerEngine "), compilerEngine); SetNumber(TEXT("Compiler Settings"), TEXT("compilerFlags"), compilerFlags); SetBoolean(TEXT("Compiler Settings"), TEXT("optimizeScript"), optimizeScript); SetBoolean(TEXT("Compiler Settings"), TEXT("useNonBiowareExtenstions"), useNonBiowareExtenstions); diff --git a/src/Settings.h b/src/Settings.h index b1d2faf..f0ed7f2 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -51,6 +51,7 @@ namespace NWScriptPlugin { generic_string neverwinterOneInstallDir; generic_string neverwinterTwoInstallDir; bool ignoreInstallPaths = false; + int compilerEngine = 0; UINT32 compilerFlags = 0; bool optimizeScript = true; bool useNonBiowareExtenstions = false; From cd95c658bd7b18553940ee5ecb60f599c45919e6 Mon Sep 17 00:00:00 2001 From: Leonardo Silva <99574879+Leonard-The-Wise@users.noreply.github.com> Date: Thu, 27 Jul 2023 01:10:35 -0300 Subject: [PATCH 5/6] Fix DarkMode colors for new dialog. --- src/Plugin Controls/WhatIsThisDialog.cpp | 5 ++++- src/Plugin Controls/WhatIsThisDialog.h | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Plugin Controls/WhatIsThisDialog.cpp b/src/Plugin Controls/WhatIsThisDialog.cpp index 39cace2..36e7775 100644 --- a/src/Plugin Controls/WhatIsThisDialog.cpp +++ b/src/Plugin Controls/WhatIsThisDialog.cpp @@ -69,7 +69,10 @@ void WhatIsThisDialog::LoadHelpTextEditor(int resourceID) if (ptr) rawText.assign((char*)ptr, _size); - generic_string rawTextW = str2wstr(rawText); + // HACK: change Bk Color of dark mode without messages + std::map replaceStrings; + replaceStrings.insert({ L"4210752", std::to_wstring(PluginDarkMode::getSofterBackgroundColor()) }); + generic_string rawTextW = replaceStringsW(str2wstr(rawText), replaceStrings); // Free memory allocated for resource if (hMemory) diff --git a/src/Plugin Controls/WhatIsThisDialog.h b/src/Plugin Controls/WhatIsThisDialog.h index e683284..89b827c 100644 --- a/src/Plugin Controls/WhatIsThisDialog.h +++ b/src/Plugin Controls/WhatIsThisDialog.h @@ -9,6 +9,8 @@ #include "StaticDialog.h" #include "AnchorMap.h" +#include "Common.h" + namespace NWScriptPlugin { class WhatIsThisDialog : public StaticDialog From 8a9027988e62741709e118c8b24f4afd7dab2048 Mon Sep 17 00:00:00 2001 From: Leonardo Silva <99574879+Leonard-The-Wise@users.noreply.github.com> Date: Thu, 27 Jul 2023 02:12:19 -0300 Subject: [PATCH 6/6] Minor UX improvement (standarization of new dialog box). --- Media/icons-svg/HelpTableOfContents.svg | 15 ++++++++ NWScript-Npp/NWScript-Npp.vcxproj | 1 + NWScript-Npp/NWScript-Npp.vcxproj.filters | 3 ++ .../CompilerSettingsDialog.cpp | 7 ++-- src/Plugin Controls/PluginControls.rc | 8 +++-- src/Plugin Controls/PluginControlsRC.h | 4 ++- src/Plugin Controls/WhatIsThisDialog.cpp | 34 +++++++++++++------ src/Plugin Controls/WhatIsThisDialog.h | 14 +++++--- 8 files changed, 63 insertions(+), 23 deletions(-) create mode 100644 Media/icons-svg/HelpTableOfContents.svg diff --git a/Media/icons-svg/HelpTableOfContents.svg b/Media/icons-svg/HelpTableOfContents.svg new file mode 100644 index 0000000..50f00ed --- /dev/null +++ b/Media/icons-svg/HelpTableOfContents.svg @@ -0,0 +1,15 @@ + + + + + HelpTableOfContents + + + + + + + + + + diff --git a/NWScript-Npp/NWScript-Npp.vcxproj b/NWScript-Npp/NWScript-Npp.vcxproj index 6961a4e..1fbbdaf 100644 --- a/NWScript-Npp/NWScript-Npp.vcxproj +++ b/NWScript-Npp/NWScript-Npp.vcxproj @@ -450,6 +450,7 @@ + diff --git a/NWScript-Npp/NWScript-Npp.vcxproj.filters b/NWScript-Npp/NWScript-Npp.vcxproj.filters index b2a3a84..e29b57f 100644 --- a/NWScript-Npp/NWScript-Npp.vcxproj.filters +++ b/NWScript-Npp/NWScript-Npp.vcxproj.filters @@ -468,6 +468,9 @@ Media + + Resource Files + diff --git a/src/Plugin Controls/CompilerSettingsDialog.cpp b/src/Plugin Controls/CompilerSettingsDialog.cpp index 691dc0e..3bb0b1a 100644 --- a/src/Plugin Controls/CompilerSettingsDialog.cpp +++ b/src/Plugin Controls/CompilerSettingsDialog.cpp @@ -292,11 +292,8 @@ intptr_t CALLBACK CompilerSettingsDialog::run_dlgProc(UINT message, WPARAM wPara case NM_CLICK: case NM_RETURN: - if (!whatIsThisDialog.isCreated()) - whatIsThisDialog.init(_hInst, _hSelf); - - if (!whatIsThisDialog.isVisible()) - whatIsThisDialog.showDialog(); + whatIsThisDialog.init(_hInst, _hSelf); + whatIsThisDialog.doDialog(); return TRUE; } diff --git a/src/Plugin Controls/PluginControls.rc b/src/Plugin Controls/PluginControls.rc index 144fc61..c5529d5 100644 --- a/src/Plugin Controls/PluginControls.rc +++ b/src/Plugin Controls/PluginControls.rc @@ -237,12 +237,12 @@ BEGIN END IDD_WHATISTHIS DIALOGEX 0, 0, 347, 195 -STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -EXSTYLE WS_EX_WINDOWEDGE | WS_EX_APPWINDOW +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_NOPARENTNOTIFY | WS_EX_WINDOWEDGE CAPTION "What is this?" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - DEFPUSHBUTTON "OK",IDOK,147,174,50,14 + DEFPUSHBUTTON "&OK",IDOK,147,174,50,14 CONTROL "",IDC_TXTHELP,"RichEdit50W",WS_BORDER | WS_VSCROLL | WS_TABSTOP | 0x804,7,7,333,158 END @@ -487,6 +487,8 @@ IDI_REPAIR SVG "..\\..\\Media\\icons-svg\\Repai IDI_APPLICATIONACCESS SVG "..\\..\\Media\\icons-svg\\ApplicationAccess.svg" +IDI_HELPTABLECONTENTS SVG "..\\..\\Media\\icons-svg\\HelpTableOfContents.svg" + ///////////////////////////////////////////////////////////////////////////// // diff --git a/src/Plugin Controls/PluginControlsRC.h b/src/Plugin Controls/PluginControlsRC.h index 823c7cd..07225f6 100644 --- a/src/Plugin Controls/PluginControlsRC.h +++ b/src/Plugin Controls/PluginControlsRC.h @@ -49,6 +49,8 @@ #define IDD_WHATISTHIS 190 #define IDR_COMPILERENGINE 192 #define IDR_COMPILERENGINEDARK 193 +#define IDR_SVG1 194 +#define IDI_HELPTABLECONTENTS 194 #define IDC_LNKHOMEPAGE 1000 #define IDC_TXTABOUT 1001 #define IDC_LBLPLUGINNAME 1002 @@ -132,7 +134,7 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 194 +#define _APS_NEXT_RESOURCE_VALUE 195 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1087 #define _APS_NEXT_SYMED_VALUE 101 diff --git a/src/Plugin Controls/WhatIsThisDialog.cpp b/src/Plugin Controls/WhatIsThisDialog.cpp index 36e7775..abe69a8 100644 --- a/src/Plugin Controls/WhatIsThisDialog.cpp +++ b/src/Plugin Controls/WhatIsThisDialog.cpp @@ -144,18 +144,37 @@ intptr_t CALLBACK WhatIsThisDialog::run_dlgProc(UINT message, WPARAM wParam, LPA // Setup control anchors InitAnchors(); + // Load PNG image for window logo + HICON hAbout = loadSVGFromResourceIcon(_hInst, IDI_HELPTABLECONTENTS, PluginDarkMode::isEnabled(), _dpiManager.scaleX(16), _dpiManager.scaleY(16)); + ::SendMessage(_hSelf, WM_SETICON, ICON_SMALL, reinterpret_cast(hAbout)); + return TRUE; } + /* + case WM_KEYDOWN: + { + WORD vkCode = LOWORD(wParam); // virtual-key code + WORD keyFlags = HIWORD(lParam); + + switch (vkCode) + { + case VK_RETURN: + case VK_ESCAPE: + EndDialog(_hSelf, wParam); + return TRUE; + } + } + */ + case WM_COMMAND: { switch (wParam) { case IDOK: case IDCANCEL: - display(false); - destroy(); - return TRUE; + EndDialog(_hSelf, wParam); + break; } break; } @@ -213,12 +232,7 @@ void WhatIsThisDialog::LaunchHyperlink(const ENLINK& link) } -void WhatIsThisDialog::showDialog() +INT_PTR WhatIsThisDialog::doDialog() { - // Create from resource - if (!isCreated()) - create(IDD_WHATISTHIS); - - //Show and centralize - goToCenter(); + return ShowModal(IDD_WHATISTHIS); } diff --git a/src/Plugin Controls/WhatIsThisDialog.h b/src/Plugin Controls/WhatIsThisDialog.h index 89b827c..b4405ea 100644 --- a/src/Plugin Controls/WhatIsThisDialog.h +++ b/src/Plugin Controls/WhatIsThisDialog.h @@ -6,19 +6,24 @@ // The License.txt file describes the conditions under which this software may be distributed. #pragma once -#include "StaticDialog.h" +#include "ModalDialog.h" #include "AnchorMap.h" #include "Common.h" namespace NWScriptPlugin { - class WhatIsThisDialog : public StaticDialog + class WhatIsThisDialog : public ModalDialog { public: WhatIsThisDialog() = default; - void showDialog(); + ~WhatIsThisDialog() { + if (_hWindowIcon) + DeleteObject(_hWindowIcon); + } + + INT_PTR doDialog(); void setInterruptFlag(std::atomic& interruptFlagVariable) { _interruptFlagVariable = &interruptFlagVariable; @@ -41,9 +46,10 @@ namespace NWScriptPlugin { RECTSIZER txtHelpSize = {}; generic_string _helpText; - int _currentDocumentID = 0; + HICON _hWindowIcon = nullptr; + void LoadHelpTextEditor(int resourceID); void LaunchHyperlink(const ENLINK& link); };