From 0c3809657e1609c346e85df180490a7deee8563f Mon Sep 17 00:00:00 2001 From: Anthony Rabine Date: Thu, 16 Oct 2025 00:11:01 +0200 Subject: [PATCH] Big update on machine class, fix addressing lcons, load etc. --- .gitignore | 3 +- .vscode/launch.json | 2 +- core/chip32/chip32_assembler.cpp | 275 +- core/chip32/chip32_assembler.h | 26 +- core/chip32/chip32_binary_format.c | 97 +- core/chip32/chip32_binary_format.h | 19 +- core/chip32/chip32_machine.h | 366 +- core/chip32/chip32_vm.c | 40 +- core/chip32/chip32_vm.h | 2 + core/tests/CMakeLists.txt | 37 +- core/tests/catch.hpp | 17966 --------------------------- core/tests/test_ast.cpp | 3 +- core/tests/test_macros.cpp | 75 + core/tests/test_parser.cpp | 131 +- core/tests/test_print_node.cpp | 10 +- core/tests/test_vm.cpp | 2 +- 16 files changed, 568 insertions(+), 18486 deletions(-) delete mode 100644 core/tests/catch.hpp create mode 100644 core/tests/test_macros.cpp diff --git a/.gitignore b/.gitignore index b3bd684..07ea0c6 100644 --- a/.gitignore +++ b/.gitignore @@ -86,4 +86,5 @@ story-editor/flatpak_build/ story-editor/AppDir/ story-player/android/build/reports/problems/problems-report.html -core/tests/build \ No newline at end of file +core/tests/build +core/story-manager/tests/build \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 1b9de62..8aed7f4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -73,7 +73,7 @@ "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/core/tests/build/core_tests", // Remplacez par le chemin de votre exécutable - "args": ["[vm]"], + "args": [], "stopAtEntry": false, "cwd": "${workspaceFolder}/core/tests/build", "environment": [], diff --git a/core/chip32/chip32_assembler.cpp b/core/chip32/chip32_assembler.cpp index e0050e5..263a7f4 100644 --- a/core/chip32/chip32_assembler.cpp +++ b/core/chip32/chip32_assembler.cpp @@ -24,7 +24,6 @@ THE SOFTWARE. #include "chip32_assembler.h" -#include "chip32_binary_format.h" #include #include @@ -37,6 +36,7 @@ THE SOFTWARE. namespace Chip32 { + // ============================================================================= // GLOBAL UTILITY FUNCTIONS // ============================================================================= @@ -263,15 +263,14 @@ bool Assembler::CompileMnemonicArguments(Instr &instr) { // We allow two forms of writing: // - load r0, @R1, 4 ; the address is located in a register - // - load r0, $variable, 4 ; we use the variable address to get the value + // - load r0, $variable, 2 ; we use the variable address to get the value // char prefix = instr.args[1].at(0); // Register based if (prefix == '@') { - - instr.args[1].erase(0, 1); + instr.args[1].erase(0, 1); // delete @ character GET_REG(instr.args[0], ra); GET_REG(instr.args[1], rb); instr.compiledArgs.push_back(ra); @@ -282,7 +281,10 @@ bool Assembler::CompileMnemonicArguments(Instr &instr) else if (prefix == '$') { instr.useLabel = true; + GET_REG(instr.args[0], ra); + instr.compiledArgs.push_back(ra | 0x80); // Flag this register with a bit to indicate an immediate address is following leu32_put(instr.compiledArgs, 0); // reserve 4 bytes + instr.compiledArgs.push_back(static_cast(strtol(instr.args[2].c_str(), NULL, 0))); } else { @@ -357,203 +359,6 @@ bool Assembler::CompileConstantArgument(Instr &instr, const std::string &a) return true; } -bool Assembler::BuildBinary(std::vector &program, Result &result) -{ - program.clear(); - result = { 0, 0, 0}; - - // ======================================================================== - // PHASE 1: Créer les sections temporaires - // ======================================================================== - std::vector constSection; // DC - ROM constants only - std::vector codeSection; // Executable code - std::vector initDataSection; // DV values + DZ zeros (RAM init) - - uint32_t bssSize = 0; // Total RAM size (DV + DZ) - uint32_t entryPoint = 0; // Entry point (.main) - - // Map to track DV init data: RAM address -> init data - std::map> dvInitData; - - // ======================================================================== - // PHASE 2: Première passe - identifier les variables DV et leurs données - // ======================================================================== - // On doit d'abord construire une map des données d'initialisation DV - for (size_t idx = 0; idx < m_instructions.size(); idx++) - { - const auto &instr = m_instructions[idx]; - - // Détecter une variable DV (le marqueur final) - if (instr.isRamData && !instr.isZeroData && instr.mnemonic[0] == '$') - { - uint16_t ramAddr = instr.addr; - uint16_t romInitAddr = instr.romInitAddr; - uint16_t dataLen = instr.dataLen; - - // Collecter les données d'init depuis les instructions précédentes - std::vector initData; - - // Chercher en arrière les instructions ROM data pour cette variable - for (size_t j = 0; j < idx; j++) - { - const auto &dataInstr = m_instructions[j]; - - // Ces instructions ont isRomData=true et leur addr correspond à romInitAddr - if (dataInstr.isRomData && !dataInstr.isRamData && - dataInstr.addr >= romInitAddr && - dataInstr.addr < romInitAddr + dataLen) - { - // Ces données appartiennent à cette variable DV - initData.insert(initData.end(), - dataInstr.compiledArgs.begin(), - dataInstr.compiledArgs.end()); - } - } - - dvInitData[ramAddr] = initData; - } - } - - // ======================================================================== - // PHASE 3: Deuxième passe - dispatcher dans les bonnes sections - // ======================================================================== - // Track which ROM data belongs to DV variables - std::set dvRomAddresses; - for (const auto &instr : m_instructions) - { - if (instr.isRamData && !instr.isZeroData && instr.mnemonic[0] == '$') - { - for (uint16_t addr = instr.romInitAddr; - addr < instr.romInitAddr + instr.dataLen; - addr++) - { - dvRomAddresses.insert(addr); - } - } - } - - for (const auto &i : m_instructions) - { - // ==================================================================== - // RAM VARIABLES (DV et DZ) - marqueurs uniquement - // ==================================================================== - if (i.isRamData) - { - bssSize += i.dataLen; - // Les données seront traitées dans la phase 4 - } - // ==================================================================== - // ROM CODE (Instructions exécutables) - // ==================================================================== - else if (i.isRomCode()) - { - // Détecter le point d'entrée AVANT d'ajouter les données - if (i.isLabel && i.mnemonic == ".main") - { - entryPoint = static_cast(codeSection.size()); - } - - // Ajouter l'opcode - codeSection.push_back(i.code.opcode); - - // Ajouter les arguments compilés - std::copy(i.compiledArgs.begin(), - i.compiledArgs.end(), - std::back_inserter(codeSection)); - } - // ==================================================================== - // ROM DATA - Distinguer DC (vraies constantes) de DV init data - // ==================================================================== - else if (i.isRomData && !i.isRamData) - { - // Vérifier si cette donnée appartient à une variable DV - bool isDvInitData = (dvRomAddresses.find(i.addr) != dvRomAddresses.end()); - - if (!isDvInitData) - { - // C'est une vraie constante DC - va dans constSection - std::copy(i.compiledArgs.begin(), - i.compiledArgs.end(), - std::back_inserter(constSection)); - - result.constantsSize += i.compiledArgs.size(); - } - // Sinon, c'est une donnée DV, elle sera traitée dans la phase 4 - } - } - - // ======================================================================== - // PHASE 4: Construire initDataSection (DV + DZ dans l'ordre RAM) - // ======================================================================== - if (bssSize > 0) - { - initDataSection.resize(bssSize, 0); // Tout à zéro par défaut (pour DZ) - - // Copier les données DV aux bonnes positions RAM - for (const auto &pair : dvInitData) - { - uint16_t ramAddr = pair.first; - const std::vector &data = pair.second; - - // Copier les données d'init à l'adresse RAM correspondante - for (size_t i = 0; i < data.size() && (ramAddr + i) < bssSize; i++) - { - initDataSection[ramAddr + i] = data[i]; - } - } - - // Les zones DZ sont déjà à zéro grâce au resize() - } - - // ======================================================================== - // PHASE 5: Créer le header binaire - // ======================================================================== - chip32_binary_header_t header; - chip32_binary_header_init(&header); - - header.const_size = static_cast(constSection.size()); - header.bss_size = bssSize; - header.code_size = static_cast(codeSection.size()); - header.entry_point = entryPoint; - header.init_data_size = static_cast(initDataSection.size()); - - if (initDataSection.size() > 0) - { - header.flags |= CHIP32_FLAG_HAS_INIT_DATA; - } - - // ======================================================================== - // PHASE 6: Assembler le binaire final - // ======================================================================== - uint32_t totalSize = chip32_binary_calculate_size(&header); - program.resize(totalSize); - - uint32_t bytesWritten = chip32_binary_write( - &header, - constSection.empty() ? nullptr : constSection.data(), - codeSection.empty() ? nullptr : codeSection.data(), - initDataSection.empty() ? nullptr : initDataSection.data(), - program.data(), - static_cast(program.size()) - ); - - if (bytesWritten == 0 || bytesWritten != totalSize) - { - // Erreur lors de l'écriture - program.clear(); - return false; - } - - // ======================================================================== - // PHASE 7: Remplir les statistiques - // ======================================================================== - result.ramUsageSize = bssSize; - result.romUsageSize = header.const_size + header.code_size; - result.constantsSize = header.const_size; - - return true; -} - bool Assembler::Parse(const std::string &data) { std::stringstream data_stream(data); @@ -561,7 +366,8 @@ bool Assembler::Parse(const std::string &data) Clear(); int code_addr = 0; - int ram_addr = 0; + int dz_ram_addr = 0; // For DZ + int dv_ram_addr = 0; // For DV int lineNum = 0; while(std::getline(data_stream, line)) { @@ -682,38 +488,20 @@ bool Assembler::Parse(const std::string &data) // ======================================================================================= else if (!instr.isZeroData) // DV { - // DV behaves like DC for data storage, but marks the location as RAM - // The initial values are stored in ROM and must be copied to RAM at startup + // DV behaves like DC for data storage - instr.addr = ram_addr; + instr.addr = dv_ram_addr; m_labels[opcode] = instr; // RAM address for this variable - // Store the ROM address where initial data starts - instr.romInitAddr = code_addr; - // Process all initial values (like DC) for (unsigned int i = 2; i < lineParts.size(); i++) { - Instr dataInstr = instr; // Copy instruction info - CHIP32_CHECK(dataInstr, CompileConstantArgument(dataInstr, lineParts[i]), + CHIP32_CHECK(instr, CompileConstantArgument(instr, lineParts[i]), "Compile argument error, stopping."); - - // This instruction contains ROM data for initialization - dataInstr.addr = code_addr; - dataInstr.isRomData = true; // Store in ROM for init data - dataInstr.isRamData = false; // But this is just init data, not the variable - m_instructions.push_back(dataInstr); - - code_addr += dataInstr.compiledArgs.size(); - instr.dataLen += dataInstr.compiledArgs.size(); + m_instructions.push_back(instr); + dv_ram_addr += instr.compiledArgs.size(); + instr.addr = dv_ram_addr; } - - // Now add the RAM variable marker - instr.addr = ram_addr; - instr.isRomData = false; - instr.isRamData = true; - ram_addr += instr.dataLen; - m_instructions.push_back(instr); } // ======================================================================================= // DZ - Zero-initialized RAM zones (no data in ROM, just reserve space) @@ -724,22 +512,39 @@ bool Assembler::Parse(const std::string &data) CHIP32_CHECK(instr, lineParts.size() == 3, "DZ directive requires exactly one argument (number of elements)"); - instr.addr = ram_addr; + instr.addr = dz_ram_addr; // Calculate size in bytes: num_elements * (type_size / 8) uint32_t numElements = static_cast(strtol(lineParts[2].c_str(), NULL, 0)); instr.dataLen = static_cast(numElements * (instr.dataTypeSize / 8)); - ram_addr += instr.dataLen; + dz_ram_addr += instr.dataLen; m_labels[opcode] = instr; m_instructions.push_back(instr); } } } + // Now that the RAM DV sier is known, compute DZ real data location after DV +/* + +Position of Data in RAM + +---------------------------- +| DV | +---------------------------- +| DZ | +---------------------------- +*/ + // 2. Second pass: replace all label or RAM data by the real address in memory for (auto &instr : m_instructions) { + if (instr.isZeroData) + { + instr.addr += dv_ram_addr; + } + if (instr.useLabel && (instr.args.size() > 0)) { // label is the first argument for jump, second position for LCONS and LOAD @@ -749,15 +554,17 @@ bool Assembler::Parse(const std::string &data) } std::string label = instr.args[argsIndex]; CHIP32_CHECK(instr, m_labels.count(label) > 0, "label not found: " + label); - uint16_t addr = m_labels[label].addr; + uint32_t addr = m_labels[label].addr; std::cout << "LABEL: " << label << " , addr: " << addr << std::endl; + if (m_labels[label].isRamData) + { + addr |= CHIP32_RAM_OFFSET; + } + instr.compiledArgs[argsIndex] = addr & 0xFF; instr.compiledArgs[argsIndex+1] = (addr >> 8U) & 0xFF; - if ((instr.code.opcode == OP_LCONS) || (instr.code.opcode == OP_LOAD)) { - // We precise if the address is from RAM or ROM - // For that, the MSB is set to 1 (for RAM) - instr.compiledArgs[argsIndex+3] = m_labels[label].isRamData ? 0x80 : 0; - } + instr.compiledArgs[argsIndex+2] = (addr >> 16U) & 0xFF; + instr.compiledArgs[argsIndex+3] = (addr >> 24U) & 0xFF; } } diff --git a/core/chip32/chip32_assembler.h b/core/chip32/chip32_assembler.h index ee0e367..3363a59 100644 --- a/core/chip32/chip32_assembler.h +++ b/core/chip32/chip32_assembler.h @@ -56,8 +56,7 @@ struct Instr { bool isRamData{false}; //!< True if RAM variable (DV or DZ) bool isZeroData{false}; //!< True if zero-initialized RAM (DZ only) - uint16_t addr{0}; //!< Instruction/data address (ROM for DC, RAM for DV/DZ) - uint16_t romInitAddr{0};//!< For DV: ROM address where initial data is stored + uint32_t addr{0}; //!< Instruction/data address (ROM for DC, RAM for DV/DZ) bool isRomCode() const { return !(isLabel || isRomData || isRamData); } }; @@ -68,23 +67,6 @@ struct RegNames std::string name; }; -struct Result -{ - int ramUsageSize{0}; - int romUsageSize{0}; - int constantsSize{0}; - - void Print() - { - std::cout << "RAM usage: " << ramUsageSize << " bytes\n" - << "IMAGE size: " << romUsageSize << " bytes\n" - << " -> ROM DATA: " << constantsSize << " bytes\n" - << " -> ROM CODE: " << romUsageSize - constantsSize << "\n" - << std::endl; - - } -}; - class Assembler { public: @@ -102,8 +84,6 @@ public: // Separated parser to allow only code check bool Parse(const std::string &data); - // Generate the executable binary after the parse pass - bool BuildBinary(std::vector &program, Result &result); void Clear() { m_labels.clear(); @@ -112,8 +92,8 @@ public: static std::vector Split(const std::string &line); - std::vector::const_iterator Begin() { return m_instructions.begin(); } - std::vector::const_iterator End() { return m_instructions.end(); } + std::vector::const_iterator begin() { return m_instructions.begin(); } + std::vector::const_iterator end() { return m_instructions.end(); } // Returns the register number from the name bool GetRegister(const std::string ®Name, uint8_t ®); diff --git a/core/chip32/chip32_binary_format.c b/core/chip32/chip32_binary_format.c index 7bc3987..add9b3f 100644 --- a/core/chip32/chip32_binary_format.c +++ b/core/chip32/chip32_binary_format.c @@ -18,17 +18,16 @@ chip32_binary_error_t chip32_binary_load( uint32_t binary_size, uint8_t* ram, uint32_t ram_size, - chip32_binary_stats_t *out_stats + chip32_binary_header_t *header ) { - memset(ctx, 0, sizeof(ctx)); + // Initialize VM + chip32_initialize(ctx); ctx->stack_size = 512; // Can be changed outside this function // Clear RAM memset(ram, 0, ram_size); - chip32_binary_header_t header; - if (!binary || !ctx) { return CHIP32_BIN_ERR_NULL_POINTER; } @@ -39,23 +38,23 @@ chip32_binary_error_t chip32_binary_load( } // Copy header - memcpy(&header, binary, sizeof(chip32_binary_header_t)); + memcpy(header, binary, sizeof(chip32_binary_header_t)); // Verify magic number - if (header.magic != CHIP32_MAGIC) { + if (header->magic != CHIP32_MAGIC) { return CHIP32_BIN_ERR_INVALID_MAGIC; } // Check version - if (header.version > CHIP32_VERSION) { + if (header->version > CHIP32_VERSION) { return CHIP32_BIN_ERR_UNSUPPORTED_VERSION; } // Calculate expected size uint32_t expected_size = sizeof(chip32_binary_header_t) + - header.const_size + - header.code_size + - header.init_data_size; + header->const_size + + header->code_size + + header->data_size; if (binary_size != expected_size) { return CHIP32_BIN_ERR_SIZE_MISMATCH; @@ -76,27 +75,13 @@ chip32_binary_error_t chip32_binary_load( ctx->ram.size = ram_size; // Set entry point (DATA size + entry point offset in CODE) - ctx->registers[PC] = header.entry_point; + ctx->registers[PC] = header->entry_point; + // Stack pointer at the end of the RAM + ctx->registers[SP] = ctx->ram.size; // Load data initialized values - const uint8_t *data = binary + header.code_size; - memcpy(ram, data, header.init_data_size); - - - out_stats->const_size = header.const_size; - out_stats->bss_size = header.bss_size; - out_stats->code_size = header.code_size; - out_stats->init_data_size = header.init_data_size; - - out_stats->total_file_size = sizeof(chip32_binary_header_t) + - header.const_size + - header.code_size + - header.init_data_size; - - out_stats->total_rom_size = header.const_size + - header.code_size; - - out_stats->total_ram_size = header.bss_size; + const uint8_t *data = binary + offset + header->const_size + header->code_size; + memcpy(ram, data, header->data_size); return CHIP32_BIN_OK; } @@ -146,12 +131,12 @@ uint32_t chip32_binary_calculate_size(const chip32_binary_header_t* header) return sizeof(chip32_binary_header_t) + header->const_size + header->code_size + - header->init_data_size; + header->data_size; } uint32_t chip32_binary_write( const chip32_binary_header_t* header, - const uint8_t* data_section, + const uint8_t* const_section, const uint8_t* code_section, const uint8_t* init_data_section, uint8_t* out_buffer, @@ -169,18 +154,16 @@ uint32_t chip32_binary_write( return 0; // Buffer too small } - uint32_t offset = 0; - // Write header - memcpy(out_buffer + offset, header, sizeof(chip32_binary_header_t)); - offset += sizeof(chip32_binary_header_t); + memcpy(out_buffer, header, sizeof(chip32_binary_header_t)); + uint32_t offset = sizeof(chip32_binary_header_t); - // Write DATA section + // Write CONST section if (header->const_size > 0) { - if (!data_section) { + if (!const_section) { return 0; // Data expected but NULL pointer } - memcpy(out_buffer + offset, data_section, header->const_size); + memcpy(out_buffer + offset, const_section, header->const_size); offset += header->const_size; } @@ -194,12 +177,12 @@ uint32_t chip32_binary_write( } // Write INIT DATA section - if (header->init_data_size > 0) { + if (header->data_size > 0) { if (!init_data_section) { return 0; // Init data expected but NULL pointer } - memcpy(out_buffer + offset, init_data_section, header->init_data_size); - offset += header->init_data_size; + memcpy(out_buffer + offset, init_data_section, header->data_size); + offset += header->data_size; } return offset; @@ -210,6 +193,26 @@ uint32_t chip32_binary_write( // DEBUG/UTILITY FUNCTIONS // ============================================================================ + +void chip32_binary_build_stats(const chip32_binary_header_t *header, chip32_binary_stats_t* out_stats) +{ + out_stats->const_size = header->const_size; + out_stats->bss_size = header->bss_size; + out_stats->code_size = header->code_size; + out_stats->data_size = header->data_size; + + out_stats->total_file_size = sizeof(chip32_binary_header_t) + + header->const_size + + header->code_size + + header->data_size; + + out_stats->total_rom_size = header->const_size + + header->code_size; + + out_stats->total_ram_size = header->bss_size; + +} + void chip32_binary_print_header(const chip32_binary_header_t* header) { if (!header) { @@ -229,11 +232,11 @@ void chip32_binary_print_header(const chip32_binary_header_t* header) printf(" (has init data)"); } printf("\n"); - printf("DATA section: %u bytes (ROM constants)\n", header->const_size); + printf("CONST section: %u bytes (ROM constants)\n", header->const_size); printf("BSS section: %u bytes (Total RAM: DV+DZ)\n", header->bss_size); printf("CODE section: %u bytes\n", header->code_size); printf("Entry point: 0x%08X\n", header->entry_point); - printf("Init data: %u bytes (DV values + DZ zeros)\n", header->init_data_size); + printf("Init data: %u bytes (DV values + DZ zeros)\n", header->data_size); printf("\n"); } @@ -244,13 +247,13 @@ void chip32_binary_print_stats(const chip32_binary_stats_t* stats) } printf("=== Chip32 Binary Statistics ===\n"); - printf("DATA section: %u bytes (ROM, initialized)\n", stats->const_size); - printf("BSS section: %u bytes (RAM, DV+DZ)\n", stats->bss_size); + printf("CONST section: %u bytes (ROM, initialized)\n", stats->const_size); + printf("BSS section: %u bytes (RAM, DZ)\n", stats->bss_size); printf("CODE section: %u bytes (ROM, executable)\n", stats->code_size); - printf("Init data: %u bytes (RAM initialization)\n", stats->init_data_size); + printf("Init data: %u bytes (RAM initialization)\n", stats->data_size); printf("---\n"); printf("File size: %u bytes\n", stats->total_file_size); printf("ROM usage: %u bytes (DATA + CODE)\n", stats->total_rom_size); printf("RAM usage: %u bytes (BSS)\n", stats->total_ram_size); printf("\n"); -} \ No newline at end of file +} diff --git a/core/chip32/chip32_binary_format.h b/core/chip32/chip32_binary_format.h index e316604..a1493cb 100644 --- a/core/chip32/chip32_binary_format.h +++ b/core/chip32/chip32_binary_format.h @@ -44,7 +44,7 @@ | Chip32 Header | <-- Début du fichier | (Magic, Versions, Sizes, Entry...) | +--------------------------------------+ -| CONST Section (DV) | <-- Données initialisées +| CONST Section (DV) | <-- Données initialisées | (Variables globales initialisées) | e.g., const int x = 5; +--------------------------------------+ | CODE Section | <-- Instructions du programme @@ -53,7 +53,6 @@ | DATA Section (Optional) | <-- Données pour l'initialisation de la RAM (copie de DV) | (Contient les valeurs initiales pour la RAM) +--------------------------------------+ - */ @@ -93,11 +92,11 @@ typedef struct { uint32_t magic; // Magic number "C32\0" uint16_t version; // Format version uint16_t flags; // Feature flags - uint32_t const_size; // Size of DATA section in bytes (ROM constants) + uint32_t const_size; // Size of CONST section in bytes (ROM constants) uint32_t bss_size; // Total RAM size (DV + DZ) uint32_t code_size; // Size of CODE section in bytes uint32_t entry_point; // Entry point offset in CODE section - uint32_t init_data_size; // Size of INIT DATA section (DV values + DZ zeros) + uint32_t data_size; // Size of INIT DATA section (DV values + DZ zeros) } chip32_binary_header_t; #pragma pack(pop) @@ -106,7 +105,7 @@ typedef struct { uint32_t const_size; // ROM constants uint32_t bss_size; // Total RAM needed uint32_t code_size; // Executable code - uint32_t init_data_size; // RAM initialization data + uint32_t data_size; // RAM initialization data uint32_t total_file_size; // File size on disk uint32_t total_rom_size; // DATA + CODE uint32_t total_ram_size; // RAM needed @@ -129,7 +128,7 @@ chip32_binary_error_t chip32_binary_load( uint32_t binary_size, uint8_t* ram, uint32_t ram_size, - chip32_binary_stats_t *out_stats + chip32_binary_header_t *header ); @@ -160,16 +159,16 @@ uint32_t chip32_binary_calculate_size(const chip32_binary_header_t* header); /** * Write a complete binary to memory * @param header Binary header - * @param data_section DATA section content (can be NULL if const_size is 0) + * @param const_section DATA section content (can be NULL if const_size is 0) * @param code_section CODE section content (can be NULL if code_size is 0) - * @param init_data_section INIT DATA section (can be NULL if init_data_size is 0) + * @param init_data_section INIT DATA section (can be NULL if data_size is 0) * @param out_buffer Output buffer (must be large enough) * @param buffer_size Size of output buffer * @return Number of bytes written, or 0 on error */ uint32_t chip32_binary_write( const chip32_binary_header_t* header, - const uint8_t* data_section, + const uint8_t* const_section, const uint8_t* code_section, const uint8_t* init_data_section, uint8_t* out_buffer, @@ -180,6 +179,8 @@ uint32_t chip32_binary_write( // DEBUG/UTILITY FUNCTIONS // ============================================================================ +void chip32_binary_build_stats(const chip32_binary_header_t *header, chip32_binary_stats_t* out_stats); + /** * Print binary header information * @param header Binary header diff --git a/core/chip32/chip32_machine.h b/core/chip32/chip32_machine.h index 79d5b93..2b0a503 100644 --- a/core/chip32/chip32_machine.h +++ b/core/chip32/chip32_machine.h @@ -12,6 +12,7 @@ #include #include #include +#include #include "chip32_assembler.h" #include "chip32_binary_format.h" @@ -28,24 +29,142 @@ public: chip32_result_t runResult{VM_OK}; std::string printOutput; chip32_ctx_t ctx; // Public pour accès aux registres dans les tests + std::vector ram; // RAM storage + std::vector program; + Chip32::Assembler assembler; + chip32_binary_header_t header; + Machine() { // Bind syscall handler to this instance m_syscallHandler = std::bind(&Machine::HandleSyscall, this, std::placeholders::_1, std::placeholders::_2); - m_ram.resize(1024); + ram.resize(1024); + } + + + bool BuildBinary(Assembler &assembler, std::vector &program, chip32_binary_stats_t &stats) + { + program.clear(); + + // ======================================================================== + // PHASE 1: Créer les sections temporaires + // ======================================================================== + std::vector constSection; // DC - ROM constants only + std::vector codeSection; // Executable code + std::vector initDataSection; // DV values + DZ zeros (RAM init) + + + chip32_binary_header_init(&header); + + Chip32::Instr instr; + if (assembler.GetMain(instr)) + { + header.entry_point = instr.addr; + } + // else: no main found, we try to start at address zero... + + for (auto instr : assembler) + { + // ==================================================================== + // CODE INSTRUCTIONS + // ==================================================================== + if (instr.isRomCode()) + { + // Ajouter l'opcode + codeSection.push_back(instr.code.opcode); + + // Ajouter les arguments compilés + std::copy(instr.compiledArgs.begin(), + instr.compiledArgs.end(), + std::back_inserter(codeSection)); + } + // ==================================================================== + // ROM DATA - Distinguer DC (vraies constantes) de DV init data + // ==================================================================== + else if (instr.isRomData && !instr.isRamData) + { + std::copy(instr.compiledArgs.begin(), + instr.compiledArgs.end(), + std::back_inserter(constSection)); + } + // ==================================================================== + // RAM VARIABLES + // ==================================================================== + else if (instr.isRamData) + { + + // ==================================================================== + // ZEROED DATA (DZ) + // ==================================================================== + if (instr.isZeroData) + { + header.bss_size += instr.dataLen; + } + // ==================================================================== + // INITIALIZED RAM VARIABLES (DV) + // ==================================================================== + else + { + // Ces données appartiennent à cette variable DV + initDataSection.insert(initDataSection.end(), + instr.compiledArgs.begin(), + instr.compiledArgs.end()); + } + } + } + + + // ======================================================================== + // PHASE 5: Créer le header binaire + // ======================================================================== + + header.const_size = static_cast(constSection.size()); + header.code_size = static_cast(codeSection.size()); + header.data_size = static_cast(initDataSection.size()); + + if (initDataSection.size() > 0) + { + header.flags |= CHIP32_FLAG_HAS_INIT_DATA; + } + + // ======================================================================== + // PHASE 6: Assembler le binaire final + // ======================================================================== + uint32_t totalSize = chip32_binary_calculate_size(&header); + program.resize(totalSize); + + uint32_t bytesWritten = chip32_binary_write( + &header, + constSection.empty() ? nullptr : constSection.data(), + codeSection.empty() ? nullptr : codeSection.data(), + initDataSection.empty() ? nullptr : initDataSection.data(), + program.data(), + static_cast(program.size()) + ); + + if (bytesWritten == 0 || bytesWritten != totalSize) + { + // Erreur lors de l'écriture + program.clear(); + return false; + } + + // ======================================================================== + // PHASE 7: Remplir les statistiques + // ======================================================================== + chip32_binary_build_stats(&header, &stats); + + return true; } // ======================================================================== // Méthode principale : Parse, Build, Execute // ======================================================================== - void QuickExecute(const std::string &assemblyCode) { - std::vector program; - Chip32::Assembler assembler; - Chip32::Result result; + chip32_binary_stats_t stats; // Parse parseResult = assembler.Parse(assemblyCode); @@ -56,24 +175,22 @@ public: } // Build binary with new format - buildResult = assembler.BuildBinary(program, result); + buildResult = BuildBinary(assembler, program, stats); + chip32_binary_print_stats(&stats); if (!buildResult) { std::cout << "Build error: " << assembler.GetLastError().ToString() << std::endl; return; } - - result.Print(); // Load binary using executable format - chip32_binary_stats_t stats; chip32_binary_error_t error = chip32_binary_load( &ctx, program.data(), static_cast(program.size()), - m_ram.data(), - static_cast(m_ram.size()), - &stats + ram.data(), + static_cast(ram.size()), + &header ); if (error != CHIP32_BIN_OK) { @@ -81,15 +198,14 @@ public: buildResult = false; return; } + + chip32_binary_build_stats(&header, &stats); + chip32_binary_print_stats(&stats); // Set syscall handler using wrapper ctx.syscall = SyscallWrapper; ctx.user_data = this; - // Initialize VM - chip32_initialize(&ctx); - - std::cout << "Starting execution at PC=0x" << std::hex << ctx.registers[PC] << std::dec << std::endl; @@ -249,77 +365,171 @@ public: } } - // ============================================================================ - // Fonction helper pour charger un binaire Chip32 dans la VM - // IMPORTANT: Le vecteur binary doit rester en vie pendant toute l'exécution - // car vm_ctx pointe directement dans ses données - // ============================================================================ - bool LoadBinaryIntoVM( - std::vector &binary, - chip32_ctx_t &vm_ctx, - uint8_t *ram_buffer, - uint32_t ram_size) + + // ======================================================================== + // Hexdump utilities + // ======================================================================== + + void HexDump(const uint8_t* data, uint32_t size, uint32_t base_addr = 0, const std::string& title = "") { - - if (err != CHIP32_BIN_OK) - { - std::cerr << "Binary load error: " << chip32_binary_error_string(err) << std::endl; - return false; + if (!title.empty()) { + std::cout << "\n=== " << title << " ===" << std::endl; } - // Afficher les informations du binaire (debug) - chip32_binary_print_header(&loaded.header); + const int bytes_per_line = 16; - // Vérifier que la RAM est suffisante - if (loaded.header.bss_size > ram_size) - { - std::cerr << "Insufficient RAM: need " << loaded.header.bss_size - << " bytes, have " << ram_size << " bytes" << std::endl; - return false; + for (uint32_t i = 0; i < size; i += bytes_per_line) { + // Adresse + std::cout << std::hex << std::setfill('0') << std::setw(8) + << (base_addr + i) << ": "; + + // Octets en hexadécimal + for (int j = 0; j < bytes_per_line; j++) { + if (i + j < size) { + std::cout << std::setw(2) << static_cast(data[i + j]) << " "; + } else { + std::cout << " "; + } + + // Séparateur au milieu + if (j == 7) { + std::cout << " "; + } + } + + std::cout << " |"; + + // Représentation ASCII + for (int j = 0; j < bytes_per_line && i + j < size; j++) { + uint8_t byte = data[i + j]; + if (byte >= 32 && byte <= 126) { + std::cout << static_cast(byte); + } else { + std::cout << '.'; + } + } + + std::cout << "|" << std::dec << std::endl; } - // ======================================================================== - // Configurer la VM - ROM pointe directement dans le binaire - // ======================================================================== - - // Pour la VM, on considère DATA + CODE comme une seule ROM - // DATA commence à l'offset après le header - // CODE suit immédiatement après DATA - - vm_ctx.rom.mem = const_cast(loaded.data_section); - vm_ctx.rom.addr = 0; - vm_ctx.rom.size = loaded.header.data_size + loaded.header.code_size; - - // ======================================================================== - // Initialiser la RAM avec INIT DATA section - // ======================================================================== - - // D'abord mettre toute la RAM à zéro - memset(ram_buffer, 0, ram_size); - - // Ensuite copier les données d'initialisation (DV values + DZ zeros) - if (loaded.header.init_data_size > 0) - { - uint32_t copied = chip32_binary_init_ram(&loaded, ram_buffer, ram_size); - std::cout << "Copied " << copied << " bytes of init data to RAM" << std::endl; - } - - // Configurer RAM dans la VM - vm_ctx.ram.mem = ram_buffer; - vm_ctx.ram.addr = 0x8000; // Bit 31 = 1 pour RAM - vm_ctx.ram.size = ram_size; - - // ======================================================================== - // Configurer le point d'entrée - // ======================================================================== - // Le PC doit pointer dans la section CODE, qui commence après DATA - vm_ctx.registers[PC] = loaded.header.data_size + loaded.header.entry_point; - - return true; + std::cout << std::endl; } + void DumpRom() + { + if (ctx.rom.mem == nullptr || ctx.rom.size == 0) { + std::cout << "ROM is empty or not loaded" << std::endl; + return; + } + + std::cout << "\n" << std::string(80, '=') << std::endl; + std::cout << "ROM DUMP (Total: " << ctx.rom.size << " bytes)" << std::endl; + std::cout << std::string(80, '=') << std::endl; + + uint32_t offset = 0; + + // Dump CONST section + if (header.const_size > 0) { + HexDump(ctx.rom.mem + offset, header.const_size, offset, + "CONST Section (" + std::to_string(header.const_size) + " bytes)"); + offset += header.const_size; + } + + // Dump CODE section + if (header.code_size > 0) { + HexDump(ctx.rom.mem + offset, header.code_size, offset, + "CODE Section (" + std::to_string(header.code_size) + " bytes)"); + offset += header.code_size; + } + + // Dump INIT DATA section + if (header.data_size > 0) { + HexDump(ctx.rom.mem + offset, header.data_size, offset, + "INIT DATA Section (" + std::to_string(header.data_size) + " bytes)"); + } + } + + void DumpRam() + { + if (ctx.ram.mem == nullptr || ctx.ram.size == 0) { + std::cout << "RAM is empty or not allocated" << std::endl; + return; + } + + std::cout << "\n" << std::string(80, '=') << std::endl; + std::cout << "RAM DUMP (Total: " << ctx.ram.size << " bytes)" << std::endl; + std::cout << std::string(80, '=') << std::endl; + + uint32_t offset = 0; + + // Dump DATA section (initialized variables) + if (header.data_size > 0) { + HexDump(ctx.ram.mem + offset, header.data_size, 0x80000000 + offset, + "DATA Section (DV - " + std::to_string(header.data_size) + " bytes)"); + offset += header.data_size; + } + + // Dump BSS section (zero-initialized variables) + if (header.bss_size > 0) { + HexDump(ctx.ram.mem + offset, header.bss_size, 0x80000000 + offset, + "BSS Section (DZ - " + std::to_string(header.bss_size) + " bytes)"); + offset += header.bss_size; + } + + // Dump remaining RAM (heap/stack area) + uint32_t remaining = ctx.ram.size - offset; + if (remaining > 0) { + // Limiter l'affichage à 256 bytes pour le reste + uint32_t display_size = std::min(remaining, 256u); + HexDump(ctx.ram.mem + offset, display_size, 0x80000000 + offset, + "Heap/Stack area (showing first " + std::to_string(display_size) + + " of " + std::to_string(remaining) + " bytes)"); + + if (remaining > display_size) { + std::cout << "... (" << (remaining - display_size) + << " more bytes not shown)" << std::endl; + } + } + } + + void DumpMemory() + { + DumpRom(); + DumpRam(); + } + + // Variante pour dumper une région spécifique de RAM + void DumpRamRegion(uint32_t start_addr, uint32_t size) + { + // Enlever le bit RAM si présent + uint32_t addr = start_addr & 0x7FFFFFFF; + + if (ctx.ram.mem == nullptr || ctx.ram.size == 0) { + std::cout << "RAM is not allocated" << std::endl; + return; + } + + if (addr >= ctx.ram.size) { + std::cout << "Start address 0x" << std::hex << addr + << " is out of RAM bounds (size: 0x" << ctx.ram.size + << ")" << std::dec << std::endl; + return; + } + + uint32_t actual_size = std::min(size, ctx.ram.size - addr); + + std::cout << "\n" << std::string(80, '=') << std::endl; + std::cout << "RAM Region Dump" << std::endl; + std::cout << "Address: 0x" << std::hex << (0x80000000 | addr) + << " - 0x" << (0x80000000 | (addr + actual_size - 1)) + << std::dec << std::endl; + std::cout << std::string(80, '=') << std::endl; + + HexDump(ctx.ram.mem + addr, actual_size, 0x80000000 | addr, ""); + } + + private: - std::vector m_ram; // RAM storage std::function m_syscallHandler; // Wrapper statique qui récupère l'instance et appelle la méthode membre diff --git a/core/chip32/chip32_vm.c b/core/chip32/chip32_vm.c index 3b2fb1a..f9ea032 100644 --- a/core/chip32/chip32_vm.c +++ b/core/chip32/chip32_vm.c @@ -102,10 +102,8 @@ static uint32_t pop(chip32_ctx_t *ctx) { // ======================================================================================= void chip32_initialize(chip32_ctx_t *ctx) { - memset(ctx->ram.mem, 0, ctx->ram.size); memset(ctx->registers, 0, REGISTER_COUNT * sizeof(uint32_t)); ctx->instr_count = 0; - ctx->registers[SP] = ctx->ram.size; } chip32_result_t chip32_run(chip32_ctx_t *ctx) @@ -225,29 +223,43 @@ chip32_result_t chip32_step(chip32_ctx_t *ctx) _CHECK_REGISTER_VALID(reg2) // address is located in reg1 reg uint32_t addr = ctx->registers[reg1]; - bool isRam = addr & 0x80000000; - addr &= 0xFFFF; // mask the RAM/ROM bit, ensure 16-bit addressing + bool isRam = addr & CHIP32_RAM_OFFSET; + addr &= ~CHIP32_RAM_OFFSET; // mask the RAM/ROM bit, ensure 31-bit addressing if (isRam) { _CHECK_RAM_ADDR_VALID(addr) memcpy(&ctx->ram.mem[addr], &ctx->registers[reg2], size); } else { - _CHECK_ROM_ADDR_VALID(addr) - memcpy(&ctx->rom.mem[addr], &ctx->registers[reg2], size); + // Invalid, cannot write in memory! + return VM_ERR_INVALID_ADDRESS; } break; } case OP_LOAD: { - const uint8_t reg1 = _NEXT_BYTE; - const uint8_t reg2 = _NEXT_BYTE; - const uint8_t size = _NEXT_BYTE; - _CHECK_REGISTER_VALID(reg1) - _CHECK_REGISTER_VALID(reg2) + uint8_t reg1 = _NEXT_BYTE; + uint32_t addr = 0; + uint8_t size = 0; + + // Variable based + if (reg1 & 0x80) + { + reg1 &= 0x7F; + addr = _NEXT_INT(ctx); + size = _NEXT_BYTE; + } // address is located in reg2 reg - uint32_t addr = ctx->registers[reg2]; - bool isRam = addr & 0x80000000; - addr &= 0xFFFF; // mask the RAM/ROM bit, ensure 16-bit addressing + else + { + const uint8_t reg2 = _NEXT_BYTE; + const uint8_t size = _NEXT_BYTE; + _CHECK_REGISTER_VALID(reg1) + _CHECK_REGISTER_VALID(reg2) + addr = ctx->registers[reg2]; + } + + bool isRam = addr & CHIP32_RAM_OFFSET; + addr &= ~CHIP32_RAM_OFFSET; // mask the RAM/ROM bit, ensure 31-bit addressing if (isRam) { _CHECK_RAM_ADDR_VALID(addr) memcpy(&ctx->registers[reg1], &ctx->ram.mem[addr], size); diff --git a/core/chip32/chip32_vm.h b/core/chip32/chip32_vm.h index fc31078..5757143 100644 --- a/core/chip32/chip32_vm.h +++ b/core/chip32/chip32_vm.h @@ -35,6 +35,8 @@ extern "C" { #include +#define CHIP32_RAM_OFFSET 0x80000000 + // General form: instruction destination, source // coded as: instr dst, src typedef enum diff --git a/core/tests/CMakeLists.txt b/core/tests/CMakeLists.txt index c312eb9..d3cb6bb 100644 --- a/core/tests/CMakeLists.txt +++ b/core/tests/CMakeLists.txt @@ -5,14 +5,32 @@ project(core_tests LANGUAGES CXX C) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) +# ============================================================================ +# Téléchargement et configuration de Catch2 v3 +# ============================================================================ +include(FetchContent) + +FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v3.7.1 # Dernière version stable de Catch2 v3 + GIT_SHALLOW TRUE +) + +FetchContent_MakeAvailable(Catch2) + +# Ajouter le module Catch2 pour la découverte automatique des tests +list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) + +# ============================================================================ +# Exécutable de tests +# ============================================================================ add_executable(${PROJECT_NAME} - main.cpp test_parser.cpp test_vm.cpp test_ast.cpp test_print_node.cpp - ../story-manager/src/nodes/base_node.cpp ../story-manager/src/nodes/branch_node.cpp ../story-manager/src/nodes/print_node.cpp @@ -25,12 +43,23 @@ add_executable(${PROJECT_NAME} ../chip32/chip32_binary_format.c ) -target_include_directories(${PROJECT_NAME} PRIVATE +target_include_directories(${PROJECT_NAME} PRIVATE ../chip32 ../story-manager/src ../story-manager/src/nodes ../story-manager/src/compiler ../story-manager/interfaces ../../shared - ) + +# ============================================================================ +# Linkage avec Catch2 +# ============================================================================ +target_link_libraries(${PROJECT_NAME} PRIVATE Catch2::Catch2WithMain) + +# ============================================================================ +# Découverte automatique des tests (optionnel mais recommandé) +# ============================================================================ +include(CTest) +include(Catch) +catch_discover_tests(${PROJECT_NAME}) \ No newline at end of file diff --git a/core/tests/catch.hpp b/core/tests/catch.hpp deleted file mode 100644 index a593e2f..0000000 --- a/core/tests/catch.hpp +++ /dev/null @@ -1,17966 +0,0 @@ -/* - * Catch v2.13.8 - * Generated: 2022-01-03 21:20:09.589503 - * ---------------------------------------------------------- - * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED -#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED -// start catch.hpp - - -#define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 13 -#define CATCH_VERSION_PATCH 8 - -#ifdef __clang__ -# pragma clang system_header -#elif defined __GNUC__ -# pragma GCC system_header -#endif - -// start catch_suppress_warnings.h - -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(push) -# pragma warning(disable: 161 1682) -# else // __ICC -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wswitch-enum" -# pragma clang diagnostic ignored "-Wcovered-switch-default" -# endif -#elif defined __GNUC__ - // Because REQUIREs trigger GCC's -Wparentheses, and because still - // supported version of g++ have only buggy support for _Pragmas, - // Wparentheses have to be suppressed globally. -# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details - -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-variable" -# pragma GCC diagnostic ignored "-Wpadded" -#endif -// end catch_suppress_warnings.h -#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) -# define CATCH_IMPL -# define CATCH_CONFIG_ALL_PARTS -#endif - -// In the impl file, we want to have access to all parts of the headers -// Can also be used to sanely support PCHs -#if defined(CATCH_CONFIG_ALL_PARTS) -# define CATCH_CONFIG_EXTERNAL_INTERFACES -# if defined(CATCH_CONFIG_DISABLE_MATCHERS) -# undef CATCH_CONFIG_DISABLE_MATCHERS -# endif -# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) -# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER -# endif -#endif - -#if !defined(CATCH_CONFIG_IMPL_ONLY) -// start catch_platform.h - -// See e.g.: -// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html -#ifdef __APPLE__ -# include -# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ - (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) -# define CATCH_PLATFORM_MAC -# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) -# define CATCH_PLATFORM_IPHONE -# endif - -#elif defined(linux) || defined(__linux) || defined(__linux__) -# define CATCH_PLATFORM_LINUX - -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) -# define CATCH_PLATFORM_WINDOWS -#endif - -// end catch_platform.h - -#ifdef CATCH_IMPL -# ifndef CLARA_CONFIG_MAIN -# define CLARA_CONFIG_MAIN_NOT_DEFINED -# define CLARA_CONFIG_MAIN -# endif -#endif - -// start catch_user_interfaces.h - -namespace Catch { - unsigned int rngSeed(); -} - -// end catch_user_interfaces.h -// start catch_tag_alias_autoregistrar.h - -// start catch_common.h - -// start catch_compiler_capabilities.h - -// Detect a number of compiler features - by compiler -// The following features are defined: -// -// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? -// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? -// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? -// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? -// **************** -// Note to maintainers: if new toggles are added please document them -// in configuration.md, too -// **************** - -// In general each macro has a _NO_ form -// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. -// Many features, at point of detection, define an _INTERNAL_ macro, so they -// can be combined, en-mass, with the _NO_ forms later. - -#ifdef __cplusplus - -# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) -# define CATCH_CPP14_OR_GREATER -# endif - -# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define CATCH_CPP17_OR_GREATER -# endif - -#endif - -// Only GCC compiler should be used in this block, so other compilers trying to -// mask themselves as GCC should be ignored. -#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) - -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) - -#endif - -#if defined(__clang__) - -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) - -// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug -// which results in calls to destructors being emitted for each temporary, -// without a matching initialization. In practice, this can result in something -// like `std::string::~string` being called on an uninitialized value. -// -// For example, this code will likely segfault under IBM XL: -// ``` -// REQUIRE(std::string("12") + "34" == "1234") -// ``` -// -// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. -# if !defined(__ibmxl__) && !defined(__CUDACC__) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ -# endif - -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ - _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") - -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) - -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) - -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) - -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) - -#endif // __clang__ - -//////////////////////////////////////////////////////////////////////////////// -// Assume that non-Windows platforms support posix signals by default -#if !defined(CATCH_PLATFORM_WINDOWS) - #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS -#endif - -//////////////////////////////////////////////////////////////////////////////// -// We know some environments not to support full POSIX signals -#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) - #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -#endif - -#ifdef __OS400__ -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -# define CATCH_CONFIG_COLOUR_NONE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Android somehow still does not support std::to_string -#if defined(__ANDROID__) -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING -# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Not all Windows environments support SEH properly -#if defined(__MINGW32__) -# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH -#endif - -//////////////////////////////////////////////////////////////////////////////// -// PS4 -#if defined(__ORBIS__) -# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Cygwin -#ifdef __CYGWIN__ - -// Required for some versions of Cygwin to declare gettimeofday -// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin -# define _BSD_SOURCE -// some versions of cygwin (most) do not support std::to_string. Use the libstd check. -// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 -# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ - && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) - -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING - -# endif -#endif // __CYGWIN__ - -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#if defined(_MSC_VER) - -// Universal Windows platform does not support SEH -// Or console colours (or console at all...) -# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) -# define CATCH_CONFIG_COLOUR_NONE -# else -# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH -# endif - -# if !defined(__clang__) // Handle Clang masquerading for msvc - -// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ -// _MSVC_TRADITIONAL == 0 means new conformant preprocessor -// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor -# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) -# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -# endif // MSVC_TRADITIONAL - -// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop` -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) -# endif // __clang__ - -#endif // _MSC_VER - -#if defined(_REENTRANT) || defined(_MSC_VER) -// Enable async processing, as -pthread is specified or no additional linking is required -# define CATCH_INTERNAL_CONFIG_USE_ASYNC -#endif // _MSC_VER - -//////////////////////////////////////////////////////////////////////////////// -// Check if we are compiled with -fno-exceptions or equivalent -#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) -# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED -#endif - -//////////////////////////////////////////////////////////////////////////////// -// DJGPP -#ifdef __DJGPP__ -# define CATCH_INTERNAL_CONFIG_NO_WCHAR -#endif // __DJGPP__ - -//////////////////////////////////////////////////////////////////////////////// -// Embarcadero C++Build -#if defined(__BORLANDC__) - #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN -#endif - -//////////////////////////////////////////////////////////////////////////////// - -// Use of __COUNTER__ is suppressed during code analysis in -// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly -// handled by it. -// Otherwise all supported compilers support COUNTER macro, -// but user still might want to turn it off -#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) - #define CATCH_INTERNAL_CONFIG_COUNTER -#endif - -//////////////////////////////////////////////////////////////////////////////// - -// RTX is a special version of Windows that is real time. -// This means that it is detected as Windows, but does not provide -// the same set of capabilities as real Windows does. -#if defined(UNDER_RTSS) || defined(RTX64_BUILD) - #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH - #define CATCH_INTERNAL_CONFIG_NO_ASYNC - #define CATCH_CONFIG_COLOUR_NONE -#endif - -#if !defined(_GLIBCXX_USE_C99_MATH_TR1) -#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER -#endif - -// Various stdlib support checks that require __has_include -#if defined(__has_include) - // Check if string_view is available and usable - #if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW - #endif - - // Check if optional is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if byte is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # include - # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) - # define CATCH_INTERNAL_CONFIG_CPP17_BYTE - # endif - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if variant is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # if defined(__clang__) && (__clang_major__ < 8) - // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 - // fix should be in clang 8, workaround in libstdc++ 8.2 - # include - # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # define CATCH_CONFIG_NO_CPP17_VARIANT - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__clang__) && (__clang_major__ < 8) - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // defined(__has_include) - -#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) -# define CATCH_CONFIG_COUNTER -#endif -#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) -# define CATCH_CONFIG_WINDOWS_SEH -#endif -// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. -#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_CONFIG_POSIX_SIGNALS -#endif -// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. -#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) -# define CATCH_CONFIG_WCHAR -#endif - -#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) -# define CATCH_CONFIG_CPP11_TO_STRING -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) -# define CATCH_CONFIG_CPP17_OPTIONAL -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) -# define CATCH_CONFIG_CPP17_STRING_VIEW -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) -# define CATCH_CONFIG_CPP17_VARIANT -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) -# define CATCH_CONFIG_CPP17_BYTE -#endif - -#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) -# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE -#endif - -#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) -# define CATCH_CONFIG_NEW_CAPTURE -#endif - -#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -# define CATCH_CONFIG_DISABLE_EXCEPTIONS -#endif - -#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) -# define CATCH_CONFIG_POLYFILL_ISNAN -#endif - -#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) -# define CATCH_CONFIG_USE_ASYNC -#endif - -#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) -# define CATCH_CONFIG_ANDROID_LOGWRITE -#endif - -#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) -# define CATCH_CONFIG_GLOBAL_NEXTAFTER -#endif - -// Even if we do not think the compiler has that warning, we still have -// to provide a macro that can be used by the code. -#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION -#endif -#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS -#endif - -// The goal of this macro is to avoid evaluation of the arguments, but -// still have the compiler warn on problems inside... -#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) -#endif - -#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#elif defined(__clang__) && (__clang_major__ < 5) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -#define CATCH_TRY if ((true)) -#define CATCH_CATCH_ALL if ((false)) -#define CATCH_CATCH_ANON(type) if ((false)) -#else -#define CATCH_TRY try -#define CATCH_CATCH_ALL catch (...) -#define CATCH_CATCH_ANON(type) catch (type) -#endif - -#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) -#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#endif - -// end catch_compiler_capabilities.h -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) -#else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) -#endif - -#include -#include -#include - -// We need a dummy global operator<< so we can bring it into Catch namespace later -struct Catch_global_namespace_dummy {}; -std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); - -namespace Catch { - - struct CaseSensitive { enum Choice { - Yes, - No - }; }; - - class NonCopyable { - NonCopyable( NonCopyable const& ) = delete; - NonCopyable( NonCopyable && ) = delete; - NonCopyable& operator = ( NonCopyable const& ) = delete; - NonCopyable& operator = ( NonCopyable && ) = delete; - - protected: - NonCopyable(); - virtual ~NonCopyable(); - }; - - struct SourceLineInfo { - - SourceLineInfo() = delete; - SourceLineInfo( char const* _file, std::size_t _line ) noexcept - : file( _file ), - line( _line ) - {} - - SourceLineInfo( SourceLineInfo const& other ) = default; - SourceLineInfo& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo( SourceLineInfo&& ) noexcept = default; - SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; - - bool empty() const noexcept { return file[0] == '\0'; } - bool operator == ( SourceLineInfo const& other ) const noexcept; - bool operator < ( SourceLineInfo const& other ) const noexcept; - - char const* file; - std::size_t line; - }; - - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); - - // Bring in operator<< from global namespace into Catch namespace - // This is necessary because the overload of operator<< above makes - // lookup stop at namespace Catch - using ::operator<<; - - // Use this in variadic streaming macros to allow - // >> +StreamEndStop - // as well as - // >> stuff +StreamEndStop - struct StreamEndStop { - std::string operator+() const; - }; - template - T const& operator + ( T const& value, StreamEndStop ) { - return value; - } -} - -#define CATCH_INTERNAL_LINEINFO \ - ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) - -// end catch_common.h -namespace Catch { - - struct RegistrarForTagAliases { - RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - }; - -} // end namespace Catch - -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION - -// end catch_tag_alias_autoregistrar.h -// start catch_test_registry.h - -// start catch_interfaces_testcase.h - -#include - -namespace Catch { - - class TestSpec; - - struct ITestInvoker { - virtual void invoke () const = 0; - virtual ~ITestInvoker(); - }; - - class TestCase; - struct IConfig; - - struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; - - bool isThrowSafe( TestCase const& testCase, IConfig const& config ); - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); - -} - -// end catch_interfaces_testcase.h -// start catch_stringref.h - -#include -#include -#include -#include - -namespace Catch { - - /// A non-owning string class (similar to the forthcoming std::string_view) - /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. - class StringRef { - public: - using size_type = std::size_t; - using const_iterator = const char*; - - private: - static constexpr char const* const s_empty = ""; - - char const* m_start = s_empty; - size_type m_size = 0; - - public: // construction - constexpr StringRef() noexcept = default; - - StringRef( char const* rawChars ) noexcept; - - constexpr StringRef( char const* rawChars, size_type size ) noexcept - : m_start( rawChars ), - m_size( size ) - {} - - StringRef( std::string const& stdString ) noexcept - : m_start( stdString.c_str() ), - m_size( stdString.size() ) - {} - - explicit operator std::string() const { - return std::string(m_start, m_size); - } - - public: // operators - auto operator == ( StringRef const& other ) const noexcept -> bool; - auto operator != (StringRef const& other) const noexcept -> bool { - return !(*this == other); - } - - auto operator[] ( size_type index ) const noexcept -> char { - assert(index < m_size); - return m_start[index]; - } - - public: // named queries - constexpr auto empty() const noexcept -> bool { - return m_size == 0; - } - constexpr auto size() const noexcept -> size_type { - return m_size; - } - - // Returns the current start pointer. If the StringRef is not - // null-terminated, throws std::domain_exception - auto c_str() const -> char const*; - - public: // substrings and searches - // Returns a substring of [start, start + length). - // If start + length > size(), then the substring is [start, size()). - // If start > size(), then the substring is empty. - auto substr( size_type start, size_type length ) const noexcept -> StringRef; - - // Returns the current start pointer. May not be null-terminated. - auto data() const noexcept -> char const*; - - constexpr auto isNullTerminated() const noexcept -> bool { - return m_start[m_size] == '\0'; - } - - public: // iterators - constexpr const_iterator begin() const { return m_start; } - constexpr const_iterator end() const { return m_start + m_size; } - }; - - auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; - auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; - - constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { - return StringRef( rawChars, size ); - } -} // namespace Catch - -constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { - return Catch::StringRef( rawChars, size ); -} - -// end catch_stringref.h -// start catch_preprocessor.hpp - - -#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ -#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) - -#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ -// MSVC needs more evaluations -#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) -#else -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) -#endif - -#define CATCH_REC_END(...) -#define CATCH_REC_OUT - -#define CATCH_EMPTY() -#define CATCH_DEFER(id) id CATCH_EMPTY() - -#define CATCH_REC_GET_END2() 0, CATCH_REC_END -#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 -#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 -#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT -#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) -#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) - -#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) - -#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) - -// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, -// and passes userdata as the first parameter to each invocation, -// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) -#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) -#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ -#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ -#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF -#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) -#else -// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF -#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) -#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) -#endif - -#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ -#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) - -#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) - -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) -#else -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) -#endif - -#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ - CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) - -#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) -#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) -#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) -#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) -#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) -#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) -#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) -#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) -#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) -#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) -#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) - -#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N - -#define INTERNAL_CATCH_TYPE_GEN\ - template struct TypeList {};\ - template\ - constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ - template class...> struct TemplateTypeList{};\ - template class...Cs>\ - constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ - template\ - struct append;\ - template\ - struct rewrap;\ - template class, typename...>\ - struct create;\ - template class, typename>\ - struct convert;\ - \ - template \ - struct append { using type = T; };\ - template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ - struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ - template< template class L1, typename...E1, typename...Rest>\ - struct append, TypeList, Rest...> { using type = L1; };\ - \ - template< template class Container, template class List, typename...elems>\ - struct rewrap, List> { using type = TypeList>; };\ - template< template class Container, template class List, class...Elems, typename...Elements>\ - struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ - \ - template