From 4bfe9fa4e4e55d9c88dcfe35e13a4395eb1f94a7 Mon Sep 17 00:00:00 2001 From: AshFungor Date: Mon, 21 Apr 2025 00:37:10 +0300 Subject: [PATCH 01/25] added build script and conan recipe --- .clang-format | 27 ++++ .flake8 | 24 ++++ .gitignore | 92 ++++++++++++- CMakeLists.txt | 1 - CONTRIBUTION.md | 2 +- conanfile.py | 31 +++++ scripts/build.py | 334 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 506 insertions(+), 5 deletions(-) create mode 100644 .clang-format create mode 100644 .flake8 create mode 100644 conanfile.py create mode 100755 scripts/build.py diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..b9c7e0e6 --- /dev/null +++ b/.clang-format @@ -0,0 +1,27 @@ +BasedOnStyle: google + +# Ident namespaces with 4 spaces (tab) +NamespaceIndentation: All +IndentWidth: 4 + +# Pointers and references are aligned to left: Type* or Type& +PointerAlignment: Left + +# Transfer closing brackets to next line +BinPackParameters: false +AllowAllParametersOfDeclarationOnNextLine: false +PenaltyReturnTypeOnItsOwnLine: 0 + +# Move access modifiers (public, private) at +# the same indention level as class keyword +AccessModifierOffset: -4 + +# Allow empty lines for includes +SortIncludes: true +IncludeBlocks: Preserve + +# Expand line columns' limit +ColumnLimit: 150 + +# Avoid short functions +AllowShortFunctionsOnASingleLine: None \ No newline at end of file diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..5daec749 --- /dev/null +++ b/.flake8 @@ -0,0 +1,24 @@ +[flake8] + +### Rules ### +# F401 - module imported but unused +# F403 - ‘from module import *’ used; unable to detect undefined names +exclude = + __init__.py::F401,F403 + +# E124 - closing bracket does not match visual indentation +ignore = + E124 + + +### File patterns ### +exclude = + .git, + __pycache__ + +filename = + *.py + +### Options ### +max-line-length = 150 +max-doc-length = 150 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9e12b3b3..45f19244 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,92 @@ +# Build directory +build/ + +# Binaries +*.o +*.os +*.so +*.obj +*.bc +*.pyc +*.dblite +*.pdb +*.lib +*.config +*.creator +*.creator.user +*.files +*.includes +*.idb +*.exp + +# Other stuff +*.log + +# C++ objects and libs +*.slo +*.lo +*.o +*.a +*.la +*.lai +*.so +*.so.* +*.dll +*.dylib + +# Qt-es +object_script.*.Release +object_script.*.Debug +*_plugin_import.cpp +/.qmake.cache +/.qmake.stash +*.pro.user +*.pro.user.* +*.qbs.user +*.qbs.user.* +*.moc +moc_*.cpp +moc_*.h +qrc_*.cpp +ui_*.h +*.qmlc +*.jsc +Makefile* +*build-* +*.qm +*.prl + +# Qt unit tests +target_wrapper.* + +# QtCreator +*.autosave + +# QtCreator Qml +*.qmlproject.user +*.qmlproject.user.* + +# QtCreator CMake +CMakeLists.txt.user* + +# QtCreator 4.8< compilation database +compile_commands.json + +# QtCreator local machine specific files for imported projects +*creator.user* +*_qmlcache.qrc + +# Auto-generated Conan Presets +CMakeUserPresets* + +# Clang language server cache +.cache + +# JetBrains IDE settings .idea + +# VS Code settings .vscode -.cache -compile_commands.json -build + +# Distribution packages dist/** diff --git a/CMakeLists.txt b/CMakeLists.txt index 67707944..fbdd4deb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,6 @@ project(HyperCPU CXX) set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(EXPORT_COMPILE_COMMANDS ON) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(ROOT_DIR ${HyperCPU_SOURCE_DIR}) diff --git a/CONTRIBUTION.md b/CONTRIBUTION.md index 9311d8fc..1f1a5119 100644 --- a/CONTRIBUTION.md +++ b/CONTRIBUTION.md @@ -9,4 +9,4 @@ ## For other contributors -1. Almost the same thing as for collabolators, BUT - you have to fork the repository, and work there. Notice, that you probably have to disable GitHub Actions, or connect the self-hosted runner (no guarantee that HyperCPU workflows will execute successfully on custom runner). When you are ready to send your changes - make a PR - and there goes the same cycle, until the PR gets merged:) \ No newline at end of file +1. Almost the same thing as for collaborators, BUT - you have to fork the repository, and work there. Notice, that you probably have to disable GitHub Actions, or connect the self-hosted runner (no guarantee that HyperCPU workflows will execute successfully on custom runner). When you are ready to send your changes - make a PR - and there goes the same cycle, until the PR gets merged:) \ No newline at end of file diff --git a/conanfile.py b/conanfile.py new file mode 100644 index 00000000..555b4d91 --- /dev/null +++ b/conanfile.py @@ -0,0 +1,31 @@ +from typing import Self, List +from functools import lru_cache + +from conan import ConanFile +from conan.tools.cmake import cmake_layout + + +class HyperCPU(ConanFile): + generators: List[str] = ['CMakeToolchain', 'CMakeDeps'] + settings: List[str] = ['os', 'compiler', 'build_type', 'arch'] + + def __init__(self: Self, display_name: str = '') -> None: + self.name = 'HyperCPU' + + self.__requirements = { + 'gtest': '1.14.0', + 'benchmark': '1.9.1', + 'abseil': '20250127.0', + 'libbacktrace': 'cci.20240730', + 'argparse': '3.2', + 'eternal': '1.0.1' + } + super().__init__(display_name) + + @lru_cache + def requirements(self: Self) -> None: + for req, version in self.__requirements.items(): + self.requires(f'{req}/{version}') + + def layout(self): + cmake_layout(self) diff --git a/scripts/build.py b/scripts/build.py new file mode 100755 index 00000000..d57f82cf --- /dev/null +++ b/scripts/build.py @@ -0,0 +1,334 @@ +#!/usr/bin/env python3 + +import os +import sys +import json +import errno +import shutil +import typing +import inspect +import logging +import argparse +import subprocess +import multiprocessing + +from pathlib import Path +from collections import OrderedDict +from dataclasses import dataclass, field +from typing import Callable, Iterable, Union, Tuple, Collection + +from conan.api.subapi.profiles import Profile +from conan.api.conan_api import ConanAPI, ConanException +# this is not part of external API, but those formatters are really cool +from conan.cli.printers.graph import print_graph_basic +from conan.cli.printers import print_profiles + + +logger = logging.getLogger(__file__ if '__file__' in vars() else 'build.helper') + + +DESCRIPTION = ''' + CLI build helper. +''' + + +@dataclass +class Config: + build_directory: Path = field(default_factory=lambda: Path.cwd().joinpath('build')) + build_configs: Iterable[str] = field(default_factory=lambda: ['Debug']) + cores: int = multiprocessing.cpu_count() + generator: str = 'Ninja' + # Conan-specific + # build profile specifies target environment + build_profile: str = 'default' + # host profile describes host environment (where build happens) + host_profile: str = 'default' + + +def routine(priority: int) -> Callable: + def factory(f: Callable) -> Callable: + f.priority = priority + f.is_routine = True + return f + + return factory + + +class Routines: + CMAKE_USER_PRESETS = Path.cwd() / 'CMakeUserPresets.json' + + def __init__(self, params: Config): + self.__params = params + + def routines(self) -> Iterable[Tuple[str, Callable]]: + def key(pair): + _, member = pair + return member.priority if hasattr(member, 'priority') else 0 + + members = inspect.getmembers(self, predicate=inspect.ismethod) + for name, method in sorted(members, key=key, reverse=True): + if hasattr(method, 'is_routine'): + yield name, method + + @routine(5) + def remove(self): + for config in self.__params.build_configs: + directory = self.__params.build_directory.joinpath(config) + if directory.is_dir(): + logger.info(f'removing directory for CMake build config "{config}"') + shutil.rmtree(directory) + else: + logger.error(f'build directory for config "{config}" was not found or was not a directory') + + @routine(4) + def conan_install(self): + api = ConanAPI() + + logger.debug(f'detected profiles: ' + ', '.join(api.profiles.list())) + logger.info( + 'please notice, this script modifies "build_type" in incoming profile, to match running params' + ) + remotes = api.remotes.list() + + for config in self.__params.build_configs: + self._conan_install_for_config(config, api, remotes) + + @routine(3) + def configure(self): + if not self.__params.build_directory.is_dir(): + self.__params.build_directory.mkdir() + + use_presets = self.CMAKE_USER_PRESETS.is_file() + if use_presets: + logger.info('found CMake presets') + presets = self._inspect_cmake_presets(self.CMAKE_USER_PRESETS) + + # make sure that presets are enabled for all possible cmake invocations (configure, build, test) + preset_types = [key for key in presets if key.endswith('Presets')] + + presets_available = {} + for config in self.__params.build_configs: + presets_available[config] = all(f'conan-{config.lower()}' in presets[key] for key in preset_types) + + logger.info('configuring for CMake build configs: ' + ', '.join(self.__params.build_configs)) + + for config in self.__params.build_configs: + source = Path.cwd() + binary = self.__params.build_directory.joinpath(config) + + command = [ + 'cmake', + '--preset', f'conan-{config.lower()}', + '-G', self.__params.generator, + '-B', str(binary), + '-S', str(source), + self._decorate_cmake_variable('CMAKE_EXPORT_COMPILE_COMMANDS', 'ON', 'BOOL'), + self._decorate_cmake_variable('CMAKE_BUILD_TYPE', config) + ] if use_presets else [ + 'cmake', + '-G', self.__params.generator, + '-B', str(binary), + '-S', str(source), + self._decorate_cmake_variable('CMAKE_EXPORT_COMPILE_COMMANDS', 'ON', 'BOOL'), + self._decorate_cmake_variable('CMAKE_BUILD_TYPE', config) + ] + + logger.info(f'running configure command for CMake config {config}') + self._run(command) + + @routine(2) + def build(self): + if not self.__params.build_directory.is_dir(): + raise FileNotFoundError(f'build directory \'{self.__params.build_directory}\' was not found') + + logger.info(f'using {self.__params.cores} threads') + for config in self.__params.build_configs: + directory = self.__params.build_directory.joinpath(config) + logger.info(f'building for CMake configuration \'{config}\'') + self._run([ + 'cmake', + '--build', str(directory), + '--parallel', str(self.__params.cores) + ]) + + @routine(1) + def symlink_compile_commands(self: 'Routines') -> None: + if 'Debug' in self.__params.build_configs: + path = self.__params.build_directory.joinpath('Debug').joinpath('compile_commands.json') + logger.info(f'creating symlink for path \'{path}\'') + + if 'Release' in self.__params.build_configs: + path = self.__params.build_directory.joinpath('Release').joinpath('compile_commands.json') + logger.info(f'creating symlink for path \'{path}\'') + + if path is None: + raise ValueError('no supported CMake configs detected') + + symlink = Path.cwd().joinpath('compile_commands.json') + if symlink.is_symlink(): + symlink.unlink() + + Path.cwd().joinpath('compile_commands.json').symlink_to(path) + + def _conan_install_for_config( + self, + config: str, + api: ConanAPI, + remotes: list + ): + # fetch requested profiles + try: + # all profile tweaks should be done here, for some reason later modifications to Profile object + # have no effect + build_profile = api.profiles.get_profile([self.__params.build_profile], settings=[f'build_type={config}']) + host_profile = api.profiles.get_profile([self.__params.host_profile], settings=[f'build_type={config}']) + print_profiles(host_profile, build_profile) + except ConanException as conan_error: + raise RuntimeError( + f'getting requested profiles failed: {(self.__params.build_profile, self.__params.host_profile)}' + ) from conan_error + + logger.info(f'computing dependency graph for config: {config}') + try: + graph = api.graph.load_graph_consumer( + path=api.local.get_conanfile_path('.', Path.cwd(), py=True), + # these 4 are provided by recipe + name=None, + version=None, + user=None, + channel=None, + # ============================== + profile_build=build_profile, + profile_host=host_profile, + lockfile=None, + remotes=remotes, + update=True, + check_updates=True + ) + api.graph.analyze_binaries(graph, build_mode=['missing'], remotes=remotes, update=True) + graph.report_graph_error() + print_graph_basic(graph) + + except ConanException as conan_error: + raise RuntimeError( + f'failed to generate build graph, Conan API failed' + ) from conan_error + + # make sure to define cmake layout in conanfile.py + try: + api.install.install_binaries(graph, remotes) + api.install.install_consumer(graph, source_folder=Path.cwd()) + except ConanException as conan_error: + raise RuntimeError( + f'failed to install deps, Conan API failed' + ) from conan_error + + def _inspect_cmake_presets(self, presets_file: Path) -> Collection[Tuple[str, dict]]: + if not presets_file.is_file(): + raise FileNotFoundError(f'file "{presets_file}" does not exist') + + with open(presets_file, 'r') as fd: + presets = json.load(fd) + + includes = presets['include'] + for file_to_include in map(Path, includes): + if not file_to_include.is_absolute(): + file_to_include = presets_file.parent / file_to_include + + with open(file_to_include, 'r') as file: + contents = json.load(file) + + for key in contents: + if key.endswith('Presets'): + if key not in presets: + presets[key] = [] + + # FIXME: probably should prefer initial values if set + presets[key].append(contents[key]) + + return presets + + def _run(self: 'Routines', command: typing.List[str]) -> None: + logger.info('running command: ' + ' '.join(command)) + code = subprocess.run(command, encoding='UTF-8', stderr=subprocess.STDOUT, env=os.environ).returncode + if code: + sys.exit(f'error: subprocess failed: {errno.errorcode[code]} (code: {code})') + + def _decorate_cmake_variable(self: 'Routines', var: str, value: str, type: Union[str, None] = None) -> str: + if type is not None: + return f'-D{var.upper()}:{type}={value}' + return f'-D{var.upper()}={value}' + + +def parse_cli_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description=DESCRIPTION, formatter_class=argparse.RawDescriptionHelpFormatter) + # Routines + parser.add_argument('-b', '--build', action='store_true', dest='build') + parser.add_argument('-c', '--configure', action='store_true', dest='configure') + parser.add_argument('-i', '--conan-install', action='store_true', dest='conan_install') + parser.add_argument('-r', '--remove', action='store_true', dest='remove') + parser.add_argument('-l', '--symlink-compile-commands', action='store_true', dest='symlink_compile_commands') + # Environment + parser.add_argument('--build-dir', action='store', dest='build_directory') + parser.add_argument('--config', action='append', dest='configs', choices=['Debug', 'Release']) + parser.add_argument('--parallel', action='store', dest='cores') + parser.add_argument('--generator', action='store', dest='generator') + parser.add_argument('--profile', action='store', dest='profile') + return parser.parse_args() + + +def main(): + try: + from rich.logging import RichHandler + from rich.traceback import install + + install(show_locals=True) + handler = RichHandler( + level=logging.DEBUG, + rich_tracebacks=True, + show_path=False, + show_time=False, + show_level=True, + ) + except ImportError: + handler = logging.StreamHandler(sys.stdout) + + logging.root.addHandler(handler) + logging.root.setLevel(logging.INFO) + + args = parse_cli_args() + + params = Config() + if getattr(args, 'build_directory') is not None: + params.build_directory = Path.cwd().joinpath(args.build_directory) + logger.info(f'config: user-provided build directory: "{params.build_directory}"') + if getattr(args, 'configs') is not None: + params.build_configs = args.configs + logger.info(f'config: user-provided build configs: {params.build_configs}') + if getattr(args, 'cores') is not None: + params.cores = int(args.cores) + logger.info(f'config: user-provided threads, that will be run in parallel: "{params.cores}"') + if getattr(args, 'profile') is not None: + params.profile = args.profile + logger.info(f'config: user-provided conan profile: "{params.profile}"') + if getattr(args, 'generator') is not None: + params.generator = args.generator + logger.info(f'config: user-provided generator: "{params.generator}"') + + r = Routines(params) + for routine, f in r.routines(): + if not hasattr(args, routine): + logger.info(f'routine \'{routine}\' is not configured for CLI, skipping') + continue + if getattr(args, routine): + logger.info(f'running {routine}') + f() + + logger.info('done!') + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + print('exited by user') \ No newline at end of file From 8734a1db94daf6a76f392932338e2b674227e87f Mon Sep 17 00:00:00 2001 From: AshFungor Date: Mon, 21 Apr 2025 02:14:27 +0300 Subject: [PATCH 02/25] refactor cmake targets and setup --- .gitignore | 3 +- .gitmodules | 18 ----- CMakeLists.txt | 102 ++++++++++++++++++++++----- cmake/Configuration.cmake | 101 -------------------------- cmake/Variables.cmake | 2 - conanfile.py | 5 +- dist/CMakeLists.txt | 10 +++ dist/HBench | 1 - dist/argparse | 1 - dist/benchmark | 1 - dist/eternal | 1 - dist/googletest | 1 - dist/libbacktrace | 1 - src/Assembler/CMakeLists.txt | 28 ++++++++ src/BacktraceProvider/CMakeLists.txt | 16 +++++ src/CMakeLists.txt | 66 +++-------------- src/Emulator/CMakeLists.txt | 88 +++++++++++++++++++++++ 17 files changed, 243 insertions(+), 202 deletions(-) delete mode 100644 cmake/Configuration.cmake delete mode 100644 cmake/Variables.cmake create mode 100644 dist/CMakeLists.txt delete mode 160000 dist/HBench delete mode 160000 dist/argparse delete mode 160000 dist/benchmark delete mode 160000 dist/eternal delete mode 160000 dist/googletest delete mode 160000 dist/libbacktrace create mode 100644 src/Assembler/CMakeLists.txt create mode 100644 src/BacktraceProvider/CMakeLists.txt create mode 100644 src/Emulator/CMakeLists.txt diff --git a/.gitignore b/.gitignore index 45f19244..87371c2c 100644 --- a/.gitignore +++ b/.gitignore @@ -89,4 +89,5 @@ CMakeUserPresets* .vscode # Distribution packages -dist/** +dist/* +!dist/CMakeLists.txt diff --git a/.gitmodules b/.gitmodules index 1ca87879..3ee6db91 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,27 +1,9 @@ -[submodule "dist/googletest"] - path = dist/googletest - url = https://github.com/google/googletest -[submodule "dist/HBench"] - path = dist/HBench - url = https://github.com/randommfs/HBench -[submodule "dist/argparse"] - path = dist/argparse - url = https://github.com/p-ranav/argparse [submodule "dist/pog"] path = dist/pog url = https://github.com/HyperWinX/HPog -[submodule "dist/eternal"] - path = dist/eternal - url = https://github.com/mapbox/eternal [submodule "dist/HPool"] path = dist/HPool url = https://github.com/randommfs/HPool -[submodule "dist/benchmark"] - path = dist/benchmark - url = https://github.com/google/benchmark -[submodule "dist/libbacktrace"] - path = dist/libbacktrace - url = https://github.com/ianlancetaylor/libbacktrace [submodule "dist/libunwind"] path = dist/libunwind url = https://github.com/libunwind/libunwind diff --git a/CMakeLists.txt b/CMakeLists.txt index fbdd4deb..cff81fe0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,26 +1,96 @@ cmake_minimum_required(VERSION 3.25) -include(cmake/Configuration.cmake) -detect_compilers() - project(HyperCPU CXX) -set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +option(HCPU_LTO "HCPU: Enable LTO if possible" OFF) +option(HCPU_NATIVE_OPTIMIZATIONS "HCPU: Optimize for current ISA implementation" OFF) +option(HCPU_SANITIZERS "HCPU: Enable sanitizers" OFF) +option(HCPU_BUILD_TESTS "HCPU: Build tests" OFF) +option(HCPU_BUILD_BENCHMARKS "HCPU: Build benchmarks" OFF) + +find_package(GTest 1.14.0 REQUIRED) # GoogleTest +find_package(benchmark 1.9.1 REQUIRED) # Google Benchmark +find_package(absl 20250127.0 REQUIRED) # Abseil (CMake target: absl::absl) +find_package(libbacktrace CONFIG REQUIRED) # libbacktrace (CMake target: libbacktrace::libbacktrace) +find_package(argparse 3.2 REQUIRED) # argparse.cpp (CMake target: argparse::argparse) +find_package(eternal 1.0.1 REQUIRED) # Eternal (CMake target: eternal::eternal) +find_package(RE2 20240702 REQUIRED) # RE2 (CMake target: re2::re2) +find_package(fmt 11.1.4 REQUIRED) # fmtlib (CMake target: fmt::fmt) +find_package(unwind 1.8.1 REQUIRED) # libunwind (CMake target: unwind::unwind) + set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(ROOT_DIR ${HyperCPU_SOURCE_DIR}) -set(BENCHMARK_ENABLE_GTEST_TESTS OFF) # Disable gtest requirement for google/benchmark -set(FMT_SYSTEM_HEADER ON) # Fix -Wstrinop-overflow warning -set_compile_flags() +# FIXME: remove later +# retained for backwards comparability, should be supplied in CLI or presets +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# FIXME: do we need this now? +# Disable gtest requirement for google/benchmark +set(BENCHMARK_ENABLE_GTEST_TESTS OFF) +# Fix -Wstrinop-overflow warning +set(FMT_SYSTEM_HEADER ON) + +add_compile_options( + -Wall -Wextra -Werror + -Wno-pointer-arith + -Wno-unused-const-variable + -Wno-missing-field-initializers + -Wno-stringop-overflow + -Wno-unknown-warning-option +) -message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") +add_compile_options( + $<$:-ggdb3 -D__HCPU_DEBUG> + $<$:-O3> +) + +# FIXME: enable this by default when this becomes operational +if(HCPU_LTO) + check_ipo_supported(RESULT result OUTPUT output) + if(result) + set_property(TARGET foo PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) + message(STATUS "HCPU: Enabled lTO") + else() + message(WARNING "HCPU: LTO is not supported: ${output}") + endif() +else() + message(STATUS "HCPU: Skipping LTO") +endif() + +if(HCPU_NATIVE_OPTIMIZATIONS) + add_compile_options(-march=native) + message(STATUS "HCPU: Enabled optimizations for current ISA implementation") +else() + message(STATUS "HCPU: Skipping current ISA optimizations") +endif() + +if(HCPU_SANITIZERS) + add_compile_options(-fsanitize=address,leak) + add_link_options(-fsanitize=address,leak) + message(STATUS "HCPU: Enabling sanitizers") +else() + message(STATUS "HCPU: Skipping sanitizers") +endif() + +add_library(hcpu_unwind INTERFACE) +target_link_libraries( + hcpu_unwind + INTERFACE + unwind::unwind backtrace::backtrace +) +target_compile_definitions( + hcpu_unwind + INTERFACE + HCPU_ENABLE_LIBUNWIND +) -message(STATUS "Generating source files list") -include(cmake/SourceListGenerator.cmake) -add_subdirectory(dist/argparse) -add_subdirectory(dist/pog) -add_subdirectory(bench) add_subdirectory(src) -add_subdirectory(test) + +if(HCPU_BUILD_BENCHMARKS) + add_subdirectory(bench) +endif() + +if(HCPU_BUILD_TESTS) + add_subdirectory(test) +endif() \ No newline at end of file diff --git a/cmake/Configuration.cmake b/cmake/Configuration.cmake deleted file mode 100644 index 16e95fa8..00000000 --- a/cmake/Configuration.cmake +++ /dev/null @@ -1,101 +0,0 @@ -include(cmake/Variables.cmake) - -function(set_compile_flags) - if ("${CMAKE_BUILD_TYPE}" STREQUAL "Release") - add_compile_options(${FAST_COMPILE_FLAGS}) - elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - add_compile_options(${DEBUG_COMPILE_FLAGS}) - elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo") - add_compile_options(${FAST_COMPILE_FLAGS} -ggdb3) - else() - message(FATAL_ERROR "Unknown CMAKE_BUILD_TYPE specified") - endif() - - get_property(CAN_USE_LLVM_LTO GLOBAL PROPERTY CAN_USE_LLVM_LTO) - - if ("${HCPU_LTO}" STREQUAL "ON" AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - add_compile_options(-flto) - add_link_options(-flto) - message(STATUS "Enabled LTO") - elseif("${HCPU_LTO}" STREQUAL "ON" AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND "${CAN_USE_LLVM_LTO}" STREQUAL "YES") - add_compile_options(-flto=thin) - add_link_options(-flto=thin) - message(STATUS "Enabled LTO") - else() - message(STATUS "LTO wasn't enabled") - endif() - - string(TOLOWER "${HCPU_MARCH_NATIVE}" HCPU_MARCH_NATIVE) - if ("${HCPU_MARCH_NATIVE}" STREQUAL "on") - add_compile_options(-march=native) - message(STATUS "Enabled -march=native flag") - endif() - - string(TOLOWER "${HCPU_SANITIZERS}" HCPU_SANITIZERS) - if (NOT "${HCPU_SANITIZERS}" STREQUAL "off") - add_compile_options(-fsanitize=address,leak) - add_link_options(-fsanitize=address,leak) - message(STATUS "Enabling sanitizers") - endif() - - find_library(LIBUNWIND unwind) - set(LIBUNWIND ${LIBUNWIND} PARENT_SCOPE) - if (LIBUNWIND) - message(STATUS "Found libunwind") - add_compile_options(-DHCPU_ENABLE_LIBUNWIND) - add_link_options(-L${ROOT_DIR}/dist/libbacktrace -lunwind -lbacktrace) - endif() -endfunction() - -function(detect_compilers) - find_program(WHICH_AVAILABLE "which") - if ("${WHICH_AVAILABLE}" STREQUAL WHICH_AVAILABLE-NOT_FOUND) - message(WARNING "which binary is not found - using CMake compiler autodetection") - set(HCPU_COMPILER auto) - endif() - - string(TOLOWER "${HCPU_COMPILER}" HCPU_COMPILER) - string(TOLOWER "${HCPU_LTO}" HCPU_LTO) - string(SUBSTRING "${CMAKE_C_COMPILER}" 0 1 IS_ABS) - - if ("${HCPU_COMPILER}" STREQUAL "auto" OR "${HCPU_COMPILER}" STREQUAL "") - message(STATUS "Using CMake compiler autodetection") - elseif ("${HCPU_COMPILER}" STREQUAL "clang") - if ("${CMAKE_C_COMPILER}" STREQUAL "" OR "${IS_ABS}" STREQUAL "/") - message(STATUS "Searching for clang") - execute_process(COMMAND which clang OUTPUT_VARIABLE CMAKE_C_COMPILER OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND which clang++ OUTPUT_VARIABLE CMAKE_CXX_COMPILER OUTPUT_STRIP_TRAILING_WHITESPACE) - set(CMAKE_C_COMPILER ${CMAKE_C_COMPILER} CACHE INTERNAL "CMAKE_C_COMPILER") - set(CMAKE_CXX_COMPILER ${CMAKE_CXX_COMPILER} CACHE INTERNAL "CMAKE_CXX_COMPILER") - elseif (NOT "${IS_ABS}" STREQUAL "/") - message(STATUS "Searching for ${CMAKE_C_COMPILER}") - execute_process(COMMAND which ${CMAKE_C_COMPILER} OUTPUT_VARIABLE CMAKE_C_COMPILER OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND which ${CMAKE_CXX_COMPILER} OUTPUT_VARIABLE CMAKE_CXX_COMPILER OUTPUT_STRIP_TRAILING_WHITESPACE) - set(CMAKE_C_COMPILER ${CMAKE_C_COMPILER} CACHE INTERNAL "CMAKE_C_COMPILER") - set(CMAKE_CXX_COMPILER ${CMAKE_CXX_COMPILER} CACHE INTERNAL "CMAKE_CXX_COMPILER") - endif() - - find_program(LLD_AVAILABLE "ld.lld") - if ("${LLD_AVAILABLE}" STREQUAL LLD_AVAILABLE-NOT_FOUND AND "${HCPU_LTO}" STREQUAL "yes") - message(WARNING "LLD not found: LLVM LTO will be disabled") - set(CAN_USE_LLVM_LTO "NO" PARENT_SCOPE) - else() - execute_process(COMMAND which ld.lld OUTPUT_VARIABLE CMAKE_LINKER OUTPUT_STRIP_TRAILING_WHITESPACE) - message(STATUS "Found LLD") - set_property(GLOBAL PROPERTY CAN_USE_LLVM_LTO "YES") - endif() - elseif ("${HCPU_COMPILER}" STREQUAL "gcc") - if ("${CMAKE_C_COMPILER}" STREQUAL "" OR "${IS_ABS}" STREQUAL "/") - message(STATUS "Searching for gcc") - execute_process(COMMAND which gcc OUTPUT_VARIABLE CMAKE_C_COMPILER) - execute_process(COMMAND which g++ OUTPUT_VARIABLE CMAKE_CXX_COMPILER) - elseif (NOT "${IS_ABS}" STREQUAL "/") - message(STATUS "Searching for ${CMAKE_C_COMPILER}") - execute_process(COMMAND which ${CMAKE_C_COMPILER} OUTPUT_VAR -IABLE CMAKE_C_COMPILER) - execute_process(COMMAND which ${CMAKE_CXX_COMPILER} OUTPUT_VARIABLE CMAKE_CXX_COMPILER) - endif() - else() - message(FATAL_ERROR "HCPU_COMPILER not specified: cannot proceed. Please specify one of: auto, clang, gcc.") - endif() -endfunction() diff --git a/cmake/Variables.cmake b/cmake/Variables.cmake deleted file mode 100644 index 1d185b4f..00000000 --- a/cmake/Variables.cmake +++ /dev/null @@ -1,2 +0,0 @@ -set(DEBUG_COMPILE_FLAGS -Wall -Wextra -Werror -Wno-pointer-arith -O0 -ggdb3 -Wno-unused-const-variable -Wno-missing-field-initializers -Wno-stringop-overflow -Wno-unknown-warning-option -D__HCPU_DEBUG) -set(FAST_COMPILE_FLAGS -Wall -Wextra -Werror -Wno-pointer-arith -O3 -Wno-unused-const-variable -Wno-missing-field-initializers -Wno-stringop-overflow -Wno-unknown-warning-option) diff --git a/conanfile.py b/conanfile.py index 555b4d91..ff57031f 100644 --- a/conanfile.py +++ b/conanfile.py @@ -18,7 +18,10 @@ def __init__(self: Self, display_name: str = '') -> None: 'abseil': '20250127.0', 'libbacktrace': 'cci.20240730', 'argparse': '3.2', - 'eternal': '1.0.1' + 'eternal': '1.0.1', + 're2': '20240702', + 'fmt': '11.1.4', + 'libunwind': '1.8.1' } super().__init__(display_name) diff --git a/dist/CMakeLists.txt b/dist/CMakeLists.txt new file mode 100644 index 00000000..8f89012a --- /dev/null +++ b/dist/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.25) + +# pog +option(POG_BUNDLED_RE2 OFF CACHE BOOL "Use bundled re2" FORCE) +option(POG_BUNDLED_FMT OFF CACHE BOOL "Use bundled fmt" FORCE) +option(POG_TESTS OFF CACHE BOOL "Build tests" FORCE) +add_subdirectory(pog) + +# hpool +add_subdirectory(HPool) \ No newline at end of file diff --git a/dist/HBench b/dist/HBench deleted file mode 160000 index adae2e68..00000000 --- a/dist/HBench +++ /dev/null @@ -1 +0,0 @@ -Subproject commit adae2e6831283f8675062a8123ac21c72dcc52a9 diff --git a/dist/argparse b/dist/argparse deleted file mode 160000 index d924b84e..00000000 --- a/dist/argparse +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d924b84eba1f0f0adf38b20b7b4829f6f65b6570 diff --git a/dist/benchmark b/dist/benchmark deleted file mode 160000 index 1bc59dce..00000000 --- a/dist/benchmark +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1bc59dce278b9145f18ef88d31d373c4ba939dc4 diff --git a/dist/eternal b/dist/eternal deleted file mode 160000 index dd2f5b9f..00000000 --- a/dist/eternal +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dd2f5b9ff38fcd36b59efd9d289127fa73efc6cb diff --git a/dist/googletest b/dist/googletest deleted file mode 160000 index 2b6b042a..00000000 --- a/dist/googletest +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2b6b042a77446ff322cd7522ca068d9f2a21c1d1 diff --git a/dist/libbacktrace b/dist/libbacktrace deleted file mode 160000 index 79392187..00000000 --- a/dist/libbacktrace +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 793921876c981ce49759114d7bb89bb89b2d3a2d diff --git a/src/Assembler/CMakeLists.txt b/src/Assembler/CMakeLists.txt new file mode 100644 index 00000000..9a014a4f --- /dev/null +++ b/src/Assembler/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.25) + + +add_library( + emulator-core STATIC + Core/StatementCompilers.cpp + Core/Parsers.cpp + Core/Tokenizers.cpp + Core/Compiler.cpp + Core/BinaryTransformer.cpp + Utils/Extension.hpp + Core/BinaryTransformer.hpp + Core/OpcodeNameAssoc.hpp + Core/RegNameAssoc.hpp + Core/Compiler.hpp + Core/ModeNameAssoc.hpp +) +target_include_directories( + emulator-core PUBLIC + ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} +) +target_link_libraries( + emulator-core PUBLIC + fmt::fmt argparse::argparse pog eternal::eternal pch +) + +add_executable(hcasm Main/Main.cpp) +target_link_libraries(hcasm PRIVATE assembler-core) \ No newline at end of file diff --git a/src/BacktraceProvider/CMakeLists.txt b/src/BacktraceProvider/CMakeLists.txt new file mode 100644 index 00000000..97b62490 --- /dev/null +++ b/src/BacktraceProvider/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.25) + + +add_library( + backtrace-provider STATIC + BacktraceProvider.cpp + BacktraceProvider.hpp +) +target_include_directories( + backtrace-provider PUBLIC + ${CMAKE_SOURCE_DIR} +) +target_link_libraries( + backtrace-provider PUBLIC + fmt::fmt argparse::argparse pog eternal::eternal hcpu_unwind pch +) \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d2a1f379..2cd5553c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,60 +1,12 @@ -include(ExternalProject) +# TODO: refactor dependencies for each target, there is something useless there for sure +# TODO: refactor all includes relevant to CMAKE_SOURCE_DIR, like #include -set(GENERIC_INCLUDE_DIR - ${ROOT_DIR}/src - ${ROOT_DIR}/dist/argparse/include - ${ROOT_DIR}/dist/pog/include - ${ROOT_DIR}/dist/eternal/include - ${ROOT_DIR}/dist/HPool) - -ExternalProject_Add( - libbacktrace - SOURCE_DIR - ${ROOT_DIR}/dist/libbacktrace - BUILD_IN_SOURCE - 0 - CONFIGURE_COMMAND - cd ${ROOT_DIR}/dist/libbacktrace && ./configure - BUILD_COMMAND - cd ${ROOT_DIR}/dist/libbacktrace && make -j$(nproc) - INSTALL_COMMAND - "" - BUILD_BYPRODUCTS - ${ROOT_DIR}/dist/libbacktrace/libbacktrace.la +add_library( + pch INTERFACE + pch.hpp ) +target_precompile_headers(pch INTERFACE pch.hpp) -add_library(emulator-core STATIC ${SOURCES_emulator-core}) -target_include_directories(emulator-core PUBLIC ${GENERIC_INCLUDE_DIR} ${ROOT_DIR}/src/Emulator) -target_link_libraries(emulator-core ${LD_FLAGS} fmt) -target_precompile_headers(emulator-core PRIVATE pch.hpp) - -add_library(assembler-core STATIC ${SOURCES_assembler-core}) -target_include_directories(assembler-core PUBLIC ${GENERIC_INCLUDE_DIR} ${ROOT_DIR}/src/Assembler) -target_link_libraries(assembler-core pog) -target_precompile_headers(assembler-core PRIVATE pch.hpp) - -add_library(backtrace-provider STATIC ${SOURCES_backtrace-provider}) -target_include_directories(backtrace-provider PUBLIC ${GENERIC_INCLUDE_DIR}) -add_dependencies(backtrace-provider libbacktrace) -target_precompile_headers(backtrace-provider PRIVATE pch.hpp) - -add_executable(hcasm ${SOURCES_assembler-main}) -target_include_directories(hcasm PUBLIC ${GENERIC_INCLUDE_DIR} ${ROOT_DIR}/src/Assembler) -target_link_libraries(hcasm assembler-core pog) -target_precompile_headers(hcasm PRIVATE pch.hpp) - -add_executable(hcemul ${SOURCES_emulator-main}) -target_include_directories(hcemul PUBLIC ${GENERIC_INCLUDE_DIR} ${ROOT_DIR}/src/Emulator) -target_link_libraries(hcemul emulator-core assembler-core) -target_precompile_headers(hcemul PRIVATE pch.hpp) - -if (LIBUNWIND) - target_link_libraries(hcasm backtrace-provider -L${ROOT_DIR}/dist/libbacktrace backtrace) - target_link_libraries(hcemul backtrace-provider -L${ROOT_DIR}/dist/libbacktrace backtrace) -endif() - -add_custom_target(default - DEPENDS - hcasm - hcemul -) +add_subdirectory(Emulator) +add_subdirectory(Assembler) +add_subdirectory(BacktraceProvider) diff --git a/src/Emulator/CMakeLists.txt b/src/Emulator/CMakeLists.txt new file mode 100644 index 00000000..36eeaa8b --- /dev/null +++ b/src/Emulator/CMakeLists.txt @@ -0,0 +1,88 @@ +cmake_minimum_required(VERSION 3.25) + + +add_library( + assembler-core STATIC + Core/CPU/Instructions/AllowedFlags.cpp + Core/CPU/Interrupts/InterruptHandler.cpp + Core/CPU/CPU.cpp + Core/CPU/InstructionsImpl/DIV.cpp + Core/CPU/InstructionsImpl/CALLL.cpp + Core/CPU/InstructionsImpl/MUL.cpp + Core/CPU/InstructionsImpl/READ.cpp + Core/CPU/InstructionsImpl/ADC.cpp + Core/CPU/InstructionsImpl/ADD.cpp + Core/CPU/InstructionsImpl/DEC.cpp + Core/CPU/InstructionsImpl/SHFL.cpp + Core/CPU/InstructionsImpl/CUDF.cpp + Core/CPU/InstructionsImpl/PUSH.cpp + Core/CPU/InstructionsImpl/OR.cpp + Core/CPU/InstructionsImpl/POP.cpp + Core/CPU/InstructionsImpl/CALLGR.cpp + Core/CPU/InstructionsImpl/CCRF.cpp + Core/CPU/InstructionsImpl/JMP.cpp + Core/CPU/InstructionsImpl/AND.cpp + Core/CPU/InstructionsImpl/HID.cpp + Core/CPU/InstructionsImpl/CALL.cpp + Core/CPU/InstructionsImpl/INC.cpp + Core/CPU/InstructionsImpl/INTR.cpp + Core/CPU/InstructionsImpl/SHFR.cpp + Core/CPU/InstructionsImpl/CALLE.cpp + Core/CPU/InstructionsImpl/JML.cpp + Core/CPU/InstructionsImpl/SUB.cpp + Core/CPU/InstructionsImpl/ANDN.cpp + Core/CPU/InstructionsImpl/LOIVT.cpp + Core/CPU/InstructionsImpl/HALT.cpp + Core/CPU/InstructionsImpl/MOV.cpp + Core/CPU/InstructionsImpl/CMP.cpp + Core/CPU/InstructionsImpl/WRITE.cpp + Core/CPU/InstructionsImpl/BSWAP.cpp + Core/CPU/InstructionsImpl/JME.cpp + Core/CPU/InstructionsImpl/COVF.cpp + Core/CPU/InstructionsImpl/JMGR.cpp + Core/CPU/Decoders/StdDecoder.cpp + Core/CPU/Stack.cpp + Core/CPU/OperandsEvaluation.cpp + Core/CPU/IO/Simple.cpp + Core/MemoryController/MemoryControllerST.hpp + Core/MemoryController/IMemoryController.hpp + Core/CPU/Instructions/AllowedFlags.hpp + Core/CPU/Instructions/Opcodes.hpp + Core/CPU/Instructions/Flags.hpp + Core/CPU/Instructions/Registers.hpp + Core/CPU/Interrupts/ReservedInterrupts.hpp + Core/CPU/CPU.hpp + Core/CPU/ALU.hpp + Core/CPU/Decoders/StdDecoder.hpp + Core/CPU/Decoders/IDecoder.hpp + Core/CPU/Assert.hpp + Core/CPU/Version.hpp + Core/CPU/Util.hpp + Core/CPU/IO/Simple.hpp + Misc/byteswap.hpp + Misc/overflow.hpp + Misc/deref.hpp + Misc/bit_cast.hpp + Misc/unreachable.hpp + Misc/underflow.hpp + Misc/print.hpp +) + +target_include_directories( + assembler-core PUBLIC + ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_link_libraries( + assembler-core PUBLIC + fmt::fmt argparse::argparse pog eternal::eternal pch +) + +add_executable( + hcemul + Main/Main.hpp + Main/Main.cpp + Main/ExceptionHandling.cpp + Main/ExceptionHandling.hpp +) +target_link_libraries(hcemul PRIVATE assembler-core) \ No newline at end of file From 269db7d4bd0037dc6efb2a5f6216b47fc5fab974 Mon Sep 17 00:00:00 2001 From: AshFungor Date: Mon, 21 Apr 2025 03:28:29 +0300 Subject: [PATCH 03/25] patched tests, removed cmake subfolder completely --- CMakeLists.txt | 20 +- cmake/SourceListGenerator.cmake | 16 -- cmake/TargetAndFolderAssoc.cmake | 38 ---- conanfile.py | 5 +- dist/CMakeLists.txt | 6 +- src/Assembler/CMakeLists.txt | 36 +-- src/BacktraceProvider/CMakeLists.txt | 22 +- src/Emulator/CMakeLists.txt | 157 +++++++------ src/Logger/Logger.hpp | 120 +++++----- test/CMakeLists.txt | 19 +- test/Integration/CMakeLists.txt | 60 +++++ .../EmulatorCore/CPU/CPU_CALLE.cpp | 109 +++++---- test/Modular/CMakeLists.txt | 111 +++++++++ test/fixtures.hpp | 215 +++++++++--------- test/main.cpp | 8 +- test/pch.hpp | 7 - 16 files changed, 545 insertions(+), 404 deletions(-) delete mode 100644 cmake/SourceListGenerator.cmake delete mode 100644 cmake/TargetAndFolderAssoc.cmake create mode 100644 test/Integration/CMakeLists.txt create mode 100644 test/Modular/CMakeLists.txt delete mode 100644 test/pch.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cff81fe0..09ead28e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,18 +5,19 @@ project(HyperCPU CXX) option(HCPU_LTO "HCPU: Enable LTO if possible" OFF) option(HCPU_NATIVE_OPTIMIZATIONS "HCPU: Optimize for current ISA implementation" OFF) option(HCPU_SANITIZERS "HCPU: Enable sanitizers" OFF) -option(HCPU_BUILD_TESTS "HCPU: Build tests" OFF) +option(HCPU_BUILD_TESTS "HCPU: Build tests" ON) option(HCPU_BUILD_BENCHMARKS "HCPU: Build benchmarks" OFF) +# FIXME: add pthread, boost & re2 find_package(GTest 1.14.0 REQUIRED) # GoogleTest find_package(benchmark 1.9.1 REQUIRED) # Google Benchmark find_package(absl 20250127.0 REQUIRED) # Abseil (CMake target: absl::absl) find_package(libbacktrace CONFIG REQUIRED) # libbacktrace (CMake target: libbacktrace::libbacktrace) find_package(argparse 3.2 REQUIRED) # argparse.cpp (CMake target: argparse::argparse) find_package(eternal 1.0.1 REQUIRED) # Eternal (CMake target: eternal::eternal) -find_package(RE2 20240702 REQUIRED) # RE2 (CMake target: re2::re2) +# find_package(RE2 20240702 REQUIRED) # RE2 (CMake target: re2::re2) find_package(fmt 11.1.4 REQUIRED) # fmtlib (CMake target: fmt::fmt) -find_package(unwind 1.8.1 REQUIRED) # libunwind (CMake target: unwind::unwind) +find_package(libunwind 1.8.1 REQUIRED) # libunwind (CMake target: libunwind::libunwind) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -31,17 +32,20 @@ set(BENCHMARK_ENABLE_GTEST_TESTS OFF) # Fix -Wstrinop-overflow warning set(FMT_SYSTEM_HEADER ON) +# FIXME: patch hpool to enable -Werror add_compile_options( - -Wall -Wextra -Werror + -Wall + -Wextra + # -Werror -Wno-pointer-arith -Wno-unused-const-variable -Wno-missing-field-initializers -Wno-stringop-overflow - -Wno-unknown-warning-option ) add_compile_options( - $<$:-ggdb3 -D__HCPU_DEBUG> + $<$:-ggdb3> + $<$:-D__HCPU_DEBUG> $<$:-O3> ) @@ -77,7 +81,7 @@ add_library(hcpu_unwind INTERFACE) target_link_libraries( hcpu_unwind INTERFACE - unwind::unwind backtrace::backtrace + libunwind::libunwind libbacktrace::libbacktrace ) target_compile_definitions( hcpu_unwind @@ -85,6 +89,7 @@ target_compile_definitions( HCPU_ENABLE_LIBUNWIND ) +add_subdirectory(dist) add_subdirectory(src) if(HCPU_BUILD_BENCHMARKS) @@ -92,5 +97,6 @@ if(HCPU_BUILD_BENCHMARKS) endif() if(HCPU_BUILD_TESTS) + # FIXME: rename to tests add_subdirectory(test) endif() \ No newline at end of file diff --git a/cmake/SourceListGenerator.cmake b/cmake/SourceListGenerator.cmake deleted file mode 100644 index 28091458..00000000 --- a/cmake/SourceListGenerator.cmake +++ /dev/null @@ -1,16 +0,0 @@ -include(${ROOT_DIR}/cmake/TargetAndFolderAssoc.cmake) - -foreach(pair IN ZIP_LISTS __TARGETS_LIST __DIRECTORIES_LIST) - # Execute find and get list of source files - execute_process( - COMMAND find ${pair_1} -type f -name "*.cpp" - OUTPUT_VARIABLE ENUMERATED_SRC - ) - - ##string(REGEX MATCHALL "[^a-zA-Z]\n[^a-zA-Z]$" SOURCES_${pair_0} ${ENUMERATED_SRC}) - #string(REGEX REPLACE "\n" ";" SOURCES_${pair_0} "${ENUMERATED_SRC}") - set(SOURCES_${pair_0} ${ENUMERATED_SRC}) - string(REPLACE "\n" ";" SOURCES_${pair_0} ${ENUMERATED_SRC}) - list(LENGTH SOURCES_${pair_0} CNT_${pair_0}) - message(STATUS "Found ${CNT_${pair_0}} source files for target ${pair_0}") -endforeach() diff --git a/cmake/TargetAndFolderAssoc.cmake b/cmake/TargetAndFolderAssoc.cmake deleted file mode 100644 index ee27762c..00000000 --- a/cmake/TargetAndFolderAssoc.cmake +++ /dev/null @@ -1,38 +0,0 @@ -set(__TARGETS_LIST -# HyperCPU core sources - emulator-core - emulator-main - - assembler-core - assembler-main - - backtrace-provider - - -# Testing - modular_testing - integration_testing -) - -set(__DIRECTORIES_LIST -# emulator-core target - ${ROOT_DIR}/src/Emulator/Core - -# emulator-main target - ${ROOT_DIR}/src/Emulator/Main - -# assembler-core target - ${ROOT_DIR}/src/Assembler/Core - -# assembler-main target - ${ROOT_DIR}/src/Assembler/Main - -# backtrace-provider target - ${ROOT_DIR}/src/BacktraceProvider - -# modulartesting_src target - ${ROOT_DIR}/test/Modular - -# integrationtesting_src - ${ROOT_DIR}/test/Integration -) diff --git a/conanfile.py b/conanfile.py index ff57031f..1b055a41 100644 --- a/conanfile.py +++ b/conanfile.py @@ -19,9 +19,10 @@ def __init__(self: Self, display_name: str = '') -> None: 'libbacktrace': 'cci.20240730', 'argparse': '3.2', 'eternal': '1.0.1', - 're2': '20240702', + # 're2': '20230801', 'fmt': '11.1.4', - 'libunwind': '1.8.1' + 'libunwind': '1.8.1', + # 'boost': '1.84.0' } super().__init__(display_name) diff --git a/dist/CMakeLists.txt b/dist/CMakeLists.txt index 8f89012a..1393d82d 100644 --- a/dist/CMakeLists.txt +++ b/dist/CMakeLists.txt @@ -1,9 +1,9 @@ cmake_minimum_required(VERSION 3.25) # pog -option(POG_BUNDLED_RE2 OFF CACHE BOOL "Use bundled re2" FORCE) -option(POG_BUNDLED_FMT OFF CACHE BOOL "Use bundled fmt" FORCE) -option(POG_TESTS OFF CACHE BOOL "Build tests" FORCE) +set(POG_BUNDLED_RE2 OFF CACHE BOOL "Use bundled re2" FORCE) +set(POG_BUNDLED_FMT OFF CACHE BOOL "Use bundled fmt" FORCE) +set(POG_TESTS OFF CACHE BOOL "Build tests" FORCE) add_subdirectory(pog) # hpool diff --git a/src/Assembler/CMakeLists.txt b/src/Assembler/CMakeLists.txt index 9a014a4f..3897dd4b 100644 --- a/src/Assembler/CMakeLists.txt +++ b/src/Assembler/CMakeLists.txt @@ -2,26 +2,30 @@ cmake_minimum_required(VERSION 3.25) add_library( - emulator-core STATIC - Core/StatementCompilers.cpp - Core/Parsers.cpp - Core/Tokenizers.cpp - Core/Compiler.cpp - Core/BinaryTransformer.cpp - Utils/Extension.hpp - Core/BinaryTransformer.hpp - Core/OpcodeNameAssoc.hpp - Core/RegNameAssoc.hpp - Core/Compiler.hpp - Core/ModeNameAssoc.hpp + assembler-core + STATIC + Core/StatementCompilers.cpp + Core/Parsers.cpp + Core/Tokenizers.cpp + Core/Compiler.cpp + Core/BinaryTransformer.cpp + Utils/Extension.hpp + Core/BinaryTransformer.hpp + Core/OpcodeNameAssoc.hpp + Core/RegNameAssoc.hpp + Core/Compiler.hpp + Core/ModeNameAssoc.hpp ) target_include_directories( - emulator-core PUBLIC - ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} + assembler-core + PUBLIC + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries( - emulator-core PUBLIC - fmt::fmt argparse::argparse pog eternal::eternal pch + assembler-core + PUBLIC + fmt::fmt argparse::argparse pog eternal::eternal pch hpool ) add_executable(hcasm Main/Main.cpp) diff --git a/src/BacktraceProvider/CMakeLists.txt b/src/BacktraceProvider/CMakeLists.txt index 97b62490..1b2ddd51 100644 --- a/src/BacktraceProvider/CMakeLists.txt +++ b/src/BacktraceProvider/CMakeLists.txt @@ -2,15 +2,23 @@ cmake_minimum_required(VERSION 3.25) add_library( - backtrace-provider STATIC - BacktraceProvider.cpp - BacktraceProvider.hpp + backtrace-provider + STATIC + BacktraceProvider.cpp + BacktraceProvider.hpp ) target_include_directories( - backtrace-provider PUBLIC - ${CMAKE_SOURCE_DIR} + backtrace-provider + PUBLIC + ${CMAKE_SOURCE_DIR}/src ) target_link_libraries( - backtrace-provider PUBLIC - fmt::fmt argparse::argparse pog eternal::eternal hcpu_unwind pch + backtrace-provider + PUBLIC + fmt::fmt + argparse::argparse + pog + eternal::eternal + hcpu_unwind + pch ) \ No newline at end of file diff --git a/src/Emulator/CMakeLists.txt b/src/Emulator/CMakeLists.txt index 36eeaa8b..f194c8d4 100644 --- a/src/Emulator/CMakeLists.txt +++ b/src/Emulator/CMakeLists.txt @@ -2,87 +2,96 @@ cmake_minimum_required(VERSION 3.25) add_library( - assembler-core STATIC - Core/CPU/Instructions/AllowedFlags.cpp - Core/CPU/Interrupts/InterruptHandler.cpp - Core/CPU/CPU.cpp - Core/CPU/InstructionsImpl/DIV.cpp - Core/CPU/InstructionsImpl/CALLL.cpp - Core/CPU/InstructionsImpl/MUL.cpp - Core/CPU/InstructionsImpl/READ.cpp - Core/CPU/InstructionsImpl/ADC.cpp - Core/CPU/InstructionsImpl/ADD.cpp - Core/CPU/InstructionsImpl/DEC.cpp - Core/CPU/InstructionsImpl/SHFL.cpp - Core/CPU/InstructionsImpl/CUDF.cpp - Core/CPU/InstructionsImpl/PUSH.cpp - Core/CPU/InstructionsImpl/OR.cpp - Core/CPU/InstructionsImpl/POP.cpp - Core/CPU/InstructionsImpl/CALLGR.cpp - Core/CPU/InstructionsImpl/CCRF.cpp - Core/CPU/InstructionsImpl/JMP.cpp - Core/CPU/InstructionsImpl/AND.cpp - Core/CPU/InstructionsImpl/HID.cpp - Core/CPU/InstructionsImpl/CALL.cpp - Core/CPU/InstructionsImpl/INC.cpp - Core/CPU/InstructionsImpl/INTR.cpp - Core/CPU/InstructionsImpl/SHFR.cpp - Core/CPU/InstructionsImpl/CALLE.cpp - Core/CPU/InstructionsImpl/JML.cpp - Core/CPU/InstructionsImpl/SUB.cpp - Core/CPU/InstructionsImpl/ANDN.cpp - Core/CPU/InstructionsImpl/LOIVT.cpp - Core/CPU/InstructionsImpl/HALT.cpp - Core/CPU/InstructionsImpl/MOV.cpp - Core/CPU/InstructionsImpl/CMP.cpp - Core/CPU/InstructionsImpl/WRITE.cpp - Core/CPU/InstructionsImpl/BSWAP.cpp - Core/CPU/InstructionsImpl/JME.cpp - Core/CPU/InstructionsImpl/COVF.cpp - Core/CPU/InstructionsImpl/JMGR.cpp - Core/CPU/Decoders/StdDecoder.cpp - Core/CPU/Stack.cpp - Core/CPU/OperandsEvaluation.cpp - Core/CPU/IO/Simple.cpp - Core/MemoryController/MemoryControllerST.hpp - Core/MemoryController/IMemoryController.hpp - Core/CPU/Instructions/AllowedFlags.hpp - Core/CPU/Instructions/Opcodes.hpp - Core/CPU/Instructions/Flags.hpp - Core/CPU/Instructions/Registers.hpp - Core/CPU/Interrupts/ReservedInterrupts.hpp - Core/CPU/CPU.hpp - Core/CPU/ALU.hpp - Core/CPU/Decoders/StdDecoder.hpp - Core/CPU/Decoders/IDecoder.hpp - Core/CPU/Assert.hpp - Core/CPU/Version.hpp - Core/CPU/Util.hpp - Core/CPU/IO/Simple.hpp - Misc/byteswap.hpp - Misc/overflow.hpp - Misc/deref.hpp - Misc/bit_cast.hpp - Misc/unreachable.hpp - Misc/underflow.hpp - Misc/print.hpp + emulator-core + STATIC + Core/CPU/Instructions/AllowedFlags.cpp + Core/CPU/Interrupts/InterruptHandler.cpp + Core/CPU/CPU.cpp + Core/CPU/InstructionsImpl/DIV.cpp + Core/CPU/InstructionsImpl/CALLL.cpp + Core/CPU/InstructionsImpl/MUL.cpp + Core/CPU/InstructionsImpl/READ.cpp + Core/CPU/InstructionsImpl/ADC.cpp + Core/CPU/InstructionsImpl/ADD.cpp + Core/CPU/InstructionsImpl/DEC.cpp + Core/CPU/InstructionsImpl/SHFL.cpp + Core/CPU/InstructionsImpl/CUDF.cpp + Core/CPU/InstructionsImpl/PUSH.cpp + Core/CPU/InstructionsImpl/OR.cpp + Core/CPU/InstructionsImpl/POP.cpp + Core/CPU/InstructionsImpl/CALLGR.cpp + Core/CPU/InstructionsImpl/CCRF.cpp + Core/CPU/InstructionsImpl/JMP.cpp + Core/CPU/InstructionsImpl/AND.cpp + Core/CPU/InstructionsImpl/HID.cpp + Core/CPU/InstructionsImpl/CALL.cpp + Core/CPU/InstructionsImpl/INC.cpp + Core/CPU/InstructionsImpl/INTR.cpp + Core/CPU/InstructionsImpl/SHFR.cpp + Core/CPU/InstructionsImpl/CALLE.cpp + Core/CPU/InstructionsImpl/JML.cpp + Core/CPU/InstructionsImpl/SUB.cpp + Core/CPU/InstructionsImpl/ANDN.cpp + Core/CPU/InstructionsImpl/LOIVT.cpp + Core/CPU/InstructionsImpl/HALT.cpp + Core/CPU/InstructionsImpl/MOV.cpp + Core/CPU/InstructionsImpl/CMP.cpp + Core/CPU/InstructionsImpl/WRITE.cpp + Core/CPU/InstructionsImpl/BSWAP.cpp + Core/CPU/InstructionsImpl/JME.cpp + Core/CPU/InstructionsImpl/COVF.cpp + Core/CPU/InstructionsImpl/JMGR.cpp + Core/CPU/Decoders/StdDecoder.cpp + Core/CPU/Stack.cpp + Core/CPU/OperandsEvaluation.cpp + Core/CPU/IO/Simple.cpp + Core/MemoryController/MemoryControllerST.hpp + Core/MemoryController/IMemoryController.hpp + Core/CPU/Instructions/AllowedFlags.hpp + Core/CPU/Instructions/Opcodes.hpp + Core/CPU/Instructions/Flags.hpp + Core/CPU/Instructions/Registers.hpp + Core/CPU/Interrupts/ReservedInterrupts.hpp + Core/CPU/CPU.hpp + Core/CPU/ALU.hpp + Core/CPU/Decoders/StdDecoder.hpp + Core/CPU/Decoders/IDecoder.hpp + Core/CPU/Assert.hpp + Core/CPU/Version.hpp + Core/CPU/Util.hpp + Core/CPU/IO/Simple.hpp + Misc/byteswap.hpp + Misc/overflow.hpp + Misc/deref.hpp + Misc/bit_cast.hpp + Misc/unreachable.hpp + Misc/underflow.hpp + Misc/print.hpp ) target_include_directories( - assembler-core PUBLIC - ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} + emulator-core + PUBLIC + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries( - assembler-core PUBLIC - fmt::fmt argparse::argparse pog eternal::eternal pch + emulator-core + PUBLIC + fmt::fmt + argparse::argparse + pog + eternal::eternal + pch + hpool ) add_executable( - hcemul - Main/Main.hpp - Main/Main.cpp - Main/ExceptionHandling.cpp - Main/ExceptionHandling.hpp + hcemul + Main/Main.hpp + Main/Main.cpp + Main/ExceptionHandling.cpp + Main/ExceptionHandling.hpp ) -target_link_libraries(hcemul PRIVATE assembler-core) \ No newline at end of file +target_link_libraries(hcemul PRIVATE emulator-core) \ No newline at end of file diff --git a/src/Logger/Logger.hpp b/src/Logger/Logger.hpp index 3600f664..4856de89 100644 --- a/src/Logger/Logger.hpp +++ b/src/Logger/Logger.hpp @@ -2,71 +2,77 @@ #include -#include #include -#include +#include #include +#include namespace HyperCPU { - enum class LogLevel : std::uint_fast8_t { - DEBUG = 0, - INFO = 1, - WARNING = 2, - ERROR = 3 - }; + enum class LogLevel : std::uint_fast8_t { DEBUG = 0, INFO = 1, WARNING = 2, ERROR = 3 }; + + class Logger { + private: + LogLevel _loglevel; - class Logger { - private: - LogLevel _loglevel; + constexpr char DefineMsgChar(HyperCPU::LogLevel lvl) const noexcept { + switch (lvl) { + case HyperCPU::LogLevel::DEBUG: + return '-'; + case HyperCPU::LogLevel::INFO: + return '*'; + case HyperCPU::LogLevel::WARNING: + return '='; + case HyperCPU::LogLevel::ERROR: + return '!'; + default: + UNREACHABLE(); + } + } - constexpr char DefineMsgChar(HyperCPU::LogLevel lvl) const noexcept { - switch (lvl) { - case HyperCPU::LogLevel::DEBUG: return '-'; - case HyperCPU::LogLevel::INFO: return '*'; - case HyperCPU::LogLevel::WARNING: return '='; - case HyperCPU::LogLevel::ERROR: return '!'; - default: - UNREACHABLE(); - } - } + constexpr const char* DefineBoldColor(HyperCPU::LogLevel lvl) const noexcept { + switch (lvl) { + case HyperCPU::LogLevel::DEBUG: + case HyperCPU::LogLevel::INFO: + return RESET; + case HyperCPU::LogLevel::WARNING: + return B_YELLOW; + case HyperCPU::LogLevel::ERROR: + return B_RED; + default: + UNREACHABLE(); + } + } - constexpr const char* DefineBoldColor(HyperCPU::LogLevel lvl) const noexcept { - switch (lvl) { - case HyperCPU::LogLevel::DEBUG: - case HyperCPU::LogLevel::INFO: return RESET; - case HyperCPU::LogLevel::WARNING: return B_YELLOW; - case HyperCPU::LogLevel::ERROR: return B_RED; - default: - UNREACHABLE(); - } - } + constexpr const char* DefineColor(HyperCPU::LogLevel lvl) const noexcept { + switch (lvl) { + case HyperCPU::LogLevel::DEBUG: + case HyperCPU::LogLevel::INFO: + return RESET; + case HyperCPU::LogLevel::WARNING: + return YELLOW; + case HyperCPU::LogLevel::ERROR: + return RED; + default: + UNREACHABLE(); + } + } - constexpr const char* DefineColor(HyperCPU::LogLevel lvl) const noexcept { - switch (lvl) { - case HyperCPU::LogLevel::DEBUG: - case HyperCPU::LogLevel::INFO: return RESET; - case HyperCPU::LogLevel::WARNING: return YELLOW; - case HyperCPU::LogLevel::ERROR: return RED; - default: - UNREACHABLE(); - } - } + public: + Logger(LogLevel default_loglevel) : _loglevel(default_loglevel) { + } - public: - Logger(LogLevel default_loglevel) : _loglevel(default_loglevel) { } + template + void Log(LogLevel lvl, std::string_view fmt, Args&&... args) const noexcept { + if (static_cast(lvl) < static_cast(_loglevel)) { + return; + } - template - void Log(LogLevel lvl, std::string_view fmt, Args&&... args) const noexcept { - if (static_cast(lvl) < static_cast(_loglevel)) { - return; - } - - auto ch = DefineMsgChar(lvl); - auto bold_col = DefineBoldColor(lvl); - auto col = DefineColor(lvl); + auto ch = DefineMsgChar(lvl); + auto bold_col = DefineBoldColor(lvl); + auto col = DefineColor(lvl); - print("{}[{}]{} {}", bold_col, ch, RESET, col); - print("{}{}\n", fmt::vformat(fmt, fmt::make_format_args(std::forward(args)...)), RESET); - } - }; -} + print("{}[{}]{} {}", bold_col, ch, RESET, col); + print("{}{}\n", fmt::vformat(fmt, fmt::make_format_args(std::forward(args)...)), RESET); + } + }; +} // namespace HyperCPU diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 32a90565..21ab4c65 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,19 +1,10 @@ -include(../cmake/Variables.cmake) +# TODO: Enable CTest integration +# FIXME: Add separate precompiled headers here, too +cmake_minimum_required(VERSION 3.25) -set(TESTS_INCLUDE_DIR - ${ROOT_DIR}/dist/eternal/include - ${ROOT_DIR}/src/Emulator - ${ROOT_DIR}/test) -add_executable(modular_testing ${ROOT_DIR}/test/main.cpp ${SOURCES_modular_testing}) -target_link_libraries(modular_testing emulator-core assembler-core gtest pthread) -target_include_directories(modular_testing PUBLIC ${TESTS_INCLUDE_DIR}) -target_precompile_headers(modular_testing PRIVATE pch.hpp) - -add_executable(integration_testing ${ROOT_DIR}/test/main.cpp ${SOURCES_integration_testing}) -target_link_libraries(integration_testing emulator-core assembler-core gtest pthread) -target_include_directories(integration_testing PUBLIC ${TESTS_INCLUDE_DIR}) -target_precompile_headers(integration_testing PRIVATE pch.hpp) +add_subdirectory(Integration) +add_subdirectory(Modular) add_custom_target(run-all-tests-github ${CMAKE_BINARY_DIR}/modular_testing --gtest_brief=1 diff --git a/test/Integration/CMakeLists.txt b/test/Integration/CMakeLists.txt new file mode 100644 index 00000000..8b68c8af --- /dev/null +++ b/test/Integration/CMakeLists.txt @@ -0,0 +1,60 @@ +cmake_minimum_required(VERSION 3.25) + + +add_executable( + integration_testing + ${CMAKE_SOURCE_DIR}/test/main.cpp + EmulatorCore/CPU/CPU_CALLE.cpp + EmulatorCore/CPU/CPU_HID.cpp + EmulatorCore/CPU/CPU_READ.cpp + EmulatorCore/CPU/CPU_AND.cpp + EmulatorCore/CPU/CPU_INC.cpp + EmulatorCore/CPU/CPU_COVF.cpp + EmulatorCore/CPU/CPU_CALLL.cpp + EmulatorCore/CPU/CPU_ADC.cpp + EmulatorCore/CPU/CPU_ADD.cpp + EmulatorCore/CPU/CPU_MOV.cpp + EmulatorCore/CPU/CPU_CMP.cpp + EmulatorCore/CPU/CPU_OR.cpp + EmulatorCore/CPU/CPU_SHFL.cpp + EmulatorCore/CPU/CPU_POP.cpp + EmulatorCore/CPU/CPU_DEC.cpp + EmulatorCore/CPU/CPU_JML.cpp + EmulatorCore/CPU/CPU_SHFR.cpp + EmulatorCore/CPU/CPU_CALL.cpp + EmulatorCore/CPU/CPU_JMP.cpp + EmulatorCore/CPU/CPU_INTR.cpp + EmulatorCore/CPU/CPU_WRITE.cpp + EmulatorCore/CPU/CPU_IRET.cpp + EmulatorCore/CPU/CPU_MUL.cpp + EmulatorCore/CPU/CPU_JME.cpp + EmulatorCore/CPU/CPU_CALLGR.cpp + EmulatorCore/CPU/CPU_ANDN.cpp + EmulatorCore/CPU/CPU_CCRF.cpp + EmulatorCore/CPU/CPU_LOIVT.cpp + EmulatorCore/CPU/CPU_SUB.cpp + EmulatorCore/CPU/CPU_PUSH.cpp + EmulatorCore/CPU/CPU_DIV.cpp + EmulatorCore/CPU/OperandsEvaluation.cpp + EmulatorCore/CPU/CPU_JMGR.cpp + EmulatorCore/CPU/CPU_CUDF.cpp + EmulatorCore/CPU/CPU_BSWAP.cpp + EmulatorCore/Exceptions/Exceptions.cpp + AssemblerCore/TwoOperandsSuccess.cpp + AssemblerCore/AssemblerSuccess.cpp + AssemblerCore/FullAssembler.cpp + AssemblerCore/AssemblerFail.cpp +) +target_link_libraries( + integration_testing + PRIVATE + emulator-core + assembler-core + GTest::gtest + pthread +) +target_include_directories( + integration_testing + PRIVATE + ${CMAKE_SOURCE_DIR}/test +) diff --git a/test/Integration/EmulatorCore/CPU/CPU_CALLE.cpp b/test/Integration/EmulatorCore/CPU/CPU_CALLE.cpp index c8d0f436..ba8194a8 100644 --- a/test/Integration/EmulatorCore/CPU/CPU_CALLE.cpp +++ b/test/Integration/EmulatorCore/CPU/CPU_CALLE.cpp @@ -1,71 +1,68 @@ -#include #include +#include #include - TEST_F(CPU_TEST, INSTR_CALLE_R_TRUE) { - cpu.mem_controller->Load16(*cpu.xip, HyperCPU::Opcode::CALLE); - cpu.mem_controller->Load8(*cpu.xip + 2, (HyperCPU::Mode::b64 << 4) | HyperCPU::OperandTypes::R); - cpu.mem_controller->Load8(*cpu.xip + 3, HyperCPU::Registers::X0); - cpu.mem_controller->Load16(1536, HyperCPU::Opcode::HALT); - cpu.mem_controller->Load8(1538, HyperCPU::OperandTypes::NONE); - *cpu.xbp = 512; - *cpu.xsp = *cpu.xbp; - *cpu.x0 = 1536; - cpu.zrf = 1; - - - cpu.Run(); - - ASSERT_EQ(*cpu.xip, 1539); + cpu.mem_controller->Load16(*cpu.xip, HyperCPU::Opcode::CALLE); + cpu.mem_controller->Load8(*cpu.xip + 2, (HyperCPU::Mode::b64 << 4) | HyperCPU::OperandTypes::R); + cpu.mem_controller->Load8(*cpu.xip + 3, HyperCPU::Registers::X0); + cpu.mem_controller->Load16(1536, HyperCPU::Opcode::HALT); + cpu.mem_controller->Load8(1538, HyperCPU::OperandTypes::NONE); + *cpu.xbp = 512; + *cpu.xsp = *cpu.xbp; + *cpu.x0 = 1536; + cpu.zrf = 1; + + cpu.Run(); + + ASSERT_EQ(*cpu.xip, 1539); } TEST_F(CPU_TEST, INSTR_CALLE_R_FALSE) { - cpu.mem_controller->Load16(*cpu.xip, HyperCPU::Opcode::CALLE); - cpu.mem_controller->Load8(*cpu.xip + 2, (HyperCPU::Mode::b64 << 4) | HyperCPU::OperandTypes::R); - cpu.mem_controller->Load8(*cpu.xip + 3, HyperCPU::Registers::X0); - cpu.mem_controller->Load16(*cpu.xip + 4, HyperCPU::Opcode::HALT); - cpu.mem_controller->Load8(*cpu.xip + 6, HyperCPU::OperandTypes::NONE); - cpu.mem_controller->Load16(1536, HyperCPU::Opcode::HALT); - cpu.mem_controller->Load8(1538, HyperCPU::OperandTypes::NONE); - *cpu.xbp = 512; - *cpu.xsp = *cpu.xbp; - *cpu.x0 = 1536; - - - cpu.Run(); - - ASSERT_EQ(*cpu.xip, 7); + cpu.mem_controller->Load16(*cpu.xip, HyperCPU::Opcode::CALLE); + cpu.mem_controller->Load8(*cpu.xip + 2, (HyperCPU::Mode::b64 << 4) | HyperCPU::OperandTypes::R); + cpu.mem_controller->Load8(*cpu.xip + 3, HyperCPU::Registers::X0); + cpu.mem_controller->Load16(*cpu.xip + 4, HyperCPU::Opcode::HALT); + cpu.mem_controller->Load8(*cpu.xip + 6, HyperCPU::OperandTypes::NONE); + cpu.mem_controller->Load16(1536, HyperCPU::Opcode::HALT); + cpu.mem_controller->Load8(1538, HyperCPU::OperandTypes::NONE); + *cpu.xbp = 512; + *cpu.xsp = *cpu.xbp; + *cpu.x0 = 1536; + + cpu.Run(); + + ASSERT_EQ(*cpu.xip, 7); } TEST_F(CPU_TEST, INSTR_CALLE_IMM_TRUE) { - cpu.mem_controller->Load16(*cpu.xip, HyperCPU::Opcode::CALLE); - cpu.mem_controller->Load8(*cpu.xip + 2, (HyperCPU::Mode::b64 << 4) | HyperCPU::OperandTypes::IMM); - cpu.mem_controller->Load64(*cpu.xip + 3, 1536); - cpu.mem_controller->Load16(1536, HyperCPU::Opcode::HALT); - cpu.mem_controller->Load8(1538, HyperCPU::OperandTypes::NONE); - *cpu.xbp = 512; - *cpu.xsp = *cpu.xbp; - cpu.zrf = 1; - - cpu.Run(); - - ASSERT_EQ(*cpu.xip, 1539); + cpu.mem_controller->Load16(*cpu.xip, HyperCPU::Opcode::CALLE); + cpu.mem_controller->Load8(*cpu.xip + 2, (HyperCPU::Mode::b64 << 4) | HyperCPU::OperandTypes::IMM); + cpu.mem_controller->Load64(*cpu.xip + 3, 1536); + cpu.mem_controller->Load16(1536, HyperCPU::Opcode::HALT); + cpu.mem_controller->Load8(1538, HyperCPU::OperandTypes::NONE); + *cpu.xbp = 512; + *cpu.xsp = *cpu.xbp; + cpu.zrf = 1; + + cpu.Run(); + + ASSERT_EQ(*cpu.xip, 1539); } TEST_F(CPU_TEST, INSTR_CALLE_IMM_FALSE) { - cpu.mem_controller->Load16(*cpu.xip, HyperCPU::Opcode::CALLE); - cpu.mem_controller->Load8(*cpu.xip + 2, (HyperCPU::Mode::b64 << 4) | HyperCPU::OperandTypes::IMM); - cpu.mem_controller->Load64(*cpu.xip + 3, 1536); - cpu.mem_controller->Load16(*cpu.xip + 11, HyperCPU::Opcode::HALT); - cpu.mem_controller->Load8(*cpu.xip + 13, HyperCPU::OperandTypes::NONE); - cpu.mem_controller->Load16(1536, HyperCPU::Opcode::HALT); - cpu.mem_controller->Load8(1538, HyperCPU::OperandTypes::NONE); - *cpu.xbp = 512; - *cpu.xsp = *cpu.xbp; - - cpu.Run(); - - ASSERT_EQ(*cpu.xip, 14); + cpu.mem_controller->Load16(*cpu.xip, HyperCPU::Opcode::CALLE); + cpu.mem_controller->Load8(*cpu.xip + 2, (HyperCPU::Mode::b64 << 4) | HyperCPU::OperandTypes::IMM); + cpu.mem_controller->Load64(*cpu.xip + 3, 1536); + cpu.mem_controller->Load16(*cpu.xip + 11, HyperCPU::Opcode::HALT); + cpu.mem_controller->Load8(*cpu.xip + 13, HyperCPU::OperandTypes::NONE); + cpu.mem_controller->Load16(1536, HyperCPU::Opcode::HALT); + cpu.mem_controller->Load8(1538, HyperCPU::OperandTypes::NONE); + *cpu.xbp = 512; + *cpu.xsp = *cpu.xbp; + + cpu.Run(); + + ASSERT_EQ(*cpu.xip, 14); } \ No newline at end of file diff --git a/test/Modular/CMakeLists.txt b/test/Modular/CMakeLists.txt new file mode 100644 index 00000000..41c74184 --- /dev/null +++ b/test/Modular/CMakeLists.txt @@ -0,0 +1,111 @@ +cmake_minimum_required(VERSION 3.25) + + +add_executable(modular_testing + ${CMAKE_SOURCE_DIR}/test/main.cpp + EmulatorCore/Decoding/CMPInstr/R_R.cpp + EmulatorCore/Decoding/CMPInstr/RM_IMM.cpp + EmulatorCore/Decoding/CMPInstr/RM_M.cpp + EmulatorCore/Decoding/CMPInstr/M_R.cpp + EmulatorCore/Decoding/CMPInstr/RM_R.cpp + EmulatorCore/Decoding/CMPInstr/Unsupported.cpp + EmulatorCore/Decoding/CMPInstr/R_RM.cpp + EmulatorCore/Decoding/CMPInstr/R_IMM.cpp + EmulatorCore/Decoding/CMPInstr/R_M.cpp + EmulatorCore/Decoding/SHFLInstr/R_R.cpp + EmulatorCore/Decoding/SHFLInstr/Unsupported.cpp + EmulatorCore/Decoding/SHFLInstr/R_IMM.cpp + EmulatorCore/Decoding/SHFRInstr/R_R.cpp + EmulatorCore/Decoding/SHFRInstr/Unsupported.cpp + EmulatorCore/Decoding/SHFRInstr/R_IMM.cpp + EmulatorCore/Decoding/JMLInstr/test_decoder_r.cpp + EmulatorCore/Decoding/JMLInstr/test_decoder_imm.cpp + EmulatorCore/Decoding/JMLInstr/test_decoder_unsupported.cpp + EmulatorCore/Decoding/BSWAPInstr/R.cpp + EmulatorCore/Decoding/BSWAPInstr/Unsupported.cpp + EmulatorCore/Decoding/CALLGRInstr/test_decoder_r.cpp + EmulatorCore/Decoding/CALLGRInstr/test_decoder_imm.cpp + EmulatorCore/Decoding/CALLGRInstr/test_decoder_unsupported.cpp + EmulatorCore/Decoding/MULInstr/R_R.cpp + EmulatorCore/Decoding/MULInstr/Unsupported.cpp + EmulatorCore/Decoding/MULInstr/R_RM.cpp + EmulatorCore/Decoding/MULInstr/R_IMM.cpp + EmulatorCore/Decoding/MULInstr/R_M.cpp + EmulatorCore/Decoding/WRITEInstr/R_R.cpp + EmulatorCore/Decoding/WRITEInstr/Unsupported.cpp + EmulatorCore/Decoding/WRITEInstr/R_IMM.cpp + EmulatorCore/Decoding/DECInstr/R.cpp + EmulatorCore/Decoding/DECInstr/Unsupported.cpp + EmulatorCore/Decoding/CUDFInstr/Unsupported.cpp + EmulatorCore/Decoding/CUDFInstr/None.cpp + EmulatorCore/Decoding/CALLEInstr/test_decoder_r.cpp + EmulatorCore/Decoding/CALLEInstr/test_decoder_imm.cpp + EmulatorCore/Decoding/CALLEInstr/test_decoder_unsupported.cpp + EmulatorCore/Decoding/INCInstr/R.cpp + EmulatorCore/Decoding/INCInstr/Unsupported.cpp + EmulatorCore/Decoding/COVFInstr/test_decoder_unsupported.cpp + EmulatorCore/Decoding/COVFInstr/test_decoder_none.cpp + EmulatorCore/Decoding/JMGRInstr/test_decoder_r.cpp + EmulatorCore/Decoding/JMGRInstr/test_decoder_imm.cpp + EmulatorCore/Decoding/JMGRInstr/test_decoder_unsupported.cpp + EmulatorCore/Decoding/JMPInstr/test_decoder_r.cpp + EmulatorCore/Decoding/JMPInstr/test_decoder_imm.cpp + EmulatorCore/Decoding/JMPInstr/test_decoder_unsupported.cpp + EmulatorCore/Decoding/SUBInstr/R_R.cpp + EmulatorCore/Decoding/SUBInstr/Unsupported.cpp + EmulatorCore/Decoding/SUBInstr/R_RM.cpp + EmulatorCore/Decoding/SUBInstr/R_IMM.cpp + EmulatorCore/Decoding/SUBInstr/R_M.cpp + EmulatorCore/Decoding/ANDInstr/R_R.cpp + EmulatorCore/Decoding/ANDInstr/Unsupported.cpp + EmulatorCore/Decoding/ANDInstr/R_RM.cpp + EmulatorCore/Decoding/ANDInstr/R_IMM.cpp + EmulatorCore/Decoding/ANDInstr/R_M.cpp + EmulatorCore/Decoding/MOVInstr/R_R.cpp + EmulatorCore/Decoding/MOVInstr/RM_IMM.cpp + EmulatorCore/Decoding/MOVInstr/RM_M.cpp + EmulatorCore/Decoding/MOVInstr/M_R.cpp + EmulatorCore/Decoding/MOVInstr/RM_R.cpp + EmulatorCore/Decoding/MOVInstr/Unsupported.cpp + EmulatorCore/Decoding/MOVInstr/R_RM.cpp + EmulatorCore/Decoding/MOVInstr/R_IMM.cpp + EmulatorCore/Decoding/MOVInstr/R_M.cpp + EmulatorCore/Decoding/ADDInstr/R_R.cpp + EmulatorCore/Decoding/ADDInstr/Unsupported.cpp + EmulatorCore/Decoding/ADDInstr/R_RM.cpp + EmulatorCore/Decoding/ADDInstr/R_IMM.cpp + EmulatorCore/Decoding/ADDInstr/R_M.cpp + EmulatorCore/Decoding/HIDInstr/Unsupported.cpp + EmulatorCore/Decoding/HIDInstr/None.cpp + EmulatorCore/Decoding/JMEInstr/test_decoder_r.cpp + EmulatorCore/Decoding/JMEInstr/test_decoder_imm.cpp + EmulatorCore/Decoding/JMEInstr/test_decoder_unsupported.cpp + EmulatorCore/Decoding/ANDNInstr/R_R.cpp + EmulatorCore/Decoding/ANDNInstr/Unsupported.cpp + EmulatorCore/Decoding/ANDNInstr/R_RM.cpp + EmulatorCore/Decoding/ANDNInstr/R_IMM.cpp + EmulatorCore/Decoding/ANDNInstr/R_M.cpp + EmulatorCore/Decoding/DIVInstr/R.cpp + EmulatorCore/Decoding/DIVInstr/Unsupported.cpp + EmulatorCore/Decoding/CALLLInstr/test_decoder_r.cpp + EmulatorCore/Decoding/CALLLInstr/test_decoder_imm.cpp + EmulatorCore/Decoding/CALLLInstr/test_decoder_unsupported.cpp + EmulatorCore/Decoding/CALLInstr/test_decoder_r.cpp + EmulatorCore/Decoding/CALLInstr/test_decoder_imm.cpp + EmulatorCore/Decoding/CALLInstr/test_decoder_unsupported.cpp + EmulatorCore/Decoding/ADCInstr/R_R.cpp +) +target_link_libraries( + modular_testing + PRIVATE + emulator-core + assembler-core + GTest::gtest + pthread + pch +) +target_include_directories( + modular_testing + PRIVATE + ${CMAKE_SOURCE_DIR}/test +) diff --git a/test/fixtures.hpp b/test/fixtures.hpp index 1b642695..1726ce6a 100644 --- a/test/fixtures.hpp +++ b/test/fixtures.hpp @@ -1,19 +1,18 @@ #pragma once +#include #include -#include #define private public -#include -#include -#include +#include +#include +#include #include #include -#include -#include +#include +#include +#include #include -#include - static constexpr std::size_t MEM_SIZE = 4096; static constexpr std::size_t MEM_FIXTURE_MEM_SIZE = 1024; @@ -21,189 +20,199 @@ static constexpr std::size_t MEM_PTR = 0x0102030405060708; class TempDir { public: - TempDir(const char* test_name) { - dir_name = "test_"; - dir_name += test_name; - std::filesystem::create_directory(dir_name); - [[maybe_unused]] auto t = chdir(dir_name.c_str()); - } - - ~TempDir() { - [[maybe_unused]] auto t = chdir(".."); - try { - std::filesystem::remove_all(dir_name); - } catch (std::filesystem::filesystem_error&) { } - } + TempDir(const char* test_name) { + dir_name = "test_"; + dir_name += test_name; + std::filesystem::create_directory(dir_name); + [[maybe_unused]] auto t = chdir(dir_name.c_str()); + } + + ~TempDir() { + [[maybe_unused]] auto t = chdir(".."); + try { + std::filesystem::remove_all(dir_name); + } catch (std::filesystem::filesystem_error&) { + } + } + private: - std::string dir_name; + std::string dir_name; }; class MC_ST_TEST : public testing::Test { - protected: +protected: HyperCPU::MemoryControllerST mcmt; char tmp_buffer[MEM_FIXTURE_MEM_SIZE]; std::size_t counter; MC_ST_TEST() : mcmt(MEM_FIXTURE_MEM_SIZE), counter(0) { - std::memset(tmp_buffer, 0x55, MEM_FIXTURE_MEM_SIZE); + std::memset(tmp_buffer, 0x55, MEM_FIXTURE_MEM_SIZE); } }; class MC_ST_FAILTEST : public testing::Test { - protected: +protected: HyperCPU::MemoryControllerST mcmt; char tmp_buffer[MEM_FIXTURE_MEM_SIZE]; std::size_t counter; MC_ST_FAILTEST() : mcmt(MEM_FIXTURE_MEM_SIZE), counter(LONG_MAX) { - std::memset(tmp_buffer, 0x55, MEM_FIXTURE_MEM_SIZE); + std::memset(tmp_buffer, 0x55, MEM_FIXTURE_MEM_SIZE); } }; class MC_ST_NEARFAILTEST : public testing::Test { - protected: +protected: HyperCPU::MemoryControllerST mcmt; std::size_t counter; - MC_ST_NEARFAILTEST() : mcmt(MEM_FIXTURE_MEM_SIZE) {} + MC_ST_NEARFAILTEST() : mcmt(MEM_FIXTURE_MEM_SIZE) { + } }; class DECODER_TEST : public ::testing::Test { protected: - HyperCPU::Decoder decoder; - std::size_t counter; + HyperCPU::Decoder decoder; + std::size_t counter; - DECODER_TEST() = default; + DECODER_TEST() = default; - void SetUp() { - decoder = HyperCPU::Decoder(new HyperCPU::MemoryControllerST(MEM_SIZE), &counter, nullptr); - counter = 0; - } + void SetUp() { + decoder = HyperCPU::Decoder(new HyperCPU::MemoryControllerST(MEM_SIZE), &counter, nullptr); + counter = 0; + } - void TearDown() { - delete dynamic_cast(decoder.mem_controller); - decoder.~Decoder(); - } + void TearDown() { + delete dynamic_cast(decoder.mem_controller); + decoder.~Decoder(); + } }; class CPU_TEST : public ::testing::Test { protected: - HyperCPU::Logger logger; - HyperCPU::CPU cpu; + HyperCPU::Logger logger; + HyperCPU::CPU cpu; - CPU_TEST() : logger(HyperCPU::LogLevel::ERROR), cpu(1, 4096) { } + CPU_TEST() : logger(HyperCPU::LogLevel::ERROR), cpu(1, 4096) { + } }; class OPERAND_EVAL_TEST : public ::testing::Test { protected: - HyperCPU::Logger logger; - HyperCPU::CPU cpu; - std::pair result; + HyperCPU::Logger logger; + HyperCPU::CPU cpu; + std::pair result; - OPERAND_EVAL_TEST() : logger(HyperCPU::LogLevel::ERROR), cpu(1, 4096) { } + OPERAND_EVAL_TEST() : logger(HyperCPU::LogLevel::ERROR), cpu(1, 4096) { + } }; class STACK_TEST : public ::testing::Test { protected: - HyperCPU::Logger logger; - HyperCPU::CPU cpu; + HyperCPU::Logger logger; + HyperCPU::CPU cpu; - STACK_TEST() : logger(HyperCPU::LogLevel::ERROR), cpu(1, 4096) { - *cpu.xbp = 1024; - *cpu.xsp = 1024; - } + STACK_TEST() : logger(HyperCPU::LogLevel::ERROR), cpu(1, 4096) { + *cpu.xbp = 1024; + *cpu.xsp = 1024; + } }; class IVT_INIT_TEST : public ::testing::Test { protected: - HyperCPU::Logger logger; - HyperCPU::CPU cpu; + HyperCPU::Logger logger; + HyperCPU::CPU cpu; - IVT_INIT_TEST() : logger(HyperCPU::LogLevel::ERROR), cpu(1, 4096) { - - } + IVT_INIT_TEST() : logger(HyperCPU::LogLevel::ERROR), cpu(1, 4096) { + } }; class EXCEPTION_TEST : public ::testing::Test { protected: - HyperCPU::Logger logger; - HyperCPU::CPU cpu; + HyperCPU::Logger logger; + HyperCPU::CPU cpu; - EXCEPTION_TEST() : logger(HyperCPU::LogLevel::ERROR), cpu(1, 4096) { } + EXCEPTION_TEST() : logger(HyperCPU::LogLevel::ERROR), cpu(1, 4096) { + } - virtual void SetUp() { - *cpu.xsp = 512; - *cpu.xbp = 512; - *cpu.xivt = 2048; - cpu.mem_controller->Load64(2048, 1536); - cpu.mem_controller->Load64(2056, 1536); - cpu.mem_controller->Load64(2064, 1536); - cpu.mem_controller->Load64(1536, HyperCPU::Opcode::HALT); - cpu.mem_controller->Load64(1538, HyperCPU::OperandTypes::NONE); - cpu.ivt_initialized = true; - } + virtual void SetUp() { + *cpu.xsp = 512; + *cpu.xbp = 512; + *cpu.xivt = 2048; + cpu.mem_controller->Load64(2048, 1536); + cpu.mem_controller->Load64(2056, 1536); + cpu.mem_controller->Load64(2064, 1536); + cpu.mem_controller->Load64(1536, HyperCPU::Opcode::HALT); + cpu.mem_controller->Load64(1538, HyperCPU::OperandTypes::NONE); + cpu.ivt_initialized = true; + } }; class ASM_PARSER_TEST : public ::testing::Test { protected: - HCAsm::HCAsmCompiler compiler; - HCAsm::CompilerState _state; - pog::Parser& parser; + HCAsm::HCAsmCompiler compiler; + HCAsm::CompilerState _state; + pog::Parser& parser; - ASM_PARSER_TEST() : compiler(HyperCPU::LogLevel::ERROR), _state(compiler.pool), parser(compiler.parser) { - parser.set_compiler_state(&_state); - } + ASM_PARSER_TEST() : compiler(HyperCPU::LogLevel::ERROR), _state(compiler.pool), parser(compiler.parser) { + parser.set_compiler_state(&_state); + } }; class ASM_PARSER_STMT_TEST : public ::testing::Test { protected: - HCAsm::HCAsmCompiler compiler; + HCAsm::HCAsmCompiler compiler; - ASM_PARSER_STMT_TEST() : compiler(HyperCPU::LogLevel::ERROR) { } + ASM_PARSER_STMT_TEST() : compiler(HyperCPU::LogLevel::ERROR) { + } - virtual void TearDown() { - HCAsm::current_index = 0; - } + virtual void TearDown() { + HCAsm::current_index = 0; + } }; class ASSEMBLER : public ::testing::Test { protected: - HCAsm::HCAsmCompiler compiler; + HCAsm::HCAsmCompiler compiler; - ASSEMBLER() : compiler(HyperCPU::LogLevel::ERROR) { } + ASSEMBLER() : compiler(HyperCPU::LogLevel::ERROR) { + } - virtual void TearDown() { - HCAsm::current_index = 0; - } + virtual void TearDown() { + HCAsm::current_index = 0; + } }; class FULL_ASSEMBLER : public ::testing::Test { protected: - HCAsm::HCAsmCompiler compiler; - TempDir dir; + HCAsm::HCAsmCompiler compiler; + TempDir dir; - FULL_ASSEMBLER() : compiler(HyperCPU::LogLevel::ERROR), dir(::testing::UnitTest::GetInstance()->current_test_info()->test_case_name()) { } + FULL_ASSEMBLER() : compiler(HyperCPU::LogLevel::ERROR), dir(::testing::UnitTest::GetInstance()->current_test_info()->test_case_name()) { + } - virtual void TearDown() { - HCAsm::current_index = 0; - } + virtual void TearDown() { + HCAsm::current_index = 0; + } }; class TWO_OPERANDS_SUCCESS : public ::testing::Test { protected: - HCAsm::HCAsmCompiler compiler; + HCAsm::HCAsmCompiler compiler; - TWO_OPERANDS_SUCCESS() : compiler(HyperCPU::LogLevel::ERROR) { } + TWO_OPERANDS_SUCCESS() : compiler(HyperCPU::LogLevel::ERROR) { + } - virtual void TearDown() { - HCAsm::current_index = 0; - } + virtual void TearDown() { + HCAsm::current_index = 0; + } }; class TWO_OPERANDS_FAILURE : public ::testing::Test { protected: - HCAsm::HCAsmCompiler compiler; + HCAsm::HCAsmCompiler compiler; - TWO_OPERANDS_FAILURE() : compiler(HyperCPU::LogLevel::ERROR) { } + TWO_OPERANDS_FAILURE() : compiler(HyperCPU::LogLevel::ERROR) { + } - virtual void TearDown() { - HCAsm::current_index = 0; - } + virtual void TearDown() { + HCAsm::current_index = 0; + } }; diff --git a/test/main.cpp b/test/main.cpp index 7c30f193..836659c0 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -1,8 +1,8 @@ +#include #include -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); + return RUN_ALL_TESTS(); } diff --git a/test/pch.hpp b/test/pch.hpp deleted file mode 100644 index 2dacb8ab..00000000 --- a/test/pch.hpp +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Precompiled headers - * ! Only add headers that will not be modified ! - */ -#include -#include -#include From f6b64c01deb1cdd9ac36adfda28a088674255080 Mon Sep 17 00:00:00 2001 From: AshFungor Date: Mon, 21 Apr 2025 03:36:09 +0300 Subject: [PATCH 04/25] fixing bugs for bot --- CMakeLists.txt | 2 +- scripts/build.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 09ead28e..e6c4e4d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,7 @@ add_compile_options( if(HCPU_LTO) check_ipo_supported(RESULT result OUTPUT output) if(result) - set_property(TARGET foo PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) message(STATUS "HCPU: Enabled lTO") else() message(WARNING "HCPU: LTO is not supported: ${output}") diff --git a/scripts/build.py b/scripts/build.py index d57f82cf..82923c42 100755 --- a/scripts/build.py +++ b/scripts/build.py @@ -153,6 +153,7 @@ def build(self): @routine(1) def symlink_compile_commands(self: 'Routines') -> None: + path = None if 'Debug' in self.__params.build_configs: path = self.__params.build_directory.joinpath('Debug').joinpath('compile_commands.json') logger.info(f'creating symlink for path \'{path}\'') From ecee2a731fbe66f324812d5a4fd3e511053e5e07 Mon Sep 17 00:00:00 2001 From: AshFungor Date: Tue, 22 Apr 2025 01:47:52 +0300 Subject: [PATCH 05/25] refactored conan api calls, added ctest and patched ci --- .clang-format | 71 ++++++-- .github/workflows/distro-ci.yml | 20 ++- .../workflows/run-tests-feature-branch.yml | 31 ++-- .github/workflows/testing.yml | 56 +++--- CMakeLists.txt | 22 +-- Testing/Temporary/CTestCostData.txt | 1 + cmake/PrecompiledHeaders.cmake | 0 conanfile.py | 8 +- docker/alpine/Dockerfile | 4 +- docker/archlinux/Dockerfile | 6 +- docker/debian-stable/Dockerfile | 8 +- docker/debian-unstable/Dockerfile | 6 +- docker/fedora/Dockerfile | 11 +- docker/ubuntu/Dockerfile | 6 +- scripts/build.py | 165 +++++++++++------- test/CMakeLists.txt | 21 --- tests/CMakeLists.txt | 8 + .../AssemblerCore/AssemblerFail.cpp | 0 .../AssemblerCore/AssemblerSuccess.cpp | 0 .../AssemblerCore/FullAssembler.cpp | 0 .../AssemblerCore/TwoOperandsSuccess.cpp | 0 {test => tests}/Integration/CMakeLists.txt | 11 +- .../Integration/EmulatorCore/CPU/CPU_ADC.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_ADD.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_AND.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_ANDN.cpp | 0 .../EmulatorCore/CPU/CPU_BSWAP.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_CALL.cpp | 0 .../EmulatorCore/CPU/CPU_CALLE.cpp | 0 .../EmulatorCore/CPU/CPU_CALLGR.cpp | 0 .../EmulatorCore/CPU/CPU_CALLL.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_CCRF.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_CMP.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_COVF.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_CUDF.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_DEC.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_DIV.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_HID.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_INC.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_INTR.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_IRET.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_JME.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_JMGR.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_JML.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_JMP.cpp | 0 .../EmulatorCore/CPU/CPU_LOIVT.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_MOV.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_MUL.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_OR.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_POP.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_PUSH.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_READ.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_SHFL.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_SHFR.cpp | 0 .../Integration/EmulatorCore/CPU/CPU_SUB.cpp | 0 .../EmulatorCore/CPU/CPU_WRITE.cpp | 0 .../EmulatorCore/CPU/OperandsEvaluation.cpp | 0 .../EmulatorCore/Exceptions/Exceptions.cpp | 0 .../Modular/AssemblerCore/Parser/Operands.cpp | 0 .../AssemblerCore/Parser/Statements.cpp | 0 .../Modular/AssemblerCore/Parser/Tokens.cpp | 0 {test => tests}/Modular/CMakeLists.txt | 36 +++- .../Modular/EmulatorCore/CPUInit/CPUInit.cpp | 2 +- .../EmulatorCore/Decoding/ADCInstr/R_IMM.cpp | 0 .../EmulatorCore/Decoding/ADCInstr/R_M.cpp | 0 .../EmulatorCore/Decoding/ADCInstr/R_R.cpp | 0 .../EmulatorCore/Decoding/ADCInstr/R_RM.cpp | 0 .../Decoding/ADCInstr/Unsupported.cpp | 0 .../EmulatorCore/Decoding/ADDInstr/R_IMM.cpp | 0 .../EmulatorCore/Decoding/ADDInstr/R_M.cpp | 0 .../EmulatorCore/Decoding/ADDInstr/R_R.cpp | 0 .../EmulatorCore/Decoding/ADDInstr/R_RM.cpp | 0 .../Decoding/ADDInstr/Unsupported.cpp | 0 .../EmulatorCore/Decoding/ANDInstr/R_IMM.cpp | 0 .../EmulatorCore/Decoding/ANDInstr/R_M.cpp | 0 .../EmulatorCore/Decoding/ANDInstr/R_R.cpp | 0 .../EmulatorCore/Decoding/ANDInstr/R_RM.cpp | 0 .../Decoding/ANDInstr/Unsupported.cpp | 0 .../EmulatorCore/Decoding/ANDNInstr/R_IMM.cpp | 0 .../EmulatorCore/Decoding/ANDNInstr/R_M.cpp | 0 .../EmulatorCore/Decoding/ANDNInstr/R_R.cpp | 0 .../EmulatorCore/Decoding/ANDNInstr/R_RM.cpp | 0 .../Decoding/ANDNInstr/Unsupported.cpp | 0 .../EmulatorCore/Decoding/BSWAPInstr/R.cpp | 0 .../Decoding/BSWAPInstr/Unsupported.cpp | 0 .../Decoding/CALLEInstr/test_decoder_imm.cpp | 0 .../Decoding/CALLEInstr/test_decoder_r.cpp | 0 .../CALLEInstr/test_decoder_unsupported.cpp | 0 .../Decoding/CALLGRInstr/test_decoder_imm.cpp | 0 .../Decoding/CALLGRInstr/test_decoder_r.cpp | 0 .../CALLGRInstr/test_decoder_unsupported.cpp | 0 .../Decoding/CALLInstr/test_decoder_imm.cpp | 0 .../Decoding/CALLInstr/test_decoder_r.cpp | 0 .../CALLInstr/test_decoder_unsupported.cpp | 0 .../Decoding/CALLLInstr/test_decoder_imm.cpp | 0 .../Decoding/CALLLInstr/test_decoder_r.cpp | 0 .../CALLLInstr/test_decoder_unsupported.cpp | 0 .../Decoding/CCRFInstr/test_decoder_none.cpp | 0 .../CCRFInstr/test_decoder_unsupported.cpp | 0 .../EmulatorCore/Decoding/CMPInstr/M_R.cpp | 0 .../EmulatorCore/Decoding/CMPInstr/RM_IMM.cpp | 0 .../EmulatorCore/Decoding/CMPInstr/RM_M.cpp | 0 .../EmulatorCore/Decoding/CMPInstr/RM_R.cpp | 0 .../EmulatorCore/Decoding/CMPInstr/R_IMM.cpp | 0 .../EmulatorCore/Decoding/CMPInstr/R_M.cpp | 0 .../EmulatorCore/Decoding/CMPInstr/R_R.cpp | 0 .../EmulatorCore/Decoding/CMPInstr/R_RM.cpp | 0 .../Decoding/CMPInstr/Unsupported.cpp | 0 .../Decoding/COVFInstr/test_decoder_none.cpp | 0 .../COVFInstr/test_decoder_unsupported.cpp | 0 .../EmulatorCore/Decoding/CUDFInstr/None.cpp | 0 .../Decoding/CUDFInstr/Unsupported.cpp | 0 .../EmulatorCore/Decoding/DECInstr/R.cpp | 0 .../Decoding/DECInstr/Unsupported.cpp | 0 .../EmulatorCore/Decoding/DIVInstr/R.cpp | 0 .../Decoding/DIVInstr/Unsupported.cpp | 0 .../Decoding/Features/AddressAddition.cpp | 0 .../EmulatorCore/Decoding/HALTInstr/None.cpp | 0 .../Decoding/HALTInstr/Unsupported.cpp | 0 .../EmulatorCore/Decoding/HIDInstr/None.cpp | 0 .../Decoding/HIDInstr/Unsupported.cpp | 0 .../EmulatorCore/Decoding/INCInstr/R.cpp | 0 .../Decoding/INCInstr/Unsupported.cpp | 0 .../Decoding/JMEInstr/test_decoder_imm.cpp | 0 .../Decoding/JMEInstr/test_decoder_r.cpp | 0 .../JMEInstr/test_decoder_unsupported.cpp | 0 .../Decoding/JMGRInstr/test_decoder_imm.cpp | 0 .../Decoding/JMGRInstr/test_decoder_r.cpp | 0 .../JMGRInstr/test_decoder_unsupported.cpp | 0 .../Decoding/JMLInstr/test_decoder_imm.cpp | 0 .../Decoding/JMLInstr/test_decoder_r.cpp | 0 .../JMLInstr/test_decoder_unsupported.cpp | 0 .../Decoding/JMPInstr/test_decoder_imm.cpp | 0 .../Decoding/JMPInstr/test_decoder_r.cpp | 0 .../JMPInstr/test_decoder_unsupported.cpp | 0 .../EmulatorCore/Decoding/MOVInstr/M_R.cpp | 0 .../EmulatorCore/Decoding/MOVInstr/RM_IMM.cpp | 0 .../EmulatorCore/Decoding/MOVInstr/RM_M.cpp | 0 .../EmulatorCore/Decoding/MOVInstr/RM_R.cpp | 0 .../EmulatorCore/Decoding/MOVInstr/R_IMM.cpp | 0 .../EmulatorCore/Decoding/MOVInstr/R_M.cpp | 0 .../EmulatorCore/Decoding/MOVInstr/R_R.cpp | 0 .../EmulatorCore/Decoding/MOVInstr/R_RM.cpp | 0 .../Decoding/MOVInstr/Unsupported.cpp | 0 .../EmulatorCore/Decoding/MULInstr/R_IMM.cpp | 0 .../EmulatorCore/Decoding/MULInstr/R_M.cpp | 0 .../EmulatorCore/Decoding/MULInstr/R_R.cpp | 0 .../EmulatorCore/Decoding/MULInstr/R_RM.cpp | 0 .../Decoding/MULInstr/Unsupported.cpp | 0 .../EmulatorCore/Decoding/ORInstr/R_IMM.cpp | 0 .../EmulatorCore/Decoding/ORInstr/R_M.cpp | 0 .../EmulatorCore/Decoding/ORInstr/R_R.cpp | 0 .../EmulatorCore/Decoding/ORInstr/R_RM.cpp | 0 .../Decoding/ORInstr/Unsupported.cpp | 0 .../EmulatorCore/Decoding/READInstr/IMM.cpp | 0 .../Decoding/READInstr/Unsupported.cpp | 0 .../EmulatorCore/Decoding/SHFLInstr/R_IMM.cpp | 0 .../EmulatorCore/Decoding/SHFLInstr/R_R.cpp | 0 .../Decoding/SHFLInstr/Unsupported.cpp | 0 .../EmulatorCore/Decoding/SHFRInstr/R_IMM.cpp | 0 .../EmulatorCore/Decoding/SHFRInstr/R_R.cpp | 0 .../Decoding/SHFRInstr/Unsupported.cpp | 0 .../EmulatorCore/Decoding/SUBInstr/R_IMM.cpp | 0 .../EmulatorCore/Decoding/SUBInstr/R_M.cpp | 0 .../EmulatorCore/Decoding/SUBInstr/R_R.cpp | 0 .../EmulatorCore/Decoding/SUBInstr/R_RM.cpp | 0 .../Decoding/SUBInstr/Unsupported.cpp | 0 .../Decoding/WRITEInstr/R_IMM.cpp | 0 .../EmulatorCore/Decoding/WRITEInstr/R_R.cpp | 0 .../Decoding/WRITEInstr/Unsupported.cpp | 0 .../EmulatorCore/MemoryControllers/ST.cpp | 0 .../Modular/EmulatorCore/Stack/Stack.cpp | 0 .../Buildsystem/DummyProject/CMakeLists.txt | 22 +++ .../Buildsystem/DummyProject/conanfile.py | 20 +++ .../Python/Buildsystem/DummyProject/main.cpp | 13 ++ tests/Python/Buildsystem/test_build.py | 92 ++++++++++ {test => tests}/fixtures.hpp | 0 {test => tests}/main.cpp | 0 178 files changed, 460 insertions(+), 180 deletions(-) create mode 100644 Testing/Temporary/CTestCostData.txt create mode 100644 cmake/PrecompiledHeaders.cmake delete mode 100644 test/CMakeLists.txt create mode 100644 tests/CMakeLists.txt rename {test => tests}/Integration/AssemblerCore/AssemblerFail.cpp (100%) rename {test => tests}/Integration/AssemblerCore/AssemblerSuccess.cpp (100%) rename {test => tests}/Integration/AssemblerCore/FullAssembler.cpp (100%) rename {test => tests}/Integration/AssemblerCore/TwoOperandsSuccess.cpp (100%) rename {test => tests}/Integration/CMakeLists.txt (91%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_ADC.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_ADD.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_AND.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_ANDN.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_BSWAP.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_CALL.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_CALLE.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_CALLGR.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_CALLL.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_CCRF.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_CMP.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_COVF.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_CUDF.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_DEC.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_DIV.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_HID.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_INC.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_INTR.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_IRET.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_JME.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_JMGR.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_JML.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_JMP.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_LOIVT.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_MOV.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_MUL.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_OR.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_POP.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_PUSH.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_READ.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_SHFL.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_SHFR.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_SUB.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/CPU_WRITE.cpp (100%) rename {test => tests}/Integration/EmulatorCore/CPU/OperandsEvaluation.cpp (100%) rename {test => tests}/Integration/EmulatorCore/Exceptions/Exceptions.cpp (100%) rename {test => tests}/Modular/AssemblerCore/Parser/Operands.cpp (100%) rename {test => tests}/Modular/AssemblerCore/Parser/Statements.cpp (100%) rename {test => tests}/Modular/AssemblerCore/Parser/Tokens.cpp (100%) rename {test => tests}/Modular/CMakeLists.txt (79%) rename {test => tests}/Modular/EmulatorCore/CPUInit/CPUInit.cpp (98%) rename {test => tests}/Modular/EmulatorCore/Decoding/ADCInstr/R_IMM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ADCInstr/R_M.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ADCInstr/R_R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ADCInstr/R_RM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ADCInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ADDInstr/R_IMM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ADDInstr/R_M.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ADDInstr/R_R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ADDInstr/R_RM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ADDInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ANDInstr/R_IMM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ANDInstr/R_M.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ANDInstr/R_R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ANDInstr/R_RM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ANDInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ANDNInstr/R_IMM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ANDNInstr/R_M.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ANDNInstr/R_R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ANDNInstr/R_RM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ANDNInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/BSWAPInstr/R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/BSWAPInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CALLEInstr/test_decoder_imm.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CALLEInstr/test_decoder_r.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CALLEInstr/test_decoder_unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CALLGRInstr/test_decoder_imm.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CALLGRInstr/test_decoder_r.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CALLGRInstr/test_decoder_unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CALLInstr/test_decoder_imm.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CALLInstr/test_decoder_r.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CALLInstr/test_decoder_unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CALLLInstr/test_decoder_imm.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CALLLInstr/test_decoder_r.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CALLLInstr/test_decoder_unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CCRFInstr/test_decoder_none.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CCRFInstr/test_decoder_unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CMPInstr/M_R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CMPInstr/RM_IMM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CMPInstr/RM_M.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CMPInstr/RM_R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CMPInstr/R_IMM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CMPInstr/R_M.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CMPInstr/R_R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CMPInstr/R_RM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CMPInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/COVFInstr/test_decoder_none.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/COVFInstr/test_decoder_unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CUDFInstr/None.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/CUDFInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/DECInstr/R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/DECInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/DIVInstr/R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/DIVInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/Features/AddressAddition.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/HALTInstr/None.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/HALTInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/HIDInstr/None.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/HIDInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/INCInstr/R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/INCInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/JMEInstr/test_decoder_imm.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/JMEInstr/test_decoder_r.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/JMEInstr/test_decoder_unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/JMGRInstr/test_decoder_imm.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/JMGRInstr/test_decoder_r.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/JMGRInstr/test_decoder_unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/JMLInstr/test_decoder_imm.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/JMLInstr/test_decoder_r.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/JMLInstr/test_decoder_unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/JMPInstr/test_decoder_imm.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/JMPInstr/test_decoder_r.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/JMPInstr/test_decoder_unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/MOVInstr/M_R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/MOVInstr/RM_IMM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/MOVInstr/RM_M.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/MOVInstr/RM_R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/MOVInstr/R_IMM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/MOVInstr/R_M.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/MOVInstr/R_R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/MOVInstr/R_RM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/MOVInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/MULInstr/R_IMM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/MULInstr/R_M.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/MULInstr/R_R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/MULInstr/R_RM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/MULInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ORInstr/R_IMM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ORInstr/R_M.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ORInstr/R_R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ORInstr/R_RM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/ORInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/READInstr/IMM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/READInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/SHFLInstr/R_IMM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/SHFLInstr/R_R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/SHFLInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/SHFRInstr/R_IMM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/SHFRInstr/R_R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/SHFRInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/SUBInstr/R_IMM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/SUBInstr/R_M.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/SUBInstr/R_R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/SUBInstr/R_RM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/SUBInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/WRITEInstr/R_IMM.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/WRITEInstr/R_R.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Decoding/WRITEInstr/Unsupported.cpp (100%) rename {test => tests}/Modular/EmulatorCore/MemoryControllers/ST.cpp (100%) rename {test => tests}/Modular/EmulatorCore/Stack/Stack.cpp (100%) create mode 100644 tests/Python/Buildsystem/DummyProject/CMakeLists.txt create mode 100644 tests/Python/Buildsystem/DummyProject/conanfile.py create mode 100644 tests/Python/Buildsystem/DummyProject/main.cpp create mode 100644 tests/Python/Buildsystem/test_build.py rename {test => tests}/fixtures.hpp (100%) rename {test => tests}/main.cpp (100%) diff --git a/.clang-format b/.clang-format index b9c7e0e6..c7dfc55e 100644 --- a/.clang-format +++ b/.clang-format @@ -1,27 +1,60 @@ -BasedOnStyle: google +# Base style configuration +# Inherit all formatting rules from the LLVM style as a starting point. +BasedOnStyle: LLVM -# Ident namespaces with 4 spaces (tab) +# Indentation settings +# - IndentWidth: number of spaces per indent level. +# - TabWidth: visual width of tab characters. +# - UseTab: whether to emit real tab characters. +# - NamespaceIndentation: ident namespaces +IndentWidth: 2 +TabWidth: 2 +UseTab: Never NamespaceIndentation: All -IndentWidth: 4 -# Pointers and references are aligned to left: Type* or Type& -PointerAlignment: Left +# Include directives +# - SortIncludes: controls ordering of #include statements. +# - IncludeBlocks: allow empty lines for includes. +SortIncludes: true +IncludeBlocks: Preserve -# Transfer closing brackets to next line -BinPackParameters: false -AllowAllParametersOfDeclarationOnNextLine: false -PenaltyReturnTypeOnItsOwnLine: 0 +# Short constructs on single lines +# Disallow collapsing blocks, loops, cases, ifs, and functions into a single line. +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false -# Move access modifiers (public, private) at -# the same indention level as class keyword -AccessModifierOffset: -4 +# Access modifiers formatting +# Shift class access specifiers (public/protected/private) relative to class indent. +AccessModifierOffset: -2 -# Allow empty lines for includes -SortIncludes: true -IncludeBlocks: Preserve +# Brace placement +# Attach opening braces to the control statement or declaration line. +BreakBeforeBraces: Attach + +# Spacing around parentheses and casts +# ControlStatements: space before parens in if/for/while only. +# No extra spaces inside parentheses, angles, container literals, or square brackets. +SpaceBeforeParens: ControlStatements +SpaceAfterCStyleCast: false +SpacesInParentheses: false +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false -# Expand line columns' limit -ColumnLimit: 150 +# Template keyword spacing +# Always place a space after the 'template' keyword. +SpaceAfterTemplateKeyword: true -# Avoid short functions -AllowShortFunctionsOnASingleLine: None \ No newline at end of file +# Line length +# ColumnLimit: 0 disables automatic wrapping; allows unlimited line length. +ColumnLimit: 0 + +# Pointer alignment +# DerivePointerAlignment: ignore inference, use PointerAlignment setting. +# PointerAlignment: align the '*' or '&' to the left with the type. +DerivePointerAlignment: false +PointerAlignment: Left diff --git a/.github/workflows/distro-ci.yml b/.github/workflows/distro-ci.yml index 247af7e0..a7a3bd3d 100644 --- a/.github/workflows/distro-ci.yml +++ b/.github/workflows/distro-ci.yml @@ -37,15 +37,17 @@ jobs: - run: | set -e DISTRO=$( cat /etc/*-release | tr [:upper:] [:lower:] | grep -Poi '(debian|ubuntu|fedora|gentoo|alpine)' | uniq ) - if [ "$DISTRO" == "gentoo" ]; then source /etc/profile; fi + if [ "$DISTRO" == "gentoo" ]; then + source /etc/profile + fi git clone https://github.com/${{ github.event.pull_request.head.repo.full_name }}.git && cd HyperCPU git checkout ${{ github.event.pull_request.head.sha }} git submodule update --init --recursive - cd dist/pog && git pull origin master && cd ../.. - cmake -S. -Bbuild -DHCPU_COMPILER=clang -DHCPU_LTO=ON -DHCPU_SANITIZERS=OFF -DCMAKE_BUILD_TYPE=Release - cmake --build build --target run-all-tests-github -j8 - cmake --build build --target default -j8 - rm -rf build - cmake -S. -Bbuild -DHCPU_COMPILER=gcc -DHCPU_LTO=ON -DHCPU_SANITIZERS=OFF -DCMAKE_BUILD_TYPE=Release - cmake --build build --target run-all-tests-github -j8 - cmake --build build --target default -j8 + + python3 scripts/build.py -icb \ + --config Release \ + --config Debug \ + --build-dir build \ + -D CMAKE_CXX_COMPILER:STRING=g++ \ + -D CMAKE_CXX_COMPILER:STRING=gcc + --detect diff --git a/.github/workflows/run-tests-feature-branch.yml b/.github/workflows/run-tests-feature-branch.yml index f6c2a64c..1eeaabc6 100644 --- a/.github/workflows/run-tests-feature-branch.yml +++ b/.github/workflows/run-tests-feature-branch.yml @@ -17,19 +17,26 @@ jobs: - name: Build and test with GCC on Release profile run: | - cmake -S. -Bbuild -DHCPU_COMPILER=gcc -DCMAKE_BUILD_TYPE=Release - cmake --build build --target build-all-tests-github -j4 - build/modular_testing --gtest_brief - build/integration_testing --gtest_brief - rm -rf build + python3 scripts/build.py -icb \ + --config Release \ + --build-dir build \ + -D CMAKE_CXX_COMPILER:STRING=g++ \ + -D CMAKE_CXX_COMPILER:STRING=gcc \ + --detect + cd build/Release && ctest -V + python3 scripts/build.py -r --config Release --build-dir build - name: Build and test with LLVM on Release profile run: | - cmake -S. -Bbuild -DHCPU_COMPILER=clang -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_BUILD_TYPE=Release - cmake --build build --target build-all-tests-github -j4 - build/modular_testing --gtest_brief - build/integration_testing --gtest_brief - - - name: Clean up + python3 scripts/build.py -icb \ + --config Release \ + --build-dir build \ + -D CMAKE_CXX_COMPILER:STRING=clang++-19 \ + -D CMAKE_C_COMPILER:STRING=clang-19 \ + --detect + cd build/Release && ctest -V + python3 scripts/build.py -r --config Release --build-dir build + + - name: Cleanup (Conan cache + leftovers in build) run: | - rm -rf build + rm -r build/ diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index d3bd2a88..cac6b0db 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -19,32 +19,48 @@ jobs: - name: Build and test with GCC on Debug profile run: | - cmake -S. -Bbuild -DHCPU_COMPILER=gcc -DCMAKE_BUILD_TYPE=Debug - cmake --build build --target build-all-tests-github -j4 - build/modular_testing --gtest_brief - build/integration_testing --gtest_brief - rm -rf build + python3 scripts/build.py -icb \ + --config Debug \ + --build-dir build \ + -D CMAKE_CXX_COMPILER:STRING=g++ \ + -D CMAKE_CXX_COMPILER:STRING=gcc \ + --detect + cd build/Debug && ctest -V + python3 scripts/build.py -r --config Debug --build-dir build - name: Build and test with GCC on Release profile run: | - cmake -S. -Bbuild -DHCPU_COMPILER=gcc -DCMAKE_BUILD_TYPE=Release - cmake --build build --target build-all-tests-github -j4 - build/modular_testing --gtest_brief - build/integration_testing --gtest_brief - rm -rf build + python3 scripts/build.py -icb \ + --config Release \ + --build-dir build \ + -D CMAKE_CXX_COMPILER:STRING=g++ \ + -D CMAKE_CXX_COMPILER:STRING=gcc \ + --detect + cd build/Release && ctest -V + python3 scripts/build.py -r --config Release --build-dir build - name: Build and test with LLVM on Debug profile run: | - cmake -S. -Bbuild -DHCPU_COMPILER=clang -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_BUILD_TYPE=Release - cmake --build build --target build-all-tests-github -j4 - build/modular_testing --gtest_brief - build/integration_testing --gtest_brief - rm -rf build + python3 scripts/build.py -icb \ + --config Debug \ + --build-dir build \ + -D CMAKE_CXX_COMPILER:STRING=clang++-19 \ + -D CMAKE_C_COMPILER:STRING=clang-19 \ + --detect + cd build/Debug && ctest -V + python3 scripts/build.py -r --config Debug --build-dir build - name: Build and test with LLVM on Release profile run: | - cmake -S. -Bbuild -DHCPU_COMPILER=clang -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_BUILD_TYPE=Release - cmake --build build --target build-all-tests-github -j4 - build/modular_testing --gtest_brief - build/integration_testing --gtest_brief - rm -rf build + python3 scripts/build.py -icb \ + --config Release \ + --build-dir build \ + -D CMAKE_CXX_COMPILER:STRING=clang++-19 \ + -D CMAKE_C_COMPILER:STRING=clang-19 \ + --detect + cd build/Release && ctest -V + python3 scripts/build.py -r --config Release --build-dir build + + - name: Cleanup (Conan cache + leftovers in build) + run: | + rm -r build/ diff --git a/CMakeLists.txt b/CMakeLists.txt index e6c4e4d3..2a7e3ea5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.25) +cmake_minimum_required(VERSION 3.22..3.25) project(HyperCPU CXX) @@ -8,16 +8,17 @@ option(HCPU_SANITIZERS "HCPU: Enable sanitizers" OFF) option(HCPU_BUILD_TESTS "HCPU: Build tests" ON) option(HCPU_BUILD_BENCHMARKS "HCPU: Build benchmarks" OFF) -# FIXME: add pthread, boost & re2 find_package(GTest 1.14.0 REQUIRED) # GoogleTest find_package(benchmark 1.9.1 REQUIRED) # Google Benchmark -find_package(absl 20250127.0 REQUIRED) # Abseil (CMake target: absl::absl) +find_package(absl 20240116.1 REQUIRED) # Abseil (CMake target: absl::absl) find_package(libbacktrace CONFIG REQUIRED) # libbacktrace (CMake target: libbacktrace::libbacktrace) find_package(argparse 3.2 REQUIRED) # argparse.cpp (CMake target: argparse::argparse) find_package(eternal 1.0.1 REQUIRED) # Eternal (CMake target: eternal::eternal) -# find_package(RE2 20240702 REQUIRED) # RE2 (CMake target: re2::re2) +find_package(RE2 20230801 REQUIRED) # RE2 (CMake target: re2::re2) find_package(fmt 11.1.4 REQUIRED) # fmtlib (CMake target: fmt::fmt) find_package(libunwind 1.8.1 REQUIRED) # libunwind (CMake target: libunwind::libunwind) +find_package(Threads REQUIRED) # pthread support (Threads::Threads) +find_package(Boost 1.87.0 REQUIRED) # Boost set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -26,11 +27,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # retained for backwards comparability, should be supplied in CLI or presets set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -# FIXME: do we need this now? -# Disable gtest requirement for google/benchmark -set(BENCHMARK_ENABLE_GTEST_TESTS OFF) # Fix -Wstrinop-overflow warning -set(FMT_SYSTEM_HEADER ON) +set(FMT_SYSTEM_HEADER ON) # FIXME: patch hpool to enable -Werror add_compile_options( @@ -54,7 +52,7 @@ if(HCPU_LTO) check_ipo_supported(RESULT result OUTPUT output) if(result) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) - message(STATUS "HCPU: Enabled lTO") + message(STATUS "HCPU: Enabled LTO") else() message(WARNING "HCPU: LTO is not supported: ${output}") endif() @@ -77,6 +75,9 @@ else() message(STATUS "HCPU: Skipping sanitizers") endif() +include(CTest) +enable_testing() + add_library(hcpu_unwind INTERFACE) target_link_libraries( hcpu_unwind @@ -97,6 +98,5 @@ if(HCPU_BUILD_BENCHMARKS) endif() if(HCPU_BUILD_TESTS) - # FIXME: rename to tests - add_subdirectory(test) + add_subdirectory(tests) endif() \ No newline at end of file diff --git a/Testing/Temporary/CTestCostData.txt b/Testing/Temporary/CTestCostData.txt new file mode 100644 index 00000000..ed97d539 --- /dev/null +++ b/Testing/Temporary/CTestCostData.txt @@ -0,0 +1 @@ +--- diff --git a/cmake/PrecompiledHeaders.cmake b/cmake/PrecompiledHeaders.cmake new file mode 100644 index 00000000..e69de29b diff --git a/conanfile.py b/conanfile.py index 1b055a41..1fe452f0 100644 --- a/conanfile.py +++ b/conanfile.py @@ -15,14 +15,14 @@ def __init__(self: Self, display_name: str = '') -> None: self.__requirements = { 'gtest': '1.14.0', 'benchmark': '1.9.1', - 'abseil': '20250127.0', - 'libbacktrace': 'cci.20240730', + 'abseil': '20240116.1', + 'libbacktrace': 'cci.20210118', 'argparse': '3.2', 'eternal': '1.0.1', - # 're2': '20230801', + 're2': '20230801', 'fmt': '11.1.4', 'libunwind': '1.8.1', - # 'boost': '1.84.0' + 'boost': '1.87.0' } super().__init__(display_name) diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index 8278b3fd..10b6a07f 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -1,4 +1,6 @@ FROM alpine:latest RUN apk update && \ - apk add clang gcc git cmake make gtest-dev gtest-src re2 re2-dev fmt fmt-dev nodejs grep + apk add --no-cache python3 py3-pip clang gcc git cmake make \ + ninja nodejs grep \ + && pip3 install --no-cache-dir --break-system-packages conan \ No newline at end of file diff --git a/docker/archlinux/Dockerfile b/docker/archlinux/Dockerfile index ccd8d5f6..7cf48575 100644 --- a/docker/archlinux/Dockerfile +++ b/docker/archlinux/Dockerfile @@ -1,3 +1,7 @@ FROM archlinux:latest -RUN pacman -Syu cmake git clang gcc gtest re2 fmt base-devel nodejs --noconfirm +RUN pacman -Syu --noconfirm --needed \ + python python-pip cmake git clang gcc \ + base-devel nodejs \ + && pip install --no-cache-dir --break-system-packages conan \ + && pacman -Scc --noconfirm diff --git a/docker/debian-stable/Dockerfile b/docker/debian-stable/Dockerfile index d48af887..585d8664 100644 --- a/docker/debian-stable/Dockerfile +++ b/docker/debian-stable/Dockerfile @@ -1,4 +1,6 @@ -FROM debian +FROM debian:latest -RUN apt update && \ - apt install git cmake clang libre2-9 libre2-dev libfmt9 libfmt-dev libgtest-dev googletest gnu-which nodejs -y +RUN apt-get update && apt-get install -y --no-install-recommends \ + git cmake clang libre2-9 libre2-dev gnu-which nodejs python3 python3-pip \ + && pip3 install --no-cache-dir --break-system-packages conan \ + && rm -rf /var/lib/apt/lists/* diff --git a/docker/debian-unstable/Dockerfile b/docker/debian-unstable/Dockerfile index 2720484f..b5d5cb12 100644 --- a/docker/debian-unstable/Dockerfile +++ b/docker/debian-unstable/Dockerfile @@ -1,4 +1,6 @@ FROM debian:unstable -RUN apt update && \ - apt install git cmake clang libre2-11 libre2-dev libfmt10 libfmt-dev libgtest-dev googletest gnu-which nodejs -y +RUN apt-get update && apt-get install -y --no-install-recommends \ + git cmake clang libre2-9 libre2-dev gnu-which nodejs python3 python3-pip \ + && pip3 install --no-cache-dir --break-system-packages conan \ + && rm -rf /var/lib/apt/lists/* diff --git a/docker/fedora/Dockerfile b/docker/fedora/Dockerfile index 2f133747..9fd7131f 100644 --- a/docker/fedora/Dockerfile +++ b/docker/fedora/Dockerfile @@ -1,5 +1,8 @@ -FROM fedora - -RUN dnf update -y && \ - dnf install clang clang-libs gcc gtest gtest-devel fmt fmt-devel re2 re2-devel gtest gtest-devel git cmake libasan which nodejs -y +FROM fedora:latest +RUN dnf update -y \ + && dnf install -y --setopt=install_weak_deps=False git cmake \ + clang gcc which nodejs python3 python3-pip \ + && pip3 install --no-cache-dir --break-system-packages conan \ + && dnf clean all \ + && rm -rf /var/cache/dnf diff --git a/docker/ubuntu/Dockerfile b/docker/ubuntu/Dockerfile index 62ba3169..41eb65b8 100644 --- a/docker/ubuntu/Dockerfile +++ b/docker/ubuntu/Dockerfile @@ -1,4 +1,6 @@ FROM ubuntu:latest -RUN apt update && \ - apt install git cmake clang libre2-10 libre2-dev libfmt9 libfmt-dev libgtest-dev googletest gnu-which nodejs -y +RUN apt update && apt install -y --no-install-recommends \ + git cmake clang libre2-9 libre2-dev gnu-which nodejs python3 python3-pip \ + && pip3 install --no-cache-dir --break-system-packages conan \ + && rm -rf /var/lib/apt/lists/* diff --git a/scripts/build.py b/scripts/build.py index 82923c42..f420eb59 100755 --- a/scripts/build.py +++ b/scripts/build.py @@ -15,13 +15,13 @@ from pathlib import Path from collections import OrderedDict from dataclasses import dataclass, field -from typing import Callable, Iterable, Union, Tuple, Collection +from typing import Callable, Iterable, Union, Tuple, Collection, Optional -from conan.api.subapi.profiles import Profile from conan.api.conan_api import ConanAPI, ConanException # this is not part of external API, but those formatters are really cool -from conan.cli.printers.graph import print_graph_basic +from conan.api.output import ConanOutput from conan.cli.printers import print_profiles +from conan.cli.printers.graph import print_graph_basic, print_graph_packages logger = logging.getLogger(__file__ if '__file__' in vars() else 'build.helper') @@ -35,10 +35,14 @@ @dataclass class Config: build_directory: Path = field(default_factory=lambda: Path.cwd().joinpath('build')) - build_configs: Iterable[str] = field(default_factory=lambda: ['Debug']) cores: int = multiprocessing.cpu_count() - generator: str = 'Ninja' + # CMake-specific + build_configs: Iterable[str] = field(default_factory=lambda: ['Debug']) + generator: Optional[str] = None + defines: Collection[Tuple[str, str, str]] = field(default_factory=lambda: []) # Conan-specific + # allow conan to detect profile (might be inaccurate) + allow_profile_detection: bool = False # build profile specifies target environment build_profile: str = 'default' # host profile describes host environment (where build happens) @@ -82,16 +86,29 @@ def remove(self): @routine(4) def conan_install(self): - api = ConanAPI() + api = ConanAPI(str(self.__params.build_directory / 'conan2')) + + detect_profiles = False + profiles = api.profiles.list() + if len(profiles) > 0: + logger.debug(f'detected profiles: ' + ', '.join(profiles)) + else: + if not self.__params.allow_profile_detection: + raise FileNotFoundError( + 'no profiles exist and autodetection is turned off' + ) + detect_profiles = True - logger.debug(f'detected profiles: ' + ', '.join(api.profiles.list())) logger.info( 'please notice, this script modifies "build_type" in incoming profile, to match running params' ) remotes = api.remotes.list() for config in self.__params.build_configs: - self._conan_install_for_config(config, api, remotes) + if not self._conan_install_for_config(config, api, remotes, detect_profiles=detect_profiles): + raise RuntimeError( + f'failed to install dependencies for config: {config}, see error from conan above' + ) @routine(3) def configure(self): @@ -101,38 +118,35 @@ def configure(self): use_presets = self.CMAKE_USER_PRESETS.is_file() if use_presets: logger.info('found CMake presets') - presets = self._inspect_cmake_presets(self.CMAKE_USER_PRESETS) - - # make sure that presets are enabled for all possible cmake invocations (configure, build, test) - preset_types = [key for key in presets if key.endswith('Presets')] - - presets_available = {} - for config in self.__params.build_configs: - presets_available[config] = all(f'conan-{config.lower()}' in presets[key] for key in preset_types) logger.info('configuring for CMake build configs: ' + ', '.join(self.__params.build_configs)) - for config in self.__params.build_configs: source = Path.cwd() binary = self.__params.build_directory.joinpath(config) + # TODO: allow multiconfigs command = [ 'cmake', '--preset', f'conan-{config.lower()}', - '-G', self.__params.generator, '-B', str(binary), '-S', str(source), self._decorate_cmake_variable('CMAKE_EXPORT_COMPILE_COMMANDS', 'ON', 'BOOL'), self._decorate_cmake_variable('CMAKE_BUILD_TYPE', config) ] if use_presets else [ 'cmake', - '-G', self.__params.generator, '-B', str(binary), '-S', str(source), self._decorate_cmake_variable('CMAKE_EXPORT_COMPILE_COMMANDS', 'ON', 'BOOL'), self._decorate_cmake_variable('CMAKE_BUILD_TYPE', config) ] + if self.__params.generator is not None: + command.extend(['-G', self.__params.generator]) + + if len(self.__params.defines) > 0: + for key, var_type, value in self.__params.defines: + command.append(self._decorate_cmake_variable(key, value, var_type)) + logger.info(f'running configure command for CMake config {config}') self._run(command) @@ -175,15 +189,27 @@ def _conan_install_for_config( self, config: str, api: ConanAPI, - remotes: list - ): - # fetch requested profiles + remotes: list, + detect_profiles: bool = False + ) -> bool: + out = ConanOutput() try: - # all profile tweaks should be done here, for some reason later modifications to Profile object - # have no effect - build_profile = api.profiles.get_profile([self.__params.build_profile], settings=[f'build_type={config}']) - host_profile = api.profiles.get_profile([self.__params.host_profile], settings=[f'build_type={config}']) + if detect_profiles: + build_profile = host_profile = api.profiles.detect() + else: + build_profile = api.profiles.get_profile([self.__params.build_profile]) + host_profile = api.profiles.get_profile([self.__params.host_profile]) + + mixin = OrderedDict({'build_type': config}) + for profile in (build_profile, host_profile): + profile.update_settings(mixin) + if profile.processed_settings is None: + profile.process_settings(api.config.settings_yml) + + if detect_profiles: + out.title('Autodetected profiles (be careful using them!)') print_profiles(host_profile, build_profile) + except ConanException as conan_error: raise RuntimeError( f'getting requested profiles failed: {(self.__params.build_profile, self.__params.host_profile)}' @@ -206,48 +232,24 @@ def _conan_install_for_config( update=True, check_updates=True ) - api.graph.analyze_binaries(graph, build_mode=['missing'], remotes=remotes, update=True) - graph.report_graph_error() print_graph_basic(graph) + graph.report_graph_error() + api.graph.analyze_binaries(graph, build_mode=['missing'], remotes=remotes, update=True) + print_graph_packages(graph) except ConanException as conan_error: - raise RuntimeError( - f'failed to generate build graph, Conan API failed' - ) from conan_error + out.error(str(conan_error)) + return False # make sure to define cmake layout in conanfile.py try: api.install.install_binaries(graph, remotes) api.install.install_consumer(graph, source_folder=Path.cwd()) except ConanException as conan_error: - raise RuntimeError( - f'failed to install deps, Conan API failed' - ) from conan_error - - def _inspect_cmake_presets(self, presets_file: Path) -> Collection[Tuple[str, dict]]: - if not presets_file.is_file(): - raise FileNotFoundError(f'file "{presets_file}" does not exist') + out.error(str(conan_error)) + return False - with open(presets_file, 'r') as fd: - presets = json.load(fd) - - includes = presets['include'] - for file_to_include in map(Path, includes): - if not file_to_include.is_absolute(): - file_to_include = presets_file.parent / file_to_include - - with open(file_to_include, 'r') as file: - contents = json.load(file) - - for key in contents: - if key.endswith('Presets'): - if key not in presets: - presets[key] = [] - - # FIXME: probably should prefer initial values if set - presets[key].append(contents[key]) - - return presets + return True def _run(self: 'Routines', command: typing.List[str]) -> None: logger.info('running command: ' + ' '.join(command)) @@ -255,10 +257,35 @@ def _run(self: 'Routines', command: typing.List[str]) -> None: if code: sys.exit(f'error: subprocess failed: {errno.errorcode[code]} (code: {code})') - def _decorate_cmake_variable(self: 'Routines', var: str, value: str, type: Union[str, None] = None) -> str: - if type is not None: - return f'-D{var.upper()}:{type}={value}' + def _decorate_cmake_variable(self: 'Routines', var: str, value: str, var_type: Union[str, None] = None) -> str: + if var_type is not None: + return f'-D{var.upper()}:{var_type}={value}' return f'-D{var.upper()}={value}' + + +def resolve_profiles(config: Config, args: argparse.Namespace): + if args.profile_all is not None: + config.build_profile = config.host_profile = args.profile_all + logger.info(f'using specified "all" (build and host) profile: {args.profile_all}') + return + + if args.profile_host is not None: + config.host_profile = args.profile_host + logger.info(f'using specified "host" profile: {args.profile_host}') + + if args.profile_build is not None: + config.build_profile = args.profile_build + logger.info(f'using specified "host" profile: {args.profile_build}') + + if all(profile is None for profile in (args.profile_all, args.profile_host, args.profile_build)): + logger.info('no profiles were specified. Using default for both "host" and "build"') + + +def resolve_defines(config: Config, args: argparse.Namespace): + for define in args.defines: + name, value = define.split('=') + var_name, var_type = name.split(':') + config.defines.append((var_name, var_type, value)) def parse_cli_args() -> argparse.Namespace: @@ -271,10 +298,17 @@ def parse_cli_args() -> argparse.Namespace: parser.add_argument('-l', '--symlink-compile-commands', action='store_true', dest='symlink_compile_commands') # Environment parser.add_argument('--build-dir', action='store', dest='build_directory') + # TODO: allow more configs parser.add_argument('--config', action='append', dest='configs', choices=['Debug', 'Release']) parser.add_argument('--parallel', action='store', dest='cores') parser.add_argument('--generator', action='store', dest='generator') - parser.add_argument('--profile', action='store', dest='profile') + # Profiles + parser.add_argument('--pr:a', action='store', dest='profile_all') + parser.add_argument('--pr:h', action='store', dest='profile_host') + parser.add_argument('--pr:b', action='store', dest='profile_build') + parser.add_argument('--profiles-detection', action='store_true', dest='profile_detect') + # Arbitrary CMake flags + parser.add_argument('-D', metavar='KEY:TYPE=VALUE', action='append', default=[], dest='defines') return parser.parse_args() @@ -309,12 +343,13 @@ def main(): if getattr(args, 'cores') is not None: params.cores = int(args.cores) logger.info(f'config: user-provided threads, that will be run in parallel: "{params.cores}"') - if getattr(args, 'profile') is not None: - params.profile = args.profile - logger.info(f'config: user-provided conan profile: "{params.profile}"') if getattr(args, 'generator') is not None: params.generator = args.generator logger.info(f'config: user-provided generator: "{params.generator}"') + params.allow_profile_detection = args.profile_detect + + resolve_profiles(params, args) + resolve_defines(params, args) r = Routines(params) for routine, f in r.routines(): diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt deleted file mode 100644 index 21ab4c65..00000000 --- a/test/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# TODO: Enable CTest integration -# FIXME: Add separate precompiled headers here, too -cmake_minimum_required(VERSION 3.25) - - -add_subdirectory(Integration) -add_subdirectory(Modular) - -add_custom_target(run-all-tests-github - ${CMAKE_BINARY_DIR}/modular_testing --gtest_brief=1 - COMMAND - ${CMAKE_BINARY_DIR}/integration_testing --gtest_brief=1 - DEPENDS - build-all-tests-github -) - -add_custom_target(build-all-tests-github - DEPENDS - modular_testing - integration_testing -) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..85759b8b --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,8 @@ +# FIXME: Add separate precompiled headers here, too +cmake_minimum_required(VERSION 3.25) + + +include(GoogleTest) + +add_subdirectory(Integration) +add_subdirectory(Modular) diff --git a/test/Integration/AssemblerCore/AssemblerFail.cpp b/tests/Integration/AssemblerCore/AssemblerFail.cpp similarity index 100% rename from test/Integration/AssemblerCore/AssemblerFail.cpp rename to tests/Integration/AssemblerCore/AssemblerFail.cpp diff --git a/test/Integration/AssemblerCore/AssemblerSuccess.cpp b/tests/Integration/AssemblerCore/AssemblerSuccess.cpp similarity index 100% rename from test/Integration/AssemblerCore/AssemblerSuccess.cpp rename to tests/Integration/AssemblerCore/AssemblerSuccess.cpp diff --git a/test/Integration/AssemblerCore/FullAssembler.cpp b/tests/Integration/AssemblerCore/FullAssembler.cpp similarity index 100% rename from test/Integration/AssemblerCore/FullAssembler.cpp rename to tests/Integration/AssemblerCore/FullAssembler.cpp diff --git a/test/Integration/AssemblerCore/TwoOperandsSuccess.cpp b/tests/Integration/AssemblerCore/TwoOperandsSuccess.cpp similarity index 100% rename from test/Integration/AssemblerCore/TwoOperandsSuccess.cpp rename to tests/Integration/AssemblerCore/TwoOperandsSuccess.cpp diff --git a/test/Integration/CMakeLists.txt b/tests/Integration/CMakeLists.txt similarity index 91% rename from test/Integration/CMakeLists.txt rename to tests/Integration/CMakeLists.txt index 8b68c8af..f45fabc2 100644 --- a/test/Integration/CMakeLists.txt +++ b/tests/Integration/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.25) add_executable( integration_testing - ${CMAKE_SOURCE_DIR}/test/main.cpp + ${CMAKE_SOURCE_DIR}/tests/main.cpp EmulatorCore/CPU/CPU_CALLE.cpp EmulatorCore/CPU/CPU_HID.cpp EmulatorCore/CPU/CPU_READ.cpp @@ -56,5 +56,12 @@ target_link_libraries( target_include_directories( integration_testing PRIVATE - ${CMAKE_SOURCE_DIR}/test + ${CMAKE_SOURCE_DIR}/tests ) + +gtest_add_tests( + TARGET + integration_testing + ARGS + --gtest_brief=1 +) \ No newline at end of file diff --git a/test/Integration/EmulatorCore/CPU/CPU_ADC.cpp b/tests/Integration/EmulatorCore/CPU/CPU_ADC.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_ADC.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_ADC.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_ADD.cpp b/tests/Integration/EmulatorCore/CPU/CPU_ADD.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_ADD.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_ADD.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_AND.cpp b/tests/Integration/EmulatorCore/CPU/CPU_AND.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_AND.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_AND.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_ANDN.cpp b/tests/Integration/EmulatorCore/CPU/CPU_ANDN.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_ANDN.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_ANDN.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_BSWAP.cpp b/tests/Integration/EmulatorCore/CPU/CPU_BSWAP.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_BSWAP.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_BSWAP.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_CALL.cpp b/tests/Integration/EmulatorCore/CPU/CPU_CALL.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_CALL.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_CALL.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_CALLE.cpp b/tests/Integration/EmulatorCore/CPU/CPU_CALLE.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_CALLE.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_CALLE.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_CALLGR.cpp b/tests/Integration/EmulatorCore/CPU/CPU_CALLGR.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_CALLGR.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_CALLGR.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_CALLL.cpp b/tests/Integration/EmulatorCore/CPU/CPU_CALLL.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_CALLL.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_CALLL.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_CCRF.cpp b/tests/Integration/EmulatorCore/CPU/CPU_CCRF.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_CCRF.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_CCRF.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_CMP.cpp b/tests/Integration/EmulatorCore/CPU/CPU_CMP.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_CMP.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_CMP.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_COVF.cpp b/tests/Integration/EmulatorCore/CPU/CPU_COVF.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_COVF.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_COVF.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_CUDF.cpp b/tests/Integration/EmulatorCore/CPU/CPU_CUDF.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_CUDF.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_CUDF.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_DEC.cpp b/tests/Integration/EmulatorCore/CPU/CPU_DEC.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_DEC.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_DEC.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_DIV.cpp b/tests/Integration/EmulatorCore/CPU/CPU_DIV.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_DIV.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_DIV.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_HID.cpp b/tests/Integration/EmulatorCore/CPU/CPU_HID.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_HID.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_HID.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_INC.cpp b/tests/Integration/EmulatorCore/CPU/CPU_INC.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_INC.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_INC.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_INTR.cpp b/tests/Integration/EmulatorCore/CPU/CPU_INTR.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_INTR.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_INTR.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_IRET.cpp b/tests/Integration/EmulatorCore/CPU/CPU_IRET.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_IRET.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_IRET.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_JME.cpp b/tests/Integration/EmulatorCore/CPU/CPU_JME.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_JME.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_JME.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_JMGR.cpp b/tests/Integration/EmulatorCore/CPU/CPU_JMGR.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_JMGR.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_JMGR.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_JML.cpp b/tests/Integration/EmulatorCore/CPU/CPU_JML.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_JML.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_JML.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_JMP.cpp b/tests/Integration/EmulatorCore/CPU/CPU_JMP.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_JMP.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_JMP.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_LOIVT.cpp b/tests/Integration/EmulatorCore/CPU/CPU_LOIVT.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_LOIVT.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_LOIVT.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_MOV.cpp b/tests/Integration/EmulatorCore/CPU/CPU_MOV.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_MOV.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_MOV.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_MUL.cpp b/tests/Integration/EmulatorCore/CPU/CPU_MUL.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_MUL.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_MUL.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_OR.cpp b/tests/Integration/EmulatorCore/CPU/CPU_OR.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_OR.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_OR.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_POP.cpp b/tests/Integration/EmulatorCore/CPU/CPU_POP.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_POP.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_POP.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_PUSH.cpp b/tests/Integration/EmulatorCore/CPU/CPU_PUSH.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_PUSH.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_PUSH.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_READ.cpp b/tests/Integration/EmulatorCore/CPU/CPU_READ.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_READ.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_READ.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_SHFL.cpp b/tests/Integration/EmulatorCore/CPU/CPU_SHFL.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_SHFL.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_SHFL.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_SHFR.cpp b/tests/Integration/EmulatorCore/CPU/CPU_SHFR.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_SHFR.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_SHFR.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_SUB.cpp b/tests/Integration/EmulatorCore/CPU/CPU_SUB.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_SUB.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_SUB.cpp diff --git a/test/Integration/EmulatorCore/CPU/CPU_WRITE.cpp b/tests/Integration/EmulatorCore/CPU/CPU_WRITE.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/CPU_WRITE.cpp rename to tests/Integration/EmulatorCore/CPU/CPU_WRITE.cpp diff --git a/test/Integration/EmulatorCore/CPU/OperandsEvaluation.cpp b/tests/Integration/EmulatorCore/CPU/OperandsEvaluation.cpp similarity index 100% rename from test/Integration/EmulatorCore/CPU/OperandsEvaluation.cpp rename to tests/Integration/EmulatorCore/CPU/OperandsEvaluation.cpp diff --git a/test/Integration/EmulatorCore/Exceptions/Exceptions.cpp b/tests/Integration/EmulatorCore/Exceptions/Exceptions.cpp similarity index 100% rename from test/Integration/EmulatorCore/Exceptions/Exceptions.cpp rename to tests/Integration/EmulatorCore/Exceptions/Exceptions.cpp diff --git a/test/Modular/AssemblerCore/Parser/Operands.cpp b/tests/Modular/AssemblerCore/Parser/Operands.cpp similarity index 100% rename from test/Modular/AssemblerCore/Parser/Operands.cpp rename to tests/Modular/AssemblerCore/Parser/Operands.cpp diff --git a/test/Modular/AssemblerCore/Parser/Statements.cpp b/tests/Modular/AssemblerCore/Parser/Statements.cpp similarity index 100% rename from test/Modular/AssemblerCore/Parser/Statements.cpp rename to tests/Modular/AssemblerCore/Parser/Statements.cpp diff --git a/test/Modular/AssemblerCore/Parser/Tokens.cpp b/tests/Modular/AssemblerCore/Parser/Tokens.cpp similarity index 100% rename from test/Modular/AssemblerCore/Parser/Tokens.cpp rename to tests/Modular/AssemblerCore/Parser/Tokens.cpp diff --git a/test/Modular/CMakeLists.txt b/tests/Modular/CMakeLists.txt similarity index 79% rename from test/Modular/CMakeLists.txt rename to tests/Modular/CMakeLists.txt index 41c74184..8ef88049 100644 --- a/test/Modular/CMakeLists.txt +++ b/tests/Modular/CMakeLists.txt @@ -1,8 +1,9 @@ cmake_minimum_required(VERSION 3.25) -add_executable(modular_testing - ${CMAKE_SOURCE_DIR}/test/main.cpp +add_executable( + modular_testing + ${CMAKE_SOURCE_DIR}/tests/main.cpp EmulatorCore/Decoding/CMPInstr/R_R.cpp EmulatorCore/Decoding/CMPInstr/RM_IMM.cpp EmulatorCore/Decoding/CMPInstr/RM_M.cpp @@ -94,6 +95,28 @@ add_executable(modular_testing EmulatorCore/Decoding/CALLInstr/test_decoder_imm.cpp EmulatorCore/Decoding/CALLInstr/test_decoder_unsupported.cpp EmulatorCore/Decoding/ADCInstr/R_R.cpp + EmulatorCore/Decoding/ADCInstr/Unsupported.cpp + EmulatorCore/Decoding/ADCInstr/R_RM.cpp + EmulatorCore/Decoding/ADCInstr/R_IMM.cpp + EmulatorCore/Decoding/ADCInstr/R_M.cpp + EmulatorCore/Decoding/Features/AddressAddition.cpp + EmulatorCore/Decoding/HALTInstr/Unsupported.cpp + EmulatorCore/Decoding/HALTInstr/None.cpp + EmulatorCore/Decoding/ORInstr/R_R.cpp + EmulatorCore/Decoding/ORInstr/Unsupported.cpp + EmulatorCore/Decoding/ORInstr/R_RM.cpp + EmulatorCore/Decoding/ORInstr/R_IMM.cpp + EmulatorCore/Decoding/ORInstr/R_M.cpp + EmulatorCore/Decoding/READInstr/IMM.cpp + EmulatorCore/Decoding/READInstr/Unsupported.cpp + EmulatorCore/Decoding/CCRFInstr/test_decoder_unsupported.cpp + EmulatorCore/Decoding/CCRFInstr/test_decoder_none.cpp + EmulatorCore/MemoryControllers/ST.cpp + EmulatorCore/CPUInit/CPUInit.cpp + EmulatorCore/Stack/Stack.cpp + AssemblerCore/Parser/Statements.cpp + AssemblerCore/Parser/Operands.cpp + AssemblerCore/Parser/Tokens.cpp ) target_link_libraries( modular_testing @@ -107,5 +130,12 @@ target_link_libraries( target_include_directories( modular_testing PRIVATE - ${CMAKE_SOURCE_DIR}/test + ${CMAKE_SOURCE_DIR}/tests +) + +gtest_add_tests( + TARGET + modular_testing + EXTRA_ARGS + --gtest_brief=1 ) diff --git a/test/Modular/EmulatorCore/CPUInit/CPUInit.cpp b/tests/Modular/EmulatorCore/CPUInit/CPUInit.cpp similarity index 98% rename from test/Modular/EmulatorCore/CPUInit/CPUInit.cpp rename to tests/Modular/EmulatorCore/CPUInit/CPUInit.cpp index 90b36c9d..57ee3cc3 100644 --- a/test/Modular/EmulatorCore/CPUInit/CPUInit.cpp +++ b/tests/Modular/EmulatorCore/CPUInit/CPUInit.cpp @@ -1,5 +1,5 @@ #include "Logger/Logger.hpp" -#include +#include #define private public #include diff --git a/test/Modular/EmulatorCore/Decoding/ADCInstr/R_IMM.cpp b/tests/Modular/EmulatorCore/Decoding/ADCInstr/R_IMM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ADCInstr/R_IMM.cpp rename to tests/Modular/EmulatorCore/Decoding/ADCInstr/R_IMM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ADCInstr/R_M.cpp b/tests/Modular/EmulatorCore/Decoding/ADCInstr/R_M.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ADCInstr/R_M.cpp rename to tests/Modular/EmulatorCore/Decoding/ADCInstr/R_M.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ADCInstr/R_R.cpp b/tests/Modular/EmulatorCore/Decoding/ADCInstr/R_R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ADCInstr/R_R.cpp rename to tests/Modular/EmulatorCore/Decoding/ADCInstr/R_R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ADCInstr/R_RM.cpp b/tests/Modular/EmulatorCore/Decoding/ADCInstr/R_RM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ADCInstr/R_RM.cpp rename to tests/Modular/EmulatorCore/Decoding/ADCInstr/R_RM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ADCInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/ADCInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ADCInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/ADCInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ADDInstr/R_IMM.cpp b/tests/Modular/EmulatorCore/Decoding/ADDInstr/R_IMM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ADDInstr/R_IMM.cpp rename to tests/Modular/EmulatorCore/Decoding/ADDInstr/R_IMM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ADDInstr/R_M.cpp b/tests/Modular/EmulatorCore/Decoding/ADDInstr/R_M.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ADDInstr/R_M.cpp rename to tests/Modular/EmulatorCore/Decoding/ADDInstr/R_M.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ADDInstr/R_R.cpp b/tests/Modular/EmulatorCore/Decoding/ADDInstr/R_R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ADDInstr/R_R.cpp rename to tests/Modular/EmulatorCore/Decoding/ADDInstr/R_R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ADDInstr/R_RM.cpp b/tests/Modular/EmulatorCore/Decoding/ADDInstr/R_RM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ADDInstr/R_RM.cpp rename to tests/Modular/EmulatorCore/Decoding/ADDInstr/R_RM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ADDInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/ADDInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ADDInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/ADDInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ANDInstr/R_IMM.cpp b/tests/Modular/EmulatorCore/Decoding/ANDInstr/R_IMM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ANDInstr/R_IMM.cpp rename to tests/Modular/EmulatorCore/Decoding/ANDInstr/R_IMM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ANDInstr/R_M.cpp b/tests/Modular/EmulatorCore/Decoding/ANDInstr/R_M.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ANDInstr/R_M.cpp rename to tests/Modular/EmulatorCore/Decoding/ANDInstr/R_M.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ANDInstr/R_R.cpp b/tests/Modular/EmulatorCore/Decoding/ANDInstr/R_R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ANDInstr/R_R.cpp rename to tests/Modular/EmulatorCore/Decoding/ANDInstr/R_R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ANDInstr/R_RM.cpp b/tests/Modular/EmulatorCore/Decoding/ANDInstr/R_RM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ANDInstr/R_RM.cpp rename to tests/Modular/EmulatorCore/Decoding/ANDInstr/R_RM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ANDInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/ANDInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ANDInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/ANDInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ANDNInstr/R_IMM.cpp b/tests/Modular/EmulatorCore/Decoding/ANDNInstr/R_IMM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ANDNInstr/R_IMM.cpp rename to tests/Modular/EmulatorCore/Decoding/ANDNInstr/R_IMM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ANDNInstr/R_M.cpp b/tests/Modular/EmulatorCore/Decoding/ANDNInstr/R_M.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ANDNInstr/R_M.cpp rename to tests/Modular/EmulatorCore/Decoding/ANDNInstr/R_M.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ANDNInstr/R_R.cpp b/tests/Modular/EmulatorCore/Decoding/ANDNInstr/R_R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ANDNInstr/R_R.cpp rename to tests/Modular/EmulatorCore/Decoding/ANDNInstr/R_R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ANDNInstr/R_RM.cpp b/tests/Modular/EmulatorCore/Decoding/ANDNInstr/R_RM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ANDNInstr/R_RM.cpp rename to tests/Modular/EmulatorCore/Decoding/ANDNInstr/R_RM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ANDNInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/ANDNInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ANDNInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/ANDNInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/BSWAPInstr/R.cpp b/tests/Modular/EmulatorCore/Decoding/BSWAPInstr/R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/BSWAPInstr/R.cpp rename to tests/Modular/EmulatorCore/Decoding/BSWAPInstr/R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/BSWAPInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/BSWAPInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/BSWAPInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/BSWAPInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CALLEInstr/test_decoder_imm.cpp b/tests/Modular/EmulatorCore/Decoding/CALLEInstr/test_decoder_imm.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CALLEInstr/test_decoder_imm.cpp rename to tests/Modular/EmulatorCore/Decoding/CALLEInstr/test_decoder_imm.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CALLEInstr/test_decoder_r.cpp b/tests/Modular/EmulatorCore/Decoding/CALLEInstr/test_decoder_r.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CALLEInstr/test_decoder_r.cpp rename to tests/Modular/EmulatorCore/Decoding/CALLEInstr/test_decoder_r.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CALLEInstr/test_decoder_unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/CALLEInstr/test_decoder_unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CALLEInstr/test_decoder_unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/CALLEInstr/test_decoder_unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CALLGRInstr/test_decoder_imm.cpp b/tests/Modular/EmulatorCore/Decoding/CALLGRInstr/test_decoder_imm.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CALLGRInstr/test_decoder_imm.cpp rename to tests/Modular/EmulatorCore/Decoding/CALLGRInstr/test_decoder_imm.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CALLGRInstr/test_decoder_r.cpp b/tests/Modular/EmulatorCore/Decoding/CALLGRInstr/test_decoder_r.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CALLGRInstr/test_decoder_r.cpp rename to tests/Modular/EmulatorCore/Decoding/CALLGRInstr/test_decoder_r.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CALLGRInstr/test_decoder_unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/CALLGRInstr/test_decoder_unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CALLGRInstr/test_decoder_unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/CALLGRInstr/test_decoder_unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CALLInstr/test_decoder_imm.cpp b/tests/Modular/EmulatorCore/Decoding/CALLInstr/test_decoder_imm.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CALLInstr/test_decoder_imm.cpp rename to tests/Modular/EmulatorCore/Decoding/CALLInstr/test_decoder_imm.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CALLInstr/test_decoder_r.cpp b/tests/Modular/EmulatorCore/Decoding/CALLInstr/test_decoder_r.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CALLInstr/test_decoder_r.cpp rename to tests/Modular/EmulatorCore/Decoding/CALLInstr/test_decoder_r.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CALLInstr/test_decoder_unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/CALLInstr/test_decoder_unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CALLInstr/test_decoder_unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/CALLInstr/test_decoder_unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CALLLInstr/test_decoder_imm.cpp b/tests/Modular/EmulatorCore/Decoding/CALLLInstr/test_decoder_imm.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CALLLInstr/test_decoder_imm.cpp rename to tests/Modular/EmulatorCore/Decoding/CALLLInstr/test_decoder_imm.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CALLLInstr/test_decoder_r.cpp b/tests/Modular/EmulatorCore/Decoding/CALLLInstr/test_decoder_r.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CALLLInstr/test_decoder_r.cpp rename to tests/Modular/EmulatorCore/Decoding/CALLLInstr/test_decoder_r.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CALLLInstr/test_decoder_unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/CALLLInstr/test_decoder_unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CALLLInstr/test_decoder_unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/CALLLInstr/test_decoder_unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CCRFInstr/test_decoder_none.cpp b/tests/Modular/EmulatorCore/Decoding/CCRFInstr/test_decoder_none.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CCRFInstr/test_decoder_none.cpp rename to tests/Modular/EmulatorCore/Decoding/CCRFInstr/test_decoder_none.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CCRFInstr/test_decoder_unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/CCRFInstr/test_decoder_unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CCRFInstr/test_decoder_unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/CCRFInstr/test_decoder_unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CMPInstr/M_R.cpp b/tests/Modular/EmulatorCore/Decoding/CMPInstr/M_R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CMPInstr/M_R.cpp rename to tests/Modular/EmulatorCore/Decoding/CMPInstr/M_R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CMPInstr/RM_IMM.cpp b/tests/Modular/EmulatorCore/Decoding/CMPInstr/RM_IMM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CMPInstr/RM_IMM.cpp rename to tests/Modular/EmulatorCore/Decoding/CMPInstr/RM_IMM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CMPInstr/RM_M.cpp b/tests/Modular/EmulatorCore/Decoding/CMPInstr/RM_M.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CMPInstr/RM_M.cpp rename to tests/Modular/EmulatorCore/Decoding/CMPInstr/RM_M.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CMPInstr/RM_R.cpp b/tests/Modular/EmulatorCore/Decoding/CMPInstr/RM_R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CMPInstr/RM_R.cpp rename to tests/Modular/EmulatorCore/Decoding/CMPInstr/RM_R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CMPInstr/R_IMM.cpp b/tests/Modular/EmulatorCore/Decoding/CMPInstr/R_IMM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CMPInstr/R_IMM.cpp rename to tests/Modular/EmulatorCore/Decoding/CMPInstr/R_IMM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CMPInstr/R_M.cpp b/tests/Modular/EmulatorCore/Decoding/CMPInstr/R_M.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CMPInstr/R_M.cpp rename to tests/Modular/EmulatorCore/Decoding/CMPInstr/R_M.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CMPInstr/R_R.cpp b/tests/Modular/EmulatorCore/Decoding/CMPInstr/R_R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CMPInstr/R_R.cpp rename to tests/Modular/EmulatorCore/Decoding/CMPInstr/R_R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CMPInstr/R_RM.cpp b/tests/Modular/EmulatorCore/Decoding/CMPInstr/R_RM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CMPInstr/R_RM.cpp rename to tests/Modular/EmulatorCore/Decoding/CMPInstr/R_RM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CMPInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/CMPInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CMPInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/CMPInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/COVFInstr/test_decoder_none.cpp b/tests/Modular/EmulatorCore/Decoding/COVFInstr/test_decoder_none.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/COVFInstr/test_decoder_none.cpp rename to tests/Modular/EmulatorCore/Decoding/COVFInstr/test_decoder_none.cpp diff --git a/test/Modular/EmulatorCore/Decoding/COVFInstr/test_decoder_unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/COVFInstr/test_decoder_unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/COVFInstr/test_decoder_unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/COVFInstr/test_decoder_unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CUDFInstr/None.cpp b/tests/Modular/EmulatorCore/Decoding/CUDFInstr/None.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CUDFInstr/None.cpp rename to tests/Modular/EmulatorCore/Decoding/CUDFInstr/None.cpp diff --git a/test/Modular/EmulatorCore/Decoding/CUDFInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/CUDFInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/CUDFInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/CUDFInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/DECInstr/R.cpp b/tests/Modular/EmulatorCore/Decoding/DECInstr/R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/DECInstr/R.cpp rename to tests/Modular/EmulatorCore/Decoding/DECInstr/R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/DECInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/DECInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/DECInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/DECInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/DIVInstr/R.cpp b/tests/Modular/EmulatorCore/Decoding/DIVInstr/R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/DIVInstr/R.cpp rename to tests/Modular/EmulatorCore/Decoding/DIVInstr/R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/DIVInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/DIVInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/DIVInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/DIVInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/Features/AddressAddition.cpp b/tests/Modular/EmulatorCore/Decoding/Features/AddressAddition.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/Features/AddressAddition.cpp rename to tests/Modular/EmulatorCore/Decoding/Features/AddressAddition.cpp diff --git a/test/Modular/EmulatorCore/Decoding/HALTInstr/None.cpp b/tests/Modular/EmulatorCore/Decoding/HALTInstr/None.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/HALTInstr/None.cpp rename to tests/Modular/EmulatorCore/Decoding/HALTInstr/None.cpp diff --git a/test/Modular/EmulatorCore/Decoding/HALTInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/HALTInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/HALTInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/HALTInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/HIDInstr/None.cpp b/tests/Modular/EmulatorCore/Decoding/HIDInstr/None.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/HIDInstr/None.cpp rename to tests/Modular/EmulatorCore/Decoding/HIDInstr/None.cpp diff --git a/test/Modular/EmulatorCore/Decoding/HIDInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/HIDInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/HIDInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/HIDInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/INCInstr/R.cpp b/tests/Modular/EmulatorCore/Decoding/INCInstr/R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/INCInstr/R.cpp rename to tests/Modular/EmulatorCore/Decoding/INCInstr/R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/INCInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/INCInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/INCInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/INCInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/JMEInstr/test_decoder_imm.cpp b/tests/Modular/EmulatorCore/Decoding/JMEInstr/test_decoder_imm.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/JMEInstr/test_decoder_imm.cpp rename to tests/Modular/EmulatorCore/Decoding/JMEInstr/test_decoder_imm.cpp diff --git a/test/Modular/EmulatorCore/Decoding/JMEInstr/test_decoder_r.cpp b/tests/Modular/EmulatorCore/Decoding/JMEInstr/test_decoder_r.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/JMEInstr/test_decoder_r.cpp rename to tests/Modular/EmulatorCore/Decoding/JMEInstr/test_decoder_r.cpp diff --git a/test/Modular/EmulatorCore/Decoding/JMEInstr/test_decoder_unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/JMEInstr/test_decoder_unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/JMEInstr/test_decoder_unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/JMEInstr/test_decoder_unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/JMGRInstr/test_decoder_imm.cpp b/tests/Modular/EmulatorCore/Decoding/JMGRInstr/test_decoder_imm.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/JMGRInstr/test_decoder_imm.cpp rename to tests/Modular/EmulatorCore/Decoding/JMGRInstr/test_decoder_imm.cpp diff --git a/test/Modular/EmulatorCore/Decoding/JMGRInstr/test_decoder_r.cpp b/tests/Modular/EmulatorCore/Decoding/JMGRInstr/test_decoder_r.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/JMGRInstr/test_decoder_r.cpp rename to tests/Modular/EmulatorCore/Decoding/JMGRInstr/test_decoder_r.cpp diff --git a/test/Modular/EmulatorCore/Decoding/JMGRInstr/test_decoder_unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/JMGRInstr/test_decoder_unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/JMGRInstr/test_decoder_unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/JMGRInstr/test_decoder_unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/JMLInstr/test_decoder_imm.cpp b/tests/Modular/EmulatorCore/Decoding/JMLInstr/test_decoder_imm.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/JMLInstr/test_decoder_imm.cpp rename to tests/Modular/EmulatorCore/Decoding/JMLInstr/test_decoder_imm.cpp diff --git a/test/Modular/EmulatorCore/Decoding/JMLInstr/test_decoder_r.cpp b/tests/Modular/EmulatorCore/Decoding/JMLInstr/test_decoder_r.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/JMLInstr/test_decoder_r.cpp rename to tests/Modular/EmulatorCore/Decoding/JMLInstr/test_decoder_r.cpp diff --git a/test/Modular/EmulatorCore/Decoding/JMLInstr/test_decoder_unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/JMLInstr/test_decoder_unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/JMLInstr/test_decoder_unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/JMLInstr/test_decoder_unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/JMPInstr/test_decoder_imm.cpp b/tests/Modular/EmulatorCore/Decoding/JMPInstr/test_decoder_imm.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/JMPInstr/test_decoder_imm.cpp rename to tests/Modular/EmulatorCore/Decoding/JMPInstr/test_decoder_imm.cpp diff --git a/test/Modular/EmulatorCore/Decoding/JMPInstr/test_decoder_r.cpp b/tests/Modular/EmulatorCore/Decoding/JMPInstr/test_decoder_r.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/JMPInstr/test_decoder_r.cpp rename to tests/Modular/EmulatorCore/Decoding/JMPInstr/test_decoder_r.cpp diff --git a/test/Modular/EmulatorCore/Decoding/JMPInstr/test_decoder_unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/JMPInstr/test_decoder_unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/JMPInstr/test_decoder_unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/JMPInstr/test_decoder_unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/MOVInstr/M_R.cpp b/tests/Modular/EmulatorCore/Decoding/MOVInstr/M_R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/MOVInstr/M_R.cpp rename to tests/Modular/EmulatorCore/Decoding/MOVInstr/M_R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/MOVInstr/RM_IMM.cpp b/tests/Modular/EmulatorCore/Decoding/MOVInstr/RM_IMM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/MOVInstr/RM_IMM.cpp rename to tests/Modular/EmulatorCore/Decoding/MOVInstr/RM_IMM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/MOVInstr/RM_M.cpp b/tests/Modular/EmulatorCore/Decoding/MOVInstr/RM_M.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/MOVInstr/RM_M.cpp rename to tests/Modular/EmulatorCore/Decoding/MOVInstr/RM_M.cpp diff --git a/test/Modular/EmulatorCore/Decoding/MOVInstr/RM_R.cpp b/tests/Modular/EmulatorCore/Decoding/MOVInstr/RM_R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/MOVInstr/RM_R.cpp rename to tests/Modular/EmulatorCore/Decoding/MOVInstr/RM_R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/MOVInstr/R_IMM.cpp b/tests/Modular/EmulatorCore/Decoding/MOVInstr/R_IMM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/MOVInstr/R_IMM.cpp rename to tests/Modular/EmulatorCore/Decoding/MOVInstr/R_IMM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/MOVInstr/R_M.cpp b/tests/Modular/EmulatorCore/Decoding/MOVInstr/R_M.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/MOVInstr/R_M.cpp rename to tests/Modular/EmulatorCore/Decoding/MOVInstr/R_M.cpp diff --git a/test/Modular/EmulatorCore/Decoding/MOVInstr/R_R.cpp b/tests/Modular/EmulatorCore/Decoding/MOVInstr/R_R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/MOVInstr/R_R.cpp rename to tests/Modular/EmulatorCore/Decoding/MOVInstr/R_R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/MOVInstr/R_RM.cpp b/tests/Modular/EmulatorCore/Decoding/MOVInstr/R_RM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/MOVInstr/R_RM.cpp rename to tests/Modular/EmulatorCore/Decoding/MOVInstr/R_RM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/MOVInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/MOVInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/MOVInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/MOVInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/MULInstr/R_IMM.cpp b/tests/Modular/EmulatorCore/Decoding/MULInstr/R_IMM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/MULInstr/R_IMM.cpp rename to tests/Modular/EmulatorCore/Decoding/MULInstr/R_IMM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/MULInstr/R_M.cpp b/tests/Modular/EmulatorCore/Decoding/MULInstr/R_M.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/MULInstr/R_M.cpp rename to tests/Modular/EmulatorCore/Decoding/MULInstr/R_M.cpp diff --git a/test/Modular/EmulatorCore/Decoding/MULInstr/R_R.cpp b/tests/Modular/EmulatorCore/Decoding/MULInstr/R_R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/MULInstr/R_R.cpp rename to tests/Modular/EmulatorCore/Decoding/MULInstr/R_R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/MULInstr/R_RM.cpp b/tests/Modular/EmulatorCore/Decoding/MULInstr/R_RM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/MULInstr/R_RM.cpp rename to tests/Modular/EmulatorCore/Decoding/MULInstr/R_RM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/MULInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/MULInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/MULInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/MULInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ORInstr/R_IMM.cpp b/tests/Modular/EmulatorCore/Decoding/ORInstr/R_IMM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ORInstr/R_IMM.cpp rename to tests/Modular/EmulatorCore/Decoding/ORInstr/R_IMM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ORInstr/R_M.cpp b/tests/Modular/EmulatorCore/Decoding/ORInstr/R_M.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ORInstr/R_M.cpp rename to tests/Modular/EmulatorCore/Decoding/ORInstr/R_M.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ORInstr/R_R.cpp b/tests/Modular/EmulatorCore/Decoding/ORInstr/R_R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ORInstr/R_R.cpp rename to tests/Modular/EmulatorCore/Decoding/ORInstr/R_R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ORInstr/R_RM.cpp b/tests/Modular/EmulatorCore/Decoding/ORInstr/R_RM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ORInstr/R_RM.cpp rename to tests/Modular/EmulatorCore/Decoding/ORInstr/R_RM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/ORInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/ORInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/ORInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/ORInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/READInstr/IMM.cpp b/tests/Modular/EmulatorCore/Decoding/READInstr/IMM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/READInstr/IMM.cpp rename to tests/Modular/EmulatorCore/Decoding/READInstr/IMM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/READInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/READInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/READInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/READInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/SHFLInstr/R_IMM.cpp b/tests/Modular/EmulatorCore/Decoding/SHFLInstr/R_IMM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/SHFLInstr/R_IMM.cpp rename to tests/Modular/EmulatorCore/Decoding/SHFLInstr/R_IMM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/SHFLInstr/R_R.cpp b/tests/Modular/EmulatorCore/Decoding/SHFLInstr/R_R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/SHFLInstr/R_R.cpp rename to tests/Modular/EmulatorCore/Decoding/SHFLInstr/R_R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/SHFLInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/SHFLInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/SHFLInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/SHFLInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/SHFRInstr/R_IMM.cpp b/tests/Modular/EmulatorCore/Decoding/SHFRInstr/R_IMM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/SHFRInstr/R_IMM.cpp rename to tests/Modular/EmulatorCore/Decoding/SHFRInstr/R_IMM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/SHFRInstr/R_R.cpp b/tests/Modular/EmulatorCore/Decoding/SHFRInstr/R_R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/SHFRInstr/R_R.cpp rename to tests/Modular/EmulatorCore/Decoding/SHFRInstr/R_R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/SHFRInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/SHFRInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/SHFRInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/SHFRInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/SUBInstr/R_IMM.cpp b/tests/Modular/EmulatorCore/Decoding/SUBInstr/R_IMM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/SUBInstr/R_IMM.cpp rename to tests/Modular/EmulatorCore/Decoding/SUBInstr/R_IMM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/SUBInstr/R_M.cpp b/tests/Modular/EmulatorCore/Decoding/SUBInstr/R_M.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/SUBInstr/R_M.cpp rename to tests/Modular/EmulatorCore/Decoding/SUBInstr/R_M.cpp diff --git a/test/Modular/EmulatorCore/Decoding/SUBInstr/R_R.cpp b/tests/Modular/EmulatorCore/Decoding/SUBInstr/R_R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/SUBInstr/R_R.cpp rename to tests/Modular/EmulatorCore/Decoding/SUBInstr/R_R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/SUBInstr/R_RM.cpp b/tests/Modular/EmulatorCore/Decoding/SUBInstr/R_RM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/SUBInstr/R_RM.cpp rename to tests/Modular/EmulatorCore/Decoding/SUBInstr/R_RM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/SUBInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/SUBInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/SUBInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/SUBInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/Decoding/WRITEInstr/R_IMM.cpp b/tests/Modular/EmulatorCore/Decoding/WRITEInstr/R_IMM.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/WRITEInstr/R_IMM.cpp rename to tests/Modular/EmulatorCore/Decoding/WRITEInstr/R_IMM.cpp diff --git a/test/Modular/EmulatorCore/Decoding/WRITEInstr/R_R.cpp b/tests/Modular/EmulatorCore/Decoding/WRITEInstr/R_R.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/WRITEInstr/R_R.cpp rename to tests/Modular/EmulatorCore/Decoding/WRITEInstr/R_R.cpp diff --git a/test/Modular/EmulatorCore/Decoding/WRITEInstr/Unsupported.cpp b/tests/Modular/EmulatorCore/Decoding/WRITEInstr/Unsupported.cpp similarity index 100% rename from test/Modular/EmulatorCore/Decoding/WRITEInstr/Unsupported.cpp rename to tests/Modular/EmulatorCore/Decoding/WRITEInstr/Unsupported.cpp diff --git a/test/Modular/EmulatorCore/MemoryControllers/ST.cpp b/tests/Modular/EmulatorCore/MemoryControllers/ST.cpp similarity index 100% rename from test/Modular/EmulatorCore/MemoryControllers/ST.cpp rename to tests/Modular/EmulatorCore/MemoryControllers/ST.cpp diff --git a/test/Modular/EmulatorCore/Stack/Stack.cpp b/tests/Modular/EmulatorCore/Stack/Stack.cpp similarity index 100% rename from test/Modular/EmulatorCore/Stack/Stack.cpp rename to tests/Modular/EmulatorCore/Stack/Stack.cpp diff --git a/tests/Python/Buildsystem/DummyProject/CMakeLists.txt b/tests/Python/Buildsystem/DummyProject/CMakeLists.txt new file mode 100644 index 00000000..fac462c6 --- /dev/null +++ b/tests/Python/Buildsystem/DummyProject/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.25) + + +project(dummy) + +find_package(plog 1.0.0 REQUIRED) + +if("${DUMMY_MESSAGE}" EQ "") + message(ERROR "Dummy: expected DUMMY_MESSAGE to be defined") +endif() + +add_executable( + dummy + main.cpp +) + +target_compile_definitions( + dummy PRIVATE + DUMMY_MESSAGE=${DUMMY_MESSAGE} +) + +target_link_libraries(dummy PRIVATE plog) diff --git a/tests/Python/Buildsystem/DummyProject/conanfile.py b/tests/Python/Buildsystem/DummyProject/conanfile.py new file mode 100644 index 00000000..b6f90766 --- /dev/null +++ b/tests/Python/Buildsystem/DummyProject/conanfile.py @@ -0,0 +1,20 @@ +from typing import Self, List + +from conan import ConanFile +from conan.tools.cmake import cmake_layout + + +class Dummy(ConanFile): + generators: List[str] = ['CMakeToolchain', 'CMakeDeps'] + settings: List[str] = ['os', 'compiler', 'build_type', 'arch'] + + def __init__(self: Self, display_name: str = '') -> None: + self.name = 'Dummy' + self.version = '1.0.0' + super().__init__(display_name) + + def requirements(self: Self) -> None: + self.requires('plog/1.1.10') + + def layout(self): + cmake_layout(self) \ No newline at end of file diff --git a/tests/Python/Buildsystem/DummyProject/main.cpp b/tests/Python/Buildsystem/DummyProject/main.cpp new file mode 100644 index 00000000..8fc55ebb --- /dev/null +++ b/tests/Python/Buildsystem/DummyProject/main.cpp @@ -0,0 +1,13 @@ +#include + +// compilation will fail if plog is not available +#include + +#ifndef DUMMY_MESSAGE +#error "Expected macro DUMMY_MESSAGE in this compilation unit" +#endif + +int main() { + std::cout << DUMMY_MESSAGE; + return 0; +} \ No newline at end of file diff --git a/tests/Python/Buildsystem/test_build.py b/tests/Python/Buildsystem/test_build.py new file mode 100644 index 00000000..a9af4850 --- /dev/null +++ b/tests/Python/Buildsystem/test_build.py @@ -0,0 +1,92 @@ +import os +import unittest + +from pathlib import Path +from scripts.build import Routines, Config + + +class BuildsystemTestCase(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.orig_cwd = Path.cwd() + dummy_dir = cls.orig_cwd / 'dummy' + os.chdir(dummy_dir) + + @classmethod + def tearDownClass(cls): + os.chdir(cls.orig_cwd) + + def setUp(self): + self.config = Config() + self.routines = Routines(self.config) + + def test_01_remove_no_dirs(self): + self.config.build_configs = ['Debug', 'Release'] + self.routines.remove() + + def test_01_remove_a_single_dir(self): + path = self.config.build_directory / 'Debug' + path.mkdir(parents=True) + + self.config.build_configs = ['Debug'] + self.routines.remove() + + self.assertFalse(path.exists()) + + def test_01_remove_selected_dir(self): + build_path = self.config.build_directory + paths = [build_path / 'Debug', build_path / 'Release'] + for path in paths: + path.mkdir(parents=True) + + self.config.build_configs = ['Debug'] + self.routines.remove() + + debug, release = paths + self.assertTrue(build_path.exists()) + self.assertFalse(debug.exists()) + self.assertTrue(release.exists()) + + def test_01_remove_all_dirs(self): + build_path = self.config.build_directory + paths = [build_path / 'Debug', build_path / 'Release'] + for path in paths: + path.mkdir(parents=True) + + self.config.build_configs = ['Debug', 'Release'] + self.routines.remove() + + debug, release = paths + # it is important to preserve top-level "build" dir + self.assertTrue(build_path.exists()) + self.assertFalse(debug.exists()) + self.assertFalse(release.exists()) + + def test_02_conan_install(self): + # it just has to work + self.config.build_configs = ['Debug', 'Release'] + self.routines.conan_install() + + # TODO: check presets? + + build_path = self.config.build_directory + debug, release = build_path / 'Debug', build_path / 'Release' + self.assertTrue(build_path.exists()) + self.assertTrue(debug.exists()) + self.assertTrue(release.exists()) + + def test_03_configure(self): + # TODO: stub out subprocess calls and verify configure() constructs correct cmake commands. + pass + + def test_04_build(self): + # TODO: create dummy build/Debug directory and test build() calls cmake --build with correct args. + pass + + def test_05_symlink_compile_commands(self): + # TODO: create compile_commands.json in build/Debug or build/Release, + # and test symlink_compile_commands() creates the top-level symlink correctly. + pass + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/test/fixtures.hpp b/tests/fixtures.hpp similarity index 100% rename from test/fixtures.hpp rename to tests/fixtures.hpp diff --git a/test/main.cpp b/tests/main.cpp similarity index 100% rename from test/main.cpp rename to tests/main.cpp From 3d13be4d9dcada38c577240c516468a09ce00184 Mon Sep 17 00:00:00 2001 From: AshFungor Date: Tue, 22 Apr 2025 02:43:01 +0300 Subject: [PATCH 06/25] make python tests functional --- cmake/PrecompiledHeaders.cmake | 0 scripts/build.py | 4 +-- tests/CMakeLists.txt | 1 + tests/Python/Buildsystem/CMakeLists.txt | 21 ++++++++++++ .../Buildsystem/DummyProject/CMakeLists.txt | 23 +++++++------ .../Python/Buildsystem/DummyProject/main.cpp | 2 +- tests/Python/Buildsystem/test_build.py | 32 +++++++++++-------- 7 files changed, 54 insertions(+), 29 deletions(-) delete mode 100644 cmake/PrecompiledHeaders.cmake create mode 100644 tests/Python/Buildsystem/CMakeLists.txt diff --git a/cmake/PrecompiledHeaders.cmake b/cmake/PrecompiledHeaders.cmake deleted file mode 100644 index e69de29b..00000000 diff --git a/scripts/build.py b/scripts/build.py index f420eb59..24cf7d52 100755 --- a/scripts/build.py +++ b/scripts/build.py @@ -59,8 +59,6 @@ def factory(f: Callable) -> Callable: class Routines: - CMAKE_USER_PRESETS = Path.cwd() / 'CMakeUserPresets.json' - def __init__(self, params: Config): self.__params = params @@ -115,7 +113,7 @@ def configure(self): if not self.__params.build_directory.is_dir(): self.__params.build_directory.mkdir() - use_presets = self.CMAKE_USER_PRESETS.is_file() + use_presets = Path.cwd().joinpath('CMakeUserPresets.json').is_file() if use_presets: logger.info('found CMake presets') diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 85759b8b..a3c1f56f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,3 +6,4 @@ include(GoogleTest) add_subdirectory(Integration) add_subdirectory(Modular) +add_subdirectory(Python/Buildsystem) diff --git a/tests/Python/Buildsystem/CMakeLists.txt b/tests/Python/Buildsystem/CMakeLists.txt new file mode 100644 index 00000000..5a097f07 --- /dev/null +++ b/tests/Python/Buildsystem/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.25) + + +add_custom_target(python_build_copy_tests ALL + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_CURRENT_SOURCE_DIR}/DummyProject" + "${CMAKE_CURRENT_BINARY_DIR}/DummyProject" + COMMAND ${CMAKE_COMMAND} -E copy + "${CMAKE_CURRENT_SOURCE_DIR}/test_build.py" + "${CMAKE_CURRENT_BINARY_DIR}/test_build.py" + COMMENT "Copy python tests and their artifacts to build directory" +) + +add_test( + NAME TestBuildsystem + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E env + PYTHONPATH=${CMAKE_SOURCE_DIR} + python3 + "${CMAKE_CURRENT_BINARY_DIR}/test_build.py" +) \ No newline at end of file diff --git a/tests/Python/Buildsystem/DummyProject/CMakeLists.txt b/tests/Python/Buildsystem/DummyProject/CMakeLists.txt index fac462c6..930b3cbc 100644 --- a/tests/Python/Buildsystem/DummyProject/CMakeLists.txt +++ b/tests/Python/Buildsystem/DummyProject/CMakeLists.txt @@ -1,22 +1,21 @@ cmake_minimum_required(VERSION 3.25) - - project(dummy) find_package(plog 1.0.0 REQUIRED) -if("${DUMMY_MESSAGE}" EQ "") - message(ERROR "Dummy: expected DUMMY_MESSAGE to be defined") +if(NOT DEFINED DUMMY_MESSAGE OR DUMMY_MESSAGE STREQUAL "") + message(FATAL_ERROR "Dummy: expected DUMMY_MESSAGE to be defined") endif() -add_executable( - dummy - main.cpp -) +add_executable(dummy main.cpp) -target_compile_definitions( - dummy PRIVATE - DUMMY_MESSAGE=${DUMMY_MESSAGE} +target_compile_definitions(dummy + PRIVATE + # The backslashes escape the quote into the generated -D flag + DUMMY_MESSAGE=\"${DUMMY_MESSAGE}\" ) -target_link_libraries(dummy PRIVATE plog) +target_link_libraries(dummy + PRIVATE + plog::plog +) \ No newline at end of file diff --git a/tests/Python/Buildsystem/DummyProject/main.cpp b/tests/Python/Buildsystem/DummyProject/main.cpp index 8fc55ebb..6d30eee3 100644 --- a/tests/Python/Buildsystem/DummyProject/main.cpp +++ b/tests/Python/Buildsystem/DummyProject/main.cpp @@ -1,7 +1,7 @@ #include // compilation will fail if plog is not available -#include +#include #ifndef DUMMY_MESSAGE #error "Expected macro DUMMY_MESSAGE in this compilation unit" diff --git a/tests/Python/Buildsystem/test_build.py b/tests/Python/Buildsystem/test_build.py index a9af4850..5228e4f6 100644 --- a/tests/Python/Buildsystem/test_build.py +++ b/tests/Python/Buildsystem/test_build.py @@ -1,16 +1,20 @@ import os +import logging import unittest from pathlib import Path from scripts.build import Routines, Config - +# TODO: make a single test runner +# TODO: add more tests + run resulting executable class BuildsystemTestCase(unittest.TestCase): @classmethod def setUpClass(cls): + cls.dummy_message = 'Hello, HyperCPU!' cls.orig_cwd = Path.cwd() - dummy_dir = cls.orig_cwd / 'dummy' - os.chdir(dummy_dir) + cls.dummy_dir = cls.orig_cwd / 'DummyProject' + + os.chdir(cls.dummy_dir) @classmethod def tearDownClass(cls): @@ -26,7 +30,7 @@ def test_01_remove_no_dirs(self): def test_01_remove_a_single_dir(self): path = self.config.build_directory / 'Debug' - path.mkdir(parents=True) + path.mkdir(parents=True, exist_ok=True) self.config.build_configs = ['Debug'] self.routines.remove() @@ -51,7 +55,7 @@ def test_01_remove_all_dirs(self): build_path = self.config.build_directory paths = [build_path / 'Debug', build_path / 'Release'] for path in paths: - path.mkdir(parents=True) + path.mkdir(parents=True, exist_ok=True) self.config.build_configs = ['Debug', 'Release'] self.routines.remove() @@ -64,11 +68,10 @@ def test_01_remove_all_dirs(self): def test_02_conan_install(self): # it just has to work + self.config.allow_profile_detection = True self.config.build_configs = ['Debug', 'Release'] self.routines.conan_install() - # TODO: check presets? - build_path = self.config.build_directory debug, release = build_path / 'Debug', build_path / 'Release' self.assertTrue(build_path.exists()) @@ -76,17 +79,20 @@ def test_02_conan_install(self): self.assertTrue(release.exists()) def test_03_configure(self): - # TODO: stub out subprocess calls and verify configure() constructs correct cmake commands. - pass + self.config.defines.append( + ('DUMMY_MESSAGE', 'STRING', self.dummy_message) + ) + self.config.build_configs = ['Debug', 'Release'] + self.routines.configure() def test_04_build(self): - # TODO: create dummy build/Debug directory and test build() calls cmake --build with correct args. + self.config.build_configs = ['Debug', 'Release'] + self.routines.build() pass - def test_05_symlink_compile_commands(self): - # TODO: create compile_commands.json in build/Debug or build/Release, - # and test symlink_compile_commands() creates the top-level symlink correctly. + def test_05_symlink_debug_compile_commands(self): pass if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG) unittest.main() \ No newline at end of file From fa5f546eb473839c65e3013ed507e0b1d568cd00 Mon Sep 17 00:00:00 2001 From: AshFungor Date: Tue, 22 Apr 2025 12:41:27 +0300 Subject: [PATCH 07/25] removed test data leftovers --- Testing/Temporary/CTestCostData.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Testing/Temporary/CTestCostData.txt diff --git a/Testing/Temporary/CTestCostData.txt b/Testing/Temporary/CTestCostData.txt deleted file mode 100644 index ed97d539..00000000 --- a/Testing/Temporary/CTestCostData.txt +++ /dev/null @@ -1 +0,0 @@ ---- From 6fb670896b4b9ffc1e76ae8ceec4d0f468cf2ef0 Mon Sep 17 00:00:00 2001 From: AshFungor Date: Tue, 22 Apr 2025 13:14:31 +0300 Subject: [PATCH 08/25] update workflow --- .github/workflows/distro-ci.yml | 2 +- tests/Python/Buildsystem/test_build.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/distro-ci.yml b/.github/workflows/distro-ci.yml index a7a3bd3d..8bd06dae 100644 --- a/.github/workflows/distro-ci.yml +++ b/.github/workflows/distro-ci.yml @@ -49,5 +49,5 @@ jobs: --config Debug \ --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=g++ \ - -D CMAKE_CXX_COMPILER:STRING=gcc + -D CMAKE_CXX_COMPILER:STRING=gcc \ --detect diff --git a/tests/Python/Buildsystem/test_build.py b/tests/Python/Buildsystem/test_build.py index 5228e4f6..e07439e2 100644 --- a/tests/Python/Buildsystem/test_build.py +++ b/tests/Python/Buildsystem/test_build.py @@ -92,6 +92,7 @@ def test_04_build(self): def test_05_symlink_debug_compile_commands(self): pass + pass if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) From 62b2d91940c1bde10238d548345e2f7ae1b19dae Mon Sep 17 00:00:00 2001 From: AshFungor Date: Tue, 22 Apr 2025 13:40:39 +0300 Subject: [PATCH 09/25] testing docker autobuild --- .github/workflows/docker-autobuild.yml | 3 +++ docker/alpine/Dockerfile | 1 + 2 files changed, 4 insertions(+) diff --git a/.github/workflows/docker-autobuild.yml b/.github/workflows/docker-autobuild.yml index 9e1ac660..71b57b5a 100644 --- a/.github/workflows/docker-autobuild.yml +++ b/.github/workflows/docker-autobuild.yml @@ -3,6 +3,9 @@ name: HyperCPU CI/CD Pipeline (update Docker images) on: schedule: - cron: "0 0 */7 * *" + push: + paths: + - 'docker/' permissions: contents: read diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index 10b6a07f..5c199bb1 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -1,5 +1,6 @@ FROM alpine:latest + RUN apk update && \ apk add --no-cache python3 py3-pip clang gcc git cmake make \ ninja nodejs grep \ From 6279addeeeff3fe8d037ec7711858f9b0d50858a Mon Sep 17 00:00:00 2001 From: AshFungor Date: Tue, 22 Apr 2025 17:56:17 +0300 Subject: [PATCH 10/25] fix --detect flag --- .github/workflows/run-tests-feature-branch.yml | 4 ++-- .github/workflows/testing.yml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/run-tests-feature-branch.yml b/.github/workflows/run-tests-feature-branch.yml index 1eeaabc6..a3bad23d 100644 --- a/.github/workflows/run-tests-feature-branch.yml +++ b/.github/workflows/run-tests-feature-branch.yml @@ -22,7 +22,7 @@ jobs: --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=g++ \ -D CMAKE_CXX_COMPILER:STRING=gcc \ - --detect + --profiles-detection cd build/Release && ctest -V python3 scripts/build.py -r --config Release --build-dir build @@ -33,7 +33,7 @@ jobs: --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=clang++-19 \ -D CMAKE_C_COMPILER:STRING=clang-19 \ - --detect + --profiles-detection cd build/Release && ctest -V python3 scripts/build.py -r --config Release --build-dir build diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index cac6b0db..0da06f66 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -24,7 +24,7 @@ jobs: --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=g++ \ -D CMAKE_CXX_COMPILER:STRING=gcc \ - --detect + --profiles-detection cd build/Debug && ctest -V python3 scripts/build.py -r --config Debug --build-dir build @@ -35,7 +35,7 @@ jobs: --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=g++ \ -D CMAKE_CXX_COMPILER:STRING=gcc \ - --detect + --profiles-detection cd build/Release && ctest -V python3 scripts/build.py -r --config Release --build-dir build @@ -46,7 +46,7 @@ jobs: --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=clang++-19 \ -D CMAKE_C_COMPILER:STRING=clang-19 \ - --detect + --profiles-detection cd build/Debug && ctest -V python3 scripts/build.py -r --config Debug --build-dir build @@ -57,7 +57,7 @@ jobs: --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=clang++-19 \ -D CMAKE_C_COMPILER:STRING=clang-19 \ - --detect + --profiles-detection cd build/Release && ctest -V python3 scripts/build.py -r --config Release --build-dir build From 39feec19259a72f148873057e52f16dd96227b2e Mon Sep 17 00:00:00 2001 From: AshFungor Date: Tue, 22 Apr 2025 18:48:04 +0300 Subject: [PATCH 11/25] add make to dockerfiles --- docker/archlinux/Dockerfile | 2 +- docker/debian-stable/Dockerfile | 2 +- docker/debian-unstable/Dockerfile | 2 +- docker/fedora/Dockerfile | 2 +- docker/ubuntu/Dockerfile | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docker/archlinux/Dockerfile b/docker/archlinux/Dockerfile index 7cf48575..635e4611 100644 --- a/docker/archlinux/Dockerfile +++ b/docker/archlinux/Dockerfile @@ -2,6 +2,6 @@ FROM archlinux:latest RUN pacman -Syu --noconfirm --needed \ python python-pip cmake git clang gcc \ - base-devel nodejs \ + base-devel nodejs make \ && pip install --no-cache-dir --break-system-packages conan \ && pacman -Scc --noconfirm diff --git a/docker/debian-stable/Dockerfile b/docker/debian-stable/Dockerfile index 585d8664..25772875 100644 --- a/docker/debian-stable/Dockerfile +++ b/docker/debian-stable/Dockerfile @@ -1,6 +1,6 @@ FROM debian:latest RUN apt-get update && apt-get install -y --no-install-recommends \ - git cmake clang libre2-9 libre2-dev gnu-which nodejs python3 python3-pip \ + git cmake clang libre2-9 libre2-dev gnu-which nodejs python3 python3-pip make \ && pip3 install --no-cache-dir --break-system-packages conan \ && rm -rf /var/lib/apt/lists/* diff --git a/docker/debian-unstable/Dockerfile b/docker/debian-unstable/Dockerfile index b5d5cb12..8c7db2a1 100644 --- a/docker/debian-unstable/Dockerfile +++ b/docker/debian-unstable/Dockerfile @@ -1,6 +1,6 @@ FROM debian:unstable RUN apt-get update && apt-get install -y --no-install-recommends \ - git cmake clang libre2-9 libre2-dev gnu-which nodejs python3 python3-pip \ + git cmake clang libre2-9 libre2-dev gnu-which nodejs python3 python3-pip make \ && pip3 install --no-cache-dir --break-system-packages conan \ && rm -rf /var/lib/apt/lists/* diff --git a/docker/fedora/Dockerfile b/docker/fedora/Dockerfile index 9fd7131f..1994eb78 100644 --- a/docker/fedora/Dockerfile +++ b/docker/fedora/Dockerfile @@ -2,7 +2,7 @@ FROM fedora:latest RUN dnf update -y \ && dnf install -y --setopt=install_weak_deps=False git cmake \ - clang gcc which nodejs python3 python3-pip \ + clang gcc which nodejs python3 python3-pip make \ && pip3 install --no-cache-dir --break-system-packages conan \ && dnf clean all \ && rm -rf /var/cache/dnf diff --git a/docker/ubuntu/Dockerfile b/docker/ubuntu/Dockerfile index 41eb65b8..19dbddd1 100644 --- a/docker/ubuntu/Dockerfile +++ b/docker/ubuntu/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:latest RUN apt update && apt install -y --no-install-recommends \ - git cmake clang libre2-9 libre2-dev gnu-which nodejs python3 python3-pip \ + git cmake clang libre2-9 libre2-dev gnu-which nodejs python3 make python3-pip \ && pip3 install --no-cache-dir --break-system-packages conan \ && rm -rf /var/lib/apt/lists/* From aa3156a0530a7dba937ac33a8b95a21e0d072f5d Mon Sep 17 00:00:00 2001 From: AshFungor Date: Tue, 22 Apr 2025 19:01:24 +0300 Subject: [PATCH 12/25] fix compiler ids --- .github/workflows/run-tests-feature-branch.yml | 2 +- .github/workflows/testing.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-tests-feature-branch.yml b/.github/workflows/run-tests-feature-branch.yml index a3bad23d..ccd33ed9 100644 --- a/.github/workflows/run-tests-feature-branch.yml +++ b/.github/workflows/run-tests-feature-branch.yml @@ -21,7 +21,7 @@ jobs: --config Release \ --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=g++ \ - -D CMAKE_CXX_COMPILER:STRING=gcc \ + -D CMAKE_C_COMPILER:STRING=gcc \ --profiles-detection cd build/Release && ctest -V python3 scripts/build.py -r --config Release --build-dir build diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 0da06f66..e14defe9 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -23,7 +23,7 @@ jobs: --config Debug \ --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=g++ \ - -D CMAKE_CXX_COMPILER:STRING=gcc \ + -D CMAKE_C_COMPILER:STRING=gcc \ --profiles-detection cd build/Debug && ctest -V python3 scripts/build.py -r --config Debug --build-dir build @@ -34,7 +34,7 @@ jobs: --config Release \ --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=g++ \ - -D CMAKE_CXX_COMPILER:STRING=gcc \ + -D CMAKE_C_COMPILER:STRING=gcc \ --profiles-detection cd build/Release && ctest -V python3 scripts/build.py -r --config Release --build-dir build From 5b2de33c4b6468e332555331a26faf893b084a86 Mon Sep 17 00:00:00 2001 From: AshFungor Date: Tue, 22 Apr 2025 19:01:51 +0300 Subject: [PATCH 13/25] added g++ where applicable --- docker/alpine/Dockerfile | 2 +- docker/debian-stable/Dockerfile | 2 +- docker/debian-unstable/Dockerfile | 2 +- docker/fedora/Dockerfile | 2 +- docker/ubuntu/Dockerfile | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index 5c199bb1..c5ce9c27 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -3,5 +3,5 @@ FROM alpine:latest RUN apk update && \ apk add --no-cache python3 py3-pip clang gcc git cmake make \ - ninja nodejs grep \ + ninja nodejs grep g++ \ && pip3 install --no-cache-dir --break-system-packages conan \ No newline at end of file diff --git a/docker/debian-stable/Dockerfile b/docker/debian-stable/Dockerfile index 25772875..ab117b0f 100644 --- a/docker/debian-stable/Dockerfile +++ b/docker/debian-stable/Dockerfile @@ -1,6 +1,6 @@ FROM debian:latest RUN apt-get update && apt-get install -y --no-install-recommends \ - git cmake clang libre2-9 libre2-dev gnu-which nodejs python3 python3-pip make \ + git cmake clang gcc g++ nodejs python3 python3-pip make \ && pip3 install --no-cache-dir --break-system-packages conan \ && rm -rf /var/lib/apt/lists/* diff --git a/docker/debian-unstable/Dockerfile b/docker/debian-unstable/Dockerfile index 8c7db2a1..5934d757 100644 --- a/docker/debian-unstable/Dockerfile +++ b/docker/debian-unstable/Dockerfile @@ -1,6 +1,6 @@ FROM debian:unstable RUN apt-get update && apt-get install -y --no-install-recommends \ - git cmake clang libre2-9 libre2-dev gnu-which nodejs python3 python3-pip make \ + git cmake clang gcc g++ nodejs python3 python3-pip make \ && pip3 install --no-cache-dir --break-system-packages conan \ && rm -rf /var/lib/apt/lists/* diff --git a/docker/fedora/Dockerfile b/docker/fedora/Dockerfile index 1994eb78..1e9163c6 100644 --- a/docker/fedora/Dockerfile +++ b/docker/fedora/Dockerfile @@ -2,7 +2,7 @@ FROM fedora:latest RUN dnf update -y \ && dnf install -y --setopt=install_weak_deps=False git cmake \ - clang gcc which nodejs python3 python3-pip make \ + clang gcc g++ nodejs python3 python3-pip make \ && pip3 install --no-cache-dir --break-system-packages conan \ && dnf clean all \ && rm -rf /var/cache/dnf diff --git a/docker/ubuntu/Dockerfile b/docker/ubuntu/Dockerfile index 19dbddd1..24377914 100644 --- a/docker/ubuntu/Dockerfile +++ b/docker/ubuntu/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:latest RUN apt update && apt install -y --no-install-recommends \ - git cmake clang libre2-9 libre2-dev gnu-which nodejs python3 make python3-pip \ + git cmake clang libre2-9 libre2-dev gcc g++ nodejs python3 make python3-pip \ && pip3 install --no-cache-dir --break-system-packages conan \ && rm -rf /var/lib/apt/lists/* From af7615fdb50d90e0227ed2dd3ec1fd77b1e91c55 Mon Sep 17 00:00:00 2001 From: AshFungor Date: Sat, 26 Apr 2025 03:52:51 +0300 Subject: [PATCH 14/25] added options for hpool --- dist/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dist/CMakeLists.txt b/dist/CMakeLists.txt index 1393d82d..0d9874f3 100644 --- a/dist/CMakeLists.txt +++ b/dist/CMakeLists.txt @@ -7,4 +7,6 @@ set(POG_TESTS OFF CACHE BOOL "Build tests" FORCE) add_subdirectory(pog) # hpool +set(HPOOL_BUILD_TESTS OFF CACHE BOOL "Whether to include test target into configuration" FORCE) +set(HPOOL_BUILD_BENCHMARKS OFF CACHE BOOL "Whether to include benchmark target into configuration" FORCE) add_subdirectory(HPool) \ No newline at end of file From 3abd374cf18fea9de09929b658508cf0f442e706 Mon Sep 17 00:00:00 2001 From: AshFungor Date: Sat, 26 Apr 2025 04:07:00 +0300 Subject: [PATCH 15/25] modified commit tags in dist --- dist/HPool | 2 +- dist/pog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/HPool b/dist/HPool index 5d09b643..352cdd4e 160000 --- a/dist/HPool +++ b/dist/HPool @@ -1 +1 @@ -Subproject commit 5d09b643d2cd43081fae8af5a7c313272485c387 +Subproject commit 352cdd4e5eb2bfb091bd4970386387b6d6f027b1 diff --git a/dist/pog b/dist/pog index d62f0d05..ca14b0d6 160000 --- a/dist/pog +++ b/dist/pog @@ -1 +1 @@ -Subproject commit d62f0d05c1cbb638ef2d4d37084ab8b54e3bfc29 +Subproject commit ca14b0d6046169b8aa8f36d01536529d5cb6e35d From a07a124e892c89defa598d63cad240129f3a2c85 Mon Sep 17 00:00:00 2001 From: AshFungor Date: Sat, 26 Apr 2025 04:11:28 +0300 Subject: [PATCH 16/25] update pog --- dist/pog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/pog b/dist/pog index ca14b0d6..501fa03d 160000 --- a/dist/pog +++ b/dist/pog @@ -1 +1 @@ -Subproject commit ca14b0d6046169b8aa8f36d01536529d5cb6e35d +Subproject commit 501fa03da44a50cdf7c6042effea7336bb16e724 From 656fa28b28822dc09633ebadf5391237cdcc8e10 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Tue, 29 Apr 2025 20:04:00 +0400 Subject: [PATCH 17/25] Fixed submodules and fixtures - whole project can be built successfully --- dist/HPool | 2 +- src/Assembler/CMakeLists.txt | 2 +- tests/fixtures.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/HPool b/dist/HPool index 352cdd4e..6b165437 160000 --- a/dist/HPool +++ b/dist/HPool @@ -1 +1 @@ -Subproject commit 352cdd4e5eb2bfb091bd4970386387b6d6f027b1 +Subproject commit 6b1654378a084f4e7cc7324670543df86d03b7fa diff --git a/src/Assembler/CMakeLists.txt b/src/Assembler/CMakeLists.txt index 3897dd4b..422e6e91 100644 --- a/src/Assembler/CMakeLists.txt +++ b/src/Assembler/CMakeLists.txt @@ -17,7 +17,7 @@ add_library( Core/ModeNameAssoc.hpp ) target_include_directories( - assembler-core + assembler-core PUBLIC ${CMAKE_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tests/fixtures.hpp b/tests/fixtures.hpp index 03137514..404be3d1 100644 --- a/tests/fixtures.hpp +++ b/tests/fixtures.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #define private public #include From ea53d62dbe8d96b4472d3d1de65d67d7d18e43a0 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Tue, 29 Apr 2025 22:23:33 +0400 Subject: [PATCH 18/25] Move pog into the project, update includes --- dist/CMakeLists.txt | 6 - src/Assembler/CMakeLists.txt | 2 +- src/Assembler/Core/Compiler.cpp | 3 +- src/Assembler/Core/Compiler.hpp | 3 +- src/Assembler/Core/Parsers.cpp | 2 +- src/Assembler/Core/StatementCompilers.cpp | 3 +- src/CMakeLists.txt | 12 + src/Pog/Action.hpp | 28 ++ src/Pog/Automaton.hpp | 159 +++++++++++ src/Pog/DigraphAlgo.hpp | 80 ++++++ src/Pog/Errors.hpp | 63 ++++ src/Pog/FilterView.hpp | 81 ++++++ src/Pog/Grammar.hpp | 288 +++++++++++++++++++ src/Pog/HTMLReport.hpp | 284 +++++++++++++++++++ src/Pog/Item.hpp | 140 +++++++++ src/Pog/LineSpecialization.hpp | 38 +++ src/Pog/Operations/Follow.hpp | 56 ++++ src/Pog/Operations/Lookahead.hpp | 75 +++++ src/Pog/Operations/Operation.hpp | 54 ++++ src/Pog/Operations/Read.hpp | 70 +++++ src/Pog/Parser.hpp | 331 ++++++++++++++++++++++ src/Pog/ParserReport.hpp | 89 ++++++ src/Pog/ParsingTable.hpp | 170 +++++++++++ src/Pog/Pog.hpp | 6 + src/Pog/Precedence.hpp | 48 ++++ src/Pog/Relations/Includes.hpp | 148 ++++++++++ src/Pog/Relations/Lookback.hpp | 116 ++++++++ src/Pog/Relations/Relation.hpp | 64 +++++ src/Pog/Rule.hpp | 94 ++++++ src/Pog/RuleBuilder.hpp | 140 +++++++++ src/Pog/State.hpp | 157 ++++++++++ src/Pog/Symbol.hpp | 56 ++++ src/Pog/Token.hpp | 81 ++++++ src/Pog/TokenBuilder.hpp | 112 ++++++++ src/Pog/Tokenizer.hpp | 285 +++++++++++++++++++ src/Pog/Types/StateAndRule.hpp | 38 +++ src/Pog/Types/StateAndSymbol.hpp | 38 +++ src/Pog/Utils.hpp | 75 +++++ 38 files changed, 3481 insertions(+), 14 deletions(-) create mode 100644 src/Pog/Action.hpp create mode 100644 src/Pog/Automaton.hpp create mode 100644 src/Pog/DigraphAlgo.hpp create mode 100644 src/Pog/Errors.hpp create mode 100644 src/Pog/FilterView.hpp create mode 100644 src/Pog/Grammar.hpp create mode 100644 src/Pog/HTMLReport.hpp create mode 100644 src/Pog/Item.hpp create mode 100644 src/Pog/LineSpecialization.hpp create mode 100644 src/Pog/Operations/Follow.hpp create mode 100644 src/Pog/Operations/Lookahead.hpp create mode 100644 src/Pog/Operations/Operation.hpp create mode 100644 src/Pog/Operations/Read.hpp create mode 100644 src/Pog/Parser.hpp create mode 100644 src/Pog/ParserReport.hpp create mode 100644 src/Pog/ParsingTable.hpp create mode 100644 src/Pog/Pog.hpp create mode 100644 src/Pog/Precedence.hpp create mode 100644 src/Pog/Relations/Includes.hpp create mode 100644 src/Pog/Relations/Lookback.hpp create mode 100644 src/Pog/Relations/Relation.hpp create mode 100644 src/Pog/Rule.hpp create mode 100644 src/Pog/RuleBuilder.hpp create mode 100644 src/Pog/State.hpp create mode 100644 src/Pog/Symbol.hpp create mode 100644 src/Pog/Token.hpp create mode 100644 src/Pog/TokenBuilder.hpp create mode 100644 src/Pog/Tokenizer.hpp create mode 100644 src/Pog/Types/StateAndRule.hpp create mode 100644 src/Pog/Types/StateAndSymbol.hpp create mode 100644 src/Pog/Utils.hpp diff --git a/dist/CMakeLists.txt b/dist/CMakeLists.txt index 0d9874f3..b765f1c9 100644 --- a/dist/CMakeLists.txt +++ b/dist/CMakeLists.txt @@ -1,11 +1,5 @@ cmake_minimum_required(VERSION 3.25) -# pog -set(POG_BUNDLED_RE2 OFF CACHE BOOL "Use bundled re2" FORCE) -set(POG_BUNDLED_FMT OFF CACHE BOOL "Use bundled fmt" FORCE) -set(POG_TESTS OFF CACHE BOOL "Build tests" FORCE) -add_subdirectory(pog) - # hpool set(HPOOL_BUILD_TESTS OFF CACHE BOOL "Whether to include test target into configuration" FORCE) set(HPOOL_BUILD_BENCHMARKS OFF CACHE BOOL "Whether to include benchmark target into configuration" FORCE) diff --git a/src/Assembler/CMakeLists.txt b/src/Assembler/CMakeLists.txt index 422e6e91..653566a4 100644 --- a/src/Assembler/CMakeLists.txt +++ b/src/Assembler/CMakeLists.txt @@ -25,7 +25,7 @@ target_include_directories( target_link_libraries( assembler-core PUBLIC - fmt::fmt argparse::argparse pog eternal::eternal pch hpool + fmt::fmt argparse::argparse pog eternal::eternal pch pog hpool ) add_executable(hcasm Main/Main.cpp) diff --git a/src/Assembler/Core/Compiler.cpp b/src/Assembler/Core/Compiler.cpp index 281c1d34..af302deb 100644 --- a/src/Assembler/Core/Compiler.cpp +++ b/src/Assembler/Core/Compiler.cpp @@ -7,8 +7,7 @@ #include #include -#include -#include +#include using HyperCPU::LogLevel; diff --git a/src/Assembler/Core/Compiler.hpp b/src/Assembler/Core/Compiler.hpp index 3e3ec031..332f8c10 100644 --- a/src/Assembler/Core/Compiler.hpp +++ b/src/Assembler/Core/Compiler.hpp @@ -10,8 +10,7 @@ #include #include -#include -#include +#include #include diff --git a/src/Assembler/Core/Parsers.cpp b/src/Assembler/Core/Parsers.cpp index 8998a0cd..a1f2c3dd 100644 --- a/src/Assembler/Core/Parsers.cpp +++ b/src/Assembler/Core/Parsers.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include using HCAsm::Value; diff --git a/src/Assembler/Core/StatementCompilers.cpp b/src/Assembler/Core/StatementCompilers.cpp index 608289a7..bab3aaa8 100644 --- a/src/Assembler/Core/StatementCompilers.cpp +++ b/src/Assembler/Core/StatementCompilers.cpp @@ -1,5 +1,4 @@ -#include "pog/line_spec.h" -#include "pog/parser.h" +#include #include #include #include diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2cd5553c..be38747e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,18 @@ add_library( ) target_precompile_headers(pch INTERFACE pch.hpp) +add_library( + pog INTERFACE + Pog/Pog.hpp +) +target_precompile_headers(pog INTERFACE pog.hpp) +target_include_directories( + pog + INTERFACE + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/Pog +) + add_subdirectory(Emulator) add_subdirectory(Assembler) add_subdirectory(BacktraceProvider) diff --git a/src/Pog/Action.hpp b/src/Pog/Action.hpp new file mode 100644 index 00000000..26ba034a --- /dev/null +++ b/src/Pog/Action.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include +#include + +namespace pog { + +template +struct Shift +{ + const State* state; +}; + +template +struct Reduce +{ + const Rule* rule; +}; + +struct Accept {}; + +template +using Action = std::variant, Reduce, Accept>; + +} // namespace pog diff --git a/src/Pog/Automaton.hpp b/src/Pog/Automaton.hpp new file mode 100644 index 00000000..731129c5 --- /dev/null +++ b/src/Pog/Automaton.hpp @@ -0,0 +1,159 @@ +#pragma once + +#include +#include +#include + +#include + +#include +#include +#include + +namespace pog { + +template +class Automaton +{ +public: + using GrammarType = Grammar; + using ItemType = Item; + using StateType = State; + using SymbolType = Symbol; + + using StateAndSymbolType = StateAndSymbol; + + Automaton(const GrammarType* grammar) : _grammar(grammar), _states(), _state_to_index() {} + + const std::vector>& get_states() const { return _states; } + + const StateType* get_state(std::size_t index) const + { + assert(index < _states.size() && "Accessing state index out of bounds"); + return _states[index].get(); + } + + template + std::pair add_state(StateT&& state) + { + auto itr = _state_to_index.find(&state); + if (itr != _state_to_index.end()) + return {_states[itr->second].get(), false}; + + _states.push_back(std::make_unique(std::forward(state))); + _state_to_index.emplace(_states.back().get(), _states.size() - 1); + return {_states.back().get(), true}; + } + + void closure(StateType& state) + { + std::deque to_process; + for (const auto& item : state) + to_process.push_back(item.get()); + + while (!to_process.empty()) + { + const auto* current_item = to_process.front(); + to_process.pop_front(); + + const auto* next_symbol = current_item->get_read_symbol(); + auto rules = _grammar->get_rules_of_symbol(next_symbol); + for (const auto* rule : rules) + { + auto new_item = Item{rule}; + auto result = state.add_item(std::move(new_item)); + if (result.second) + to_process.push_back(result.first); + } + } + } + + void construct_states() + { + StateType initial_state; + initial_state.add_item(ItemType{_grammar->get_start_rule()}); + initial_state.set_index(0); + closure(initial_state); + auto result = add_state(std::move(initial_state)); + + std::deque to_process{result.first}; + while (!to_process.empty()) + { + auto* state = to_process.front(); + to_process.pop_front(); + + std::map> prepared_states; + for (const auto& item : *state) + { + if (item->is_final()) + continue; + + auto next_sym = item->get_read_symbol(); + if (next_sym->is_end()) + continue; + + auto new_item = Item{*item}; + new_item.step(); + + auto itr = prepared_states.find(next_sym); + if (itr == prepared_states.end()) + std::tie(itr, std::ignore) = prepared_states.emplace(next_sym, StateType{}); + itr->second.add_item(std::move(new_item)); + } + + for (auto&& [symbol, prepared_state] : prepared_states) + { + prepared_state.set_index(static_cast(_states.size())); + auto result = add_state(std::move(prepared_state)); + auto* target_state = result.first; + if (result.second) + { + // We calculate closure only if it's new state introduced in the automaton. + // States can be compared only with their kernel items so it's better to just do it + // once for each state. + closure(*target_state); + to_process.push_back(target_state); + } + state->add_transition(symbol, target_state); + target_state->add_back_transition(symbol, state); + } + } + } + + std::string generate_graph() const + { + std::vector states_str(_states.size()); + std::transform(_states.begin(), _states.end(), states_str.begin(), [](const auto& state) { + std::vector items_str(state->size()); + std::transform(state->begin(), state->end(), items_str.begin(), [](const auto& item) { + return item->to_string("→", "ε", "•"); + }); + return fmt::format("{} [label=\"{}\\l\", xlabel=\"{}\"]", state->get_index(), fmt::join(items_str.begin(), items_str.end(), "\\l"), state->get_index()); + }); + std::vector edges_str; + for (const auto& state : _states) + { + for (const auto& [sym, dest] : state->get_transitions()) + { + edges_str.push_back(fmt::format("{} -> {} [label=\"{}\"]", state->get_index(), dest->get_index(), sym->get_name())); + } + } + return fmt::format(R"(digraph Automaton {{ +node [shape=rect]; + +{} + +{} +}})", + fmt::join(states_str.begin(), states_str.end(), "\n"), + fmt::join(edges_str.begin(), edges_str.end(), "\n") + ); + } + +private: + const GrammarType* _grammar; + std::vector> _states; + std::unordered_map, StateKernelEquals> _state_to_index; +}; + +} // namespace pog diff --git a/src/Pog/DigraphAlgo.hpp b/src/Pog/DigraphAlgo.hpp new file mode 100644 index 00000000..085ded10 --- /dev/null +++ b/src/Pog/DigraphAlgo.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include +#include + +namespace pog { + +namespace detail { + +template +void digraph_traverse(const NodeT& x, std::deque& stack, std::unordered_map& depths, const R& rel, BaseF& base_f, F& f) +{ + stack.push_back(x); // push x + std::size_t current_depth = stack.size(); // d <- depth of stack + depths.insert_or_assign(x, current_depth); // N[x] <- d + f.insert_or_assign(x, base_f[x]); // F(x) <- F'(x) + + auto rel_with = rel.find(x); + if (rel_with) + { + for (const auto& y : *rel_with) // for each y such that xRy + { + auto include_itr = depths.find(y); + if (include_itr == depths.end()) // if N[y] == 0 + digraph_traverse(y, stack, depths, rel, base_f, f); // recursive call Traverse(y) + + include_itr = depths.find(y); // possible iterator invalidation + include_itr->second = std::min(depths[x], include_itr->second); // N[y] <- min(N[x], N[y]) + auto& fx = f[x]; + auto& fy = f[y]; + std::copy(fy.begin(), fy.end(), std::inserter(fx, fx.begin())); // F(x) <- F(x) union F(y) + } + } + + if (depths[x] == current_depth) // if N[x] == d + { + auto top_x = std::move(stack.back()); + stack.pop_back(); + depths[top_x] = std::numeric_limits::max(); // N(top of stack) <- Infinity + if (top_x != x) + f[top_x] = f[x]; // F(top of stack) <- F(x) + + while (top_x != x) // while top of stack != x + { + top_x = std::move(stack.back()); + stack.pop_back(); + depths[top_x] = std::numeric_limits::max(); // N(top of stack) <- Infinity + if (top_x != x) + f[top_x] = f[x]; // F(top of stack) <- F(x) + } + } +} + +} // namespace detail + +/** + * Digraph algorithm for finding SCCs (Strongly Connected Components). It is used for + * computation of function F(x) using base function F'(x) over directed graph. It first + * computes F'(x) as F(x) for each node x and then perform unions of F(x) over edges + * of directed graph. Finding SCC is a crucial part to not get into infinite loops and properly + * propagate F(x) in looped relations. + * + * You can specify custom relation R which specifies edges of the directed graph, base function + * F'(x) which needs to be already precomunted. The output is operation F(x) which will + * be computed along the way. + * + * TODO: base_f should not be non-const but we require operator[] right now + */ +template +void digraph_algo(const R& rel, BaseF& base_f, F& f) +{ + std::unordered_map depths; + std::deque stack; + for (const auto& x : rel) + { + detail::digraph_traverse(x.first, stack, depths, rel, base_f, f); + } +} + +} // namespace pog diff --git a/src/Pog/Errors.hpp b/src/Pog/Errors.hpp new file mode 100644 index 00000000..5b517005 --- /dev/null +++ b/src/Pog/Errors.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include +#include + +namespace pog { + +class Error : public std::exception +{ +public: + Error() : _msg() {} + template + Error(T&& msg) noexcept : _msg(std::forward(msg)) {} + Error(const Error& o) noexcept : _msg(o._msg) {} + virtual ~Error() noexcept {} + + virtual const char* what() const noexcept override { return _msg.c_str(); } + +protected: + std::string _msg; +}; + +class SyntaxError : public Error +{ +public: + template + SyntaxError(const Symbol* unexpected_symbol, const std::vector*>& expected_symbols) : Error() + { + std::vector expected_symbols_str(expected_symbols.size()); + std::transform(expected_symbols.begin(), expected_symbols.end(), expected_symbols_str.begin(), [](const auto& sym) { + return sym->get_description(); + }); + + _msg = fmt::format( + "Syntax error: Unexpected {}, expected one of {}", + unexpected_symbol->get_description(), + fmt::join(expected_symbols_str.begin(), expected_symbols_str.end(), ", ") + ); + } + + template + SyntaxError(const std::vector*>& expected_symbols) : Error() + { + std::vector expected_symbols_str(expected_symbols.size()); + std::transform(expected_symbols.begin(), expected_symbols.end(), expected_symbols_str.begin(), [](const auto& sym) { + return sym->get_description(); + }); + + _msg = fmt::format( + "Syntax error: Unknown symbol on input, expected one of {}", + fmt::join(expected_symbols_str.begin(), expected_symbols_str.end(), ", ") + ); + } +}; + +} // namespace pog diff --git a/src/Pog/FilterView.hpp b/src/Pog/FilterView.hpp new file mode 100644 index 00000000..d5b53d68 --- /dev/null +++ b/src/Pog/FilterView.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include + +#include + +template +class FilterView +{ +public: + using ValueType = typename std::iterator_traits::value_type; + + class iterator + { + public: + using difference_type = typename std::iterator_traits::difference_type; + using value_type = typename std::iterator_traits::value_type; + using reference = typename std::iterator_traits::reference; + using pointer = typename std::iterator_traits::pointer; + using iterator_category = std::forward_iterator_tag; + + iterator(const FilterView* parent) : _parent(parent), _itr(_parent->_begin) { _find_next(); } + iterator(const FilterView* parent, const It& itr) : _parent(parent), _itr(itr) {} + iterator(const iterator&) = default; + iterator(iterator&&) noexcept = default; + + iterator& operator=(const iterator&) = default; + iterator& operator=(iterator&&) noexcept = default; + + reference operator*() const { return *_itr; } + pointer operator->() const { return &*_itr; } + + iterator& operator++() + { + ++_itr; + _find_next(); + return *this; + } + + iterator operator++(int) + { + auto tmp = *this; + ++_itr; + _find_next(); + return tmp; + } + + bool operator==(const iterator& rhs) const { return _itr == rhs._itr; } + bool operator!=(const iterator& rhs) const { return !(*this == rhs); } + + private: + void _find_next() + { + while (_itr != _parent->_end && !_parent->_filter(*_itr)) + ++_itr; + } + + const FilterView* _parent; + It _itr; + }; + + template + FilterView(I&& begin, I&& end, F&& filter) + : _begin(std::forward(begin)), _end(std::forward(end)), _filter(std::forward(filter)) {} + + FilterView(const FilterView&) = default; + FilterView(FilterView&&) noexcept = default; + + FilterView& operator=(const FilterView&) = default; + FilterView& operator=(FilterView&&) noexcept = default; + + auto begin() const { return iterator{this}; } + auto end() const { return iterator{this, _end}; } + +private: + It _begin, _end; + std::function _filter; +}; + +template +FilterView(It&&, It&&, Filter&&) -> FilterView; diff --git a/src/Pog/Grammar.hpp b/src/Pog/Grammar.hpp new file mode 100644 index 00000000..6cdc4a54 --- /dev/null +++ b/src/Pog/Grammar.hpp @@ -0,0 +1,288 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace pog { + +template +class Grammar +{ +public: + using RuleType = Rule; + using SymbolType = Symbol; + using TokenType = Token; + + Grammar() : _rules(), _symbols(), _name_to_symbol(), _internal_start_symbol(nullptr), _internal_end_of_input(nullptr), + _start_rule(nullptr), _empty_table(), _first_table(), _follow_table() + { + _internal_start_symbol = add_symbol(SymbolKind::Nonterminal, "@start"); + _internal_end_of_input = add_symbol(SymbolKind::End, "@end"); + } + + const std::vector>& get_symbols() const { return _symbols; } + const std::vector>& get_rules() const { return _rules; } + + const SymbolType* get_end_of_input_symbol() const { return _internal_end_of_input; } + const RuleType* get_start_rule() const { return _start_rule; } + + std::vector get_terminal_symbols() const + { + std::vector result; + transform_if(_symbols.begin(), _symbols.end(), std::back_inserter(result), + [](const auto& s) { return s->is_terminal() || s->is_end(); }, + [](const auto& s) { return s.get(); } + ); + return result; + } + + std::vector get_nonterminal_symbols() const + { + std::vector result; + transform_if(_symbols.begin(), _symbols.end(), std::back_inserter(result), + [](const auto& s) { return s->is_nonterminal(); }, + [](const auto& s) { return s.get(); } + ); + return result; + } + + SymbolType* get_symbol(const std::string& name) const + { + auto itr = _name_to_symbol.find(name); + if (itr == _name_to_symbol.end()) + return nullptr; + + return itr->second; + } + + // TODO: return filter_view<> + std::vector get_rules_of_symbol(const SymbolType* sym) const + { + std::vector result; + transform_if(_rules.begin(), _rules.end(), std::back_inserter(result), [&](const auto& rule) { + return sym == rule->get_lhs(); + }, [](const auto& rule) { + return rule.get(); + }); + return result; + } + + // TODO: return filter_view<> + std::vector get_rules_with_symbol(const SymbolType* sym) const + { + std::vector result; + transform_if(_rules.begin(), _rules.end(), std::back_inserter(result), [&](const auto& rule) { + return std::find(rule->get_rhs().begin(), rule->get_rhs().end(), sym) != rule->get_rhs().end(); + }, [](const auto& rule) { + return rule.get(); + }); + return result; + } + + void set_start_symbol(const SymbolType* symbol) + { + auto start_rule = add_rule(_internal_start_symbol, std::vector{symbol, _internal_end_of_input}, [](Parser&, std::vector>&& args) { + return std::move(args[0].value); + }); + start_rule->set_start_rule(true); + _start_rule = start_rule; + } + + SymbolType* add_symbol(SymbolKind kind, const std::string& name) + { + if (auto itr = _name_to_symbol.find(name); itr != _name_to_symbol.end()) + return itr->second; + + _symbols.push_back(std::make_unique(static_cast(_symbols.size()), kind, name)); + _name_to_symbol.emplace(_symbols.back()->get_name(), _symbols.back().get()); + return _symbols.back().get(); + } + + template + RuleType* add_rule(const SymbolType* lhs, const std::vector& rhs, CallbackT&& action) + { + _rules.push_back(std::make_unique(static_cast(_rules.size()), lhs, rhs, std::forward(action))); + return _rules.back().get(); + } + + bool empty(const SymbolType* sym) const + { + std::unordered_set visited_lhss; + auto result = empty(sym, visited_lhss); + _empty_table[sym] = result; + return result; + } + + bool empty(const std::vector& seq) const + { + std::unordered_set visited_lhss; + auto result = empty(seq, visited_lhss); + return result; + } + + std::unordered_set first(const SymbolType* sym) const + { + std::unordered_set visited_lhss; + auto result = first(sym, visited_lhss); + _first_table[sym] = result; + return result; + } + + std::unordered_set first(const std::vector& seq) const + { + std::unordered_set visited_lhss; + auto result = first(seq, visited_lhss); + return result; + } + + std::unordered_set follow(const SymbolType* sym) + { + std::unordered_set visited; + auto result = follow(sym, visited); + _follow_table[sym] = result; + return result; + } + + bool empty(const SymbolType* sym, std::unordered_set& visited_lhss) const + { + if (auto itr = _empty_table.find(sym); itr != _empty_table.end()) + return itr->second; + + // Empty of terminal or end of input marker '$' remains the same + if (sym->is_terminal() || sym->is_end()) + return false; + + // In case of nonterminal A, there exist rules A -> ... and we need to inspect them all + auto rules = get_rules_of_symbol(sym); + for (const auto* rule : rules) + { + visited_lhss.insert(rule->get_lhs()); + if (empty(rule->get_rhs(), visited_lhss)) + return true; + } + + return false; + } + + bool empty(const std::vector& seq, std::unordered_set& visited_lhss) const + { + for (const auto* sym : seq) + { + if (auto itr = visited_lhss.find(sym); itr == visited_lhss.end()) + { + if (!empty(sym, visited_lhss)) + return false; + } + else + return false; + } + + return true; + } + + std::unordered_set first(const SymbolType* sym, std::unordered_set& visited_lhss) const + { + if (auto itr = _first_table.find(sym); itr != _first_table.end()) + return itr->second; + + if (sym->is_terminal() || sym->is_end()) + return {sym}; + else if (sym->is_nonterminal()) + { + std::unordered_set result; + + if (auto itr = visited_lhss.find(sym); itr == visited_lhss.end()) + { + visited_lhss.insert(sym); + auto rules = get_rules_of_symbol(sym); + for (const auto* rule : rules) + { + auto tmp = first(rule->get_rhs(), visited_lhss); + std::copy(tmp.begin(), tmp.end(), std::inserter(result, result.begin())); + } + } + + return result; + } + + return {}; + } + + std::unordered_set first(const std::vector& seq, std::unordered_set& visited_lhss) const + { + std::unordered_set result; + + for (const auto* sym : seq) + { + auto tmp = first(sym, visited_lhss); + std::copy(tmp.begin(), tmp.end(), std::inserter(result, result.begin())); + if (!empty(sym)) + break; + } + + return result; + } + + std::unordered_set follow(const SymbolType* sym, std::unordered_set& visited) const + { + if (auto itr = _follow_table.find(sym); itr != _follow_table.end()) + return itr->second; + + std::unordered_set result; + if (visited.find(sym) != visited.end()) + return result; + + visited.insert(sym); + auto rules = get_rules_with_symbol(sym); + for (const auto* rule : rules) + { + for (auto itr = rule->get_rhs().begin(), end = rule->get_rhs().end(); itr != end; ++itr) + { + if (*itr == sym) + { + bool can_be_last_in_production = true; + // If we have a production A -> a B b and we are doing Follow(B), we need to inspect everything + // right of B (so in this case 'b') whether there exist production b =>* eps. + // If so, B can be last in production and and therefore we need to add Follow(A) to Follow(B) + if (itr + 1 != end) + { + std::vector tail(itr + 1, end); + auto tmp = first(tail); + std::copy(tmp.begin(), tmp.end(), std::inserter(result, result.begin())); + can_be_last_in_production = empty(tail); + } + + // There exists production b =>* eps so add Follow(A) to Follow(B) + if (can_be_last_in_production) + { + auto tmp = follow(rule->get_lhs(), visited); + std::copy(tmp.begin(), tmp.end(), std::inserter(result, result.begin())); + } + } + } + } + + return result; + } + +private: + std::vector> _rules; + std::vector> _symbols; + std::unordered_map _name_to_symbol; + const SymbolType* _internal_start_symbol; + const SymbolType* _internal_end_of_input; + const RuleType* _start_rule; + + mutable std::unordered_map _empty_table; + mutable std::unordered_map> _first_table; + mutable std::unordered_map> _follow_table; +}; + +} // namespace pog diff --git a/src/Pog/HTMLReport.hpp b/src/Pog/HTMLReport.hpp new file mode 100644 index 00000000..fc2edd40 --- /dev/null +++ b/src/Pog/HTMLReport.hpp @@ -0,0 +1,284 @@ +#pragma once + +#include + +#include + +namespace pog { + +template +class HtmlReport +{ +public: + using ParserType = Parser; + + using ShiftActionType = Shift; + using ReduceActionType = Reduce; + + HtmlReport(const ParserType& parser) : _parser(parser) {} + + void save(const std::string& file_path) + { + static const std::string html_page_template = R"( + + + + + + + + + + +
+ {issues} + {parsing_table} + {states} + {automaton} +
+ Generated: {generated_at} +
+ + + + + + + +)"; + + using namespace fmt; + + std::ofstream file(file_path, std::ios::out | std::ios::trunc); + if (file.is_open()) + file << fmt::format(html_page_template, + "issues"_a = build_issues(), + "parsing_table"_a = build_parsing_table(), + "states"_a = build_states(), + "automaton"_a = generate_automaton_graph(), + "generated_at"_a = current_time("%Y-%m-%d %H:%M:%S %Z") + ); + file.close(); + } + +private: + std::string build_issues() + { + using namespace fmt; + + if (_parser._report) + return std::string{}; + + std::vector issues(_parser._report.number_of_issues()); + std::transform(_parser._report.begin(), _parser._report.end(), issues.begin(), [](const auto& issue) { + return fmt::format("
  • {}
  • ", visit_with(issue, + [&](const ShiftReduceConflict& sr) { return sr.to_string("→", "ε"); }, + [&](const ReduceReduceConflict& rr) { return rr.to_string("→", "ε"); } + )); + }); + + return fmt::format( + R"(
    +
    +

    Issues

    +
      + {issues} +
    +
    +
    )", + "issues"_a = fmt::join(issues.begin(), issues.end(), "") + ); + } + + std::string build_parsing_table() + { + using namespace fmt; + + auto terminal_symbols = _parser._grammar.get_terminal_symbols(); + auto nonterminal_symbols = _parser._grammar.get_nonterminal_symbols(); + + std::vector symbol_headers(terminal_symbols.size() + nonterminal_symbols.size()); + std::transform(terminal_symbols.begin(), terminal_symbols.end(), symbol_headers.begin(), [](const auto& s) { + return fmt::format("{}", s->get_name()); + }); + std::transform(nonterminal_symbols.begin(), nonterminal_symbols.end(), symbol_headers.begin() + terminal_symbols.size(), [](const auto& s) { + return fmt::format("{}", s->get_name()); + }); + + std::vector rows(_parser._automaton.get_states().size()); + for (const auto& state : _parser._automaton.get_states()) + { + std::vector row; + row.push_back(fmt::format( + "{}", + state->to_string("→", "ε", "•", "
    "), + state->get_index() + )); + for (const auto& sym : terminal_symbols) + { + auto action = _parser._parsing_table.get_action(state.get(), sym); + if (!action) + { + row.push_back(""); + continue; + } + + row.push_back(visit_with(action.value(), + [](const ShiftActionType& shift) { + return fmt::format( + "s{state_id}", + "state_str"_a = shift.state->to_string("→", "ε", "•", "
    "), + "state_id"_a = shift.state->get_index() + ); + }, + [](const ReduceActionType& reduce) { + return fmt::format( + "r{}", + reduce.rule->to_string("→", "ε"), + reduce.rule->get_index() + ); + }, + [](const Accept&) -> std::string { return ""; } + )); + } + + for (const auto& sym : nonterminal_symbols) + { + auto go_to = _parser._parsing_table.get_transition(state.get(), sym); + if (!go_to) + { + row.push_back(""); + continue; + } + + row.push_back(fmt::format( + "{state_id}", + "state_str"_a = go_to.value()->to_string("→", "ε", "•", "
    "), + "state_id"_a = go_to.value()->get_index() + )); + } + + row.push_back(""); + rows.push_back(fmt::format("{}", fmt::join(row.begin(), row.end(), ""))); + } + + return fmt::format( + R"(
    +

    Parsing Table

    +
    +
    + + + + + + + + + {symbols} + + + + {rows} + +
    StateActionGoto
    +
    +
    )", + "number_of_terminals"_a = terminal_symbols.size(), + "number_of_nonterminals"_a = nonterminal_symbols.size(), + "symbols"_a = fmt::join(symbol_headers.begin(), symbol_headers.end(), ""), + "rows"_a = fmt::join(rows.begin(), rows.end(), "") + ); + } + + std::string build_states() + { + using namespace fmt; + + static const std::string single_state_template = + R"(
    + + + + + + {rows} + +
    State {id}
    +
    )"; + + std::vector states; + for (const auto& state : _parser._automaton.get_states()) + { + std::vector cols(state->size()); + std::transform(state->begin(), state->end(), cols.begin(), [](const auto& item) { + return fmt::format("{}", item->to_string("→", "ε", "•")); + }); + states.push_back(fmt::format( + single_state_template, + "id"_a = state->get_index(), + "rows"_a = fmt::join(cols.begin(), cols.end(), "") + )); + } + + return fmt::format( + R"(
    +

    States

    +
    + {states} +
    )", + "states"_a = fmt::join(states.begin(), states.end(), "") + ); + } + + std::string generate_automaton_graph() + { + using namespace fmt; + + return fmt::format(R"(
    +

    Automaton (graphviz)

    +
    + +
    +
    + +
    +
    )", + "automaton"_a = _parser._automaton.generate_graph() + ); + } + + const ParserType& _parser; +}; + +template +HtmlReport(const Parser&) -> HtmlReport; + +} // namespace pog diff --git a/src/Pog/Item.hpp b/src/Pog/Item.hpp new file mode 100644 index 00000000..3d860010 --- /dev/null +++ b/src/Pog/Item.hpp @@ -0,0 +1,140 @@ +#pragma once + +#include +#include +#include + +#include + +#include +#include + +namespace pog { + +template +class Item +{ +public: + using RuleType = Rule; + using SymbolType = Symbol; + + Item(const RuleType* rule, std::size_t read_pos = 0) + : _rule(rule), _read_pos(read_pos) {} + Item(const Item&) = default; + Item(Item&&) noexcept = default; + + const RuleType* get_rule() const { return _rule; } + std::size_t get_read_pos() const { return _read_pos; } + + const SymbolType* get_previous_symbol() const + { + return _read_pos == 0 ? nullptr : _rule->get_rhs()[_read_pos - 1]; + } + + const SymbolType* get_read_symbol() const + { + return is_final() ? nullptr : _rule->get_rhs()[_read_pos]; + } + + std::vector get_left_side_without_read_symbol() + { + if (_read_pos == 0) + return {}; + + // TODO: return just iterator range + std::vector result(_read_pos); + std::copy(_rule->get_rhs().begin(), _rule->get_rhs().begin() + _read_pos, result.begin()); + return result; + } + + std::vector get_right_side_without_read_symbol() + { + if (is_final()) + { + assert(false && "Shouldn't call get_right_side_without_read_symbol() on final item"); + return {}; + } + + auto rest_size = _rule->get_rhs().size() - _read_pos - 1; + if (rest_size == 0) + return {}; + + // TODO: possibly just return iterator range? + std::vector result(rest_size); + std::copy(_rule->get_rhs().begin() + _read_pos + 1, _rule->get_rhs().end(), result.begin()); + return result; + } + + void step() + { + if (!is_final()) + _read_pos++; + } + + void step_back() + { + if (_read_pos > 0) + _read_pos--; + } + + bool is_kernel() const + { + return _read_pos > 0 || _rule->is_start_rule(); + } + + bool is_final() const + { + return _read_pos == _rule->get_rhs().size(); + } + + bool is_accepting() const + { + return !is_final() && get_read_symbol()->is_end(); + } + + std::string to_string(std::string_view arrow = "->", std::string_view eps = "", std::string_view sep = "<*>") const + { + const auto& rhs = _rule->get_rhs(); + std::vector left_of_read_pos(_read_pos); + std::vector right_of_read_pos(rhs.size() - _read_pos); + std::transform(rhs.begin(), rhs.begin() + _read_pos, left_of_read_pos.begin(), [](const auto* sym) { + return sym->get_name(); + }); + std::transform(rhs.begin() + _read_pos, rhs.end(), right_of_read_pos.begin(), [](const auto* sym) { + return sym->get_name(); + }); + + std::vector parts; + if (!left_of_read_pos.empty()) + parts.push_back(fmt::format("{}", fmt::join(left_of_read_pos.begin(), left_of_read_pos.end(), " "))); + parts.push_back(std::string{sep}); + if (!right_of_read_pos.empty()) + parts.push_back(fmt::format("{}", fmt::join(right_of_read_pos.begin(), right_of_read_pos.end(), " "))); + + if (parts.size() == 1) + parts.push_back(std::string{eps}); + + return fmt::format("{} {} {}", _rule->get_lhs()->get_name(), arrow, fmt::join(parts.begin(), parts.end(), " ")); + } + + bool operator==(const Item& rhs) const + { + return get_rule()->get_index() == rhs.get_rule()->get_index() && get_read_pos() == rhs.get_read_pos(); + } + + bool operator!=(const Item& rhs) const + { + return !(*this == rhs); + } + + bool operator<(const Item& rhs) const + { + return std::tuple{is_kernel() ? 0 : 1, _rule->get_index(), _read_pos} < std::tuple{rhs.is_kernel() ? 0 : 1, rhs._rule->get_index(), rhs._read_pos}; + } + +private: + const RuleType* _rule; + std::size_t _read_pos; +}; + +} // namespace pog diff --git a/src/Pog/LineSpecialization.hpp b/src/Pog/LineSpecialization.hpp new file mode 100644 index 00000000..c8dcea80 --- /dev/null +++ b/src/Pog/LineSpecialization.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +namespace HCAsm { + +struct CompilerState; + +} + +namespace pog { + +struct LineSpecialization { + LineSpecialization(std::uint32_t line, std::uint16_t offset, std::uint16_t length) + : line(line) + , offset(offset) + , length(length) { } + LineSpecialization() + : line(1) + , offset(0) + , length(0) { } + + std::uint32_t line; + std::uint16_t offset; + std::uint16_t length; +}; + +template +struct TokenWithLineSpec { + TokenWithLineSpec() = default; + TokenWithLineSpec(ValueT& val, LineSpecialization spec) : value(val), line_spec(spec) { } + + ValueT value; + LineSpecialization line_spec; +}; + +} \ No newline at end of file diff --git a/src/Pog/Operations/Follow.hpp b/src/Pog/Operations/Follow.hpp new file mode 100644 index 00000000..34dd6bc2 --- /dev/null +++ b/src/Pog/Operations/Follow.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace pog { + +/** + * Follow operations maps (q,x) where q is state and x is symbol to set of symbols. + * + * Formal definition for what Follow() represents is + * Follow(q, A) = Read(q, A) union (union { Follow(p, B) | (q, A) includes (p, B) }) + * So Follow(q, A) represents Read(q, A) with union of all Follow sets of (p, B) such that + * (q, A) is in include relation with (p, B). + * + * To put it simply by individual parts: + * 1. Read(q, A) means what symbols can directly follow A in state q. See Read() operation for + * more information. + * 2. Includes relation of (q, A) with (p, B) means that when we are about to read A in state q + * and everything after A can be empty string, meaning that what can follow B in state p can + * also follow A in state q. + * + * So Follow(q, A) represents what symbols can follow A while in state q. It is constructed from + * generated follow using Read(q, A) and propagated follow using include relation. + */ +template +class Follow : public Operation, const Symbol*> +{ +public: + using Parent = Operation, const Symbol*>; + + using AutomatonType = Automaton; + using GrammarType = Grammar; + + using StateAndSymbolType = StateAndSymbol; + + Follow(const AutomatonType* automaton, const GrammarType* grammar, const Includes& includes, Read& read_op) + : Parent(automaton, grammar), _includes(includes), _read_op(read_op) {} + Follow(const Follow&) = delete; + Follow(Follow&&) noexcept = default; + + virtual void calculate() override + { + // We use digraph algorithm which calculates Follow() for us. See digraph_algo() for more information. + digraph_algo(_includes, _read_op, Parent::_operation); + } + +private: + const Includes& _includes; + Read& _read_op; +}; + +} // namespace pog diff --git a/src/Pog/Operations/Lookahead.hpp b/src/Pog/Operations/Lookahead.hpp new file mode 100644 index 00000000..4de7c0fc --- /dev/null +++ b/src/Pog/Operations/Lookahead.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include +#include + +namespace pog { + +/** + * Lookahead operation maps (q, R), where q is state and R is rule from grammar, to set of symbols. + * + * Formal definition for Lookahead(q, A -> x) is + * Lookahead(q, A -> x) = union { Follow(p, B) | (q, A -> x) lookback (p, B) } + * So it's union of all Follow sets of state p and symbol B such that (q, A -> x) is in lookback + * relation with (p, B). + * + * To put it simply: + * 1. Follow set of (p, B) represents what symbols can follow symbol B when we are in the state B. + * 2. Lookback relation represents that in order to preform some reduction A -> x in state q, we first + * had to go through some other state p and use what follows A in B -> a A b to know when to perform + * reduction. + * So we'll take all rules A -> x and find in which state they can be reduced (there is an item A -> x <*>). + * We'll then union all Follow() sets according to lookback relation and for each state and rule, we now + * know what symbols need to follow in order to perform reductions by such rule in that particular state. + */ +template +class Lookahead : public Operation, const Symbol*> +{ +public: + using Parent = Operation, const Symbol*>; + + using AutomatonType = Automaton; + using GrammarType = Grammar; + + using StateAndRuleType = StateAndRule; + + // TODO: Follow<> should not be non-const but we need it for operator[] + Lookahead(const AutomatonType* automaton, const GrammarType* grammar, const Lookback& lookback, Follow& follow_op) + : Parent(automaton, grammar), _lookback(lookback), _follow_op(follow_op) {} + Lookahead(const Lookahead&) = delete; + Lookahead(Lookahead&&) noexcept = default; + + virtual void calculate() override + { + // Iterate over all rules in grammar + for (const auto& rule : Parent::_grammar->get_rules()) + { + for (const auto& state : Parent::_automaton->get_states()) + { + // Find lookback of the current state and rule + auto sr = StateAndRuleType{state.get(), rule.get()}; + auto lookback_with = _lookback.find(sr); + if (!lookback_with) + continue; + + // Union all Follow() sets of the current state and rule to compute Lookahead() + for (const auto& ss : *lookback_with) + { + if (auto itr = Parent::_operation.find(sr); itr == Parent::_operation.end()) + Parent::_operation.emplace(std::move(sr), _follow_op[ss]); + else if (auto follow_res = _follow_op.find(ss); follow_res) + std::copy(follow_res->begin(), follow_res->end(), std::inserter(itr->second, itr->second.begin())); + } + } + } + + } + +private: + const Lookback& _lookback; + Follow& _follow_op; +}; + +} // namespace pog diff --git a/src/Pog/Operations/Operation.hpp b/src/Pog/Operations/Operation.hpp new file mode 100644 index 00000000..c4f323ab --- /dev/null +++ b/src/Pog/Operations/Operation.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include +#include + +#include +#include + +namespace pog { + +template +class Operation +{ +public: + using AutomatonType = Automaton; + using GrammarType = Grammar; + + Operation(const AutomatonType* automaton, const GrammarType* grammar) : _automaton(automaton), _grammar(grammar) {} + Operation(const Operation&) = delete; + Operation(Operation&&) noexcept = default; + virtual ~Operation() = default; + + virtual void calculate() = 0; + + auto& operator[](const ArgT& key) { return _operation[key]; } + auto& operator[](ArgT& key) { return _operation[key]; } + + template + std::unordered_set* find(const T& key) + { + auto itr = _operation.find(key); + if (itr == _operation.end()) + return nullptr; + + return &itr->second; + } + + template + const std::unordered_set* find(const T& key) const + { + auto itr = _operation.find(key); + if (itr == _operation.end()) + return nullptr; + + return &itr->second; + } + +protected: + const AutomatonType* _automaton; + const GrammarType* _grammar; + std::unordered_map> _operation; +}; + +} // namespace pog diff --git a/src/Pog/Operations/Read.hpp b/src/Pog/Operations/Read.hpp new file mode 100644 index 00000000..dfb8c610 --- /dev/null +++ b/src/Pog/Operations/Read.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include + +namespace pog { + +/** + * Read operations maps (q,x) where q is state and x is symbol into set of symbols. + * + * Let's take state Q and non-final item A -> a <*> B b. Read(Q, B) represents set + * of symbols we can possibly read after reading B while in state Q. Originally, + * in all papers you see this proposed to be calculated using DirectRead() function + * and reads relation but in the end, it's the same as if we do First(b). First(b) + * incorporates situation if some symbol in sequence b can be reduced to empty string. + * + * So to put it shortly, for state Q and item A -> a <*> B b, Read(Q, B) = First(b). + */ +template +class Read : public Operation, const Symbol*> +{ +public: + using Parent = Operation, const Symbol*>; + + using AutomatonType = Automaton; + using GrammarType = Grammar; + using SymbolType = Symbol; + + using StateAndSymbolType = StateAndSymbol; + + Read(const AutomatonType* automaton, const GrammarType* grammar) : Parent(automaton, grammar) {} + Read(const Read&) = delete; + Read(Read&&) noexcept = default; + + virtual void calculate() override + { + // Iterate over all states of LR automaton + for (const auto& state : Parent::_automaton->get_states()) + { + for (const auto& item : *state.get()) + { + // We don't care about final items, only those in form A -> a <*> B b + if (item->is_final()) + continue; + + // Symbol right to <*> needs to be nonterminal + auto next_symbol = item->get_read_symbol(); + if (!next_symbol->is_nonterminal()) + continue; + + // Observe everything right of B, so in this case 'b' and calculate First() + auto right_rest = item->get_right_side_without_read_symbol(); + auto symbols = Parent::_grammar->first(right_rest); + + // Insert operation result + auto ss = StateAndSymbolType{state.get(), next_symbol}; + auto itr = Parent::_operation.find(ss); + if (itr == Parent::_operation.end()) + Parent::_operation.emplace(std::move(ss), std::move(symbols)); + else + { + // TODO: std::vector + std::set_union or std::unordered_set::extract + std::copy(symbols.begin(), symbols.end(), std::inserter(itr->second, itr->second.begin())); + } + } + } + } +}; + +} // namespace pog diff --git a/src/Pog/Parser.hpp b/src/Pog/Parser.hpp new file mode 100644 index 00000000..2a7243e2 --- /dev/null +++ b/src/Pog/Parser.hpp @@ -0,0 +1,331 @@ +#pragma once + +#include +#include +#include + +#include +#ifdef POG_DEBUG +#define POG_DEBUG_PARSER 1 +#endif + +#ifdef POG_DEBUG_PARSER +#define debug_parser(...) fmt::print(stderr, "[parser] {}\n", fmt::format(__VA_ARGS__)) +#else +#define debug_parser(...) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace HCAsm { + +class HCAsmCompiler; + +} + +namespace pog { + +template +class HtmlReport; + +template +class Parser +{ +public: + friend class HtmlReport; + + using ActionType = Action; + using ShiftActionType = Shift; + using ReduceActionType = Reduce; + + using BacktrackingInfoType = BacktrackingInfo; + using ItemType = Item; + using ParserReportType = ParserReport; + using RuleBuilderType = RuleBuilder; + using RuleType = Rule; + using StateType = State; + using SymbolType = Symbol; + using StateAndRuleType = StateAndRule; + using StateAndSymbolType = StateAndSymbol; + using TokenBuilderType = TokenBuilder; + using TokenMatchType = TokenMatch; + using TokenType = Token; + using TokenizerType = Tokenizer; + + Parser() : _grammar(), _tokenizer(&_grammar), _automaton(&_grammar), _includes(&_automaton, &_grammar), + _lookback(&_automaton, &_grammar), _read_operation(&_automaton, &_grammar), _follow_operation(&_automaton, &_grammar, _includes, _read_operation), + _lookahead_operation(&_automaton, &_grammar, _lookback, _follow_operation), _parsing_table(&_automaton, &_grammar, _lookahead_operation) + { + static_assert(std::is_default_constructible_v, "Value type needs to be default constructible"); + } + + Parser(const Parser&) = delete; + Parser(Parser&&) noexcept = default; + + const ParserReportType& prepare() + { + for (auto& tb : _token_builders) + tb.done(); + for (auto& rb : _rule_builders) + rb.done(); + _automaton.construct_states(); + _includes.calculate(); + _lookback.calculate(); + _read_operation.calculate(); + _follow_operation.calculate(); + _lookahead_operation.calculate(); + _parsing_table.calculate(_report); + _tokenizer.prepare(); + return _report; + } + + TokenBuilderType& token(const std::string& pattern) + { + _token_builders.emplace_back(&_grammar, &_tokenizer, pattern); + return _token_builders.back(); + } + + TokenBuilderType& end_token() + { + _token_builders.emplace_back(&_grammar, &_tokenizer); + return _token_builders.back(); + } + + RuleBuilderType& rule(const std::string& lhs) + { + _rule_builders.emplace_back(&_grammar, lhs); + return _rule_builders.back(); + } + + void set_start_symbol(const std::string& name) + { + _grammar.set_start_symbol(_grammar.add_symbol(SymbolKind::Nonterminal, name)); + } + + void enter_tokenizer_state(const std::string& state_name) + { + _tokenizer.enter_state(state_name); + } + + void push_input_stream(std::string& input) + { + _tokenizer.push_input_stream(input); + } + + void pop_input_stream() + { + _tokenizer.pop_input_stream(); + } + + void global_tokenizer_action(typename TokenizerType::CallbackType&& global_action) + { + _tokenizer.global_action(std::move(global_action)); + } + + std::string& get_top_file() + { + return files.back(); + } + + void set_compiler_state(HCAsm::CompilerState* state) + { + _state = state; + } + + HCAsm::CompilerState* get_compiler_state() + { + return _state; + } + + void reset_line_offset() + { + _tokenizer._reset_line_offset(); + } + + std::uint32_t& get_line_counter() + { + return _tokenizer._get_line_counter(); + } + + std::uint16_t& get_line_offset() { + return _tokenizer._get_line_offset(); + } + + std::optional parse(std::string& contents) + { + files.push(contents); + + _tokenizer.enter_state(std::string{decltype(_tokenizer)::DefaultState}); + + std::optional token; + _tokenizer.clear_input_streams(); + _tokenizer.push_input_stream(contents); + + std::deque>>> stack; + stack.emplace_back(0, std::nullopt); + + while (!stack.empty()) + { + // Check if we remember token from the last iteration because we did reduction + // so the token was not "consumed" from the input. + if (!token) + { + token = _tokenizer.next_token(); + if (!token) [[unlikely]] + { + auto expected_symbols = _parsing_table.get_expected_symbols_from_state(_automaton.get_state(stack.back().first)); + throw SyntaxError(expected_symbols); + } + + debug_parser("Tokenizer returned new token with symbol \'{}\'", token.value().symbol->get_name()); + } + else { + debug_parser("Reusing old token with symbol \'{}\'", token.value().symbol->get_name()); + } + + debug_parser("Top of the stack is state {}", stack.back().first); + + const auto* next_symbol = token.value().symbol; + auto maybe_action = _parsing_table.get_action(_automaton.get_state(stack.back().first), next_symbol); + if (!maybe_action) + { + auto expected_symbols = _parsing_table.get_expected_symbols_from_state(_automaton.get_state(stack.back().first)); + throw SyntaxError(next_symbol, expected_symbols); + } + + // TODO: use visit + auto action = maybe_action.value(); + if (std::holds_alternative(action)) + { + const auto& reduce = std::get(action); + debug_parser("Reducing by rule \'{}\'", reduce.rule->to_string()); + + // Each symbol on right-hand side of the rule should have record on the stack + // We'll pop them out and put them in reverse order so user have them available + // left-to-right and not right-to-left. + std::vector> action_arg; + action_arg.reserve(reduce.rule->get_number_of_required_arguments_for_action()); + assert(stack.size() >= action_arg.capacity() && "Stack is too small"); + + for (std::size_t i = 0; i < action_arg.capacity(); ++i) + { + // Notice how std::move() is only around optional itself and not the whole expressions + // We need to do this in order to perform move together with value_or() + // See: https://en.cppreference.com/w/cpp/utility/optional/value_or + // std::move(*this) is performed only when value_or() is called from r-value + // + // Also do not pop from stack here because midrule actions can still return us arguments back + action_arg.insert(action_arg.begin(), std::move(stack[stack.size() - i - 1].second).value_or(TokenWithLineSpec{})); + } + + // What left on the stack now determines what state we get into now + // We use size of RHS to determine stack top because midrule actions might have only borrowed something from stack so the + // real stack top is not the actual top. Midrule actions have 0 RHS size even though they borrow items. Other rules + // have same size of RHS and what they take out of stack. + auto maybe_next_state = _parsing_table.get_transition(_automaton.get_state(stack[stack.size() - reduce.rule->get_rhs().size() - 1].first), reduce.rule->get_lhs()); + if (!maybe_next_state) + { + assert(false && "Reduction happened but corresponding GOTO table record is empty"); + return std::nullopt; + } + + auto action_result = reduce.rule->has_action() ? reduce.rule->perform_action(*this, std::move(action_arg)) : ValueT{}; + // Midrule actions only borrowed arguments and it is returning them back + if (reduce.rule->is_midrule()) + { + for (std::size_t i = 0; i < action_arg.size(); ++i) + stack[stack.size() - i - 1].second = std::move(action_arg[action_arg.size() - i - 1]); + } + // Non-midrule actions actually consumed those arguments so pop them out + else + { + for (std::size_t i = 0; i < action_arg.size(); ++i) + stack.pop_back(); + } + + debug_parser("Pushing state {}", maybe_next_state.value()->get_index()); + + stack.emplace_back( + maybe_next_state.value()->get_index(), + std::optional { TokenWithLineSpec { action_result, {} } } + ); + } + else if (std::holds_alternative(action)) + { + const auto& shift = std::get(action); + debug_parser("Shifting state {}", shift.state->get_index()); + + // Notice how std::move() is only around optional itself and not the whole expressions + // We need to do this in order to perform move together with value() + // See: https://en.cppreference.com/w/cpp/utility/optional/value + // Return by rvalue is performed only when value() is called from r-value + stack.emplace_back( + shift.state->get_index(), + std::optional { TokenWithLineSpec { token.value().value, token.value().line_spec } } + ); + + // We did shift so the token value is moved onto stack, "forget" the token + token.reset(); + } + else if (std::holds_alternative(action)) + { + debug_parser("Accept"); + // Notice how std::move() is only around optional itself and not the whole expressions + // We need to do this in order to perform move together with value() + // See: https://en.cppreference.com/w/cpp/utility/optional/value + // Return by rvalue is performed only when value() is called from r-value + return { std::move(stack.back().second).value().value }; + } + } + + assert(false && "Stack was emptied too early"); + return std::nullopt; + } + + std::string generate_automaton_graph() + { + return _automaton.generate_graph(); + } + + std::string generate_includes_relation_graph() + { + return _includes.generate_relation_graph(); + } + +private: + Grammar _grammar; + Tokenizer _tokenizer; + Automaton _automaton; + Includes _includes; + Lookback _lookback; + Read _read_operation; + Follow _follow_operation; + Lookahead _lookahead_operation; + ParsingTable _parsing_table; + + std::vector _rule_builders; + std::vector _token_builders; + + ParserReportType _report; + HCAsm::CompilerState* _state; + std::queue files; +}; + +} // namespace pog diff --git a/src/Pog/ParserReport.hpp b/src/Pog/ParserReport.hpp new file mode 100644 index 00000000..ebe8fa98 --- /dev/null +++ b/src/Pog/ParserReport.hpp @@ -0,0 +1,89 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +namespace pog { + +template +struct ShiftReduceConflict +{ + const State* state; + const Symbol* symbol; + const Rule* rule; + + std::string to_string(std::string_view arrow = "->", std::string_view eps = "") const + { + return fmt::format("Shift-reduce conflict of symbol \'{}\' and rule \'{}\' in state {}", symbol->get_name(), rule->to_string(arrow, eps), state->get_index()); + } +}; + +template +struct ReduceReduceConflict +{ + const State* state; + const Rule* rule1; + const Rule* rule2; + + std::string to_string(std::string_view arrow = "->", std::string_view eps = "") const + { + return fmt::format("Reduce-reduce conflict of rule \'{}\' and rule \'{}\' in state {}", rule1->to_string(arrow, eps), rule2->to_string(arrow, eps), state->get_index()); + } +}; + +template +using Issue = std::variant, ReduceReduceConflict>; + +template +class ParserReport +{ +public: + using IssueType = Issue; + using RuleType = Rule; + using StateType = State; + using SymbolType = Symbol; + + using ReduceReduceConflictType = ReduceReduceConflict; + using ShiftReduceConflictType = ShiftReduceConflict; + + bool ok() const { return _issues.empty(); } + operator bool() const { return ok(); } + + std::size_t number_of_issues() const { return _issues.size(); } + auto begin() { return _issues.begin(); } + auto end() { return _issues.end(); } + auto begin() const { return _issues.begin(); } + auto end() const { return _issues.end(); } + + void add_shift_reduce_conflict(const StateType* state, const SymbolType* symbol, const RuleType* rule) + { + _issues.push_back(ShiftReduceConflictType{state, symbol, rule}); + } + + void add_reduce_reduce_conflict(const StateType* state, const RuleType* rule1, const RuleType* rule2) + { + _issues.push_back(ReduceReduceConflictType{state, rule1, rule2}); + } + + std::string to_string(std::string_view arrow = "->", std::string_view eps = "") const + { + std::vector issues_str(_issues.size()); + std::transform(_issues.begin(), _issues.end(), issues_str.begin(), [&](const auto& issue) { + return visit_with(issue, + [&](const ShiftReduceConflictType& sr) { return sr.to_string(arrow, eps); }, + [&](const ReduceReduceConflictType& rr) { return rr.to_string(arrow, eps); } + ); + }); + return fmt::format("{}", fmt::join(issues_str.begin(), issues_str.end(), "\n")); + } + +private: + std::vector _issues; +}; + +} // namespace pog diff --git a/src/Pog/ParsingTable.hpp b/src/Pog/ParsingTable.hpp new file mode 100644 index 00000000..8abc1fc0 --- /dev/null +++ b/src/Pog/ParsingTable.hpp @@ -0,0 +1,170 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace pog { + +template +class ParsingTable +{ +public: + using ActionType = Action; + using ShiftActionType = Shift; + using ReduceActionType = Reduce; + + using AutomatonType = Automaton; + using GrammarType = Grammar; + using RuleType = Rule; + using StateType = State; + using SymbolType = Symbol; + + using StateAndRuleType = StateAndRule; + using StateAndSymbolType = StateAndSymbol; + + // TODO: Lookahead<> should be non-const but we need it for operator[] + ParsingTable(const AutomatonType* automaton, const GrammarType* grammar, Lookahead& lookahead_op) + : _automaton(automaton), _grammar(grammar), _lookahead_op(lookahead_op) {} + + void calculate(ParserReport& report) + { + for (const auto& state : _automaton->get_states()) + { + if (state->is_accepting()) + add_accept(state.get(), _grammar->get_end_of_input_symbol()); + + for (const auto& [sym, dest_state] : state->get_transitions()) + add_state_transition(report, state.get(), sym, dest_state); + + for (const auto& item : state->get_production_items()) + { + for (const auto& sym : _lookahead_op[StateAndRuleType{state.get(), item->get_rule()}]) + add_reduction(report, state.get(), sym, item->get_rule()); + } + } + } + + void add_accept(const StateType* state, const SymbolType* symbol) + { + auto ss = StateAndSymbolType{state, symbol}; + auto itr = _action_table.find(ss); + if (itr != _action_table.end()) + assert(false && "Conflict happened in placing accept but this shouldn't happen"); + + _action_table.emplace(std::move(ss), Accept{}); + } + + void add_state_transition(ParserReport& report, const StateType* src_state, const SymbolType* symbol, const StateType* dest_state) + { + auto ss = StateAndSymbolType{src_state, symbol}; + if (symbol->is_terminal()) + { + auto itr = _action_table.find(ss); + if (itr != _action_table.end()) + { + if (std::holds_alternative(itr->second)) + report.add_shift_reduce_conflict(ss.state, ss.symbol, std::get(itr->second).rule); + } + else + _action_table.emplace(std::move(ss), ShiftActionType{dest_state}); + } + else if (symbol->is_nonterminal()) + { + auto itr = _goto_table.find(ss); + if (itr != _goto_table.end()) + assert(false && "Conflict happened in filling GOTO table but this shouldn't happen"); + + _goto_table.emplace(std::move(ss), dest_state); + } + } + + void add_reduction(ParserReport& report, const StateType* state, const SymbolType* symbol, const RuleType* rule) + { + auto ss = StateAndSymbolType{state, symbol}; + auto itr = _action_table.find(ss); + if (itr != _action_table.end()) + { + std::optional stack_prec; + if (rule->has_precedence()) + stack_prec = rule->get_precedence(); + else if (auto op_symbol = rule->get_rightmost_terminal(); op_symbol && op_symbol->has_precedence()) + stack_prec = op_symbol->get_precedence(); + + if (stack_prec.has_value() && symbol->has_precedence()) + { + const auto& input_prec = symbol->get_precedence(); + // Stack symbol precedence is lower, keep shift in the table + if (stack_prec < input_prec) + { + return; + } + // Stack symbol precedence is greater, prefer reduce + else if (stack_prec > input_prec) + { + itr->second = ReduceActionType{rule}; + return; + } + } + + if (std::holds_alternative(itr->second)) + report.add_reduce_reduce_conflict(ss.state, std::get(itr->second).rule, rule); + else if (std::holds_alternative(itr->second)) + report.add_shift_reduce_conflict(ss.state, ss.symbol, rule); + } + else + _action_table.emplace(std::move(ss), ReduceActionType{rule}); + } + + std::optional get_action(const StateType* state, const SymbolType* symbol) const + { + auto action_itr = _action_table.find(StateAndSymbolType{state, symbol}); + if (action_itr == _action_table.end()) + return std::nullopt; + + return action_itr->second; + } + + std::optional get_transition(const StateType* state, const SymbolType* symbol) const + { + auto goto_itr = _goto_table.find(StateAndSymbolType{state, symbol}); + if (goto_itr == _goto_table.end()) + return std::nullopt; + + return goto_itr->second; + } + + std::vector get_expected_symbols_from_state(const StateType* state) const + { + std::vector result; + for (const auto& sym : _grammar->get_symbols()) + { + if (sym->is_nonterminal()) + continue; + + StateAndSymbolType ss{state, sym.get()}; + if (auto itr = _action_table.find(ss); itr != _action_table.end()) + result.push_back(sym.get()); + } + + return result; + } + +private: + const AutomatonType* _automaton; + const GrammarType* _grammar; + std::unordered_map _action_table; + std::unordered_map _goto_table; + Lookahead& _lookahead_op; +}; + +} // namespace pog diff --git a/src/Pog/Pog.hpp b/src/Pog/Pog.hpp new file mode 100644 index 00000000..3651c84d --- /dev/null +++ b/src/Pog/Pog.hpp @@ -0,0 +1,6 @@ +#pragma once + +#define POG_VERSION "0.5.3" + +#include +#include diff --git a/src/Pog/Precedence.hpp b/src/Pog/Precedence.hpp new file mode 100644 index 00000000..bd16eb1f --- /dev/null +++ b/src/Pog/Precedence.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include + +namespace pog { + +enum class Associativity +{ + Left, + Right +}; + +struct Precedence +{ + std::uint32_t level; + Associativity assoc; + + bool operator==(const Precedence& rhs) const { return level == rhs.level && assoc == rhs.assoc; } + bool operator!=(const Precedence& rhs) const { return !(*this == rhs); } + + bool operator<(const Precedence& rhs) const + { + if (level < rhs.level) + return true; + else if (level == rhs.level) + { + if (assoc == Associativity::Right) + return true; + } + + return false; + } + + bool operator>(const Precedence& rhs) const + { + if (level > rhs.level) + return true; + else if (level == rhs.level) + { + if (assoc == Associativity::Left) + return true; + } + + return false; + } +}; + +} // namespace pog diff --git a/src/Pog/Relations/Includes.hpp b/src/Pog/Relations/Includes.hpp new file mode 100644 index 00000000..191b2db2 --- /dev/null +++ b/src/Pog/Relations/Includes.hpp @@ -0,0 +1,148 @@ +#pragma once + +#include + +#include + +#include +#include + +namespace pog { + +/** + * Includes is a relation on tuples of (q,x) where q is state and x is symbol. + * Basically it's a relation on transitions over symbols coming out from states. + * + * Let's imagine that state Q contains item A -> a <*> B b where a and b are sequences + * of terminals and nonterminals. We had to get to this state Q from state P which + * contains item A -> <*> a B b. If and only if b can be reduced down to empty string (b =>* eps), + * we say that (Q, B) includes (P, A). + * + * To put this into human-friendly language, if we are in a state with item A -> a <*> B b, + * that means we are about to read B from the input. If the sequence b can possibly be empty, + * then we say that the state with item A -> a <*> B b and symbol B includes state with + * item A -> <*> a B b and symbol A. + * + * This is useful for construction of Follow sets because if b can be completely empty, then + * what can follow A can also follow B (with respect to states they are in). + */ +template +class Includes : public Relation> +{ +public: + using Parent = Relation>; + + using AutomatonType = Automaton; + using BacktrackingInfoType = BacktrackingInfo; + using GrammarType = Grammar; + using StateType = State; + using SymbolType = Symbol; + + using StateAndSymbolType = StateAndSymbol; + + Includes(const AutomatonType* automaton, const GrammarType* grammar) : Parent(automaton, grammar) {} + Includes(const Includes&) = delete; + Includes(Includes&&) noexcept = default; + + virtual void calculate() override + { + // Iterate over all states in the LR automaton + for (const auto& state : Parent::_automaton->get_states()) + { + for (const auto& item : *state.get()) + { + // We are looking for items in form A -> a <*> B b so we are not intersted in final items + if (item->is_final()) + continue; + + // Get the symbol right next to <*> in an item + auto next_symbol = item->get_read_symbol(); + + // If the next symbol is not nonterminal then we are again not interested + if (!next_symbol->is_nonterminal()) + continue; + + StateAndSymbolType src_ss{state.get(), next_symbol}; + Parent::_relation.emplace(src_ss, std::unordered_set{}); + + // Get the 'b' out of A -> a <*> B b + // If b can't be reduced down to empty string - Empty(b) - then we are not interested + auto right_rest = item->get_right_side_without_read_symbol(); + if (!right_rest.empty() && !Parent::_grammar->empty(right_rest)) + continue; + + // Now we'll start backtracking through LR automaton using backtransitions. + // We'll basically just go in the different direction of arrows in the automata. + // We know of what symbols 'a' in A -> a <*> B b is made of so we exactly know which + // backtransitions to take. There can be multiple transitions through the same symbol + // going into current state so we'll put them into queue and process until queue is empty. + std::unordered_set visited_states; + std::deque to_process; + // Let's insert the current state and item A -> a <*> B b into the queue as a starting point + to_process.push_back(BacktrackingInfoType{state.get(), *item.get()}); + while (!to_process.empty()) + { + auto backtracking_info = std::move(to_process.front()); + to_process.pop_front(); + + // If we've reached state with item A -> <*> a B b, we've reached our destination + if (backtracking_info.item.get_read_pos() == 0) + { + // Insert relation + StateAndSymbolType dest_ss{backtracking_info.state, backtracking_info.item.get_rule()->get_lhs()}; + auto itr = Parent::_relation.find(src_ss); + if (itr == Parent::_relation.end()) + assert(false && "This shouldn't happen"); + itr->second.insert(std::move(dest_ss)); + continue; + } + + // Observe backtransitions over the symbol left to the <*> in an item + const auto& back_trans = backtracking_info.state->get_back_transitions(); + auto itr = back_trans.find(backtracking_info.item.get_previous_symbol()); + if (itr == back_trans.end()) + assert(false && "This shouldn't happen"); + + // Perform step back of an item so that <*> in an item is moved one symbol to the left + backtracking_info.item.step_back(); + for (const auto& dest_state : itr->second) + { + if (visited_states.find(dest_state) == visited_states.end()) + { + // Put non-visited states from backtransitions into the queue + to_process.push_back(BacktrackingInfoType{dest_state, backtracking_info.item}); + visited_states.emplace(dest_state); + } + } + } + } + } + } + + std::string generate_relation_graph() + { + std::vector states_str, edges_str; + for (const auto& [ss, dests] : Parent::_relation) + { + states_str.push_back(fmt::format("n_{}_{} [label=\"({}, {})\"]", ss.state->get_index(), ss.symbol->get_index(), ss.state->get_index(), ss.symbol->get_name())); + for (const auto& dest_ss : dests) + { + states_str.push_back(fmt::format("n_{}_{} [label=\"({}, {})\"]", dest_ss.state->get_index(), dest_ss.symbol->get_index(), dest_ss.state->get_index(), dest_ss.symbol->get_name())); + edges_str.push_back(fmt::format("n_{}_{} -> n_{}_{}", ss.state->get_index(), ss.symbol->get_index(), dest_ss.state->get_index(), dest_ss.symbol->get_index())); + } + } + + return fmt::format(R"(digraph Includes {{ +node [shape=circle]; + +{} + +{} +}})", + fmt::join(states_str.begin(), states_str.end(), "\n"), + fmt::join(edges_str.begin(), edges_str.end(), "\n") + ); + } +}; + +} // namespace pog diff --git a/src/Pog/Relations/Lookback.hpp b/src/Pog/Relations/Lookback.hpp new file mode 100644 index 00000000..925edc01 --- /dev/null +++ b/src/Pog/Relations/Lookback.hpp @@ -0,0 +1,116 @@ +#pragma once + +#include + +#include +#include +#include + +namespace pog { + +/** + * Lookback is a relation of tuples (q,R) where q is state and R is a rule from grammar and + * tuples of (p,x) where p is state and x is symbol. Basically it's a relation on state and rule + * with state and symbol. + * + * Let's imagine that state Q contains final item A -> x <*> where x is a sequence of terminals and + * nonterminals. That means we need to perform production of rule A -> x and reduce x on the stack + * into A. That also means that there is some state P with item B -> a <*> A b through which we had to + * go into state Q. If it happens then (Q, A -> x) lookbacks (P, A). + * + * To put it simply, in order to get to state with final item A -> x <*>, we first had to go through + * state with item B -> a <*> A b. We just simply put state with item A -> x <*> and rule A -> x into relation + * with the origin state with item B -> a <*> A b and symbol A. + * + * This is useful for so-called propagation of lookaheads. If we know that rule A -> x is being used + * and it all originated in certain state where rule B -> a A b is being processed, we can use what + * can possible follow A in B -> a A b to know whether to use production of A -> x. + */ +template +class Lookback : public Relation, StateAndSymbol> +{ +public: + using Parent = Relation, StateAndSymbol>; + + using AutomatonType = Automaton; + using BacktrackingInfoType = BacktrackingInfo; + using GrammarType = Grammar; + using StateType = State; + using SymbolType = Symbol; + + using StateAndSymbolType = StateAndSymbol; + using StateAndRuleType = StateAndRule; + + Lookback(const AutomatonType* automaton, const GrammarType* grammar) : Parent(automaton, grammar) {} + Lookback(const Lookback&) = delete; + Lookback(Lookback&&) noexcept = default; + + virtual void calculate() override + { + // Iterate over all states of LR automaton + for (const auto& state : Parent::_automaton->get_states()) + { + for (const auto& item : *state.get()) + { + // We are not interested in items other than in form A -> x <*> + if (!item->is_final()) + continue; + + // Get left-hand side symbol of a rule + auto prod_symbol = item->get_rule()->get_lhs(); + + // Now we'll start backtracking through LR automaton using backtransitions. + // We'll basically just go in the different direction of arrows in the automata. + // We know that we have item A -> x <*> so we know which backtransitions to take (those contained in sequence x). + // There can be multiple transitions through the same symbol + // going into current state so we'll put them into queue and process until queue is empty. + std::unordered_set visited_states; + std::deque to_process; + // Let's insert the current state and item A -> x <*> into the queue as a starting point + to_process.push_back(BacktrackingInfoType{state.get(), *item.get()}); + while (!to_process.empty()) + { + auto backtracking_info = std::move(to_process.front()); + to_process.pop_front(); + + // If the state has transition over the symbol A, that means there is an item B -> a <*> A b + if (backtracking_info.state->get_transitions().find(prod_symbol) != backtracking_info.state->get_transitions().end()) + { + // Insert relation + StateAndRuleType src_sr{state.get(), item->get_rule()}; + StateAndSymbolType dest_ss{backtracking_info.state, prod_symbol}; + auto itr = Parent::_relation.find(src_sr); + if (itr == Parent::_relation.end()) + Parent::_relation.emplace(std::move(src_sr), std::unordered_set{std::move(dest_ss)}); + else + itr->second.insert(std::move(dest_ss)); + } + + // We've reached item with <*> at the start so we are no longer interested in it + if (backtracking_info.item.get_read_pos() == 0) + continue; + + // Observe backtransitions over the symbol left to the <*> in an item + const auto& back_trans = backtracking_info.state->get_back_transitions(); + auto itr = back_trans.find(backtracking_info.item.get_previous_symbol()); + if (itr == back_trans.end()) + assert(false && "This shouldn't happen"); + + // Perform step back of an item so that <*> in an item is moved one symbol to the left + backtracking_info.item.step_back(); + for (const auto& dest_state : itr->second) + { + if (visited_states.find(dest_state) == visited_states.end()) + { + // Put non-visited states from backtransitions into the queue + to_process.push_back(BacktrackingInfoType{dest_state, backtracking_info.item}); + visited_states.emplace(dest_state); + } + } + } + } + } + } +}; + +} // namespace pog diff --git a/src/Pog/Relations/Relation.hpp b/src/Pog/Relations/Relation.hpp new file mode 100644 index 00000000..d4313b5c --- /dev/null +++ b/src/Pog/Relations/Relation.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include +#include + +#include +#include + +namespace pog { + +template +struct BacktrackingInfo +{ + const State* state; + Item item; +}; + +template +class Relation +{ +public: + using AutomatonType = Automaton; + using GrammarType = Grammar; + + Relation(const AutomatonType* automaton, const GrammarType* grammar) : _automaton(automaton), _grammar(grammar) {} + Relation(const Relation&) = delete; + Relation(Relation&&) noexcept = default; + virtual ~Relation() = default; + + virtual void calculate() = 0; + + auto begin() { return _relation.begin(); } + auto end() { return _relation.end(); } + + auto begin() const { return _relation.begin(); } + auto end() const { return _relation.end(); } + + template + std::unordered_set* find(const T& key) + { + auto itr = _relation.find(key); + if (itr == _relation.end()) + return nullptr; + + return &itr->second; + } + + template + const std::unordered_set* find(const T& key) const + { + auto itr = _relation.find(key); + if (itr == _relation.end()) + return nullptr; + + return &itr->second; + } + +protected: + const AutomatonType* _automaton; + const GrammarType* _grammar; + std::unordered_map> _relation; +}; + +} // namespace pog diff --git a/src/Pog/Rule.hpp b/src/Pog/Rule.hpp new file mode 100644 index 00000000..01fe7f9c --- /dev/null +++ b/src/Pog/Rule.hpp @@ -0,0 +1,94 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace pog { + +template +class Parser; + +template +class Rule +{ +public: + using SymbolType = Symbol; + using CallbackType = std::function&, std::vector>&&)>; + + Rule(std::uint32_t index, const SymbolType* lhs, const std::vector& rhs) + : _index(index), _lhs(lhs), _rhs(rhs), _action(), _midrule_size(std::nullopt), _start(false) {} + + template + Rule(std::uint32_t index, const SymbolType* lhs, const std::vector& rhs, CallbackT&& action) + : _index(index), _lhs(lhs), _rhs(rhs), _action(std::forward(action)), _midrule_size(std::nullopt), _start(false) {} + + std::uint32_t get_index() const { return _index; } + const SymbolType* get_lhs() const { return _lhs; } + const std::vector& get_rhs() const { return _rhs; } + + bool has_precedence() const { return static_cast(_precedence); } + const Precedence& get_precedence() const { return _precedence.value(); } + void set_precedence(std::uint32_t level, Associativity assoc) { _precedence = Precedence{level, assoc}; } + + std::size_t get_number_of_required_arguments_for_action() const + { + return is_midrule() ? get_midrule_size() : get_rhs().size(); + } + + const SymbolType* get_rightmost_terminal() const + { + auto itr = std::find_if(_rhs.rbegin(), _rhs.rend(), [](const auto& symbol) { + return symbol->is_terminal(); + }); + + return itr != _rhs.rend() ? *itr : nullptr; + } + + std::string to_string(std::string_view arrow = "->", std::string_view eps = "") const + { + std::vector rhs_strings(_rhs.size()); + std::transform(_rhs.begin(), _rhs.end(), rhs_strings.begin(), [](const SymbolType* s) { + return s->get_name(); + }); + + if (rhs_strings.empty()) + rhs_strings.push_back(std::string{eps}); + + return fmt::format("{} {} {}", _lhs->get_name(), arrow, fmt::join(rhs_strings.begin(), rhs_strings.end(), " ")); + } + + bool has_action() const { return static_cast(_action); } + bool is_start_rule() const { return _start; } + + void set_start_rule(bool set) { _start = set; } + void set_midrule(std::size_t size) { _midrule_size = size; } + bool is_midrule() const { return static_cast(_midrule_size); } + std::size_t get_midrule_size() const { return _midrule_size.value(); } + + template + ValueT perform_action(Args&&... args) const { return _action(std::forward(args)...); } + + bool operator==(const Rule& rhs) const { return _index == rhs._index; } + bool operator!=(const Rule& rhs) const { return !(*this == rhs); } + +private: + std::uint32_t _index; + const SymbolType* _lhs; + std::vector _rhs; + CallbackType _action; + std::optional _precedence; + std::optional _midrule_size; + bool _start; +}; + +} // namespace pog diff --git a/src/Pog/RuleBuilder.hpp b/src/Pog/RuleBuilder.hpp new file mode 100644 index 00000000..813d953b --- /dev/null +++ b/src/Pog/RuleBuilder.hpp @@ -0,0 +1,140 @@ +#pragma once + +#include +#include + +namespace pog { + +template +class RuleBuilder +{ +public: + using GrammarType = Grammar; + using RuleType = Rule; + using SymbolType = Symbol; + + struct SymbolsAndAction + { + std::vector symbols; + typename RuleType::CallbackType action; + }; + + struct RightHandSide + { + std::vector symbols_and_action; + std::optional precedence; + }; + + RuleBuilder(GrammarType* grammar, const std::string& lhs) : _grammar(grammar), _lhs(lhs), _rhss() {} + + void done() + { + if (_rhss.empty()) + return; + + const auto* lhs_symbol = _grammar->add_symbol(SymbolKind::Nonterminal, _lhs); + + std::size_t rhs_counter = 0; + for (auto&& rhs : _rhss) + { + assert(!rhs.symbols_and_action.empty() && "No symbols and action associated to right-hand side of the rule. This shouldn't happen"); + + std::vector rhs_symbols; + for (std::size_t i = 0; i < rhs.symbols_and_action.size(); ++i) + { + auto&& symbols_and_action = rhs.symbols_and_action[i]; + + std::transform(symbols_and_action.symbols.begin(), symbols_and_action.symbols.end(), std::back_inserter(rhs_symbols), [this](const auto& sym_name) { + return _grammar->add_symbol(SymbolKind::Nonterminal, sym_name); + }); + + // There are multple actions (mid-rule actions) so we need to create new symbol and epsilon rule + // for each midrule action. Midrule symbols will be inserted into the original rule. + // + // If you have rule A -> B C D and you want to perform action after B, then we'll create rules + // A -> B X C D + // X -> + // where X -> will have assigned the midrule action. + if (i < rhs.symbols_and_action.size() - 1) + { + // Create unique nonterminal for midrule action + auto midsymbol = _grammar->add_symbol( + SymbolKind::Nonterminal, + fmt::format("_{}#{}.{}", _lhs, rhs_counter, i) + ); + + // Create rule to which midrule action can be assigned and set midrule size. + // Midrule size is number of symbols preceding the midrule symbol. It represents how many + // items from stack we need to borrow for action arguments. + auto rule = _grammar->add_rule(midsymbol, std::vector{}, std::move(symbols_and_action.action)); + rule->set_midrule(rhs_symbols.size()); + rhs_symbols.push_back(midsymbol); + } + // This is the last action so do not mark it as midrule + else + { + auto rule = _grammar->add_rule(lhs_symbol, rhs_symbols, std::move(symbols_and_action.action)); + if (rule && rhs.precedence) + { + const auto& prec = rhs.precedence.value(); + rule->set_precedence(prec.level, prec.assoc); + } + } + } + + rhs_counter++; + } + } + + template + RuleBuilder& production(Args&&... args) + { + _rhss.push_back(RightHandSide{ + std::vector{ + SymbolsAndAction{ + std::vector{}, + {} + } + }, + std::nullopt + }); + _production(_rhss.back().symbols_and_action, std::forward(args)...); + return *this; + } + + RuleBuilder& precedence(std::uint32_t level, Associativity assoc) + { + _rhss.back().precedence = Precedence{level, assoc}; + return *this; + } + +private: + void _production(std::vector&) {} + + template + void _production(std::vector& sa, const std::string& symbol, Args&&... args) + { + sa.back().symbols.push_back(symbol); + _production(sa, std::forward(args)...); + } + + template + void _production(std::vector& sa, typename RuleType::CallbackType&& action, Args&&... args) + { + sa.back().action = std::move(action); + // We have ran into action so create new record in symbols and actions vector + // but only if it isn't the very last thing in the production + if constexpr (sizeof...(args) > 0) + sa.push_back(SymbolsAndAction{ + std::vector{}, + {} + }); + _production(sa, std::forward(args)...); + } + + GrammarType* _grammar; + std::string _lhs; + std::vector _rhss; +}; + +} // namespace pog diff --git a/src/Pog/State.hpp b/src/Pog/State.hpp new file mode 100644 index 00000000..ad9ba829 --- /dev/null +++ b/src/Pog/State.hpp @@ -0,0 +1,157 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace pog { + +template +class State +{ +public: + using ItemType = Item; + using SymbolType = Symbol; + + State() : _index(std::numeric_limits::max()) {} + State(std::uint32_t index) : _index(index) {} + + std::uint32_t get_index() const { return _index; } + void set_index(std::uint32_t index) { _index = index; } + + std::size_t size() const { return _items.size(); } + auto begin() const { return _items.begin(); } + auto end() const { return _items.end(); } + + template + std::pair add_item(T&& item) + { + auto itr = std::lower_bound(_items.begin(), _items.end(), item, [](const auto& left, const auto& needle) { + return *left.get() < needle; + }); + + if (itr == _items.end() || *itr->get() != item) + { + auto new_itr = _items.insert(itr, std::make_unique(std::forward(item))); + return {new_itr->get(), true}; + } + else + return {itr->get(), false}; + } + + void add_transition(const SymbolType* symbol, const State* state) + { + _transitions.emplace(symbol, state); + } + + void add_back_transition(const SymbolType* symbol, const State* state) + { + auto itr = _back_transitions.find(symbol); + if (itr == _back_transitions.end()) + { + _back_transitions.emplace(symbol, std::vector{state}); + return; + } + + auto state_itr = std::lower_bound(itr->second.begin(), itr->second.end(), state->get_index(), [](const auto& left, const auto& needle) { + return left->get_index() < needle; + }); + + if (state_itr == itr->second.end() || (*state_itr)->get_index() != state->get_index()) + itr->second.insert(state_itr, state); + } + + bool is_accepting() const + { + return std::count_if(_items.begin(), _items.end(), [](const auto& item) { + return item->is_accepting(); + }) == 1; + } + + std::string to_string(std::string_view arrow = "->", std::string_view eps = "", std::string_view sep = "<*>", const std::string& newline = "\n") const + { + std::vector item_strings(_items.size()); + std::transform(_items.begin(), _items.end(), item_strings.begin(), [&](const auto& item) { + return item->to_string(arrow, eps, sep); + }); + return fmt::format("{}", fmt::join(item_strings.begin(), item_strings.end(), newline)); + } + + std::vector get_production_items() const + { + std::vector result; + transform_if(_items.begin(), _items.end(), std::back_inserter(result), + [](const auto& item) { + return item->is_final(); + }, + [](const auto& item) { + return item.get(); + } + ); + return result; + } + + auto get_kernel() const + { + return FilterView{_items.begin(), _items.end(), [](const auto& item) { + return item->is_kernel(); + }}; + } + + bool contains(const ItemType& item) const + { + auto itr = std::lower_bound(_items.begin(), _items.end(), item, [](const auto& left, const auto& needle) { + return *left.get() < needle; + }); + return itr != _items.end() && *itr->get() == item; + } + + bool operator==(const State& rhs) const + { + auto lhs_kernel = get_kernel(); + auto rhs_kernel = rhs.get_kernel(); + return std::equal(lhs_kernel.begin(), lhs_kernel.end(), rhs_kernel.begin(), rhs_kernel.end(), [](const auto& left, const auto& right) { + return *left.get() == *right.get(); + }); + } + + bool operator !=(const State& rhs) const + { + return !(*this == rhs); + } + + const std::map>& get_transitions() const { return _transitions; } + const std::map, SymbolLess>& get_back_transitions() const { return _back_transitions; } + +private: + std::uint32_t _index; + std::vector> _items; + std::map> _transitions; + std::map, SymbolLess> _back_transitions; +}; + +template +struct StateKernelHash +{ + std::size_t operator()(const State* state) const + { + std::size_t kernel_hash = 0; + for (const auto& item : state->get_kernel()) + hash_combine(kernel_hash, item->get_rule()->get_index(), item->get_read_pos()); + return kernel_hash; + } +}; + +template +struct StateKernelEquals +{ + bool operator()(const State* state1, const State* state2) const + { + return *state1 == *state2; + } +}; + +} // namespace pog diff --git a/src/Pog/Symbol.hpp b/src/Pog/Symbol.hpp new file mode 100644 index 00000000..3f913902 --- /dev/null +++ b/src/Pog/Symbol.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace pog { + +enum class SymbolKind +{ + End, + Nonterminal, + Terminal +}; + +template +class Symbol +{ +public: + Symbol(std::uint32_t index, SymbolKind kind, const std::string& name) : _index(index), _kind(kind), _name(name) {} + + std::uint32_t get_index() const { return _index; } + const Precedence& get_precedence() const { return _precedence.value(); } + const std::string& get_name() const { return _name; } + const std::string& get_description() const { return _description.has_value() ? *_description : _name; } + + bool has_precedence() const { return static_cast(_precedence); } + bool is_end() const { return _kind == SymbolKind::End; } + bool is_nonterminal() const { return _kind == SymbolKind::Nonterminal; } + bool is_terminal() const { return _kind == SymbolKind::Terminal; } + + void set_precedence(std::uint32_t level, Associativity assoc) { _precedence = Precedence{level, assoc}; } + void set_description(const std::string& description) { _description = description; } + +private: + std::uint32_t _index; + SymbolKind _kind; + std::string _name; + std::optional _description; + std::optional _precedence; +}; + + +template +struct SymbolLess +{ + bool operator()(const Symbol* lhs, const Symbol* rhs) const + { + return lhs->get_index() < rhs->get_index(); + } +}; + +} // namespace pog diff --git a/src/Pog/Token.hpp b/src/Pog/Token.hpp new file mode 100644 index 00000000..0134d04f --- /dev/null +++ b/src/Pog/Token.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include + +namespace pog { + +template +class Token +{ +public: + using SymbolType = Symbol; + using CallbackType = std::function; + + template + Token(std::uint32_t index, const std::string& pattern, StatesT&& active_in_states) : Token(index, pattern, std::forward(active_in_states), nullptr) {} + + template + Token(std::uint32_t index, const std::string& pattern, StatesT&& active_in_states, const SymbolType* symbol) + : _index(index), _pattern(pattern), _symbol(symbol), _regexp(std::make_unique(_pattern)), _action(), + _enter_state(), _active_in_states(std::forward(active_in_states)) {} + + std::uint32_t get_index() const { return _index; } + const std::string& get_pattern() const { return _pattern; } + const SymbolType* get_symbol() const { return _symbol; } + const re2::RE2* get_regexp() const { return _regexp.get(); } + + bool has_symbol() const { return _symbol != nullptr; } + bool has_action() const { return static_cast(_action); } + bool has_transition_to_state() const { return static_cast(_enter_state); } + + template + void set_action(CallbackT&& action) + { + _action = std::forward(action); + } + + template + ValueT perform_action(Args&&... args) const + { + return _action(std::forward(args)...); + } + + void set_transition_to_state(const std::string& state) + { + _enter_state = state; + } + + const std::string& get_transition_to_state() const + { + return _enter_state.value(); + } + + template + void add_active_in_state(StrT&& state) + { + _active_in_states.push_back(std::forward(state)); + } + + const std::vector& get_active_in_states() const + { + return _active_in_states; + } + +private: + std::uint32_t _index; + std::string _pattern; + const SymbolType* _symbol; + std::unique_ptr _regexp; + CallbackType _action; + std::optional _enter_state; + std::vector _active_in_states; +}; + +} // namespace pog diff --git a/src/Pog/TokenBuilder.hpp b/src/Pog/TokenBuilder.hpp new file mode 100644 index 00000000..a4c01c66 --- /dev/null +++ b/src/Pog/TokenBuilder.hpp @@ -0,0 +1,112 @@ +#pragma once + +#include +#include +#include + +namespace pog { + +template +class TokenBuilder +{ +public: + using GrammarType = Grammar; + using SymbolType = Symbol; + using TokenType = Token; + using TokenizerType = Tokenizer; + + TokenBuilder(GrammarType* grammar, TokenizerType* tokenizer) : _grammar(grammar), _tokenizer(tokenizer), _pattern("$"), + _symbol_name(), _precedence(), _action(), _fullword(false), _end_token(true), _in_states{std::string{TokenizerType::DefaultState}}, _enter_state() {} + + TokenBuilder(GrammarType* grammar, TokenizerType* tokenizer, const std::string& pattern) : _grammar(grammar), _tokenizer(tokenizer), _pattern(pattern), + _symbol_name(), _precedence(), _action(), _fullword(false), _end_token(false), _in_states{std::string{TokenizerType::DefaultState}}, _enter_state() {} + + void done() + { + TokenType* token; + if (!_end_token) + { + auto* symbol = !_symbol_name.empty() ? _grammar->add_symbol(SymbolKind::Terminal, _symbol_name) : nullptr; + token = _tokenizer->add_token(_fullword ? fmt::format("{}(\\b|$)", _pattern) : _pattern, symbol, std::move(_in_states)); + if (symbol && _precedence) + { + const auto& prec = _precedence.value(); + symbol->set_precedence(prec.level, prec.assoc); + } + + if(symbol && _description.size() != 0) + symbol->set_description(_description); + + if (_enter_state) + token->set_transition_to_state(_enter_state.value()); + } + else + { + token = _tokenizer->get_end_token(); + for (auto&& state : _in_states) + token->add_active_in_state(std::move(state)); + } + + if (_action) + token->set_action(std::move(_action)); + } + + TokenBuilder& symbol(const std::string& symbol_name) + { + _symbol_name = symbol_name; + return *this; + } + + TokenBuilder& precedence(std::uint32_t level, Associativity assoc) + { + _precedence = Precedence{level, assoc}; + return *this; + } + + TokenBuilder& description(const std::string& text) + { + _description = text; + return *this; + } + + template + TokenBuilder& action(CallbackT&& action) + { + _action = std::forward(action); + return *this; + } + + TokenBuilder& fullword() + { + _fullword = true; + return *this; + } + + template + TokenBuilder& states(Args&&... args) + { + _in_states = {std::forward(args)...}; + return *this; + } + + TokenBuilder& enter_state(const std::string& state) + { + _enter_state = state; + return *this; + } + +private: + GrammarType* _grammar; + TokenizerType* _tokenizer; + std::string _description; + std::string _pattern; + std::string _symbol_name; + std::optional _precedence; + typename TokenType::CallbackType _action; + bool _fullword; + bool _end_token; + std::vector _in_states; + std::optional _enter_state; +}; + +} // namespace pog diff --git a/src/Pog/Tokenizer.hpp b/src/Pog/Tokenizer.hpp new file mode 100644 index 00000000..9530b94e --- /dev/null +++ b/src/Pog/Tokenizer.hpp @@ -0,0 +1,285 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#ifdef POG_DEBUG +#define POG_DEBUG_TOKENIZER 1 +#endif + +#ifdef POG_DEBUG_TOKENIZER +#define debug_tokenizer(...) fmt::print(stderr, "[tokenizer] {}\n", fmt::format(__VA_ARGS__)) +#else +#define debug_tokenizer(...) +#endif + +#include +#include +#include + +namespace pog { + +template +struct TokenMatch +{ + TokenMatch(const Symbol* sym) : symbol(sym), value(), match_length(0) {} + template + TokenMatch(const Symbol* sym, T&& v, std::size_t len, LineSpecialization spec) : symbol(sym), line_spec(spec), value(std::forward(v)), match_length(len) {} + TokenMatch(const TokenMatch&) = default; + TokenMatch(TokenMatch&&) noexcept = default; + + TokenMatch& operator=(const TokenMatch&) = default; + TokenMatch& operator=(TokenMatch&&) noexcept = default; + + const Symbol* symbol; + LineSpecialization line_spec; + ValueT value; + std::size_t match_length; +}; + +struct InputStream +{ + std::unique_ptr content; + re2::StringPiece stream; + bool at_end; +}; + +template +struct StateInfo +{ + std::string name; + std::unique_ptr re_set; + std::vector*> tokens; +}; + +template +class Tokenizer +{ +public: + using CallbackType = std::function; + + static constexpr std::string_view DefaultState = "@default"; + + using GrammarType = Grammar; + using StateInfoType = StateInfo; + using SymbolType = Symbol; + using TokenType = Token; + using TokenMatchType = TokenMatch; + + Tokenizer(const GrammarType* grammar) : _grammar(grammar), _tokens(), _state_info(), _input_stack(), _current_state(nullptr), _global_action() + { + _current_line = 1; + _current_offset = 0; + _current_state = get_or_make_state_info(std::string{DefaultState}); + add_token("$", nullptr, std::vector{std::string{DefaultState}}); + } + + void prepare() + { + std::string error; + + for (const auto& token : _tokens) + { + for (const auto& state : token->get_active_in_states()) + { + error.clear(); + auto* state_info = get_or_make_state_info(state); + state_info->re_set->Add(token->get_pattern(), &error); + state_info->tokens.push_back(token.get()); + assert(1); + assert(error.empty() && "Error when compiling token regexp"); + } + } + + for (auto&& [name, info] : _state_info) + info.re_set->Compile(); + } + + const std::vector>& get_tokens() const + { + return _tokens; + } + + TokenType* get_end_token() const + { + return _tokens[0].get(); + } + + TokenType* add_token(const std::string& pattern, const SymbolType* symbol, const std::vector& states) + { + _tokens.push_back(std::make_unique(static_cast(_tokens.size()), pattern, states, symbol)); + return _tokens.back().get(); + } + + void push_input_stream(std::string& stream) + { + _input_stack.emplace_back(InputStream{std::make_unique(std::move(stream)), re2::StringPiece{}, false}); + _input_stack.back().stream = re2::StringPiece{_input_stack.back().content->c_str()}; + } + + void pop_input_stream() + { + _input_stack.pop_back(); + } + + void clear_input_streams() + { + _input_stack.clear(); + } + + void global_action(CallbackType&& global_action) + { + _global_action = std::move(global_action); + } + + void _reset_line_offset() + { + _current_offset = 0; + } + + std::uint32_t& _get_line_counter() + { + return _current_line; + } + + std::uint16_t& _get_line_offset() { + return _current_offset; + } + + std::optional next_token() + { + bool repeat = true; + while (repeat) + { + // We've emptied the stack so that means return end symbol to parser + if (_input_stack.empty()) + { + debug_tokenizer("Input stack empty - returing end of input"); + return _grammar->get_end_of_input_symbol(); + } + + auto& current_input = _input_stack.back(); + if (!current_input.at_end) + { + // Matched patterns doesn't have to be sorted (used to be in older re2 versions) but we shouldn't count on that + std::vector matched_patterns; + _current_state->re_set->Match(current_input.stream, &matched_patterns); + + // Haven't matched anything, tokenization failure, we will get into endless loop + if (matched_patterns.empty()) [[unlikely]] + { + debug_tokenizer("Nothing matched on the current input"); + return std::nullopt; + } + + re2::StringPiece submatch; + const TokenType* best_match = nullptr; + int longest_match = -1; + for (auto pattern_index : matched_patterns) + { + _current_state->tokens[pattern_index]->get_regexp()->Match(current_input.stream, 0, current_input.stream.size(), re2::RE2::Anchor::ANCHOR_START, &submatch, 1); + if (longest_match < static_cast(submatch.size())) + { + best_match = _current_state->tokens[pattern_index]; + longest_match = static_cast(submatch.size()); + } + // In case of equal matches, index of tokens chooses which one is it (lower index has higher priority) + else if (longest_match == static_cast(submatch.size())) + { + if (!best_match || best_match->get_index() > static_cast(pattern_index)) + best_match = _current_state->tokens[pattern_index]; + } + } + + if (current_input.stream.size() == 0) + { + debug_tokenizer("Reached end of input"); + current_input.at_end = true; + } + + if (best_match->has_transition_to_state()) + { + enter_state(best_match->get_transition_to_state()); + debug_tokenizer("Entered state \'{}\'", best_match->get_transition_to_state()); + } + + std::string_view token_str{current_input.stream.data(), static_cast(longest_match)}; + _current_offset += longest_match; + current_input.stream.remove_prefix(longest_match); + debug_tokenizer("Matched \'{}\' with token \'{}\' (index {})", token_str, best_match->get_pattern(), best_match->get_index()); + + if (_global_action) + _global_action(token_str); + + ValueT value{}; + if (best_match->has_action()) + value = best_match->perform_action(token_str); + + if (!best_match->has_symbol()) + continue; + + return TokenMatchType { + best_match->get_symbol(), + std::move(value), + static_cast(longest_match), + LineSpecialization { + _current_line, + static_cast(_current_offset - longest_match), + static_cast(longest_match) + } + }; + } + else { + debug_tokenizer("At the end of input"); + } + + // There is still something on stack but we've reached the end and noone popped it so return end symbol to parser + return _grammar->get_end_of_input_symbol(); + } + + return std::nullopt; + } + + void enter_state(const std::string& state) + { + _current_state = get_state_info(state); + assert(_current_state && "Transition to unknown state in tokenizer"); + } + +private: + StateInfoType* get_or_make_state_info(const std::string& name) + { + auto itr = _state_info.find(name); + if (itr == _state_info.end()) + std::tie(itr, std::ignore) = _state_info.emplace(name, StateInfoType{ + name, + std::make_unique(re2::RE2::DefaultOptions, re2::RE2::Anchor::ANCHOR_START), + std::vector{} + }); + return &itr->second; + } + + StateInfoType* get_state_info(const std::string& name) + { + auto itr = _state_info.find(name); + if (itr == _state_info.end()) + return nullptr; + return &itr->second; + } + + const GrammarType* _grammar; + std::vector> _tokens; + + std::unordered_map _state_info; + std::vector _input_stack; + StateInfoType* _current_state; + CallbackType _global_action; + std::uint32_t _current_line; + std::uint16_t _current_offset; +}; + +} // namespace pog diff --git a/src/Pog/Types/StateAndRule.hpp b/src/Pog/Types/StateAndRule.hpp new file mode 100644 index 00000000..2a8cc2a2 --- /dev/null +++ b/src/Pog/Types/StateAndRule.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +namespace pog { + +template +struct StateAndRule +{ + const State* state; + const Rule* rule; + + bool operator==(const StateAndRule& rhs) const + { + return state->get_index() == rhs.state->get_index() && rule->get_index() == rhs.rule->get_index(); + } + + bool operator!=(const StateAndRule& rhs) const + { + return !(*this == rhs); + } +}; + +} // namespace pog + +namespace std { + +template +struct hash> +{ + std::size_t operator()(const pog::StateAndRule& sr) const + { + return pog::hash_combine(sr.state->get_index(), sr.rule->get_index()); + } +}; + +} diff --git a/src/Pog/Types/StateAndSymbol.hpp b/src/Pog/Types/StateAndSymbol.hpp new file mode 100644 index 00000000..271124f6 --- /dev/null +++ b/src/Pog/Types/StateAndSymbol.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +namespace pog { + +template +struct StateAndSymbol +{ + const State* state; + const Symbol* symbol; + + bool operator==(const StateAndSymbol& rhs) const + { + return state->get_index() == rhs.state->get_index() && symbol->get_index() == rhs.symbol->get_index(); + } + + bool operator!=(const StateAndSymbol& rhs) const + { + return !(*this == rhs); + } +}; + +} // namespace pog + +namespace std { + +template +struct hash> +{ + std::size_t operator()(const pog::StateAndSymbol& ss) const + { + return pog::hash_combine(ss.state->get_index(), ss.symbol->get_index()); + } +}; + +} diff --git a/src/Pog/Utils.hpp b/src/Pog/Utils.hpp new file mode 100644 index 00000000..f637ba17 --- /dev/null +++ b/src/Pog/Utils.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace pog { + +template +OutputIterator transform_if(InputIterator first1, InputIterator last1, OutputIterator result, Pred pred, UnaryOperator op) +{ + while (first1 != last1) + { + if (pred(*first1)) { + *result = op(*first1); + ++result; + } + ++first1; + } + return result; +} + +template +T accumulate_if(InputIterator first, InputIterator last, T init, Pred pred, BinaryOperation op) +{ + for (; first != last; ++first) + { + if (pred(*first)) + init = op(std::move(init), *first); + } + return init; +} + +inline void hash_combine(std::size_t&) { } + +template +inline void hash_combine(std::size_t& seed, const T& v, const Rest&... rest) { + seed ^= std::hash{}(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); + hash_combine(seed, rest...); +} + +template +inline std::size_t hash_combine(const Rest&... rest) +{ + std::size_t seed = 0; + hash_combine(seed, rest...); + return seed; +} + +template struct overloaded : Ts... { using Ts::operator()...; }; +template overloaded(Ts...) -> overloaded; + +template +auto visit_with(Variant& v, Fs&&... fs) +{ + return std::visit(overloaded{ + std::forward(fs)... + }, v); +} + +template +inline std::string current_time(FormatT&& format) +{ + auto now = std::time(nullptr); + auto tm = std::localtime(&now); + + std::ostringstream ss; + ss << std::put_time(tm, std::forward(format)); + return ss.str(); +} + +} // namespace pog From aa009a21c5266b10e10e07796b2e2ac0d8ce43c8 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Wed, 30 Apr 2025 11:33:18 +0400 Subject: [PATCH 19/25] Reorder all includes, move pog, add PCH --- src/Assembler/CMakeLists.txt | 8 +++++++- src/Assembler/Core/BinaryTransformer.cpp | 4 ++++ src/Assembler/Core/BinaryTransformer.hpp | 5 ++--- src/Assembler/Core/Compiler.cpp | 3 +-- src/Assembler/Core/Compiler.hpp | 5 +---- src/Assembler/Core/ModeNameAssoc.hpp | 2 ++ src/Assembler/Core/OpcodeNameAssoc.hpp | 2 ++ src/Assembler/Core/Parsers.cpp | 5 +++-- src/Assembler/Core/RegNameAssoc.hpp | 2 ++ src/Assembler/Core/StatementCompilers.cpp | 2 ++ src/Assembler/Core/Tokenizers.cpp | 3 +++ src/Assembler/Main/Main.cpp | 2 +- src/BacktraceProvider/BacktraceProvider.cpp | 2 ++ src/CMakeLists.txt | 2 +- src/Emulator/Core/CPU/CPU.cpp | 6 +++--- src/Emulator/Core/CPU/CPU.hpp | 4 ++-- src/Emulator/Core/CPU/Decoders/IDecoder.hpp | 5 ----- src/Emulator/Core/CPU/Decoders/StdDecoder.cpp | 3 --- src/Emulator/Core/CPU/Decoders/StdDecoder.hpp | 2 ++ src/Emulator/Core/CPU/IO/Simple.hpp | 2 -- src/Emulator/Core/CPU/InstructionsImpl/ADC.cpp | 4 +++- src/Emulator/Core/CPU/InstructionsImpl/ADD.cpp | 3 ++- src/Emulator/Core/CPU/InstructionsImpl/AND.cpp | 5 +++-- src/Emulator/Core/CPU/InstructionsImpl/ANDN.cpp | 5 +++-- src/Emulator/Core/CPU/InstructionsImpl/BSWAP.cpp | 5 +---- src/Emulator/Core/CPU/InstructionsImpl/CALL.cpp | 4 ++-- src/Emulator/Core/CPU/InstructionsImpl/CALLE.cpp | 4 ++-- src/Emulator/Core/CPU/InstructionsImpl/CALLGR.cpp | 4 ++-- src/Emulator/Core/CPU/InstructionsImpl/CALLL.cpp | 4 ++-- src/Emulator/Core/CPU/InstructionsImpl/CCRF.cpp | 2 ++ src/Emulator/Core/CPU/InstructionsImpl/CMP.cpp | 4 +++- src/Emulator/Core/CPU/InstructionsImpl/COVF.cpp | 2 ++ src/Emulator/Core/CPU/InstructionsImpl/CUDF.cpp | 2 ++ src/Emulator/Core/CPU/InstructionsImpl/DEC.cpp | 6 ++---- src/Emulator/Core/CPU/InstructionsImpl/DIV.cpp | 5 ++--- src/Emulator/Core/CPU/InstructionsImpl/HALT.cpp | 2 ++ src/Emulator/Core/CPU/InstructionsImpl/HID.cpp | 7 ++----- src/Emulator/Core/CPU/InstructionsImpl/INC.cpp | 6 ++---- src/Emulator/Core/CPU/InstructionsImpl/INTR.cpp | 4 ++-- src/Emulator/Core/CPU/InstructionsImpl/JME.cpp | 4 ++-- src/Emulator/Core/CPU/InstructionsImpl/JMGR.cpp | 4 ++-- src/Emulator/Core/CPU/InstructionsImpl/JML.cpp | 4 ++-- src/Emulator/Core/CPU/InstructionsImpl/JMP.cpp | 4 ++-- src/Emulator/Core/CPU/InstructionsImpl/LOIVT.cpp | 6 +++--- src/Emulator/Core/CPU/InstructionsImpl/MOV.cpp | 2 ++ src/Emulator/Core/CPU/InstructionsImpl/MUL.cpp | 4 +++- src/Emulator/Core/CPU/InstructionsImpl/OR.cpp | 5 +++-- src/Emulator/Core/CPU/InstructionsImpl/POP.cpp | 4 ++-- src/Emulator/Core/CPU/InstructionsImpl/PUSH.cpp | 3 +-- src/Emulator/Core/CPU/InstructionsImpl/READ.cpp | 4 +++- src/Emulator/Core/CPU/InstructionsImpl/SHFL.cpp | 4 ++-- src/Emulator/Core/CPU/InstructionsImpl/SHFR.cpp | 6 +++--- src/Emulator/Core/CPU/InstructionsImpl/SUB.cpp | 4 +++- src/Emulator/Core/CPU/InstructionsImpl/WRITE.cpp | 4 ++-- src/Emulator/Core/CPU/Interrupts/InterruptHandler.cpp | 3 ++- src/Emulator/Core/CPU/Interrupts/ReservedInterrupts.hpp | 2 +- src/Emulator/Core/CPU/OperandsEvaluation.cpp | 2 ++ src/Emulator/Core/CPU/Stack.cpp | 2 ++ src/Emulator/Core/MemoryController/IMemoryController.hpp | 1 - src/Emulator/Core/MemoryController/MemoryControllerST.hpp | 2 ++ src/Emulator/Main/ExceptionHandling.cpp | 7 +++++-- src/Emulator/Main/Main.cpp | 3 ++- src/Logger/Logger.hpp | 4 ++-- src/Pog/Tokenizer.hpp | 6 +++--- src/pch.hpp | 1 + tests/Modular/AssemblerCore/Parser/Tokens.cpp | 2 +- 66 files changed, 139 insertions(+), 105 deletions(-) diff --git a/src/Assembler/CMakeLists.txt b/src/Assembler/CMakeLists.txt index 653566a4..cb0ab849 100644 --- a/src/Assembler/CMakeLists.txt +++ b/src/Assembler/CMakeLists.txt @@ -25,7 +25,13 @@ target_include_directories( target_link_libraries( assembler-core PUBLIC - fmt::fmt argparse::argparse pog eternal::eternal pch pog hpool + fmt::fmt + argparse::argparse + eternal::eternal + re2::re2 + pch + pog + hpool ) add_executable(hcasm Main/Main.cpp) diff --git a/src/Assembler/Core/BinaryTransformer.cpp b/src/Assembler/Core/BinaryTransformer.cpp index 9499da93..81e9656b 100644 --- a/src/Assembler/Core/BinaryTransformer.cpp +++ b/src/Assembler/Core/BinaryTransformer.cpp @@ -1,3 +1,6 @@ +#include +#include + #include #include #include @@ -5,6 +8,7 @@ #include #include + HyperCPU::OperandTypes HCAsm::BinaryTransformer::DetermineOperandTypes(Operand& op1, Operand& op2) { Op1T tp1; Op2T tp2; diff --git a/src/Assembler/Core/BinaryTransformer.hpp b/src/Assembler/Core/BinaryTransformer.hpp index 509e3986..3cbe1c81 100644 --- a/src/Assembler/Core/BinaryTransformer.hpp +++ b/src/Assembler/Core/BinaryTransformer.hpp @@ -1,10 +1,9 @@ #pragma once +#include + #include #include -#include - -#include namespace HCAsm { diff --git a/src/Assembler/Core/Compiler.cpp b/src/Assembler/Core/Compiler.cpp index af302deb..3df0b693 100644 --- a/src/Assembler/Core/Compiler.cpp +++ b/src/Assembler/Core/Compiler.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -7,8 +8,6 @@ #include #include -#include - using HyperCPU::LogLevel; diff --git a/src/Assembler/Core/Compiler.hpp b/src/Assembler/Core/Compiler.hpp index 332f8c10..1886a8ee 100644 --- a/src/Assembler/Core/Compiler.hpp +++ b/src/Assembler/Core/Compiler.hpp @@ -1,17 +1,14 @@ #pragma once +#include #include #include #include -#include -#include #include #include #include -#include - #include diff --git a/src/Assembler/Core/ModeNameAssoc.hpp b/src/Assembler/Core/ModeNameAssoc.hpp index 0359d1de..6ae6d56d 100644 --- a/src/Assembler/Core/ModeNameAssoc.hpp +++ b/src/Assembler/Core/ModeNameAssoc.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include diff --git a/src/Assembler/Core/OpcodeNameAssoc.hpp b/src/Assembler/Core/OpcodeNameAssoc.hpp index 5679d136..0d7705f6 100644 --- a/src/Assembler/Core/OpcodeNameAssoc.hpp +++ b/src/Assembler/Core/OpcodeNameAssoc.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include diff --git a/src/Assembler/Core/Parsers.cpp b/src/Assembler/Core/Parsers.cpp index a1f2c3dd..b2f8f144 100644 --- a/src/Assembler/Core/Parsers.cpp +++ b/src/Assembler/Core/Parsers.cpp @@ -1,9 +1,10 @@ +#include +#include + #include #include #include -#include - using HCAsm::Value; Value HCAsm::ParseOperand1(pog::Parser& parser, std::vector>&& args) { diff --git a/src/Assembler/Core/RegNameAssoc.hpp b/src/Assembler/Core/RegNameAssoc.hpp index 97687a2c..698261d5 100644 --- a/src/Assembler/Core/RegNameAssoc.hpp +++ b/src/Assembler/Core/RegNameAssoc.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include diff --git a/src/Assembler/Core/StatementCompilers.cpp b/src/Assembler/Core/StatementCompilers.cpp index bab3aaa8..3f0fa948 100644 --- a/src/Assembler/Core/StatementCompilers.cpp +++ b/src/Assembler/Core/StatementCompilers.cpp @@ -1,4 +1,6 @@ #include +#include + #include #include #include diff --git a/src/Assembler/Core/Tokenizers.cpp b/src/Assembler/Core/Tokenizers.cpp index 3054ec97..024f1a18 100644 --- a/src/Assembler/Core/Tokenizers.cpp +++ b/src/Assembler/Core/Tokenizers.cpp @@ -1,3 +1,6 @@ +#include +#include + #include using HCAsm::Value; diff --git a/src/Assembler/Main/Main.cpp b/src/Assembler/Main/Main.cpp index 723737fd..320960b8 100644 --- a/src/Assembler/Main/Main.cpp +++ b/src/Assembler/Main/Main.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -5,7 +6,6 @@ #include #include #include -#include #include #include diff --git a/src/BacktraceProvider/BacktraceProvider.cpp b/src/BacktraceProvider/BacktraceProvider.cpp index e4e8b5c2..da889253 100644 --- a/src/BacktraceProvider/BacktraceProvider.cpp +++ b/src/BacktraceProvider/BacktraceProvider.cpp @@ -1,6 +1,8 @@ #ifdef HCPU_ENABLE_LIBUNWIND #define UNW_LOCAL_ONLY +#include + #include #include diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index be38747e..798be99a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,7 +11,7 @@ add_library( pog INTERFACE Pog/Pog.hpp ) -target_precompile_headers(pog INTERFACE pog.hpp) +target_precompile_headers(pog INTERFACE Pog/Pog.hpp) target_include_directories( pog INTERFACE diff --git a/src/Emulator/Core/CPU/CPU.cpp b/src/Emulator/Core/CPU/CPU.cpp index 6bb4aecb..db773b45 100644 --- a/src/Emulator/Core/CPU/CPU.cpp +++ b/src/Emulator/Core/CPU/CPU.cpp @@ -1,12 +1,12 @@ -#include "Core/CPU/Instructions/Opcodes.hpp" -#include "Logger/Logger.hpp" #include #include -#include +#include #include +#include #include + HyperCPU::CPU::CPU(std::uint16_t core_count, std::uint64_t mem_size, char* binary, std::uint64_t binary_size) : mem_controller(dynamic_cast(new MemoryControllerST(mem_size, this))), logger(LogLevel::ERROR), diff --git a/src/Emulator/Core/CPU/CPU.hpp b/src/Emulator/Core/CPU/CPU.hpp index 18cb9d01..b39538e0 100644 --- a/src/Emulator/Core/CPU/CPU.hpp +++ b/src/Emulator/Core/CPU/CPU.hpp @@ -1,6 +1,5 @@ #pragma once -#include "Logger/Logger.hpp" #include #include @@ -8,7 +7,8 @@ #include #include #include -#include +#include + #define DECLARE_INSTR(name) void Exec##name(const IInstruction& instr, OperandContainer op1, OperandContainer op2) diff --git a/src/Emulator/Core/CPU/Decoders/IDecoder.hpp b/src/Emulator/Core/CPU/Decoders/IDecoder.hpp index 786506e2..0bf51c75 100644 --- a/src/Emulator/Core/CPU/Decoders/IDecoder.hpp +++ b/src/Emulator/Core/CPU/Decoders/IDecoder.hpp @@ -1,10 +1,5 @@ #pragma once -#include -#include -#include - - namespace HyperCPU { struct IInstruction; diff --git a/src/Emulator/Core/CPU/Decoders/StdDecoder.cpp b/src/Emulator/Core/CPU/Decoders/StdDecoder.cpp index 1b3ec5ae..85ffe863 100644 --- a/src/Emulator/Core/CPU/Decoders/StdDecoder.cpp +++ b/src/Emulator/Core/CPU/Decoders/StdDecoder.cpp @@ -6,9 +6,6 @@ #include #include #include -#include -#include -#include #include #include diff --git a/src/Emulator/Core/CPU/Decoders/StdDecoder.hpp b/src/Emulator/Core/CPU/Decoders/StdDecoder.hpp index 3741a485..8d8c8deb 100644 --- a/src/Emulator/Core/CPU/Decoders/StdDecoder.hpp +++ b/src/Emulator/Core/CPU/Decoders/StdDecoder.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include diff --git a/src/Emulator/Core/CPU/IO/Simple.hpp b/src/Emulator/Core/CPU/IO/Simple.hpp index 1e5e892e..78cc4e57 100644 --- a/src/Emulator/Core/CPU/IO/Simple.hpp +++ b/src/Emulator/Core/CPU/IO/Simple.hpp @@ -1,8 +1,6 @@ #include #include -#include - namespace HyperCPU { class SimpleIOImpl { diff --git a/src/Emulator/Core/CPU/InstructionsImpl/ADC.cpp b/src/Emulator/Core/CPU/InstructionsImpl/ADC.cpp index fb8e257a..3ff6a94f 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/ADC.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/ADC.cpp @@ -1,10 +1,12 @@ +#include + #include #include -#include #include #include + using namespace HyperALU; void HyperCPU::CPU::ExecADC(const IInstruction& instr, OperandContainer op1, OperandContainer op2) { diff --git a/src/Emulator/Core/CPU/InstructionsImpl/ADD.cpp b/src/Emulator/Core/CPU/InstructionsImpl/ADD.cpp index 832db1aa..df52d676 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/ADD.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/ADD.cpp @@ -1,7 +1,8 @@ +#include + #include #include -#include #include #include diff --git a/src/Emulator/Core/CPU/InstructionsImpl/AND.cpp b/src/Emulator/Core/CPU/InstructionsImpl/AND.cpp index 99dffd26..2ed7d08b 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/AND.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/AND.cpp @@ -1,9 +1,10 @@ +#include + #include #include -#include #include -#include + using namespace HyperALU; diff --git a/src/Emulator/Core/CPU/InstructionsImpl/ANDN.cpp b/src/Emulator/Core/CPU/InstructionsImpl/ANDN.cpp index ba6ee7f7..a04dea5f 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/ANDN.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/ANDN.cpp @@ -1,9 +1,10 @@ +#include + #include #include -#include #include -#include + using namespace HyperALU; diff --git a/src/Emulator/Core/CPU/InstructionsImpl/BSWAP.cpp b/src/Emulator/Core/CPU/InstructionsImpl/BSWAP.cpp index de2e73d4..c8931954 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/BSWAP.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/BSWAP.cpp @@ -1,10 +1,7 @@ -#include +#include #include -#include -#include -#include #include diff --git a/src/Emulator/Core/CPU/InstructionsImpl/CALL.cpp b/src/Emulator/Core/CPU/InstructionsImpl/CALL.cpp index 1276cb66..7751e572 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/CALL.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/CALL.cpp @@ -1,11 +1,11 @@ +#include + #include #include #include -#include #include -#include #pragma GCC diagnostic push diff --git a/src/Emulator/Core/CPU/InstructionsImpl/CALLE.cpp b/src/Emulator/Core/CPU/InstructionsImpl/CALLE.cpp index 3077884e..c70a1dc7 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/CALLE.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/CALLE.cpp @@ -1,11 +1,11 @@ +#include + #include #include #include -#include #include -#include #pragma GCC diagnostic push diff --git a/src/Emulator/Core/CPU/InstructionsImpl/CALLGR.cpp b/src/Emulator/Core/CPU/InstructionsImpl/CALLGR.cpp index 7bc060e7..08e0aeac 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/CALLGR.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/CALLGR.cpp @@ -1,11 +1,11 @@ +#include + #include #include #include -#include #include -#include #pragma GCC diagnostic push diff --git a/src/Emulator/Core/CPU/InstructionsImpl/CALLL.cpp b/src/Emulator/Core/CPU/InstructionsImpl/CALLL.cpp index f3d2cae3..49414d2e 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/CALLL.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/CALLL.cpp @@ -1,11 +1,11 @@ +#include + #include #include #include -#include #include -#include #pragma GCC diagnostic push diff --git a/src/Emulator/Core/CPU/InstructionsImpl/CCRF.cpp b/src/Emulator/Core/CPU/InstructionsImpl/CCRF.cpp index 3b935b48..7415e6f9 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/CCRF.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/CCRF.cpp @@ -1,3 +1,5 @@ +#include + #include diff --git a/src/Emulator/Core/CPU/InstructionsImpl/CMP.cpp b/src/Emulator/Core/CPU/InstructionsImpl/CMP.cpp index b9f4f733..dccafe94 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/CMP.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/CMP.cpp @@ -1,5 +1,7 @@ -#include "Core/CPU/ALU.hpp" +#include + #include +#include #include diff --git a/src/Emulator/Core/CPU/InstructionsImpl/COVF.cpp b/src/Emulator/Core/CPU/InstructionsImpl/COVF.cpp index ffc0f62e..41e6787f 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/COVF.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/COVF.cpp @@ -1,3 +1,5 @@ +#include + #include diff --git a/src/Emulator/Core/CPU/InstructionsImpl/CUDF.cpp b/src/Emulator/Core/CPU/InstructionsImpl/CUDF.cpp index 3b7ee95b..2f4b1cc5 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/CUDF.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/CUDF.cpp @@ -1,3 +1,5 @@ +#include + #include diff --git a/src/Emulator/Core/CPU/InstructionsImpl/DEC.cpp b/src/Emulator/Core/CPU/InstructionsImpl/DEC.cpp index 1c50ab7e..4561af36 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/DEC.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/DEC.cpp @@ -1,8 +1,6 @@ -#include +#include -#include -#include -#include +#include #pragma GCC diagnostic push diff --git a/src/Emulator/Core/CPU/InstructionsImpl/DIV.cpp b/src/Emulator/Core/CPU/InstructionsImpl/DIV.cpp index 9165ba8d..4b76b4ab 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/DIV.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/DIV.cpp @@ -1,10 +1,9 @@ +#include + #include #include #include -#include -#include -#include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" diff --git a/src/Emulator/Core/CPU/InstructionsImpl/HALT.cpp b/src/Emulator/Core/CPU/InstructionsImpl/HALT.cpp index a445b20c..beb5ae16 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/HALT.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/HALT.cpp @@ -1,3 +1,5 @@ +#include + #include diff --git a/src/Emulator/Core/CPU/InstructionsImpl/HID.cpp b/src/Emulator/Core/CPU/InstructionsImpl/HID.cpp index 0cb55eb0..4fd64f49 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/HID.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/HID.cpp @@ -1,11 +1,8 @@ +#include + #include #include -#include -#include -#include - - #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" diff --git a/src/Emulator/Core/CPU/InstructionsImpl/INC.cpp b/src/Emulator/Core/CPU/InstructionsImpl/INC.cpp index c7aea1da..1c5c7298 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/INC.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/INC.cpp @@ -1,8 +1,6 @@ -#include +#include -#include -#include -#include +#include #pragma GCC diagnostic push diff --git a/src/Emulator/Core/CPU/InstructionsImpl/INTR.cpp b/src/Emulator/Core/CPU/InstructionsImpl/INTR.cpp index 36101995..71cb764e 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/INTR.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/INTR.cpp @@ -1,9 +1,9 @@ +#include + #include #include -#include #include -#include #pragma GCC diagnostic push diff --git a/src/Emulator/Core/CPU/InstructionsImpl/JME.cpp b/src/Emulator/Core/CPU/InstructionsImpl/JME.cpp index 8c41e6e2..fec958f7 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/JME.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/JME.cpp @@ -1,11 +1,11 @@ +#include + #include #include #include -#include #include -#include #pragma GCC diagnostic push diff --git a/src/Emulator/Core/CPU/InstructionsImpl/JMGR.cpp b/src/Emulator/Core/CPU/InstructionsImpl/JMGR.cpp index b8d45b30..6878635c 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/JMGR.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/JMGR.cpp @@ -1,11 +1,11 @@ +#include + #include #include #include -#include #include -#include #pragma GCC diagnostic push diff --git a/src/Emulator/Core/CPU/InstructionsImpl/JML.cpp b/src/Emulator/Core/CPU/InstructionsImpl/JML.cpp index 6ac2e875..4e7419c5 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/JML.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/JML.cpp @@ -1,11 +1,11 @@ +#include + #include #include #include -#include #include -#include #pragma GCC diagnostic push diff --git a/src/Emulator/Core/CPU/InstructionsImpl/JMP.cpp b/src/Emulator/Core/CPU/InstructionsImpl/JMP.cpp index 44ce86ae..d70137bd 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/JMP.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/JMP.cpp @@ -1,11 +1,11 @@ +#include + #include #include #include -#include #include -#include #pragma GCC diagnostic push diff --git a/src/Emulator/Core/CPU/InstructionsImpl/LOIVT.cpp b/src/Emulator/Core/CPU/InstructionsImpl/LOIVT.cpp index 5f34e3c4..4e5a35c0 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/LOIVT.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/LOIVT.cpp @@ -1,9 +1,9 @@ -#include "Exit.hpp" +#include + #include +#include -#include #include -#include #pragma GCC diagnostic push diff --git a/src/Emulator/Core/CPU/InstructionsImpl/MOV.cpp b/src/Emulator/Core/CPU/InstructionsImpl/MOV.cpp index 393723ab..6a094886 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/MOV.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/MOV.cpp @@ -1,3 +1,5 @@ +#include + #include #include diff --git a/src/Emulator/Core/CPU/InstructionsImpl/MUL.cpp b/src/Emulator/Core/CPU/InstructionsImpl/MUL.cpp index 6ca04a7c..ac710823 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/MUL.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/MUL.cpp @@ -1,12 +1,14 @@ +#include + #include #include #include -#include #include #include + using namespace HyperALU; void HyperCPU::CPU::ExecMUL(const IInstruction& instr, OperandContainer op1, OperandContainer op2) { diff --git a/src/Emulator/Core/CPU/InstructionsImpl/OR.cpp b/src/Emulator/Core/CPU/InstructionsImpl/OR.cpp index bfd92f8e..460d87f3 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/OR.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/OR.cpp @@ -1,9 +1,10 @@ +#include + #include #include -#include #include -#include + using namespace HyperALU; diff --git a/src/Emulator/Core/CPU/InstructionsImpl/POP.cpp b/src/Emulator/Core/CPU/InstructionsImpl/POP.cpp index afca8c11..ae74fb70 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/POP.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/POP.cpp @@ -1,7 +1,7 @@ +#include + #include -#include #include -#include #include diff --git a/src/Emulator/Core/CPU/InstructionsImpl/PUSH.cpp b/src/Emulator/Core/CPU/InstructionsImpl/PUSH.cpp index 29b8f5db..b2fcd438 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/PUSH.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/PUSH.cpp @@ -1,7 +1,6 @@ -#include +#include #include -#include #include #include #include diff --git a/src/Emulator/Core/CPU/InstructionsImpl/READ.cpp b/src/Emulator/Core/CPU/InstructionsImpl/READ.cpp index 04a478a2..7de544ac 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/READ.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/READ.cpp @@ -1,9 +1,11 @@ +#include + #include -#include #include #include #include + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" diff --git a/src/Emulator/Core/CPU/InstructionsImpl/SHFL.cpp b/src/Emulator/Core/CPU/InstructionsImpl/SHFL.cpp index 90df5fe3..6969e596 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/SHFL.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/SHFL.cpp @@ -1,8 +1,8 @@ +#include + #include -#include #include -#include void HyperCPU::CPU::ExecSHFL(const IInstruction& instr, OperandContainer op1, OperandContainer op2) { diff --git a/src/Emulator/Core/CPU/InstructionsImpl/SHFR.cpp b/src/Emulator/Core/CPU/InstructionsImpl/SHFR.cpp index b51c0f4d..c97183ac 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/SHFR.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/SHFR.cpp @@ -1,9 +1,9 @@ -#include "Core/CPU/Instructions/Flags.hpp" +#include + +#include #include -#include #include -#include void HyperCPU::CPU::ExecSHFR(const IInstruction& instr, OperandContainer op1, OperandContainer op2) { diff --git a/src/Emulator/Core/CPU/InstructionsImpl/SUB.cpp b/src/Emulator/Core/CPU/InstructionsImpl/SUB.cpp index 79841f99..f3a8f8e1 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/SUB.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/SUB.cpp @@ -1,10 +1,12 @@ +#include + #include #include -#include #include #include + using namespace HyperALU; void HyperCPU::CPU::ExecSUB(const IInstruction& instr, OperandContainer op1, OperandContainer op2) { diff --git a/src/Emulator/Core/CPU/InstructionsImpl/WRITE.cpp b/src/Emulator/Core/CPU/InstructionsImpl/WRITE.cpp index de573bb6..ae38520e 100644 --- a/src/Emulator/Core/CPU/InstructionsImpl/WRITE.cpp +++ b/src/Emulator/Core/CPU/InstructionsImpl/WRITE.cpp @@ -1,11 +1,11 @@ -#include +#include #include -#include #include #include #include + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" diff --git a/src/Emulator/Core/CPU/Interrupts/InterruptHandler.cpp b/src/Emulator/Core/CPU/Interrupts/InterruptHandler.cpp index 6df98bd9..da072fb3 100644 --- a/src/Emulator/Core/CPU/Interrupts/InterruptHandler.cpp +++ b/src/Emulator/Core/CPU/Interrupts/InterruptHandler.cpp @@ -1,6 +1,7 @@ +#include + #include #include -#include #include #include #include diff --git a/src/Emulator/Core/CPU/Interrupts/ReservedInterrupts.hpp b/src/Emulator/Core/CPU/Interrupts/ReservedInterrupts.hpp index 8e6d1ae8..e1b48bfb 100644 --- a/src/Emulator/Core/CPU/Interrupts/ReservedInterrupts.hpp +++ b/src/Emulator/Core/CPU/Interrupts/ReservedInterrupts.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace HyperCPU { diff --git a/src/Emulator/Core/CPU/OperandsEvaluation.cpp b/src/Emulator/Core/CPU/OperandsEvaluation.cpp index b775f9b0..03e6e8af 100644 --- a/src/Emulator/Core/CPU/OperandsEvaluation.cpp +++ b/src/Emulator/Core/CPU/OperandsEvaluation.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include diff --git a/src/Emulator/Core/CPU/Stack.cpp b/src/Emulator/Core/CPU/Stack.cpp index 66908ddc..739a6932 100644 --- a/src/Emulator/Core/CPU/Stack.cpp +++ b/src/Emulator/Core/CPU/Stack.cpp @@ -1,3 +1,5 @@ +#include + #include #include diff --git a/src/Emulator/Core/MemoryController/IMemoryController.hpp b/src/Emulator/Core/MemoryController/IMemoryController.hpp index b8a7d6ab..22b36a34 100644 --- a/src/Emulator/Core/MemoryController/IMemoryController.hpp +++ b/src/Emulator/Core/MemoryController/IMemoryController.hpp @@ -2,7 +2,6 @@ #include -#include #define mem_ctlr_assert(expr) \ do { \ diff --git a/src/Emulator/Core/MemoryController/MemoryControllerST.hpp b/src/Emulator/Core/MemoryController/MemoryControllerST.hpp index 51577c28..5beeb57c 100644 --- a/src/Emulator/Core/MemoryController/MemoryControllerST.hpp +++ b/src/Emulator/Core/MemoryController/MemoryControllerST.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include diff --git a/src/Emulator/Main/ExceptionHandling.cpp b/src/Emulator/Main/ExceptionHandling.cpp index e57bc395..83308482 100644 --- a/src/Emulator/Main/ExceptionHandling.cpp +++ b/src/Emulator/Main/ExceptionHandling.cpp @@ -1,8 +1,11 @@ -#include "Logger/Logger.hpp" +#include + #include
    +#include -#include #include +#include + [[noreturn]] void HyperCPU::ThrowError(CPU* cpu, std::string message) { cpu->GetLogger().Log(HyperCPU::LogLevel::ERROR, message); diff --git a/src/Emulator/Main/Main.cpp b/src/Emulator/Main/Main.cpp index 7eb6036f..7271cf04 100644 --- a/src/Emulator/Main/Main.cpp +++ b/src/Emulator/Main/Main.cpp @@ -1,8 +1,9 @@ +#include + #include #include #include #include -#include #include #include diff --git a/src/Logger/Logger.hpp b/src/Logger/Logger.hpp index 4856de89..6a5d0cac 100644 --- a/src/Logger/Logger.hpp +++ b/src/Logger/Logger.hpp @@ -3,9 +3,9 @@ #include #include -#include -#include #include +#include + namespace HyperCPU { enum class LogLevel : std::uint_fast8_t { DEBUG = 0, INFO = 1, WARNING = 2, ERROR = 3 }; diff --git a/src/Pog/Tokenizer.hpp b/src/Pog/Tokenizer.hpp index 9530b94e..d61265b0 100644 --- a/src/Pog/Tokenizer.hpp +++ b/src/Pog/Tokenizer.hpp @@ -17,9 +17,9 @@ #define debug_tokenizer(...) #endif -#include -#include -#include +#include +#include +#include namespace pog { diff --git a/src/pch.hpp b/src/pch.hpp index 55951a1d..07388a4e 100644 --- a/src/pch.hpp +++ b/src/pch.hpp @@ -35,3 +35,4 @@ #include #include #include +#include diff --git a/tests/Modular/AssemblerCore/Parser/Tokens.cpp b/tests/Modular/AssemblerCore/Parser/Tokens.cpp index b64e2378..9e3cfbd2 100644 --- a/tests/Modular/AssemblerCore/Parser/Tokens.cpp +++ b/tests/Modular/AssemblerCore/Parser/Tokens.cpp @@ -1,4 +1,4 @@ -#include "pog/line_spec.h" +#include #include TEST_F(ASM_PARSER_TEST, TOKEN_STRING) { From 975296b5d119a990b6838f3ed1a06ba9d197932e Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Wed, 30 Apr 2025 12:48:40 +0400 Subject: [PATCH 20/25] Move pog tests into the project, integrate with the build system, fix workflows --- .github/workflows/distro-ci.yml | 3 + .../workflows/run-tests-feature-branch.yml | 21 +- .github/workflows/testing.yml | 37 +- tests/CMakeLists.txt | 1 + tests/Pog/Automaton.cpp | 180 ++++ tests/Pog/CMakeLists.txt | 44 + tests/Pog/FilterView.cpp | 83 ++ tests/Pog/Grammar.cpp | 187 ++++ tests/Pog/Item.cpp | 236 +++++ tests/Pog/Parser.cpp | 919 ++++++++++++++++++ tests/Pog/ParsingTable.cpp | 12 + tests/Pog/PogTests.cpp | 7 + tests/Pog/Precedence.cpp | 81 ++ tests/Pog/Rule.cpp | 92 ++ tests/Pog/RuleBuilder.cpp | 68 ++ tests/Pog/State.cpp | 278 ++++++ tests/Pog/Symbol.cpp | 57 ++ tests/Pog/Token.cpp | 81 ++ tests/Pog/TokenBuilder.cpp | 145 +++ tests/Pog/Tokenizer.cpp | 295 ++++++ tests/Pog/Utils.cpp | 62 ++ 21 files changed, 2865 insertions(+), 24 deletions(-) create mode 100644 tests/Pog/Automaton.cpp create mode 100644 tests/Pog/CMakeLists.txt create mode 100644 tests/Pog/FilterView.cpp create mode 100644 tests/Pog/Grammar.cpp create mode 100644 tests/Pog/Item.cpp create mode 100644 tests/Pog/Parser.cpp create mode 100644 tests/Pog/ParsingTable.cpp create mode 100644 tests/Pog/PogTests.cpp create mode 100644 tests/Pog/Precedence.cpp create mode 100644 tests/Pog/Rule.cpp create mode 100644 tests/Pog/RuleBuilder.cpp create mode 100644 tests/Pog/State.cpp create mode 100644 tests/Pog/Symbol.cpp create mode 100644 tests/Pog/Token.cpp create mode 100644 tests/Pog/TokenBuilder.cpp create mode 100644 tests/Pog/Tokenizer.cpp create mode 100644 tests/Pog/Utils.cpp diff --git a/.github/workflows/distro-ci.yml b/.github/workflows/distro-ci.yml index 8bd06dae..19d86f9a 100644 --- a/.github/workflows/distro-ci.yml +++ b/.github/workflows/distro-ci.yml @@ -51,3 +51,6 @@ jobs: -D CMAKE_CXX_COMPILER:STRING=g++ \ -D CMAKE_CXX_COMPILER:STRING=gcc \ --detect + + cd build/Release && ctest && cd ../.. + cd build/Debug && ctest && cd ../.. \ No newline at end of file diff --git a/.github/workflows/run-tests-feature-branch.yml b/.github/workflows/run-tests-feature-branch.yml index ccd33ed9..18fb38ba 100644 --- a/.github/workflows/run-tests-feature-branch.yml +++ b/.github/workflows/run-tests-feature-branch.yml @@ -14,27 +14,32 @@ jobs: uses: actions/checkout@v4 with: submodules: 'true' + + - name: Build dependencies with Conan + run: | + python3 scripts/build.py -i \ + --profiles-detection - name: Build and test with GCC on Release profile run: | - python3 scripts/build.py -icb \ + python3 scripts/build.py -cb \ --config Release \ --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=g++ \ - -D CMAKE_C_COMPILER:STRING=gcc \ - --profiles-detection - cd build/Release && ctest -V + -D CMAKE_C_COMPILER:STRING=gcc + + cd build/Release && ctest -V && cd ../.. python3 scripts/build.py -r --config Release --build-dir build - name: Build and test with LLVM on Release profile run: | - python3 scripts/build.py -icb \ + python3 scripts/build.py -cb \ --config Release \ --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=clang++-19 \ - -D CMAKE_C_COMPILER:STRING=clang-19 \ - --profiles-detection - cd build/Release && ctest -V + -D CMAKE_C_COMPILER:STRING=clang-19 + + cd build/Release && ctest -V && cd ../.. python3 scripts/build.py -r --config Release --build-dir build - name: Cleanup (Conan cache + leftovers in build) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index e14defe9..7d2da610 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -16,49 +16,54 @@ jobs: uses: actions/checkout@v4 with: submodules: 'true' + + - name: Build all dependencies + run: | + python3 scripts/build.py -i \ + --profiles-detection - name: Build and test with GCC on Debug profile run: | - python3 scripts/build.py -icb \ + python3 scripts/build.py -cb \ --config Debug \ --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=g++ \ - -D CMAKE_C_COMPILER:STRING=gcc \ - --profiles-detection - cd build/Debug && ctest -V + -D CMAKE_C_COMPILER:STRING=gcc + + cd build/Debug && ctest -V && cd ../.. python3 scripts/build.py -r --config Debug --build-dir build - name: Build and test with GCC on Release profile run: | - python3 scripts/build.py -icb \ + python3 scripts/build.py -cb \ --config Release \ --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=g++ \ - -D CMAKE_C_COMPILER:STRING=gcc \ - --profiles-detection - cd build/Release && ctest -V + -D CMAKE_C_COMPILER:STRING=gcc + + cd build/Release && ctest -V && cd ../.. python3 scripts/build.py -r --config Release --build-dir build - name: Build and test with LLVM on Debug profile run: | - python3 scripts/build.py -icb \ + python3 scripts/build.py -cb \ --config Debug \ --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=clang++-19 \ - -D CMAKE_C_COMPILER:STRING=clang-19 \ - --profiles-detection - cd build/Debug && ctest -V + -D CMAKE_C_COMPILER:STRING=clang-19 + + cd build/Debug && ctest -V && cd ../.. python3 scripts/build.py -r --config Debug --build-dir build - name: Build and test with LLVM on Release profile run: | - python3 scripts/build.py -icb \ + python3 scripts/build.py -cb \ --config Release \ --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=clang++-19 \ - -D CMAKE_C_COMPILER:STRING=clang-19 \ - --profiles-detection - cd build/Release && ctest -V + -D CMAKE_C_COMPILER:STRING=clang-19 + + cd build/Release && ctest -V && cd ../.. python3 scripts/build.py -r --config Release --build-dir build - name: Cleanup (Conan cache + leftovers in build) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a3c1f56f..bb1fd19a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,4 +6,5 @@ include(GoogleTest) add_subdirectory(Integration) add_subdirectory(Modular) +add_subdirectory(Pog) add_subdirectory(Python/Buildsystem) diff --git a/tests/Pog/Automaton.cpp b/tests/Pog/Automaton.cpp new file mode 100644 index 00000000..02eaca91 --- /dev/null +++ b/tests/Pog/Automaton.cpp @@ -0,0 +1,180 @@ +#include + +#include + +#include + +using namespace pog; + +class TestAutomaton : public ::testing::Test +{ +public: + using SetupGrammarTuple = std::tuple; + + TestAutomaton() : grammar() {} + + template + State new_state(const Args&... args) + { + State state; + _new_state(state, args...); + return state; + } + + void _new_state(State& state, const std::string& lhs, const std::vector& left_rhs, const std::vector& right_rhs) + { + _add_item_to_state(state, lhs, left_rhs, right_rhs); + } + + template + void _new_state(State& state, const std::string& lhs, const std::vector& left_rhs, const std::vector& right_rhs, const Args&... args) + { + _add_item_to_state(state, lhs, left_rhs, right_rhs); + _new_state(state, args...); + } + + void _add_item_to_state(State& state, const std::string& lhs, const std::vector& left_rhs, const std::vector& right_rhs) + { + auto lhs_sym = grammar.add_symbol(SymbolKind::Nonterminal, lhs); + auto sym_transform = [this](const auto& name) { + return grammar.add_symbol(std::islower(name[0]) ? SymbolKind::Terminal : SymbolKind::Nonterminal, name); + }; + + std::vector*> left_rhs_syms(left_rhs.size()); + std::vector*> right_rhs_syms(right_rhs.size()); + std::vector*> rhs_syms(left_rhs.size() + right_rhs.size()); + + std::transform(left_rhs.begin(), left_rhs.end(), left_rhs_syms.begin(), sym_transform); + std::transform(right_rhs.begin(), right_rhs.end(), right_rhs_syms.begin(), sym_transform); + std::copy(left_rhs_syms.begin(), left_rhs_syms.end(), rhs_syms.begin()); + std::copy(right_rhs_syms.begin(), right_rhs_syms.end(), rhs_syms.begin() + left_rhs.size()); + + Rule* rule = nullptr; + for (auto& r : grammar.get_rules()) + { + bool rhs_equal = std::equal(r->get_rhs().begin(), r->get_rhs().end(), rhs_syms.begin(), rhs_syms.end(), [](const auto* sym1, const auto* sym2) { + return sym1->get_index() == sym2->get_index(); + }); + if (r->get_lhs() == lhs_sym && rhs_equal) + { + rule = r.get(); + break; + } + } + + if (!rule) + rule = grammar.add_rule(lhs_sym, rhs_syms, [](auto&&...) -> int { return 0; }); + + state.add_item(Item{rule, left_rhs.size()}); + } + + Grammar grammar; +}; + +TEST_F(TestAutomaton, +AddState) { + auto state = new_state( + "S", std::vector{}, std::vector{"a", "S", "b"}, + "S", std::vector{}, std::vector{} + ); + + Automaton a(&grammar); + auto result = a.add_state(std::move(state)); + + EXPECT_TRUE(result.second); + EXPECT_EQ(result.first->to_string(), "S -> <*> a S b\nS -> <*> "); +} + +TEST_F(TestAutomaton, +AddStateUnique) { + auto state1 = new_state( + "S", std::vector{}, std::vector{"a", "S", "b"}, + "S", std::vector{}, std::vector{} + ); + auto state2 = new_state( + "S", std::vector{"a"}, std::vector{"S", "b"} + ); + + Automaton a(&grammar); + auto result1 = a.add_state(std::move(state1)); + auto result2 = a.add_state(std::move(state2)); + + EXPECT_TRUE(result1.second); + EXPECT_EQ(result1.first->to_string(), "S -> <*> a S b\nS -> <*> "); + EXPECT_TRUE(result2.second); + EXPECT_EQ(result2.first->to_string(), "S -> a <*> S b"); +} + +TEST_F(TestAutomaton, +AddStateDuplicate) { + auto state1 = new_state( + "S", std::vector{}, std::vector{"a", "S", "b"}, + "S", std::vector{}, std::vector{} + ); + auto state2 = new_state( + "S", std::vector{}, std::vector{"a", "S", "b"}, + "S", std::vector{}, std::vector{} + ); + + Automaton a(&grammar); + auto result1 = a.add_state(std::move(state1)); + auto result2 = a.add_state(std::move(state2)); + + EXPECT_TRUE(result1.second); + EXPECT_EQ(result1.first->to_string(), "S -> <*> a S b\nS -> <*> "); + EXPECT_FALSE(result2.second); + EXPECT_EQ(result1.first, result2.first); +} + +TEST_F(TestAutomaton, +GetState) { + auto state = new_state( + "S", std::vector{}, std::vector{"a", "S", "b"}, + "S", std::vector{}, std::vector{} + ); + + Automaton a(&grammar); + a.add_state(std::move(state)); + + state = new_state( + "S", std::vector{}, std::vector{"a", "S", "b"}, + "S", std::vector{}, std::vector{} + ); + + EXPECT_EQ(*a.get_state(0), state); +} + +TEST_F(TestAutomaton, +Closure) { + auto state = new_state( + "S", std::vector{"A"}, std::vector{"S", "b"} + ); + new_state( + "A", std::vector{"a"}, std::vector{"A"}, + "A", std::vector{}, std::vector{} + ); + + Automaton a(&grammar); + a.closure(state); + + EXPECT_EQ(state.to_string(), "S -> A <*> S b\nS -> <*> A S b\nA -> <*> a A\nA -> <*> "); +} + +TEST_F(TestAutomaton, +ConstructStates) { + grammar.set_start_symbol(grammar.add_symbol(SymbolKind::Nonterminal, "S")); + new_state( + "S", std::vector{}, std::vector{"a", "S", "b"}, + "S", std::vector{}, std::vector{} + ); + + Automaton a(&grammar); + a.construct_states(); + + EXPECT_EQ(a.get_states().size(), 5u); + EXPECT_EQ(a.get_states()[0]->to_string(), "@start -> <*> S @end\nS -> <*> a S b\nS -> <*> "); + EXPECT_EQ(a.get_states()[1]->to_string(), "@start -> S <*> @end"); + EXPECT_EQ(a.get_states()[2]->to_string(), "S -> a <*> S b\nS -> <*> a S b\nS -> <*> "); + EXPECT_EQ(a.get_states()[3]->to_string(), "S -> a S <*> b"); + EXPECT_EQ(a.get_states()[4]->to_string(), "S -> a S b <*>"); +} diff --git a/tests/Pog/CMakeLists.txt b/tests/Pog/CMakeLists.txt new file mode 100644 index 00000000..c0cc31fe --- /dev/null +++ b/tests/Pog/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 3.25) + +add_executable( + pog_testing + Automaton.cpp + FilterView.cpp + Grammar.cpp + Item.cpp + Parser.cpp + ParsingTable.cpp + PogTests.cpp + Precedence.cpp + Rule.cpp + RuleBuilder.cpp + State.cpp + Symbol.cpp + Token.cpp + TokenBuilder.cpp + Tokenizer.cpp + Utils.cpp +) + +target_link_libraries( + pog_testing + PRIVATE + emulator-core + assembler-core + GTest::gtest + pthread + pch + pog +) +target_include_directories( + pog_testing + PRIVATE + ${CMAKE_SOURCE_DIR}/tests +) + +gtest_add_tests( + TARGET + pog_testing + EXTRA_ARGS + --gtest_brief=1 +) diff --git a/tests/Pog/FilterView.cpp b/tests/Pog/FilterView.cpp new file mode 100644 index 00000000..5791d4de --- /dev/null +++ b/tests/Pog/FilterView.cpp @@ -0,0 +1,83 @@ +#include + +#include + +class TestFilterView : public ::testing::Test {}; + +TEST_F(TestFilterView, +EmptyContainer) { + std::vector v; + + FilterView fv(v.begin(), v.end(), [](int x) { return x & 1; }); + + std::vector actual; + for (auto x : fv) + actual.push_back(x); + + EXPECT_EQ(actual, (std::vector{})); +} + +TEST_F(TestFilterView, +BasicFilter) { + std::vector v = {0, 1, 2, 3, 4, 5, 6}; + + FilterView fv(v.begin(), v.end(), [](int x) { return x & 1; }); + + std::vector actual; + for (auto x : fv) + actual.push_back(x); + + EXPECT_EQ(actual, (std::vector{1, 3, 5})); +} + +TEST_F(TestFilterView, +SharedEnd) { + std::vector v = {0, 1, 2, 3, 4, 5}; + + FilterView fv(v.begin(), v.end(), [](int x) { return x & 1; }); + + std::vector actual; + for (auto x : fv) + actual.push_back(x); + + EXPECT_EQ(actual, (std::vector{1, 3, 5})); +} + +TEST_F(TestFilterView, +SharedBegin) { + std::vector v = {1, 2, 3, 4, 5, 6}; + + FilterView fv(v.begin(), v.end(), [](int x) { return x & 1; }); + + std::vector actual; + for (auto x : fv) + actual.push_back(x); + + EXPECT_EQ(actual, (std::vector{1, 3, 5})); +} + +TEST_F(TestFilterView, +SharedBeginAndEnd) { + std::vector v = {1, 2, 3, 4, 5}; + + FilterView fv(v.begin(), v.end(), [](int x) { return x & 1; }); + + std::vector actual; + for (auto x : fv) + actual.push_back(x); + + EXPECT_EQ(actual, (std::vector{1, 3, 5})); +} + +TEST_F(TestFilterView, +NoElements) { + std::vector v = {0, 1, 2, 3, 4, 5, 6}; + + FilterView fv(v.begin(), v.end(), [](int x) { return x > 10; }); + + std::vector actual; + for (auto x : fv) + actual.push_back(x); + + EXPECT_EQ(actual, (std::vector{})); +} diff --git a/tests/Pog/Grammar.cpp b/tests/Pog/Grammar.cpp new file mode 100644 index 00000000..0c1239e9 --- /dev/null +++ b/tests/Pog/Grammar.cpp @@ -0,0 +1,187 @@ +#include + +#include + +using namespace pog; + +class TestGrammar : public ::testing::Test {}; + +TEST_F(TestGrammar, +DefaultGrammar) { + Grammar g; + + EXPECT_EQ(g.get_symbols().size(), 2u); + EXPECT_EQ(g.get_rules().size(), 0u); + + EXPECT_TRUE(g.get_symbols()[0]->is_nonterminal()); + EXPECT_EQ(g.get_symbols()[0]->get_name(), "@start"); + + EXPECT_TRUE(g.get_symbols()[1]->is_end()); + EXPECT_EQ(g.get_symbols()[1]->get_name(), "@end"); + + EXPECT_EQ(g.get_end_of_input_symbol(), g.get_symbols()[1].get()); + EXPECT_EQ(g.get_start_rule(), nullptr); +} + +TEST_F(TestGrammar, +AddSymbol) { + Grammar g; + + g.add_symbol(SymbolKind::Nonterminal, "A"); + g.add_symbol(SymbolKind::Nonterminal, "B"); + + EXPECT_EQ(g.get_symbols().size(), 4u); +} + +TEST_F(TestGrammar, +AddSymbolDuplicate) { + Grammar g; + + g.add_symbol(SymbolKind::Nonterminal, "A"); + g.add_symbol(SymbolKind::Terminal, "A"); + + EXPECT_EQ(g.get_symbols().size(), 3u); +} + +TEST_F(TestGrammar, +GetSymbol) { + Grammar g; + + auto sym = g.add_symbol(SymbolKind::Nonterminal, "A"); + EXPECT_EQ(g.get_symbol("A"), sym); + EXPECT_EQ(g.get_symbol("B"), nullptr); +} + +TEST_F(TestGrammar, +AddRule) { + Grammar g; + + auto s1 = g.add_symbol(SymbolKind::Nonterminal, "A"); + auto s2 = g.add_symbol(SymbolKind::Nonterminal, "B"); + auto s3 = g.add_symbol(SymbolKind::Nonterminal, "C"); + + auto result = g.add_rule(s1, std::vector*>{s2, s3}, [](auto&&...) -> int { return 0; }); + EXPECT_EQ(g.get_rules().size(), 1u); + EXPECT_EQ(result->get_lhs(), s1); + EXPECT_EQ(result->get_rhs(), (std::vector*>{s2, s3})); +} + +TEST_F(TestGrammar, +GetRulesOfSymbol) { + Grammar g; + + auto s1 = g.add_symbol(SymbolKind::Nonterminal, "A"); + auto s2 = g.add_symbol(SymbolKind::Nonterminal, "B"); + auto s3 = g.add_symbol(SymbolKind::Nonterminal, "C"); + + auto r1 = g.add_rule(s1, std::vector*>{s2, s3}, [](auto&&...) -> int { return 0; }); + auto r2 = g.add_rule(s1, std::vector*>{}, [](auto&&...) -> int { return 0; }); + auto r3 = g.add_rule(s2, std::vector*>{s1, s3}, [](auto&&...) -> int { return 0; }); + + EXPECT_EQ(g.get_rules().size(), 3u); + EXPECT_EQ(g.get_rules_of_symbol(s1), (std::vector*>{r1, r2})); + EXPECT_EQ(g.get_rules_of_symbol(s2), (std::vector*>{r3})); + EXPECT_EQ(g.get_rules_of_symbol(s3), (std::vector*>{})); +} + +TEST_F(TestGrammar, +GetRulesWithSymbol) { + Grammar g; + + auto s1 = g.add_symbol(SymbolKind::Nonterminal, "A"); + auto s2 = g.add_symbol(SymbolKind::Nonterminal, "B"); + auto s3 = g.add_symbol(SymbolKind::Nonterminal, "C"); + + auto r1 = g.add_rule(s1, std::vector*>{s2, s3}, [](auto&&...) -> int { return 0; }); + auto r2 = g.add_rule(s1, std::vector*>{}, [](auto&&...) -> int { return 0; }); + auto r3 = g.add_rule(s2, std::vector*>{s1, s3}, [](auto&&...) -> int { return 0; }); + static_cast(r2); + + EXPECT_EQ(g.get_rules().size(), 3u); + EXPECT_EQ(g.get_rules_with_symbol(s1), (std::vector*>{r3})); + EXPECT_EQ(g.get_rules_with_symbol(s2), (std::vector*>{r1})); + EXPECT_EQ(g.get_rules_with_symbol(s3), (std::vector*>{r1, r3})); +} + +TEST_F(TestGrammar, +StartSymbol) { + Grammar g; + + auto s = g.add_symbol(SymbolKind::Nonterminal, "A"); + g.set_start_symbol(s); + + EXPECT_EQ(g.get_rules().size(), 1u); + EXPECT_EQ(g.get_rules()[0]->to_string(), "@start -> A @end"); + EXPECT_EQ(g.get_rules()[0].get(), g.get_start_rule()); +} + +TEST_F(TestGrammar, +Empty) { + Grammar g; + + auto a = g.add_symbol(SymbolKind::Terminal, "a"); + auto b = g.add_symbol(SymbolKind::Terminal, "b"); + auto S = g.add_symbol(SymbolKind::Nonterminal, "S"); + auto A = g.add_symbol(SymbolKind::Nonterminal, "A"); + + g.add_rule(S, std::vector*>{a, S, b}, [](auto&&...) -> int { return 0; }); + g.add_rule(S, std::vector*>{a, b}, [](auto&&...) -> int { return 0; }); + g.add_rule(A, std::vector*>{a}, [](auto&&...) -> int { return 0; }); + g.add_rule(A, std::vector*>{}, [](auto&&...) -> int { return 0; }); + + EXPECT_FALSE(g.empty(a)); + EXPECT_FALSE(g.empty(b)); + EXPECT_FALSE(g.empty(S)); + EXPECT_TRUE(g.empty(A)); + + EXPECT_TRUE(g.empty(std::vector*>{A, A, A})); + EXPECT_FALSE(g.empty(std::vector*>{A, A, A, S})); +} + +TEST_F(TestGrammar, +First) { + Grammar g; + + auto a = g.add_symbol(SymbolKind::Terminal, "a"); + auto b = g.add_symbol(SymbolKind::Terminal, "b"); + auto S = g.add_symbol(SymbolKind::Nonterminal, "S"); + auto A = g.add_symbol(SymbolKind::Nonterminal, "A"); + + g.add_rule(S, std::vector*>{a, S, b}, [](auto&&...) -> int { return 0; }); + g.add_rule(S, std::vector*>{a, b}, [](auto&&...) -> int { return 0; }); + g.add_rule(S, std::vector*>{b}, [](auto&&...) -> int { return 0; }); + g.add_rule(A, std::vector*>{a}, [](auto&&...) -> int { return 0; }); + g.add_rule(A, std::vector*>{}, [](auto&&...) -> int { return 0; }); + + EXPECT_EQ(g.first(a), (std::unordered_set*>{a})); + EXPECT_EQ(g.first(b), (std::unordered_set*>{b})); + EXPECT_EQ(g.first(S), (std::unordered_set*>{a, b})); + EXPECT_EQ(g.first(A), (std::unordered_set*>{a})); + + EXPECT_EQ(g.first(std::vector*>{A, A, A}), (std::unordered_set*>{a})); + EXPECT_EQ(g.first(std::vector*>{A, A, A, S}), (std::unordered_set*>{a, b})); + EXPECT_EQ(g.first(std::vector*>{b, A, A, S}), (std::unordered_set*>{b})); +} + +TEST_F(TestGrammar, +Follow) { + Grammar g; + + auto a = g.add_symbol(SymbolKind::Terminal, "a"); + auto b = g.add_symbol(SymbolKind::Terminal, "b"); + auto S = g.add_symbol(SymbolKind::Nonterminal, "S"); + auto A = g.add_symbol(SymbolKind::Nonterminal, "A"); + + g.add_rule(S, std::vector*>{a, S, b}, [](auto&&...) -> int { return 0; }); + g.add_rule(S, std::vector*>{a, b}, [](auto&&...) -> int { return 0; }); + g.add_rule(S, std::vector*>{b}, [](auto&&...) -> int { return 0; }); + g.add_rule(A, std::vector*>{a, A}, [](auto&&...) -> int { return 0; }); + g.add_rule(A, std::vector*>{}, [](auto&&...) -> int { return 0; }); + + EXPECT_EQ(g.follow(S), (std::unordered_set*>{b})); + EXPECT_EQ(g.follow(A), (std::unordered_set*>{})); + + // To test out caching + EXPECT_EQ(g.follow(S), (std::unordered_set*>{b})); + EXPECT_EQ(g.follow(A), (std::unordered_set*>{})); +} diff --git a/tests/Pog/Item.cpp b/tests/Pog/Item.cpp new file mode 100644 index 00000000..ea16c301 --- /dev/null +++ b/tests/Pog/Item.cpp @@ -0,0 +1,236 @@ +#include + +#include + +using namespace pog; + +class TestItem : public ::testing::Test {}; + +TEST_F(TestItem, +SimpleItem) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, std::vector>&&) -> int { return 0; }); + Item item(&rule); + + EXPECT_EQ(item.get_rule(), &rule); + EXPECT_EQ(item.get_read_pos(), 0u); + EXPECT_EQ(item.get_previous_symbol(), nullptr); + EXPECT_EQ(item.get_read_symbol(), &s2); + + EXPECT_FALSE(item.is_kernel()); + EXPECT_FALSE(item.is_final()); + EXPECT_FALSE(item.is_accepting()); +} + +TEST_F(TestItem, +SimpleItemWithReadPosShifted) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, std::vector>&&) -> int { return 0; }); + Item item(&rule, 1); + + EXPECT_EQ(item.get_rule(), &rule); + EXPECT_EQ(item.get_read_pos(), 1u); + EXPECT_EQ(item.get_previous_symbol(), &s2); + EXPECT_EQ(item.get_read_symbol(), &s3); + + EXPECT_TRUE(item.is_kernel()); + EXPECT_FALSE(item.is_final()); + EXPECT_FALSE(item.is_accepting()); +} + +TEST_F(TestItem, +SimpleItemWithReadPosAtTheEnd) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, std::vector>&&) -> int { return 0; }); + Item item(&rule, 2); + + EXPECT_EQ(item.get_rule(), &rule); + EXPECT_EQ(item.get_read_pos(), 2u); + EXPECT_EQ(item.get_previous_symbol(), &s3); + EXPECT_EQ(item.get_read_symbol(), nullptr); + + EXPECT_TRUE(item.is_kernel()); + EXPECT_TRUE(item.is_final()); + EXPECT_FALSE(item.is_accepting()); +} + +TEST_F(TestItem, +SimpleAcceptingItem) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::End, "3"); + Rule rule(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, std::vector>&&) -> int { return 0; }); + Item item(&rule, 1); + + EXPECT_EQ(item.get_rule(), &rule); + EXPECT_EQ(item.get_read_pos(), 1u); + EXPECT_EQ(item.get_previous_symbol(), &s2); + EXPECT_EQ(item.get_read_symbol(), &s3); + + EXPECT_TRUE(item.is_kernel()); + EXPECT_FALSE(item.is_final()); + EXPECT_TRUE(item.is_accepting()); +} + +TEST_F(TestItem, +LeftSideWithoutReadSymbol) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, std::vector>&&) -> int { return 0; }); + Item item(&rule, 1); + + EXPECT_EQ(item.get_left_side_without_read_symbol(), std::vector*>{&s2}); +} + +TEST_F(TestItem, +LeftSideWithoutReadSymbolWhenReadPosAtStart) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, std::vector>&&) -> int { return 0; }); + Item item(&rule, 0); + + EXPECT_EQ(item.get_left_side_without_read_symbol(), std::vector*>{}); +} + +TEST_F(TestItem, +RightSideWithoutReadSymbol) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, std::vector>&&) -> int { return 0; }); + Item item(&rule, 0); + + EXPECT_EQ(item.get_right_side_without_read_symbol(), std::vector*>{&s3}); +} + +TEST_F(TestItem, +RightSideWithoutReadSymbolWhenNothingIsReturned) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, std::vector>&&) -> int { return 0; }); + Item item(&rule, 1); + + EXPECT_EQ(item.get_right_side_without_read_symbol(), std::vector*>{}); +} + +TEST_F(TestItem, +Step) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, std::vector>&&) -> int { return 0; }); + Item item(&rule, 0); + + item.step(); + EXPECT_EQ(item.get_read_pos(), 1u); + item.step(); + EXPECT_EQ(item.get_read_pos(), 2u); + item.step(); + EXPECT_EQ(item.get_read_pos(), 2u); +} + +TEST_F(TestItem, +StepBack) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, std::vector>&&) -> int { return 0; }); + Item item(&rule, 2); + + item.step_back(); + EXPECT_EQ(item.get_read_pos(), 1u); + item.step_back(); + EXPECT_EQ(item.get_read_pos(), 0u); + item.step_back(); + EXPECT_EQ(item.get_read_pos(), 0u); +} + +TEST_F(TestItem, +ToString) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, std::vector>&&) -> int { return 0; }); + Item item(&rule, 1); + + EXPECT_EQ(item.to_string(), "1 -> 2 <*> 3"); +} + +TEST_F(TestItem, +EpsilonToString) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Rule rule(42, &s1, std::vector*>{}, [](Parser&, std::vector>&&) -> int { return 0; }); + Item item(&rule, 0); + + EXPECT_EQ(item.to_string(), "1 -> <*> "); +} + +TEST_F(TestItem, +Equality) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule1(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, std::vector>&&) -> int { return 0; }); + Rule rule2(42, &s1, std::vector*>{&s2}, [](Parser&, std::vector>&&) -> int { return 0; }); + Rule rule3(43, &s1, std::vector*>{&s2, &s3}, [](Parser&, std::vector>&&) -> int { return 0; }); + Item item1(&rule1, 1); + Item item2(&rule2, 1); + Item item3(&rule3, 1); + + EXPECT_TRUE(item1 == item2); + EXPECT_FALSE(item1 == item3); + + EXPECT_FALSE(item1 != item2); + EXPECT_TRUE(item1 != item3); + + item1.step(); + EXPECT_FALSE(item1 == item2); +} + +TEST_F(TestItem, +LessThanDifferentRule) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule1(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, std::vector>&&) -> int { return 0; }); + Rule rule2(41, &s1, std::vector*>{&s2}, [](Parser&, std::vector>&&) -> int { return 0; }); + Item item1(&rule1, 0); + Item item2(&rule2, 0); + + EXPECT_FALSE(item1 < item2); +} + +TEST_F(TestItem, +LessThanDifferentReadPos) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule1(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, std::vector>&&) -> int { return 0; }); + Rule rule2(42, &s1, std::vector*>{&s2}, [](Parser&, std::vector>&&) -> int { return 0; }); + Item item1(&rule1, 0); + Item item2(&rule2, 1); + + EXPECT_FALSE(item1 < item2); +} + +TEST_F(TestItem, +LessThanWithKernelItemPriority) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule1(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, std::vector>&&) -> int { return 0; }); + Rule rule2(41, &s1, std::vector*>{&s2}, [](Parser&, std::vector>&&) -> int { return 0; }); + Item item1(&rule1, 1); + Item item2(&rule2, 0); + + EXPECT_TRUE(item1 < item2); +} diff --git a/tests/Pog/Parser.cpp b/tests/Pog/Parser.cpp new file mode 100644 index 00000000..ed119afe --- /dev/null +++ b/tests/Pog/Parser.cpp @@ -0,0 +1,919 @@ +#include + +#include + +using namespace pog; +using namespace ::testing; + +class TestParser : public ::testing::Test {}; + +TEST_F(TestParser, +RepeatingAs) { + Parser p; + + p.token("a").symbol("a"); + + p.set_start_symbol("A"); + p.rule("A") + .production("A", "a", [](Parser&, auto&& args) { + return 1 + args[0].value; + }) + .production("a", [](Parser&, auto&&) { + return 1; + }); + EXPECT_TRUE(p.prepare()); + + std::string input1("a"); + auto result = p.parse(input1); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), 1); + + std::string input2("aaaa"); + result = p.parse(input2); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), 4); + + try + { + std::string input3("aa aaa"); + p.parse(input3); + FAIL() << "Expected syntax error"; + } + catch (const SyntaxError& e) + { + EXPECT_STREQ(e.what(), "Syntax error: Unknown symbol on input, expected one of @end, a"); + } +} + +TEST_F(TestParser, +RepeatingAsWithIgnoringWhitespaces) { + Parser p; + + p.token(R"(\s+)"); + p.token("a").symbol("a"); + + p.set_start_symbol("A"); + p.rule("A") + .production("A", "a", [](Parser&, auto&& args) { + return 1 + args[0].value; + }) + .production("a", [](Parser&, auto&&) { + return 1; + }); + EXPECT_TRUE(p.prepare()); + + std::string input1("a"); + auto result = p.parse(input1); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), 1); + + std::string input2("aaaa"); + result = p.parse(input2); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), 4); + + std::string input3("aa aaa"); + result = p.parse(input3); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), 5); +} + +TEST_F(TestParser, +SameNumberOfAsAndBs) { + Parser p; + + p.token("a").symbol("a"); + p.token("b").symbol("b"); + + p.set_start_symbol("S"); + p.rule("S") + .production("a", "S", "b", [](Parser&, auto&& args) { + return 1 + args[1].value; + }) + .production("a", "b", [](Parser&, auto&&) { + return 1; + }); + EXPECT_TRUE(p.prepare()); + + std::string input1("ab"); + auto result = p.parse(input1); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), 1); + + std::string input2("aaabbb"); + result = p.parse(input2); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), 3); + + try + { + std::string input3("aabbb"); + p.parse(input3); + FAIL() << "Expected syntax error"; + } + catch (const SyntaxError& e) + { + EXPECT_STREQ(e.what(), "Syntax error: Unexpected b, expected one of @end"); + } + + try + { + std::string input4("aaabb"); + p.parse(input4); + FAIL() << "Expected syntax error"; + } + catch (const SyntaxError& e) + { + EXPECT_STREQ(e.what(), "Syntax error: Unexpected @end, expected one of b"); + } +} + +TEST_F(TestParser, +SymbolDescriptionInErrorMessages) { + Parser p; + + p.token("a").symbol("a"); + p.token("b").symbol("b").description("symbol_b"); + + p.set_start_symbol("S"); + p.rule("S") + .production("a", "S", "b", [](Parser&, auto&& args) { + return 1 + args[1].value; + }) + .production("a", "b", [](Parser&, auto&&) { + return 1; + }); + EXPECT_TRUE(p.prepare()); + + std::string input1("ab"); + auto result = p.parse(input1); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), 1); + + std::string input2("aaabbb"); + result = p.parse(input2); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), 3); + + try + { + std::string input3("aabbb"); + p.parse(input3); + FAIL() << "Expected syntax error"; + } + catch (const SyntaxError& e) + { + EXPECT_STREQ(e.what(), "Syntax error: Unexpected symbol_b, expected one of @end"); + } + + try + { + std::string input4("aaabb"); + p.parse(input4); + FAIL() << "Expected syntax error"; + } + catch (const SyntaxError& e) + { + EXPECT_STREQ(e.what(), "Syntax error: Unexpected @end, expected one of symbol_b"); + } +} + +TEST_F(TestParser, +LalrButNotLrNorNqlalr) { + Parser p; + + p.token("a").symbol("a"); + p.token("b").symbol("b"); + p.token("c").symbol("c"); + p.token("d").symbol("d"); + p.token("g").symbol("g"); + + p.set_start_symbol("S"); + p.rule("S") + .production("a", "g", "d") + .production("a", "A", "c") + .production("b", "A", "d") + .production("b", "g", "c"); + p.rule("A") + .production("B"); + p.rule("B") + .production("g"); + EXPECT_TRUE(p.prepare()); + + std::string input("agc"); + auto result = p.parse(input); + EXPECT_TRUE(result); +} + +TEST_F(TestParser, +Precedence) { + Parser p; + + p.token(R"(\s+)"); + p.token(R"(\+)").symbol("+").precedence(0, Associativity::Left); + p.token(R"(-)").symbol("-").precedence(0, Associativity::Left); + p.token(R"(\*)").symbol("*").precedence(1, Associativity::Left); + p.token("[0-9]+").symbol("int").action([](std::string_view str) { + return std::stoi(std::string{str}); + }); + + p.set_start_symbol("E"); + p.rule("E") + .production("E", "+", "E", [](Parser&, auto&& args) { + return args[0].value + args[2].value; + }) + .production("E", "-", "E", [](Parser&, auto&& args) { + return args[0].value - args[2].value; + }) + .production("E", "*", "E", [](Parser&, auto&& args) { + return args[0].value * args[2].value; + }) + .production("-", "E", [](Parser&, auto&& args) { + return -args[1].value; + }).precedence(2, Associativity::Right) + .production("int", [](Parser&, auto&& args) { + return args[0].value; + }); + EXPECT_TRUE(p.prepare()); + + std::string input1("2 + 3 * 4 + 5"); + auto result = p.parse(input1); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), 19); + + std::string input2("-5 - 3 - -10"); + result = p.parse(input2); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), 2); + + std::string input3("5 + -3 * 10"); + result = p.parse(input3); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), -25); +} + +TEST_F(TestParser, +Conflicts1) { + Parser p; + + p.token("a").symbol("a"); + + p.set_start_symbol("sequence"); + p.rule("sequence") + .production("sequence", "a") + .production("maybea") + .production(); + p.rule("maybea") + .production("a") + .production(); + + auto report = p.prepare(); + EXPECT_FALSE(report); + EXPECT_EQ(report.number_of_issues(), 3u); + EXPECT_EQ(report.to_string(), + "Shift-reduce conflict of symbol 'a' and rule 'sequence -> ' in state 0\n" + "Reduce-reduce conflict of rule 'sequence -> ' and rule 'maybea -> ' in state 0\n" + "Shift-reduce conflict of symbol 'a' and rule 'maybea -> ' in state 0" + ); +} + +TEST_F(TestParser, +Conflicts2) { + Parser p; + + p.token("b").symbol("b"); + p.token("c").symbol("c"); + + p.set_start_symbol("Y"); + p.rule("Y") + .production("c", "c", "Z", "b"); + p.rule("Z") + .production("c", "Z", "b") + .production("c", "Z") + .production(); + + auto report = p.prepare(); + EXPECT_FALSE(report); + EXPECT_EQ(report.number_of_issues(), 1u); + EXPECT_EQ(report.to_string(), "Shift-reduce conflict of symbol 'b' and rule 'Z -> c Z' in state 6"); +} + +TEST_F(TestParser, +Conflicts3) { + Parser> p; + + p.token("\\(").symbol("("); + p.token("\\)").symbol(")"); + p.token("a").symbol("a"); + + p.set_start_symbol("E"); + p.rule("E") + .production("(", "E", ")", [](Parser>&, auto&& args) { + args[1].value.push_back("E -> ( E )"); + return std::move(args[1].value); + }) + .production("PE", [](Parser>&, auto&& args) { + args[0].value.push_back("E -> PE"); + return std::move(args[0].value); + }); + p.rule("PE") + .production("(", "PE", ")", [](Parser>&, auto&& args) { + args[1].value.push_back("PE -> ( PE )"); + return std::move(args[1].value); + }) + .production("a", [](Parser>&, auto&&) { + return std::vector{"PE -> a"}; + }); + + auto report = p.prepare(); + EXPECT_FALSE(report); + EXPECT_EQ(report.number_of_issues(), 1u); + EXPECT_EQ(report.to_string(), "Shift-reduce conflict of symbol ')' and rule 'E -> PE' in state 6"); + + std::string input1("(((a)))"); + auto result = p.parse(input1); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), (std::vector{ + "PE -> a", + "PE -> ( PE )", + "PE -> ( PE )", + "PE -> ( PE )", + "E -> PE" + })); +} + +TEST_F(TestParser, +ResolveConflictWithPrecedence) { + Parser> p; + + p.token("\\(").symbol("("); + p.token("\\)").symbol(")").precedence(0, Associativity::Left); + p.token("a").symbol("a"); + + p.set_start_symbol("E"); + p.rule("E") + .production("(", "E", ")", [](Parser>&, auto&& args) { + args[1].value.push_back("E -> ( E )"); + return std::move(args[1].value); + }) + .production("PE", [](Parser>&, auto&& args) { + args[0].value.push_back("E -> PE"); + return std::move(args[0].value); + }).precedence(1, Associativity::Left); + p.rule("PE") + .production("(", "PE", ")", [](Parser>&, auto&& args) { + args[1].value.push_back("PE -> ( PE )"); + return std::move(args[1].value); + }) + .production("a", [](Parser>&, auto&&) { + return std::vector{"PE -> a"}; + }); + EXPECT_TRUE(p.prepare()); + + std::string input1("(((a)))"); + auto result = p.parse(input1); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), (std::vector{ + "PE -> a", + "E -> PE", + "E -> ( E )", + "E -> ( E )", + "E -> ( E )" + })); +} + +TEST_F(TestParser, +EndTokenAction) { + int end_call_count = 0; + Parser p; + + p.token("a").symbol("a"); + p.end_token().action([&](std::string_view) { + end_call_count++; + return 0; + }); + + p.set_start_symbol("A"); + p.rule("A") + .production("A", "a", [](Parser&, auto&& args) { + return 1 + args[0].value; + }) + .production("a", [](Parser&, auto&&) { + return 1; + }); + EXPECT_TRUE(p.prepare()); + + std::string input1("aaaa"); + auto result = p.parse(input1); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), 4); + EXPECT_EQ(end_call_count, 1); +} + +TEST_F(TestParser, +TokenActionsCalledOnce) { + int a_call_count = 0; + Parser p; + + p.token("a").symbol("a").action([&](std::string_view) { + a_call_count++; + return 0; + }); + + p.set_start_symbol("A"); + p.rule("A") + .production("B", [](Parser&, auto&& args) { + return args[0].value; + }); + p.rule("B") + .production("A", "a", [](Parser&, auto&& args) { + return 1 + args[0].value; + }) + .production("a", [](Parser&, auto&&) { + return 1; + }); + EXPECT_TRUE(p.prepare()); + + std::string input1("aaaa"); + auto result = p.parse(input1); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), 4); + EXPECT_EQ(a_call_count, 4); +} + +TEST_F(TestParser, +MultistateTokenizer) { + using Value = std::variant< + std::string, + std::pair, + std::vector> + >; + + Parser p; + std::string built_string; + + p.token("\\s+"); + p.token("=").symbol("="); + p.token("[a-zA-Z_][a-zA-Z0-9_]*").symbol("id").action([](std::string_view str) -> Value { + return std::string{str}; + }); + + p.token(R"(")").enter_state("string").action([&](std::string_view) -> Value { + built_string.clear(); + return {}; + }); + p.token(R"(\\n)").states("string").action([&](std::string_view) -> Value { + built_string += '\n'; + return {}; + }); + p.token(R"(\\t)").states("string").action([&](std::string_view) -> Value { + built_string += '\t'; + return {}; + }); + p.token(R"(\\r)").states("string").action([&](std::string_view) -> Value { + built_string += '\r'; + return {}; + }); + p.token(R"(\\x[0-9a-fA-F]{2})").states("string").action([&](std::string_view str) -> Value { + auto s = std::string{str.begin() + 2, str.end()}; + built_string += static_cast(std::stoi(s, nullptr, 16)); + return {}; + }); + p.token(R"([^\\"]+)").states("string").action([&](std::string_view str) -> Value { + built_string += str; + return {}; + }); + p.token(R"(")").states("string").enter_state("@default").symbol("string_literal").action([&](std::string_view) -> Value { + return built_string; + }); + + p.set_start_symbol("root"); + p.rule("root") + .production("strings", [](Parser&, auto&& args) -> Value { + return std::move(args[0].value); + }) + .production([](Parser&, auto&&) -> Value { + return std::vector>{}; + }); + p.rule("strings") + .production("strings", "string", [](Parser&, auto&& args) -> Value { + std::get>>(args[0].value).push_back( + std::get>(args[1].value) + ); + return std::move(args[0].value); + }) + .production("string", [](Parser&, auto&& args) -> Value { + return std::vector>{ + std::get>(args[0].value) + }; + }); + p.rule("string") + .production("id", "=", "string_literal", [](Parser&, auto&& args) -> Value { + return std::make_pair( + std::get(args[0].value), + std::get(args[2].value) + ); + }); + EXPECT_TRUE(p.prepare()); + + std::string input( + "abc = \"xyz\"\n" + "x = \"ab\\n\\t\\r\\x20cd\"" + ); + auto result = p.parse(input); + EXPECT_TRUE(result); + auto strings = std::get>>(result.value()); + EXPECT_EQ(strings.size(), 2u); + EXPECT_THAT(strings[0], Pair(Eq("abc"), Eq("xyz"))); + EXPECT_THAT(strings[1], Pair(Eq("x"), Eq("ab\n\t\r cd"))); +} + +TEST_F(TestParser, +MidruleActionsToCheckRedefinition) { + using Value = std::variant< + int, + std::string + >; + + Parser p; + + p.token("\\s+"); + p.token("=").symbol("="); + p.token(";").symbol(";"); + p.token("\\{").symbol("{"); + p.token("\\}").symbol("}"); + p.token("function").symbol("function"); + p.token("var").symbol("var"); + p.token("[_a-zA-Z][_a-zA-Z0-9]*").symbol("id").action([](std::string_view str) -> Value { + return std::string{str}; + }); + p.token("[0-9]+").symbol("num").action([](std::string_view str) -> Value { + return std::stoi(std::string{str}); + }); + + std::unordered_set defs, redefs; + + p.set_start_symbol("prog"); + p.rule("prog") + .production("funcs") + .production(); + p.rule("funcs") + .production("funcs", "func") + .production("func"); + p.rule("func") + .production( + "function", "id", [&](Parser&, auto&& args) -> Value { + auto func_name = std::get(args[1].value); + auto [itr, inserted] = defs.insert(func_name); + if (!inserted) + redefs.insert(func_name); + return {}; + }, + "{", "func_body", "}" + ); + p.rule("func_body") + .production("stmts") + .production(); + p.rule("stmts") + .production("stmts", "stmt") + .production("stmt"); + p.rule("stmt") + .production( + "var", "id", [&](Parser&, auto&& args) -> Value { + auto var_name = std::get(args[1].value); + auto [itr, inserted] = defs.insert(var_name); + if (!inserted) + redefs.insert(var_name); + return {}; + }, + "=", "num", ";" + ); + EXPECT_TRUE(p.prepare()); + + std::string input1( +R"(function x { + var y = 5; + var z = 10; +})" + ); + auto result1 = p.parse(input1); + EXPECT_TRUE(result1); + EXPECT_EQ(defs, (std::unordered_set{"x", "y", "z"})); + EXPECT_EQ(redefs, (std::unordered_set{})); + + defs.clear(); + redefs.clear(); + + std::string input2( +R"(function x { + var y = 5; + var x = 10; +})" + ); + auto result2 = p.parse(input2); + EXPECT_TRUE(result2); + EXPECT_EQ(defs, (std::unordered_set{"x", "y"})); + EXPECT_EQ(redefs, (std::unordered_set{"x"})); + + defs.clear(); + redefs.clear(); + + std::string input3( +R"(function x { + var y = 5; + var z = 10; +} + +function z { + var a = 1; +})" + ); + auto result3 = p.parse(input3); + EXPECT_TRUE(result3); + EXPECT_EQ(defs, (std::unordered_set{"x", "y", "z", "a"})); + EXPECT_EQ(redefs, (std::unordered_set{"z"})); +} + +TEST_F(TestParser, +InputStreamStackManipulation) { + static std::vector input_streams = { + "10", + "include 0", + "30", + "40" + }; + + std::vector> inputs; + + Parser p; + + p.token("\\s+"); + p.token("\\+").symbol("+").precedence(1, Associativity::Left); + p.token("\\*").symbol("*").precedence(2, Associativity::Left); + p.token("include [0-9]+").action([&](std::string_view str) { + auto stream_idx = std::stoi(std::string{str.begin() + 8, str.end()}); + inputs.emplace_back(std::make_unique(input_streams[stream_idx])); + p.push_input_stream(*inputs.back().get()); + return 0; + }); + p.token("[0-9]+").symbol("number").action([](std::string_view str) { + return std::stoi(std::string{str}); + }); + p.end_token().action([&](std::string_view) { + p.pop_input_stream(); + return 0; + }); + + p.set_start_symbol("E"); + p.rule("E") + .production("E", "+", "E", [](Parser&, auto&& args) { return args[0].value + args[2].value; }) + .production("E", "*", "E", [](Parser&, auto&& args) { return args[0].value * args[2].value; }) + .production("number", [](Parser&, auto&& args) { return args[0].value; }); + + EXPECT_TRUE(p.prepare()); + + std::string input("include 1 + include 2 * include 3 + 5"); + auto result = p.parse(input); + + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), 1215); +} + +TEST_F(TestParser, +RuleActionsAccessingDataBeforeMidruleAction) { + Parser p; + + p.token("\\s+"); + p.token("a").symbol("a").action([](std::string_view) { + return 1; + }); + p.token("function").symbol("func").action([](std::string_view) { + return 10; + }); + p.token("[a-zA-Z_]+").symbol("id").action([](std::string_view) { + return 100; + }); + + std::vector all_values; + + p.set_start_symbol("S"); + p.rule("S") + .production("func", "id", [](Parser&, auto&& args) { + return args[0].value + args[1].value; + }, + "A", [&](Parser&, auto&& args) { + for (const auto& arg : args) + all_values.push_back(arg.value); + return args[2].value + args[3].value; + }); + p.rule("A") + .production("A", "a", [](Parser&, auto&& args) { return args[0].value + args[1].value; }) + .production("a", [](Parser&, auto&& args) { return args[0].value; }); + + + EXPECT_TRUE(p.prepare()); + + std::string input("function abc a a a a a"); + auto result = p.parse(input); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), 115); + EXPECT_EQ(all_values, (std::vector{10, 100, 110, 5})); +} + +TEST_F(TestParser, +MultistateTokenizerWithExplicitCalls) { + using Value = std::variant< + std::string, + std::pair, + std::vector> + >; + + Parser p; + std::string built_string; + + p.token("\\s+"); + p.token("=").symbol("="); + p.token("[a-zA-Z_][a-zA-Z0-9_]*").symbol("id").action([](std::string_view str) -> Value { + return std::string{str}; + }); + + p.token(R"(")").action([&](std::string_view) -> Value { + p.enter_tokenizer_state("string"); + built_string.clear(); + return {}; + }); + p.token(R"(\\n)").states("string").action([&](std::string_view) -> Value { + built_string += '\n'; + return {}; + }); + p.token(R"(\\t)").states("string").action([&](std::string_view) -> Value { + built_string += '\t'; + return {}; + }); + p.token(R"(\\r)").states("string").action([&](std::string_view) -> Value { + built_string += '\r'; + return {}; + }); + p.token(R"(\\x[0-9a-fA-F]{2})").states("string").action([&](std::string_view str) -> Value { + auto s = std::string{str.begin() + 2, str.end()}; + built_string += static_cast(std::stoi(s, nullptr, 16)); + return {}; + }); + p.token(R"([^\\"]+)").states("string").action([&](std::string_view str) -> Value { + built_string += str; + return {}; + }); + p.token(R"(")").states("string").symbol("string_literal").action([&](std::string_view) -> Value { + p.enter_tokenizer_state("@default"); + return built_string; + }); + + p.set_start_symbol("root"); + p.rule("root") + .production("strings", [](Parser&, auto&& args) -> Value { + return std::move(args[0].value); + }) + .production([](Parser&, auto&&) -> Value { + return std::vector>{}; + }); + p.rule("strings") + .production("strings", "string", [](Parser&, auto&& args) -> Value { + std::get>>(args[0].value).push_back( + std::get>(args[1].value) + ); + return std::move(args[0].value); + }) + .production("string", [](Parser&, auto&& args) -> Value { + return std::vector>{ + std::get>(args[0].value) + }; + }); + p.rule("string") + .production("id", "=", "string_literal", [](Parser&, auto&& args) -> Value { + return std::make_pair( + std::get(args[0].value), + std::get(args[2].value) + ); + }); + EXPECT_TRUE(p.prepare()); + + std::string input( + "abc = \"xyz\"\n" + "x = \"ab\\n\\t\\r\\x20cd\"" + ); + auto result = p.parse(input); + EXPECT_TRUE(result); + auto strings = std::get>>(result.value()); + EXPECT_EQ(strings.size(), 2u); + EXPECT_THAT(strings[0], Pair(Eq("abc"), Eq("xyz"))); + EXPECT_THAT(strings[1], Pair(Eq("x"), Eq("ab\n\t\r cd"))); +} + +TEST_F(TestParser, +EndInputInNonDefaultTokenizerState) { + Parser p; + + p.token("a").symbol("a").enter_state("special"); + p.token("b").symbol("b").enter_state("@default"); + p.end_token().states("@default", "special"); + + p.set_start_symbol("A"); + p.rule("A") + .production("A", "a", [](Parser&, auto&& args) { + return 1 + args[0].value; + }) + .production("A", "b", [](Parser&, auto&& args) { + return 1 + args[0].value; + }) + .production("a", [](Parser&, auto&&) { + return 1; + }) + .production("b", [](Parser&, auto&&) { + return 1; + }); + EXPECT_TRUE(p.prepare()); + + std::string input1("a"); + auto result = p.parse(input1); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), 1); + + std::string input2("bbbba"); + result = p.parse(input2); + EXPECT_TRUE(result); + EXPECT_EQ(result.value(), 5); + + try + { + std::string input3("bbbbab"); + p.parse(input3); + FAIL() << "Expected syntax error"; + } + catch (const SyntaxError& e) + { + EXPECT_STREQ(e.what(), "Syntax error: Unknown symbol on input, expected one of @end, a, b"); + } +} + +TEST_F(TestParser, +IncludesRelationCalulcatedCorrectlyForSameRightHandSifePrefix) { + Parser p; + + p.token("a").symbol("a"); + p.token("b").symbol("b"); + + p.set_start_symbol("S"); + p.rule("S") + .production("A"); + p.rule("A") + .production("B", "b") + .production("B"); + p.rule("B") + .production("a"); + EXPECT_TRUE(p.prepare()); + + std::string input1("a"); + auto result = p.parse(input1); + EXPECT_TRUE(result); + + std::string input2("ab"); + result = p.parse(input2); + EXPECT_TRUE(result); +} + +TEST_F(TestParser, +GlobalTokenizerAction) { + Parser p; + + std::pair location = {1, 0}; + p.global_tokenizer_action([&](std::string_view str) { + location.second += str.length(); + }); + + p.token(R"(\n)").action([&](std::string_view) { + location.first++; + location.second = 0; + return 0; + }); + p.token(R"([ \t\v\r]+)"); + p.token("a+").symbol("As"); + p.token("b{3}").symbol("Bs"); + + p.set_start_symbol("S"); + p.rule("S") + .production("S", "As") + .production("S", "Bs") + .production(); + EXPECT_TRUE(p.prepare()); + + std::string input1("aaaaa"); + auto result = p.parse(input1); + EXPECT_TRUE(result); + EXPECT_THAT(location, Pair(Eq(1), Eq(5))); + + location = {1, 0}; + std::string input2("aaa \nbbb bbb\n \n\na"); + result = p.parse(input2); + EXPECT_TRUE(result); + EXPECT_THAT(location, Pair(Eq(5), Eq(1))); +} diff --git a/tests/Pog/ParsingTable.cpp b/tests/Pog/ParsingTable.cpp new file mode 100644 index 00000000..b59db1fa --- /dev/null +++ b/tests/Pog/ParsingTable.cpp @@ -0,0 +1,12 @@ +#include + +#include + +using namespace pog; + +class TestParsingTable : public ::testing::Test {}; + +TEST_F(TestParsingTable, +AddAccept) { + //ParsingTable pt; +} diff --git a/tests/Pog/PogTests.cpp b/tests/Pog/PogTests.cpp new file mode 100644 index 00000000..5ff23f2f --- /dev/null +++ b/tests/Pog/PogTests.cpp @@ -0,0 +1,7 @@ +#include + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/Pog/Precedence.cpp b/tests/Pog/Precedence.cpp new file mode 100644 index 00000000..4bd379d3 --- /dev/null +++ b/tests/Pog/Precedence.cpp @@ -0,0 +1,81 @@ +#include + +#include + +class TestPrecedence : public ::testing::Test {}; + +using namespace pog; + +TEST_F(TestPrecedence, +Equality) { + Precedence p1{1, Associativity::Left}; + Precedence p2{1, Associativity::Left}; + Precedence p3{1, Associativity::Right}; + Precedence p4{0, Associativity::Left}; + Precedence p5{2, Associativity::Left}; + + EXPECT_EQ(p1, p2); + EXPECT_NE(p1, p3); + EXPECT_NE(p1, p4); + EXPECT_NE(p1, p5); +} + +TEST_F(TestPrecedence, +SameLevelLeftAssociative) { + EXPECT_FALSE( + (Precedence{1, Associativity::Left}) < (Precedence{1, Associativity::Left}) + ); + EXPECT_TRUE( + (Precedence{1, Associativity::Left}) > (Precedence{1, Associativity::Left}) + ); +} + +TEST_F(TestPrecedence, +SameLevelRightAssociative) { + EXPECT_TRUE( + (Precedence{1, Associativity::Right}) < (Precedence{1, Associativity::Right}) + ); + EXPECT_FALSE( + (Precedence{1, Associativity::Right}) > (Precedence{1, Associativity::Right}) + ); +} + +TEST_F(TestPrecedence, +LowerLevelLeftAssociative) { + EXPECT_TRUE( + (Precedence{0, Associativity::Left}) < (Precedence{1, Associativity::Left}) + ); + EXPECT_FALSE( + (Precedence{0, Associativity::Left}) > (Precedence{1, Associativity::Left}) + ); +} + +TEST_F(TestPrecedence, +LowerLevelRightAssociative) { + EXPECT_TRUE( + (Precedence{0, Associativity::Right}) < (Precedence{1, Associativity::Right}) + ); + EXPECT_FALSE( + (Precedence{0, Associativity::Right}) > (Precedence{1, Associativity::Right}) + ); +} + +TEST_F(TestPrecedence, +HigherLevelLeftAssociative) { + EXPECT_FALSE( + (Precedence{2, Associativity::Left}) < (Precedence{1, Associativity::Left}) + ); + EXPECT_TRUE( + (Precedence{2, Associativity::Left}) > (Precedence{1, Associativity::Left}) + ); +} + +TEST_F(TestPrecedence, +HigherLevelRightAssociative) { + EXPECT_FALSE( + (Precedence{2, Associativity::Right}) < (Precedence{1, Associativity::Right}) + ); + EXPECT_TRUE( + (Precedence{2, Associativity::Right}) > (Precedence{1, Associativity::Right}) + ); +} diff --git a/tests/Pog/Rule.cpp b/tests/Pog/Rule.cpp new file mode 100644 index 00000000..6318b568 --- /dev/null +++ b/tests/Pog/Rule.cpp @@ -0,0 +1,92 @@ +#include + +#include + +class TestRule : public ::testing::Test {}; + +using namespace pog; + +TEST_F(TestRule, +SimpleRule) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + + EXPECT_EQ(rule.get_index(), 42u); + EXPECT_EQ(rule.get_lhs(), &s1); + EXPECT_EQ(rule.get_rhs(), (std::vector*>{&s2, &s3})); + EXPECT_FALSE(rule.has_precedence()); +} + +TEST_F(TestRule, +Precedence) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + rule.set_precedence(1, Associativity::Right); + + EXPECT_EQ(rule.get_index(), 42u); + EXPECT_EQ(rule.get_lhs(), &s1); + EXPECT_EQ(rule.get_rhs(), (std::vector*>{&s2, &s3})); + EXPECT_TRUE(rule.has_precedence()); + EXPECT_EQ(rule.get_precedence(), (Precedence{1, Associativity::Right})); +} + +TEST_F(TestRule, +RightmostTerminalWhileThereIsNone) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + + EXPECT_EQ(rule.get_rightmost_terminal(), nullptr); +} + +TEST_F(TestRule, +RightmostTerminal) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Terminal, "2"); + Symbol s3(3, SymbolKind::Terminal, "3"); + Symbol s4(4, SymbolKind::Nonterminal, "4"); + Rule rule(42, &s1, std::vector*>{&s2, &s3, &s4}, [](Parser&, auto&&) -> int { return 0; }); + + EXPECT_EQ(rule.get_rightmost_terminal(), &s3); +} + +TEST_F(TestRule, +ToString) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Terminal, "2"); + Symbol s3(3, SymbolKind::Terminal, "3"); + Symbol s4(4, SymbolKind::Nonterminal, "4"); + Rule rule(42, &s1, std::vector*>{&s2, &s3, &s4}, [](Parser&, auto&&) -> int { return 0; }); + + EXPECT_EQ(rule.to_string(), "1 -> 2 3 4"); +} + +TEST_F(TestRule, +EpsilonToString) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Rule rule(42, &s1, std::vector*>{}, [](Parser&, auto&&) -> int { return 0; }); + + EXPECT_EQ(rule.to_string(), "1 -> "); +} + +TEST_F(TestRule, +Equality) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Terminal, "2"); + Symbol s3(3, SymbolKind::Terminal, "3"); + Symbol s4(4, SymbolKind::Nonterminal, "4"); + Rule rule1(42, &s1, std::vector*>{&s2, &s3, &s4}, [](Parser&, auto&&) -> int { return 0; }); + Rule rule2(42, &s1, std::vector*>{}, [](Parser&, auto&&) -> int { return 0; }); + Rule rule3(43, &s1, std::vector*>{&s2, &s3, &s4}, [](Parser&, auto&&) -> int { return 0; }); + + EXPECT_TRUE(rule1 == rule2); + EXPECT_FALSE(rule1 == rule3); + + EXPECT_FALSE(rule1 != rule2); + EXPECT_TRUE(rule1 != rule3); +} diff --git a/tests/Pog/RuleBuilder.cpp b/tests/Pog/RuleBuilder.cpp new file mode 100644 index 00000000..fe33cd29 --- /dev/null +++ b/tests/Pog/RuleBuilder.cpp @@ -0,0 +1,68 @@ +#include + +#include + +using namespace pog; + +class TestRuleBuilder : public ::testing::Test +{ +public: + Grammar grammar; +}; + +TEST_F(TestRuleBuilder, +Initialization) { + RuleBuilder rb(&grammar, "A"); + + EXPECT_EQ(grammar.get_symbols().size(), 2u); // start and end symbol + EXPECT_TRUE(grammar.get_rules().empty()); +} + +TEST_F(TestRuleBuilder, +NoProductions) { + RuleBuilder rb(&grammar, "A"); + rb.done(); + + EXPECT_EQ(grammar.get_symbols().size(), 2u); + EXPECT_TRUE(grammar.get_rules().empty()); +} + +TEST_F(TestRuleBuilder, +SingleProductionWithoutAction) { + RuleBuilder rb(&grammar, "A"); + rb.production("a"); + rb.done(); + + EXPECT_EQ(grammar.get_symbols().size(), 4u); + EXPECT_EQ(grammar.get_rules().size(), 1u); + EXPECT_EQ(grammar.get_rules()[0]->to_string(), "A -> a"); + EXPECT_FALSE(grammar.get_rules()[0]->has_action()); +} + +TEST_F(TestRuleBuilder, +SingleProductionWithAction) { + RuleBuilder rb(&grammar, "A"); + rb.production("a", [](Parser&, auto&&) { return 42; }); + rb.done(); + + EXPECT_EQ(grammar.get_symbols().size(), 4u); + EXPECT_EQ(grammar.get_rules().size(), 1u); + EXPECT_EQ(grammar.get_rules()[0]->to_string(), "A -> a"); + EXPECT_TRUE(grammar.get_rules()[0]->has_action()); +} + +TEST_F(TestRuleBuilder, +MultipleProductionsWithActions) { + RuleBuilder rb(&grammar, "A"); + rb.production("A", "a", [](Parser&, auto&&) { return 42; }) + .production("a", [](Parser&, auto&&) { return 42; }); + rb.done(); + + EXPECT_EQ(grammar.get_symbols().size(), 4u); + EXPECT_EQ(grammar.get_rules().size(), 2u); + EXPECT_EQ(grammar.get_rules()[0]->to_string(), "A -> A a"); + EXPECT_EQ(grammar.get_rules()[1]->to_string(), "A -> a"); + EXPECT_TRUE(grammar.get_rules()[0]->has_action()); + EXPECT_TRUE(grammar.get_rules()[1]->has_action()); +} + diff --git a/tests/Pog/State.cpp b/tests/Pog/State.cpp new file mode 100644 index 00000000..526e8d36 --- /dev/null +++ b/tests/Pog/State.cpp @@ -0,0 +1,278 @@ +#include + +#include + +using namespace pog; +using namespace ::testing; + +class TestState : public ::testing::Test {}; + +TEST_F(TestState, +DefultState) { + State state; + + EXPECT_EQ(state.get_index(), std::numeric_limits::max()); +} + +TEST_F(TestState, +SimpleState) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + + State state(1); + + EXPECT_EQ(state.get_index(), 1u); + EXPECT_EQ(state.size(), 0u); +} + +TEST_F(TestState, +SetIndex) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + + State state(1); + state.set_index(2); + + EXPECT_EQ(state.get_index(), 2u); + EXPECT_EQ(state.size(), 0u); +} + +TEST_F(TestState, +AddItem) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule1(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + Rule rule2(43, &s1, std::vector*>{&s2}, [](Parser&, auto&&) -> int { return 0; }); + + State state(1); + auto result1 = state.add_item(Item{&rule1, 0}); + auto result2 = state.add_item(Item{&rule2, 0}); + EXPECT_EQ(state.size(), 2u); + EXPECT_THAT(result1, Pair(An*>(), Eq(true))); + EXPECT_THAT(result2, Pair(An*>(), Eq(true))); + EXPECT_NE(result1.first, result2.first); +} + +TEST_F(TestState, +AddItemAlreadyExists) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule1(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + Rule rule2(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + + State state(1); + auto result1 = state.add_item(Item{&rule1, 0}); + auto result2 = state.add_item(Item{&rule2, 0}); + EXPECT_EQ(state.size(), 1u); + EXPECT_THAT(result1, Pair(An*>(), Eq(true))); + EXPECT_THAT(result2, Pair(An*>(), Eq(false))); + EXPECT_EQ(result1.first, result2.first); +} + +TEST_F(TestState, +ItemsAreSorted) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule1(44, &s1, std::vector*>{}, [](Parser&, auto&&) -> int { return 0; }); + Rule rule2(43, &s1, std::vector*>{&s2}, [](Parser&, auto&&) -> int { return 0; }); + Rule rule3(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + + State state(1); + state.add_item(Item{&rule1, 0}); + state.add_item(Item{&rule2, 0}); + state.add_item(Item{&rule3, 0}); + EXPECT_EQ(state.size(), 3u); + EXPECT_EQ(state.begin()->get()->get_rule()->get_index(), 42u); + EXPECT_EQ((state.begin() + 1)->get()->get_rule()->get_index(), 43u); + EXPECT_EQ((state.begin() + 2)->get()->get_rule()->get_index(), 44u); +} + +TEST_F(TestState, +ItemAreIterable) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule1(44, &s1, std::vector*>{}, [](Parser&, auto&&) -> int { return 0; }); + Rule rule2(43, &s1, std::vector*>{&s2}, [](Parser&, auto&&) -> int { return 0; }); + Rule rule3(42, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + + State state(1); + auto result1 = state.add_item(Item{&rule1, 0}); + auto result2 = state.add_item(Item{&rule2, 0}); + auto result3 = state.add_item(Item{&rule3, 0}); + + auto expected = std::vector*>{result3.first, result2.first, result1.first}; + std::size_t i = 0; + for (const auto& item : state) + EXPECT_EQ(item.get(), expected[i++]); +} + +TEST_F(TestState, +AddTransition) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule1(42, &s1, std::vector*>{}, [](Parser&, auto&&) -> int { return 0; }); + Rule rule2(43, &s1, std::vector*>{&s2}, [](Parser&, auto&&) -> int { return 0; }); + Rule rule3(44, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + + State state1(1); + state1.add_item(Item{&rule1, 0}); + State state2(2); + state2.add_item(Item{&rule2, 0}); + + state1.add_transition(&s1, &state2); + + EXPECT_EQ(state1.get_transitions().size(), 1u); + auto itr = state1.get_transitions().find(&s1); + EXPECT_NE(itr, state1.get_transitions().end()); + EXPECT_EQ(itr->second, &state2); +} + +TEST_F(TestState, +AddBackTransition) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::Nonterminal, "3"); + Rule rule1(42, &s1, std::vector*>{}, [](Parser&, auto&&) -> int { return 0; }); + Rule rule2(43, &s1, std::vector*>{&s2}, [](Parser&, auto&&) -> int { return 0; }); + Rule rule3(44, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + + State state1(1); + state1.add_item(Item{&rule1, 0}); + State state2(30); + state2.add_item(Item{&rule2, 0}); + State state3(20); + state2.add_item(Item{&rule3, 0}); + + state1.add_back_transition(&s1, &state2); + state1.add_back_transition(&s1, &state3); + + EXPECT_EQ(state1.get_back_transitions().size(), 1u); + auto itr = state1.get_back_transitions().find(&s1); + EXPECT_NE(itr, state1.get_back_transitions().end()); + EXPECT_EQ(itr->second, (std::vector*>{&state3, &state2})); +} + +TEST_F(TestState, +IsAccepting) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::End, "3"); + Rule rule1(42, &s1, std::vector*>{}, [](Parser&, auto&&) -> int { return 0; }); + Rule rule2(43, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + + State state1(1); + state1.add_item(Item{&rule1, 0}); + State state2(2); + state2.add_item(Item{&rule2, 0}); + State state3(3); + state3.add_item(Item{&rule2, 1}); + + EXPECT_FALSE(state1.is_accepting()); + EXPECT_FALSE(state2.is_accepting()); + EXPECT_TRUE(state3.is_accepting()); +} + +TEST_F(TestState, +ToString) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::End, "3"); + Rule rule1(42, &s1, std::vector*>{}, [](Parser&, auto&&) -> int { return 0; }); + Rule rule2(43, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + + State state(1); + state.add_item(Item{&rule1, 0}); + state.add_item(Item{&rule2, 0}); + state.add_item(Item{&rule2, 1}); + + EXPECT_EQ(state.to_string(), "1 -> 2 <*> 3\n1 -> <*> \n1 -> <*> 2 3"); +} + +TEST_F(TestState, +GetProductionItems) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::End, "3"); + Rule rule(43, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + + State state(1); + state.add_item(Item{&rule, 0}); + state.add_item(Item{&rule, 1}); + + auto expected = std::vector*>{}; + EXPECT_EQ(state.get_production_items(), expected); + + auto i = state.add_item(Item{&rule, 2}); + expected.push_back(i.first); + EXPECT_EQ(state.get_production_items(), expected); +} + +TEST_F(TestState, +Contains) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::End, "3"); + Rule rule(43, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + + State state(1); + state.add_item(Item{&rule, 0}); + state.add_item(Item{&rule, 1}); + + EXPECT_TRUE(state.contains(Item{&rule, 0})); + EXPECT_TRUE(state.contains(Item{&rule, 1})); + EXPECT_FALSE(state.contains(Item{&rule, 2})); +} + +TEST_F(TestState, +Equality) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::End, "3"); + Rule rule(43, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + + State state1(1); + state1.add_item(Item{&rule, 0}); + state1.add_item(Item{&rule, 1}); + + State state2(2); + state2.add_item(Item{&rule, 0}); + state2.add_item(Item{&rule, 1}); + + State state3(3); + state3.add_item(Item{&rule, 0}); + state3.add_item(Item{&rule, 2}); + + EXPECT_TRUE(state1 == state2); + EXPECT_FALSE(state1 == state3); + + EXPECT_FALSE(state1 != state2); + EXPECT_TRUE(state1 != state3); +} + +TEST_F(TestState, +Kernel) { + Symbol s1(1, SymbolKind::Nonterminal, "1"); + Symbol s2(2, SymbolKind::Nonterminal, "2"); + Symbol s3(3, SymbolKind::End, "3"); + Rule rule(43, &s1, std::vector*>{&s2, &s3}, [](Parser&, auto&&) -> int { return 0; }); + + State state(1); + state.add_item(Item{&rule, 0}); + state.add_item(Item{&rule, 1}); + state.add_item(Item{&rule, 2}); + + std::vector kernel; + for (const auto& item : state.get_kernel()) + kernel.push_back(item->to_string()); + + EXPECT_EQ(kernel, (std::vector{"1 -> 2 <*> 3", "1 -> 2 3 <*>"})); +} diff --git a/tests/Pog/Symbol.cpp b/tests/Pog/Symbol.cpp new file mode 100644 index 00000000..cabccb75 --- /dev/null +++ b/tests/Pog/Symbol.cpp @@ -0,0 +1,57 @@ +#include + +#include + +class TestSymbol : public ::testing::Test {}; + +using namespace pog; + +TEST_F(TestSymbol, +Nonterminal) { + Symbol symbol(42, SymbolKind::Nonterminal, "testing_nonterminal"); + + EXPECT_EQ(symbol.get_index(), 42u); + EXPECT_EQ(symbol.get_name(), "testing_nonterminal"); + EXPECT_FALSE(symbol.is_end()); + EXPECT_TRUE(symbol.is_nonterminal()); + EXPECT_FALSE(symbol.is_terminal()); + EXPECT_FALSE(symbol.has_precedence()); +} + +TEST_F(TestSymbol, +Terminal) { + Symbol symbol(42, SymbolKind::Terminal, "testing_terminal"); + + EXPECT_EQ(symbol.get_index(), 42u); + EXPECT_EQ(symbol.get_name(), "testing_terminal"); + EXPECT_FALSE(symbol.is_end()); + EXPECT_FALSE(symbol.is_nonterminal()); + EXPECT_TRUE(symbol.is_terminal()); + EXPECT_FALSE(symbol.has_precedence()); +} + +TEST_F(TestSymbol, +End) { + Symbol symbol(42, SymbolKind::End, "testing_end"); + + EXPECT_EQ(symbol.get_index(), 42u); + EXPECT_EQ(symbol.get_name(), "testing_end"); + EXPECT_TRUE(symbol.is_end()); + EXPECT_FALSE(symbol.is_nonterminal()); + EXPECT_FALSE(symbol.is_terminal()); + EXPECT_FALSE(symbol.has_precedence()); +} + +TEST_F(TestSymbol, +Precedence) { + Symbol symbol(42, SymbolKind::Terminal, "testing_terminal"); + symbol.set_precedence(1, Associativity::Right); + + EXPECT_EQ(symbol.get_index(), 42u); + EXPECT_EQ(symbol.get_name(), "testing_terminal"); + EXPECT_FALSE(symbol.is_end()); + EXPECT_FALSE(symbol.is_nonterminal()); + EXPECT_TRUE(symbol.is_terminal()); + EXPECT_TRUE(symbol.has_precedence()); + EXPECT_EQ(symbol.get_precedence(), (Precedence{1, Associativity::Right})); +} diff --git a/tests/Pog/Token.cpp b/tests/Pog/Token.cpp new file mode 100644 index 00000000..5c362ca0 --- /dev/null +++ b/tests/Pog/Token.cpp @@ -0,0 +1,81 @@ +#include + +#include + +using namespace pog; +using namespace ::testing; + +class TestToken : public ::testing::Test {}; + +TEST_F(TestToken, +SimpleTokenWithoutSymbol) { + Token t(1, "abc", std::vector{"s1", "s2"}); + + EXPECT_EQ(t.get_index(), 1u); + EXPECT_EQ(t.get_pattern(), "abc"); + EXPECT_EQ(t.get_active_in_states(), (std::vector{"s1", "s2"})); + EXPECT_EQ(t.get_symbol(), nullptr); + EXPECT_THAT(t.get_regexp(), A()); + + EXPECT_FALSE(t.has_symbol()); + EXPECT_FALSE(t.has_action()); + EXPECT_FALSE(t.has_transition_to_state()); +} + +TEST_F(TestToken, +SimpleTokenWithSymbol) { + Symbol s(1, SymbolKind::Nonterminal, "a"); + Token t(1, "abc", std::vector{"s1", "s2"}, &s); + + EXPECT_EQ(t.get_index(), 1u); + EXPECT_EQ(t.get_pattern(), "abc"); + EXPECT_EQ(t.get_active_in_states(), (std::vector{"s1", "s2"})); + EXPECT_EQ(t.get_symbol(), &s); + EXPECT_THAT(t.get_regexp(), A()); + + EXPECT_TRUE(t.has_symbol()); + EXPECT_FALSE(t.has_action()); + EXPECT_FALSE(t.has_transition_to_state()); +} + +TEST_F(TestToken, +TransitionToState) { + Token t(1, "abc", std::vector{"s1", "s2"}); + t.set_transition_to_state("dest_state"); + + EXPECT_EQ(t.get_index(), 1u); + EXPECT_EQ(t.get_pattern(), "abc"); + EXPECT_EQ(t.get_active_in_states(), (std::vector{"s1", "s2"})); + EXPECT_EQ(t.get_symbol(), nullptr); + EXPECT_THAT(t.get_regexp(), A()); + + EXPECT_FALSE(t.has_symbol()); + EXPECT_FALSE(t.has_action()); + EXPECT_TRUE(t.has_transition_to_state()); + EXPECT_EQ(t.get_transition_to_state(), "dest_state"); +} + +TEST_F(TestToken, +AddActiveInState) { + Token t(1, "abc", std::vector{"s1", "s2"}); + + t.add_active_in_state("s3"); + + EXPECT_EQ(t.get_active_in_states(), (std::vector{"s1", "s2", "s3"})); +} + +TEST_F(TestToken, +Action) { + bool called = false; + + Token t(1, "abc", std::vector{"s1", "s2"}); + t.set_action([&](std::string_view str) -> int { + called = true; + return static_cast(str.length()); + }); + + EXPECT_EQ(t.get_index(), 1u); + EXPECT_TRUE(t.has_action()); + EXPECT_EQ(t.perform_action("abcdef"), 6); + EXPECT_TRUE(called); +} diff --git a/tests/Pog/TokenBuilder.cpp b/tests/Pog/TokenBuilder.cpp new file mode 100644 index 00000000..b29601a5 --- /dev/null +++ b/tests/Pog/TokenBuilder.cpp @@ -0,0 +1,145 @@ +#include + +#include + +using namespace pog; + +class TestTokenBuilder : public ::testing::Test +{ +public: + TestTokenBuilder() : grammar(), tokenizer(&grammar) {} + + Grammar grammar; + Tokenizer tokenizer; +}; + +TEST_F(TestTokenBuilder, +Initialization) { + TokenBuilder tb(&grammar, &tokenizer); + + EXPECT_EQ(grammar.get_symbols().size(), 2u); + EXPECT_EQ(tokenizer.get_tokens().size(), 1u); +} + +TEST_F(TestTokenBuilder, +NoTokens) { + TokenBuilder tb(&grammar, &tokenizer); + tb.done(); + + EXPECT_EQ(grammar.get_symbols().size(), 2u); + EXPECT_EQ(tokenizer.get_tokens().size(), 1u); +} + +TEST_F(TestTokenBuilder, +SingleTokenWithoutAnything) { + TokenBuilder tb(&grammar, &tokenizer, "abc"); + tb.done(); + + EXPECT_EQ(grammar.get_symbols().size(), 2u); + EXPECT_EQ(tokenizer.get_tokens().size(), 2u); + + EXPECT_EQ(tokenizer.get_tokens()[1]->get_pattern(), "abc"); +} + +TEST_F(TestTokenBuilder, +SingleTokenWithSymbol) { + TokenBuilder tb(&grammar, &tokenizer, "abc"); + tb.symbol("ABC"); + tb.done(); + + EXPECT_EQ(grammar.get_symbols().size(), 3u); + EXPECT_EQ(tokenizer.get_tokens().size(), 2u); + + EXPECT_EQ(grammar.get_symbols()[2]->get_name(), "ABC"); + EXPECT_EQ(tokenizer.get_tokens()[1]->get_pattern(), "abc"); +} + +TEST_F(TestTokenBuilder, +SingleTokenWithAction) { + TokenBuilder tb(&grammar, &tokenizer, "abc"); + tb.action([](std::string_view) { return 42; }); + tb.done(); + + EXPECT_EQ(grammar.get_symbols().size(), 2u); + EXPECT_EQ(tokenizer.get_tokens().size(), 2u); + + EXPECT_EQ(tokenizer.get_tokens()[1]->get_pattern(), "abc"); + EXPECT_TRUE(tokenizer.get_tokens()[1]->has_action()); + EXPECT_EQ(tokenizer.get_tokens()[1]->perform_action("xyz"), 42); +} + +TEST_F(TestTokenBuilder, +SingleTokenWithFullwordSpecifier) { + TokenBuilder tb(&grammar, &tokenizer, "abc"); + tb.fullword(); + tb.done(); + + EXPECT_EQ(grammar.get_symbols().size(), 2u); + EXPECT_EQ(tokenizer.get_tokens().size(), 2u); + + EXPECT_EQ(tokenizer.get_tokens()[1]->get_pattern(), "abc(\\b|$)"); +} + +TEST_F(TestTokenBuilder, +SingleTokenWithStates) { + TokenBuilder tb(&grammar, &tokenizer, "abc"); + tb.states("state1", "state2"); + tb.done(); + + EXPECT_EQ(grammar.get_symbols().size(), 2u); + EXPECT_EQ(tokenizer.get_tokens().size(), 2u); + + EXPECT_EQ(tokenizer.get_tokens()[1]->get_pattern(), "abc"); +} + +TEST_F(TestTokenBuilder, +SingleTokenWithTransitionToState) { + TokenBuilder tb(&grammar, &tokenizer, "abc"); + tb.enter_state("state1"); + tb.done(); + + EXPECT_EQ(grammar.get_symbols().size(), 2u); + EXPECT_EQ(tokenizer.get_tokens().size(), 2u); + + EXPECT_EQ(tokenizer.get_tokens()[1]->get_pattern(), "abc"); + EXPECT_TRUE(tokenizer.get_tokens()[1]->has_transition_to_state()); + EXPECT_EQ(tokenizer.get_tokens()[1]->get_transition_to_state(), "state1"); +} + +TEST_F(TestTokenBuilder, +SingleTokenWithPrecedence) { + TokenBuilder tb(&grammar, &tokenizer, "abc"); + tb.symbol("ABC"); + tb.precedence(1, Associativity::Left); + tb.done(); + + EXPECT_EQ(grammar.get_symbols().size(), 3u); + EXPECT_EQ(tokenizer.get_tokens().size(), 2u); + + EXPECT_EQ(tokenizer.get_tokens()[1]->get_pattern(), "abc"); + EXPECT_TRUE(grammar.get_symbols()[2]->has_precedence()); + EXPECT_EQ(grammar.get_symbols()[2]->get_precedence(), (Precedence{1, Associativity::Left})); +} + +TEST_F(TestTokenBuilder, +MultipleTokensDescription) { + TokenBuilder tb1(&grammar, &tokenizer, "abc"); + tb1.symbol("ABC"); + tb1.description("abc token"); + tb1.done(); + + TokenBuilder tb2(&grammar, &tokenizer, "def"); + tb2.symbol("DEF"); + tb2.done(); + + + EXPECT_EQ(grammar.get_symbols().size(), 4u); + EXPECT_EQ(tokenizer.get_tokens().size(), 3u); + + EXPECT_EQ(grammar.get_symbols()[2]->get_name(), "ABC"); + EXPECT_EQ(grammar.get_symbols()[2]->get_description(), "abc token"); + EXPECT_EQ(grammar.get_symbols()[3]->get_name(), "DEF"); + EXPECT_EQ(grammar.get_symbols()[3]->get_description(), "DEF"); + EXPECT_EQ(tokenizer.get_tokens()[1]->get_pattern(), "abc"); + EXPECT_EQ(tokenizer.get_tokens()[2]->get_pattern(), "def"); +} diff --git a/tests/Pog/Tokenizer.cpp b/tests/Pog/Tokenizer.cpp new file mode 100644 index 00000000..ad2197b1 --- /dev/null +++ b/tests/Pog/Tokenizer.cpp @@ -0,0 +1,295 @@ +#include + +#include + +using namespace pog; + +class TestTokenizer : public ::testing::Test +{ +public: + Grammar grammar; +}; + +TEST_F(TestTokenizer, +Initialization) { + Tokenizer t(&grammar); + + EXPECT_EQ(t.get_tokens().size(), 1u); + EXPECT_EQ(t.get_tokens()[0].get(), t.get_end_token()); + EXPECT_FALSE(t.get_tokens()[0]->has_symbol()); +} + +TEST_F(TestTokenizer, +AddToken) { + auto a = grammar.add_symbol(SymbolKind::Terminal, "a"); + auto b = grammar.add_symbol(SymbolKind::Terminal, "b"); + + Tokenizer t(&grammar); + + t.add_token("aaa", a, std::vector{std::string{decltype(t)::DefaultState}}); + t.add_token("bbb", b, std::vector{std::string{decltype(t)::DefaultState}}); + t.add_token("ccc", nullptr, std::vector{std::string{decltype(t)::DefaultState}}); + + EXPECT_EQ(t.get_tokens().size(), 4u); + EXPECT_EQ(t.get_tokens()[1]->get_pattern(), "aaa"); + EXPECT_TRUE(t.get_tokens()[1]->has_symbol()); + EXPECT_EQ(t.get_tokens()[1]->get_symbol(), a); + EXPECT_EQ(t.get_tokens()[2]->get_pattern(), "bbb"); + EXPECT_TRUE(t.get_tokens()[2]->has_symbol()); + EXPECT_EQ(t.get_tokens()[2]->get_symbol(), b); + EXPECT_EQ(t.get_tokens()[3]->get_pattern(), "ccc"); + EXPECT_FALSE(t.get_tokens()[3]->has_symbol()); +} + +TEST_F(TestTokenizer, +NextToken) { + auto a = grammar.add_symbol(SymbolKind::Terminal, "a"); + auto b = grammar.add_symbol(SymbolKind::Terminal, "b"); + + Tokenizer t(&grammar); + + t.add_token("aaa", a, std::vector{std::string{decltype(t)::DefaultState}}); + t.add_token("bbb", b, std::vector{std::string{decltype(t)::DefaultState}}); + t.add_token("ccc", nullptr, std::vector{std::string{decltype(t)::DefaultState}}); + t.prepare(); + + std::string input("aaacccbbb"); + t.push_input_stream(input); + + auto result = t.next_token(); + EXPECT_TRUE(result); + EXPECT_EQ(result.value().symbol, a); + + result = t.next_token(); + EXPECT_TRUE(result); + EXPECT_EQ(result.value().symbol, b); + + result = t.next_token(); + EXPECT_TRUE(result); + EXPECT_EQ(result.value().symbol, grammar.get_end_of_input_symbol()); +} + +TEST_F(TestTokenizer, +NextTokenWithUnknownToken) { + auto a = grammar.add_symbol(SymbolKind::Terminal, "a"); + auto b = grammar.add_symbol(SymbolKind::Terminal, "b"); + + Tokenizer t(&grammar); + + t.add_token("aaa", a, std::vector{std::string{decltype(t)::DefaultState}}); + t.add_token("bbb", b, std::vector{std::string{decltype(t)::DefaultState}}); + t.add_token("ccc", nullptr, std::vector{std::string{decltype(t)::DefaultState}}); + t.prepare(); + + std::string input("aaaccbbb"); + t.push_input_stream(input); + + auto result = t.next_token(); + EXPECT_TRUE(result); + EXPECT_EQ(result.value().symbol, a); + + result = t.next_token(); + EXPECT_FALSE(result); +} + +TEST_F(TestTokenizer, +NextTokenLongestMatchWins) { + auto a1 = grammar.add_symbol(SymbolKind::Terminal, "a1"); + auto a2 = grammar.add_symbol(SymbolKind::Terminal, "a3"); + auto a3 = grammar.add_symbol(SymbolKind::Terminal, "a3"); + + Tokenizer t(&grammar); + + t.add_token("a", a1, std::vector{std::string{decltype(t)::DefaultState}}); + t.add_token("aaa", a3, std::vector{std::string{decltype(t)::DefaultState}}); + t.add_token("aa", a2, std::vector{std::string{decltype(t)::DefaultState}}); + t.prepare(); + + std::string input("aaaaa"); + t.push_input_stream(input); + + auto result = t.next_token(); + EXPECT_TRUE(result); + EXPECT_EQ(result.value().symbol, a3); +} + +TEST_F(TestTokenizer, +NextTokenIndexWinsInCaseOfEqualMatch) { + auto a3 = grammar.add_symbol(SymbolKind::Terminal, "a3"); + auto an = grammar.add_symbol(SymbolKind::Terminal, "an"); + + Tokenizer t(&grammar); + + t.add_token("aaa", a3, std::vector{std::string{decltype(t)::DefaultState}}); + t.add_token("a*", an, std::vector{std::string{decltype(t)::DefaultState}}); + t.prepare(); + + std::string input("aaa"); + t.push_input_stream(input); + + auto result = t.next_token(); + EXPECT_TRUE(result); + EXPECT_EQ(result.value().symbol, a3); +} + +TEST_F(TestTokenizer, +TokenActionsPerformed) { + auto a = grammar.add_symbol(SymbolKind::Terminal, "a"); + auto b = grammar.add_symbol(SymbolKind::Terminal, "b"); + + Tokenizer t(&grammar); + + std::vector matches; + + auto a_t = t.add_token("a+", a, std::vector{std::string{decltype(t)::DefaultState}}); + a_t->set_action([&](std::string_view str) { + matches.push_back(std::string{str}); + return 0; + }); + auto b_t = t.add_token("b+", b, std::vector{std::string{decltype(t)::DefaultState}}); + b_t->set_action([&](std::string_view str) { + matches.push_back(std::string{str}); + return 0; + }); + t.get_end_token()->set_action([&](std::string_view str) { + matches.push_back(std::string{str}); + return 0; + }); + t.prepare(); + + std::string input("aabbbbaaaaabb"); + t.push_input_stream(input); + + for (auto i = 0; i < 5; ++i) + t.next_token(); + + EXPECT_EQ(matches.size(), 5u); + EXPECT_EQ(matches, (std::vector{"aa", "bbbb", "aaaaa", "bb", ""})); +} + +TEST_F(TestTokenizer, +NextTokenGlobalActionPerformed) { + auto a = grammar.add_symbol(SymbolKind::Terminal, "a"); + auto b = grammar.add_symbol(SymbolKind::Terminal, "b"); + + Tokenizer t(&grammar); + + std::vector matches; + + t.global_action([&](std::string_view str) { + matches.push_back(fmt::format("global:{}", str)); + }); + auto a_t = t.add_token("a+", a, std::vector{std::string{decltype(t)::DefaultState}}); + a_t->set_action([&](std::string_view str) { + matches.push_back(std::string{str}); + return 0; + }); + t.add_token("b+", b, std::vector{std::string{decltype(t)::DefaultState}}); + t.prepare(); + + std::string input("aabbbbaaaaabb"); + t.push_input_stream(input); + + for (auto i = 0; i < 5; ++i) + t.next_token(); + + EXPECT_EQ(matches.size(), 7u); + EXPECT_EQ(matches, (std::vector{"global:aa", "aa", "global:bbbb", "global:aaaaa", "aaaaa", "global:bb", "global:"})); +} + +TEST_F(TestTokenizer, +InputStreamStackManipulation) { + auto a = grammar.add_symbol(SymbolKind::Terminal, "a"); + auto b = grammar.add_symbol(SymbolKind::Terminal, "b"); + + Tokenizer t(&grammar); + + t.add_token("aaa", a, std::vector{std::string{decltype(t)::DefaultState}}); + t.add_token("bbb", b, std::vector{std::string{decltype(t)::DefaultState}}); + t.prepare(); + + std::string input("aaabbb"); + t.push_input_stream(input); + + auto result = t.next_token(); + EXPECT_TRUE(result); + EXPECT_EQ(result.value().symbol, a); + + std::string input2("aaaaaa"); + t.push_input_stream(input2); + + result = t.next_token(); + EXPECT_TRUE(result); + EXPECT_EQ(result.value().symbol, a); + + result = t.next_token(); + EXPECT_TRUE(result); + EXPECT_EQ(result.value().symbol, a); + + result = t.next_token(); + EXPECT_TRUE(result); + EXPECT_EQ(result.value().symbol, grammar.get_end_of_input_symbol()); + + t.pop_input_stream(); + + result = t.next_token(); + EXPECT_TRUE(result); + EXPECT_EQ(result.value().symbol, b); + + result = t.next_token(); + EXPECT_TRUE(result); + EXPECT_EQ(result.value().symbol, grammar.get_end_of_input_symbol()); +} + +TEST_F(TestTokenizer, +StatesAndTransitions) { + auto a = grammar.add_symbol(SymbolKind::Terminal, "a"); + auto b = grammar.add_symbol(SymbolKind::Terminal, "b"); + + Tokenizer t(&grammar); + + auto a_t = t.add_token("aaa", a, std::vector{std::string{decltype(t)::DefaultState}}); + auto b_t = t.add_token("bbb", b, std::vector{"state1"}); + a_t->set_transition_to_state("state1"); + b_t->set_transition_to_state(std::string{decltype(t)::DefaultState}); + t.prepare(); + + std::string input("aaabbb"); + t.push_input_stream(input); + EXPECT_TRUE(t.next_token()); + EXPECT_TRUE(t.next_token()); + EXPECT_TRUE(t.next_token()); + t.pop_input_stream(); + + std::string input2("aaaaaa"); + t.push_input_stream(input2); + EXPECT_TRUE(t.next_token()); + EXPECT_FALSE(t.next_token()); + t.pop_input_stream(); +} + +TEST_F(TestTokenizer, +EnterState) { + auto a = grammar.add_symbol(SymbolKind::Terminal, "a"); + auto b = grammar.add_symbol(SymbolKind::Terminal, "b"); + + Tokenizer t(&grammar); + + [[maybe_unused]] auto a_t = t.add_token("aaa", a, std::vector{std::string{decltype(t)::DefaultState}}); + [[maybe_unused]] auto b_t = t.add_token("bbb", b, std::vector{"state1"}); + t.prepare(); + + t.enter_state("state1"); + + std::string input("aaabbb"); + t.push_input_stream(input); + EXPECT_FALSE(t.next_token()); + t.pop_input_stream(); + + std::string input2("bbbbbb"); + t.push_input_stream(input2); + EXPECT_TRUE(t.next_token()); + EXPECT_TRUE(t.next_token()); + EXPECT_FALSE(t.next_token()); + t.pop_input_stream(); +} diff --git a/tests/Pog/Utils.cpp b/tests/Pog/Utils.cpp new file mode 100644 index 00000000..6a207247 --- /dev/null +++ b/tests/Pog/Utils.cpp @@ -0,0 +1,62 @@ +#include + +#include + +class TestUtils : public ::testing::Test {}; + +TEST_F(TestUtils, +transform_if) { + std::vector v{0, 1, 2, 3, 4, 5, 6}; + + std::vector result; + pog::transform_if(v.begin(), v.end(), std::back_inserter(result), + [](auto i) { return i % 2 == 0; }, + [](auto i) { return i + 10; } + ); + EXPECT_EQ(result, (std::vector{10, 12, 14, 16})); + + result.clear(); + pog::transform_if(v.begin(), v.end(), std::back_inserter(result), + [](auto i) { return i < 100; }, + [](auto i) { return i + 10; } + ); + EXPECT_EQ(result, (std::vector{10, 11, 12, 13, 14, 15, 16})); + + result.clear(); + pog::transform_if(v.begin(), v.end(), std::back_inserter(result), + [](auto i) { return i > 100; }, + [](auto i) { return i + 10; } + ); + EXPECT_EQ(result, (std::vector{})); +} + +TEST_F(TestUtils, +accumulate_if) { + std::vector v{1, 2, 3, 4, 5, 6}; + + auto result = pog::accumulate_if(v.begin(), v.end(), 0, + [](auto i) { return i % 2 == 0; }, + [](auto res, auto i) { return res + i; } + ); + EXPECT_EQ(result, 12); + + result = pog::accumulate_if(v.begin(), v.end(), 0, + [](auto i) { return i < 100; }, + [](auto res, auto i) { return res + i; } + ); + EXPECT_EQ(result, 21); + + result = pog::accumulate_if(v.begin(), v.end(), 0, + [](auto i) { return i > 100; }, + [](auto res, auto i) { return res + i; } + ); + EXPECT_EQ(result, 0); +} + +TEST_F(TestUtils, +hash_combine) { + EXPECT_EQ(pog::hash_combine(1, 2), pog::hash_combine(1, 2)); + EXPECT_NE(pog::hash_combine(1, 2), pog::hash_combine(1, 3)); + EXPECT_NE(pog::hash_combine(1, 2), pog::hash_combine(2, 1)); + EXPECT_NE(pog::hash_combine(1, 2), pog::hash_combine(1, 2, 3)); +} From a583c9b8d4f2e064e88513cb1b51b9eb855425dd Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Wed, 30 Apr 2025 12:59:57 +0400 Subject: [PATCH 21/25] Fixed workflows --- .github/workflows/run-tests-feature-branch.yml | 1 + .github/workflows/testing.yml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.github/workflows/run-tests-feature-branch.yml b/.github/workflows/run-tests-feature-branch.yml index 18fb38ba..e7402e56 100644 --- a/.github/workflows/run-tests-feature-branch.yml +++ b/.github/workflows/run-tests-feature-branch.yml @@ -18,6 +18,7 @@ jobs: - name: Build dependencies with Conan run: | python3 scripts/build.py -i \ + --config Release \ --profiles-detection - name: Build and test with GCC on Release profile diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 7d2da610..cd4b45ed 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -20,6 +20,8 @@ jobs: - name: Build all dependencies run: | python3 scripts/build.py -i \ + --config Release \ + --config Debug \ --profiles-detection - name: Build and test with GCC on Debug profile From 1809bcf1c314fc5e9cd4db5a200eadd5b57a257d Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Wed, 30 Apr 2025 16:39:29 +0400 Subject: [PATCH 22/25] Rollback separate dependencies compilation step --- .github/workflows/distro-ci.yml | 3 -- .../workflows/run-tests-feature-branch.yml | 18 ++++------- .github/workflows/testing.yml | 31 +++++++------------ 3 files changed, 18 insertions(+), 34 deletions(-) diff --git a/.github/workflows/distro-ci.yml b/.github/workflows/distro-ci.yml index 19d86f9a..8bd06dae 100644 --- a/.github/workflows/distro-ci.yml +++ b/.github/workflows/distro-ci.yml @@ -51,6 +51,3 @@ jobs: -D CMAKE_CXX_COMPILER:STRING=g++ \ -D CMAKE_CXX_COMPILER:STRING=gcc \ --detect - - cd build/Release && ctest && cd ../.. - cd build/Debug && ctest && cd ../.. \ No newline at end of file diff --git a/.github/workflows/run-tests-feature-branch.yml b/.github/workflows/run-tests-feature-branch.yml index e7402e56..07630e22 100644 --- a/.github/workflows/run-tests-feature-branch.yml +++ b/.github/workflows/run-tests-feature-branch.yml @@ -14,32 +14,26 @@ jobs: uses: actions/checkout@v4 with: submodules: 'true' - - - name: Build dependencies with Conan - run: | - python3 scripts/build.py -i \ - --config Release \ - --profiles-detection - name: Build and test with GCC on Release profile run: | - python3 scripts/build.py -cb \ + python3 scripts/build.py -icb \ --config Release \ --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=g++ \ - -D CMAKE_C_COMPILER:STRING=gcc - + -D CMAKE_C_COMPILER:STRING=gcc \ + --profiles-detection cd build/Release && ctest -V && cd ../.. python3 scripts/build.py -r --config Release --build-dir build - name: Build and test with LLVM on Release profile run: | - python3 scripts/build.py -cb \ + python3 scripts/build.py -icb \ --config Release \ --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=clang++-19 \ - -D CMAKE_C_COMPILER:STRING=clang-19 - + -D CMAKE_C_COMPILER:STRING=clang-19 \ + --profiles-detection cd build/Release && ctest -V && cd ../.. python3 scripts/build.py -r --config Release --build-dir build diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index cd4b45ed..90a44b1e 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -16,55 +16,48 @@ jobs: uses: actions/checkout@v4 with: submodules: 'true' - - - name: Build all dependencies - run: | - python3 scripts/build.py -i \ - --config Release \ - --config Debug \ - --profiles-detection - name: Build and test with GCC on Debug profile run: | - python3 scripts/build.py -cb \ + python3 scripts/build.py -icb \ --config Debug \ --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=g++ \ - -D CMAKE_C_COMPILER:STRING=gcc - + -D CMAKE_C_COMPILER:STRING=gcc \ + --profiles-detection cd build/Debug && ctest -V && cd ../.. python3 scripts/build.py -r --config Debug --build-dir build - name: Build and test with GCC on Release profile run: | - python3 scripts/build.py -cb \ + python3 scripts/build.py -icb \ --config Release \ --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=g++ \ - -D CMAKE_C_COMPILER:STRING=gcc - + -D CMAKE_C_COMPILER:STRING=gcc \ + --profiles-detection cd build/Release && ctest -V && cd ../.. python3 scripts/build.py -r --config Release --build-dir build - name: Build and test with LLVM on Debug profile run: | - python3 scripts/build.py -cb \ + python3 scripts/build.py -icb \ --config Debug \ --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=clang++-19 \ - -D CMAKE_C_COMPILER:STRING=clang-19 - + -D CMAKE_C_COMPILER:STRING=clang-19 \ + --profiles-detection cd build/Debug && ctest -V && cd ../.. python3 scripts/build.py -r --config Debug --build-dir build - name: Build and test with LLVM on Release profile run: | - python3 scripts/build.py -cb \ + python3 scripts/build.py -icb \ --config Release \ --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=clang++-19 \ - -D CMAKE_C_COMPILER:STRING=clang-19 - + -D CMAKE_C_COMPILER:STRING=clang-19 \ + --profiles-detection cd build/Release && ctest -V && cd ../.. python3 scripts/build.py -r --config Release --build-dir build From edb54a4fa41f5063c72ebe98e6affa4d7afdb879 Mon Sep 17 00:00:00 2001 From: HyperWinX Date: Thu, 1 May 2025 15:41:13 +0400 Subject: [PATCH 23/25] Remove pog submodule --- .gitmodules | 3 --- dist/pog | 1 - 2 files changed, 4 deletions(-) delete mode 160000 dist/pog diff --git a/.gitmodules b/.gitmodules index 3ee6db91..14ab5e2b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "dist/pog"] - path = dist/pog - url = https://github.com/HyperWinX/HPog [submodule "dist/HPool"] path = dist/HPool url = https://github.com/randommfs/HPool diff --git a/dist/pog b/dist/pog deleted file mode 160000 index 501fa03d..00000000 --- a/dist/pog +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 501fa03da44a50cdf7c6042effea7336bb16e724 From 53ec633d787a2fb2f5596e3266c545f2e340b99f Mon Sep 17 00:00:00 2001 From: AshFungor Date: Thu, 1 May 2025 17:13:31 +0300 Subject: [PATCH 24/25] fix flags --- .github/workflows/distro-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/distro-ci.yml b/.github/workflows/distro-ci.yml index 8bd06dae..0a938baf 100644 --- a/.github/workflows/distro-ci.yml +++ b/.github/workflows/distro-ci.yml @@ -50,4 +50,4 @@ jobs: --build-dir build \ -D CMAKE_CXX_COMPILER:STRING=g++ \ -D CMAKE_CXX_COMPILER:STRING=gcc \ - --detect + --profiles-detection From 5f5d24fb94e5a023b2cc66391a7a048ed2ff892f Mon Sep 17 00:00:00 2001 From: AshFungor Date: Thu, 1 May 2025 17:18:35 +0300 Subject: [PATCH 25/25] docker --- docker/alpine/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index c5ce9c27..52918024 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -3,5 +3,5 @@ FROM alpine:latest RUN apk update && \ apk add --no-cache python3 py3-pip clang gcc git cmake make \ - ninja nodejs grep g++ \ + ninja nodejs grep g++ linux-headers \ && pip3 install --no-cache-dir --break-system-packages conan \ No newline at end of file