Initial commit

This commit is contained in:
IRBorisov 2024-04-15 22:16:14 +03:00
parent ac0e5cfd2b
commit 9c09995d17
384 changed files with 109196 additions and 1 deletions

39
.clang-tidy Normal file
View File

@ -0,0 +1,39 @@
Checks: "*,\
-altera-struct-pack-align,\
-altera-unroll-loops,\
-altera-id-dependent-backward-branch,\
-llvmlibc-callee-namespace,\
-llvmlibc-restrict-system-libc-headers,\
-llvmlibc-implementation-in-namespace,\
-llvmlibc-inline-function-decl,\
-llvm-else-after-return,\
-llvm-header-guard,\
-llvm-include-order,\
-bugprone-branch-clone,\
-bugprone-suspicious-include,\
-bugprone-easily-swappable-parameters,\
-modernize-use-trailing-return-type,\
-hicpp-special-member-functions,\
-google-runtime-references,\
-fuchsia-overloaded-operator,\
-fuchsia-default-arguments,\
-google-readability-todo,\
-google-global-names-in-headers,\
-readability-redundant-access-specifiers,\
-readability-else-after-return,\
-readability-implicit-bool-conversion,\
-readability-use-anyofallof,\
-readability-identifier-length,\
-performance-inefficient-string-concatenation,\
-performance-unnecessary-value-param,\
-fuchsia-default-arguments-declarations,\
-fuchsia-trailing-return,\
-fuchsia-multiple-inheritance,\
-fuchsia-default-arguments-calls,\
-misc-non-private-member-variables-in-classes,\
-misc-no-recursion,\
-misc-include-cleaner,\
-cppcoreguidelines-special-member-functions,\
-cppcoreguidelines-prefer-member-initializer,\
-cppcoreguidelines-avoid-const-or-ref-data-members,\
-cppcoreguidelines-non-private-member-variables-in-classes"

29
.dockerignore Normal file
View File

@ -0,0 +1,29 @@
# Git
.git
.gitignore
.github
# Windows specific
*.ps1
# Local build/utility folders
.vscode
.vs
*.vcxproj.user
*.pyc
*.pyd
**/CMakeUserPresets.json
output
packages
**/build
**/venv
**/pyconcept/import
**/egg-info
# Specific items
docker-compose.yml
rslang/src/RSParserImpl.output
rslang/src/RSParserImpl.gv

18
.gitignore vendored Normal file
View File

@ -0,0 +1,18 @@
~*
.vs
*.vcxproj.user
*.pch.tmp
*.clang.pch
*.pyc
*.pyd
packages
build
output
venv/
CMakeUserPresets.json
pyconcept/import/
*egg-info
ccl/rslang/src/RSParserImpl.output
ccl/rslang/src/RSParserImpl.gv

17
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,17 @@
{
"cSpell.words": [
"DCMAKE",
"debool",
"Decartian",
"defexpr",
"gtest",
"MSVC",
"nlohmann",
"noteq",
"notsubset",
"pybind",
"pyconcept",
"rslang",
"symmdiff"
]
}

152
CCL-Full.sln Normal file
View File

@ -0,0 +1,152 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33213.308
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ConceptCoreLibrary", "ccl\core\ConceptLibrary.vcxproj", "{B0ABA27B-9D39-4B48-9977-AFF20925B309}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RSlang", "ccl\rslang\RSlang.vcxproj", "{A8529C63-42F5-43E6-97B8-2EC83F23E1F9}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RSlangTest", "ccl\rslang\test\rslTest.vcxproj", "{32469CE1-303B-4DB4-8E03-B7EBED5851EB}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cclGraph", "ccl\cclGraph\cclGraph.vcxproj", "{7E1D5338-F819-4C96-B461-9EAAB8D02E1D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cclGraphTest", "ccl\cclGraph\test\cclGraphTest.vcxproj", "{5A2501C1-FEFB-4B14-A94D-E8F19ADEA239}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cclLang", "ccl\cclLang\cclLang.vcxproj", "{76B03803-56CC-47C2-A8F0-2241FCAF2898}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cclLangTest", "ccl\cclLang\test\cclLangTest.vcxproj", "{4754356B-DC01-4564-A035-270FFB72F6A0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "libs", "libs", "{FB8E3011-BB07-4F6E-B2C7-A0A183BC5000}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ConceptCoreDLL", "coredll\ConceptCoreDLL.vcxproj", "{6FDADD78-2FC1-4DBD-A2C4-B2EF35025AC0}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cclTest", "ccl\core\test\cclTest.vcxproj", "{F87048D4-952A-460E-96E8-1E2E1EAE34FC}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ccdTest", "coredll\test\ccdTest.vcxproj", "{6BDE1298-8F16-4275-BA8A-F6CC54CB9B9D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cclCommonsTest", "ccl\cclCommons\test\cclCommonsTest.vcxproj", "{53A380CF-B599-4170-89B1-642F1C3772E1}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pyconcept", "pyconcept\pyconcept.vcxproj", "{692F2E27-1749-4A3C-9543-C4101E7B8E49}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B0ABA27B-9D39-4B48-9977-AFF20925B309}.Debug|x64.ActiveCfg = Debug|x64
{B0ABA27B-9D39-4B48-9977-AFF20925B309}.Debug|x64.Build.0 = Debug|x64
{B0ABA27B-9D39-4B48-9977-AFF20925B309}.Debug|x86.ActiveCfg = Debug|Win32
{B0ABA27B-9D39-4B48-9977-AFF20925B309}.Debug|x86.Build.0 = Debug|Win32
{B0ABA27B-9D39-4B48-9977-AFF20925B309}.Release|x64.ActiveCfg = Release|x64
{B0ABA27B-9D39-4B48-9977-AFF20925B309}.Release|x64.Build.0 = Release|x64
{B0ABA27B-9D39-4B48-9977-AFF20925B309}.Release|x86.ActiveCfg = Release|Win32
{B0ABA27B-9D39-4B48-9977-AFF20925B309}.Release|x86.Build.0 = Release|Win32
{A8529C63-42F5-43E6-97B8-2EC83F23E1F9}.Debug|x64.ActiveCfg = Debug|x64
{A8529C63-42F5-43E6-97B8-2EC83F23E1F9}.Debug|x64.Build.0 = Debug|x64
{A8529C63-42F5-43E6-97B8-2EC83F23E1F9}.Debug|x86.ActiveCfg = Debug|Win32
{A8529C63-42F5-43E6-97B8-2EC83F23E1F9}.Debug|x86.Build.0 = Debug|Win32
{A8529C63-42F5-43E6-97B8-2EC83F23E1F9}.Release|x64.ActiveCfg = Release|x64
{A8529C63-42F5-43E6-97B8-2EC83F23E1F9}.Release|x64.Build.0 = Release|x64
{A8529C63-42F5-43E6-97B8-2EC83F23E1F9}.Release|x86.ActiveCfg = Release|Win32
{A8529C63-42F5-43E6-97B8-2EC83F23E1F9}.Release|x86.Build.0 = Release|Win32
{32469CE1-303B-4DB4-8E03-B7EBED5851EB}.Debug|x64.ActiveCfg = Debug|x64
{32469CE1-303B-4DB4-8E03-B7EBED5851EB}.Debug|x64.Build.0 = Debug|x64
{32469CE1-303B-4DB4-8E03-B7EBED5851EB}.Debug|x86.ActiveCfg = Debug|Win32
{32469CE1-303B-4DB4-8E03-B7EBED5851EB}.Debug|x86.Build.0 = Debug|Win32
{32469CE1-303B-4DB4-8E03-B7EBED5851EB}.Release|x64.ActiveCfg = Release|x64
{32469CE1-303B-4DB4-8E03-B7EBED5851EB}.Release|x64.Build.0 = Release|x64
{32469CE1-303B-4DB4-8E03-B7EBED5851EB}.Release|x86.ActiveCfg = Release|Win32
{32469CE1-303B-4DB4-8E03-B7EBED5851EB}.Release|x86.Build.0 = Release|Win32
{7E1D5338-F819-4C96-B461-9EAAB8D02E1D}.Debug|x64.ActiveCfg = Debug|x64
{7E1D5338-F819-4C96-B461-9EAAB8D02E1D}.Debug|x64.Build.0 = Debug|x64
{7E1D5338-F819-4C96-B461-9EAAB8D02E1D}.Debug|x86.ActiveCfg = Debug|Win32
{7E1D5338-F819-4C96-B461-9EAAB8D02E1D}.Debug|x86.Build.0 = Debug|Win32
{7E1D5338-F819-4C96-B461-9EAAB8D02E1D}.Release|x64.ActiveCfg = Release|x64
{7E1D5338-F819-4C96-B461-9EAAB8D02E1D}.Release|x64.Build.0 = Release|x64
{7E1D5338-F819-4C96-B461-9EAAB8D02E1D}.Release|x86.ActiveCfg = Release|Win32
{7E1D5338-F819-4C96-B461-9EAAB8D02E1D}.Release|x86.Build.0 = Release|Win32
{5A2501C1-FEFB-4B14-A94D-E8F19ADEA239}.Debug|x64.ActiveCfg = Debug|x64
{5A2501C1-FEFB-4B14-A94D-E8F19ADEA239}.Debug|x64.Build.0 = Debug|x64
{5A2501C1-FEFB-4B14-A94D-E8F19ADEA239}.Debug|x86.ActiveCfg = Debug|Win32
{5A2501C1-FEFB-4B14-A94D-E8F19ADEA239}.Debug|x86.Build.0 = Debug|Win32
{5A2501C1-FEFB-4B14-A94D-E8F19ADEA239}.Release|x64.ActiveCfg = Release|x64
{5A2501C1-FEFB-4B14-A94D-E8F19ADEA239}.Release|x64.Build.0 = Release|x64
{5A2501C1-FEFB-4B14-A94D-E8F19ADEA239}.Release|x86.ActiveCfg = Release|Win32
{5A2501C1-FEFB-4B14-A94D-E8F19ADEA239}.Release|x86.Build.0 = Release|Win32
{76B03803-56CC-47C2-A8F0-2241FCAF2898}.Debug|x64.ActiveCfg = Debug|x64
{76B03803-56CC-47C2-A8F0-2241FCAF2898}.Debug|x64.Build.0 = Debug|x64
{76B03803-56CC-47C2-A8F0-2241FCAF2898}.Debug|x86.ActiveCfg = Debug|Win32
{76B03803-56CC-47C2-A8F0-2241FCAF2898}.Debug|x86.Build.0 = Debug|Win32
{76B03803-56CC-47C2-A8F0-2241FCAF2898}.Release|x64.ActiveCfg = Release|x64
{76B03803-56CC-47C2-A8F0-2241FCAF2898}.Release|x64.Build.0 = Release|x64
{76B03803-56CC-47C2-A8F0-2241FCAF2898}.Release|x86.ActiveCfg = Release|Win32
{76B03803-56CC-47C2-A8F0-2241FCAF2898}.Release|x86.Build.0 = Release|Win32
{4754356B-DC01-4564-A035-270FFB72F6A0}.Debug|x64.ActiveCfg = Debug|x64
{4754356B-DC01-4564-A035-270FFB72F6A0}.Debug|x64.Build.0 = Debug|x64
{4754356B-DC01-4564-A035-270FFB72F6A0}.Debug|x86.ActiveCfg = Debug|Win32
{4754356B-DC01-4564-A035-270FFB72F6A0}.Debug|x86.Build.0 = Debug|Win32
{4754356B-DC01-4564-A035-270FFB72F6A0}.Release|x64.ActiveCfg = Release|x64
{4754356B-DC01-4564-A035-270FFB72F6A0}.Release|x64.Build.0 = Release|x64
{4754356B-DC01-4564-A035-270FFB72F6A0}.Release|x86.ActiveCfg = Release|Win32
{4754356B-DC01-4564-A035-270FFB72F6A0}.Release|x86.Build.0 = Release|Win32
{6FDADD78-2FC1-4DBD-A2C4-B2EF35025AC0}.Debug|x64.ActiveCfg = Debug|x64
{6FDADD78-2FC1-4DBD-A2C4-B2EF35025AC0}.Debug|x64.Build.0 = Debug|x64
{6FDADD78-2FC1-4DBD-A2C4-B2EF35025AC0}.Debug|x86.ActiveCfg = Debug|Win32
{6FDADD78-2FC1-4DBD-A2C4-B2EF35025AC0}.Debug|x86.Build.0 = Debug|Win32
{6FDADD78-2FC1-4DBD-A2C4-B2EF35025AC0}.Release|x64.ActiveCfg = Release|x64
{6FDADD78-2FC1-4DBD-A2C4-B2EF35025AC0}.Release|x64.Build.0 = Release|x64
{6FDADD78-2FC1-4DBD-A2C4-B2EF35025AC0}.Release|x86.ActiveCfg = Release|Win32
{6FDADD78-2FC1-4DBD-A2C4-B2EF35025AC0}.Release|x86.Build.0 = Release|Win32
{F87048D4-952A-460E-96E8-1E2E1EAE34FC}.Debug|x64.ActiveCfg = Debug|x64
{F87048D4-952A-460E-96E8-1E2E1EAE34FC}.Debug|x64.Build.0 = Debug|x64
{F87048D4-952A-460E-96E8-1E2E1EAE34FC}.Debug|x86.ActiveCfg = Debug|Win32
{F87048D4-952A-460E-96E8-1E2E1EAE34FC}.Debug|x86.Build.0 = Debug|Win32
{F87048D4-952A-460E-96E8-1E2E1EAE34FC}.Release|x64.ActiveCfg = Release|x64
{F87048D4-952A-460E-96E8-1E2E1EAE34FC}.Release|x64.Build.0 = Release|x64
{F87048D4-952A-460E-96E8-1E2E1EAE34FC}.Release|x86.ActiveCfg = Release|Win32
{F87048D4-952A-460E-96E8-1E2E1EAE34FC}.Release|x86.Build.0 = Release|Win32
{6BDE1298-8F16-4275-BA8A-F6CC54CB9B9D}.Debug|x64.ActiveCfg = Debug|x64
{6BDE1298-8F16-4275-BA8A-F6CC54CB9B9D}.Debug|x64.Build.0 = Debug|x64
{6BDE1298-8F16-4275-BA8A-F6CC54CB9B9D}.Debug|x86.ActiveCfg = Debug|Win32
{6BDE1298-8F16-4275-BA8A-F6CC54CB9B9D}.Debug|x86.Build.0 = Debug|Win32
{6BDE1298-8F16-4275-BA8A-F6CC54CB9B9D}.Release|x64.ActiveCfg = Release|x64
{6BDE1298-8F16-4275-BA8A-F6CC54CB9B9D}.Release|x64.Build.0 = Release|x64
{6BDE1298-8F16-4275-BA8A-F6CC54CB9B9D}.Release|x86.ActiveCfg = Release|Win32
{6BDE1298-8F16-4275-BA8A-F6CC54CB9B9D}.Release|x86.Build.0 = Release|Win32
{53A380CF-B599-4170-89B1-642F1C3772E1}.Debug|x64.ActiveCfg = Debug|x64
{53A380CF-B599-4170-89B1-642F1C3772E1}.Debug|x64.Build.0 = Debug|x64
{53A380CF-B599-4170-89B1-642F1C3772E1}.Debug|x86.ActiveCfg = Debug|Win32
{53A380CF-B599-4170-89B1-642F1C3772E1}.Debug|x86.Build.0 = Debug|Win32
{53A380CF-B599-4170-89B1-642F1C3772E1}.Release|x64.ActiveCfg = Release|x64
{53A380CF-B599-4170-89B1-642F1C3772E1}.Release|x64.Build.0 = Release|x64
{53A380CF-B599-4170-89B1-642F1C3772E1}.Release|x86.ActiveCfg = Release|Win32
{53A380CF-B599-4170-89B1-642F1C3772E1}.Release|x86.Build.0 = Release|Win32
{692F2E27-1749-4A3C-9543-C4101E7B8E49}.Debug|x64.ActiveCfg = Debug|x64
{692F2E27-1749-4A3C-9543-C4101E7B8E49}.Debug|x64.Build.0 = Debug|x64
{692F2E27-1749-4A3C-9543-C4101E7B8E49}.Debug|x86.ActiveCfg = Debug|Win32
{692F2E27-1749-4A3C-9543-C4101E7B8E49}.Debug|x86.Build.0 = Debug|Win32
{692F2E27-1749-4A3C-9543-C4101E7B8E49}.Release|x64.ActiveCfg = Release|x64
{692F2E27-1749-4A3C-9543-C4101E7B8E49}.Release|x86.ActiveCfg = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{B0ABA27B-9D39-4B48-9977-AFF20925B309} = {FB8E3011-BB07-4F6E-B2C7-A0A183BC5000}
{A8529C63-42F5-43E6-97B8-2EC83F23E1F9} = {FB8E3011-BB07-4F6E-B2C7-A0A183BC5000}
{32469CE1-303B-4DB4-8E03-B7EBED5851EB} = {FB8E3011-BB07-4F6E-B2C7-A0A183BC5000}
{7E1D5338-F819-4C96-B461-9EAAB8D02E1D} = {FB8E3011-BB07-4F6E-B2C7-A0A183BC5000}
{5A2501C1-FEFB-4B14-A94D-E8F19ADEA239} = {FB8E3011-BB07-4F6E-B2C7-A0A183BC5000}
{76B03803-56CC-47C2-A8F0-2241FCAF2898} = {FB8E3011-BB07-4F6E-B2C7-A0A183BC5000}
{4754356B-DC01-4564-A035-270FFB72F6A0} = {FB8E3011-BB07-4F6E-B2C7-A0A183BC5000}
{F87048D4-952A-460E-96E8-1E2E1EAE34FC} = {FB8E3011-BB07-4F6E-B2C7-A0A183BC5000}
{53A380CF-B599-4170-89B1-642F1C3772E1} = {FB8E3011-BB07-4F6E-B2C7-A0A183BC5000}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F00AAF54-A857-4895-B4D8-1A09BC7CAD50}
EndGlobalSection
EndGlobal

103
Dockerfile Normal file
View File

@ -0,0 +1,103 @@
# ubunutu is the base image
FROM ubuntu:jammy as cpp-builder
LABEL version="1.0"
LABEL maintainer="IRBorisov iborisov@acconcept.ru"
LABEL description="Linux build environment"
ARG LINUX_FLAVOR=ubuntu
ARG LINUX_DISTR=jammy
ARG DEBIAN_FRONTEND=noninteractive
# Install standard packages
RUN apt-get update -qq && \
apt-get full-upgrade -y && \
apt-get install -y --no-install-recommends \
nano \
wget \
curl \
tar \
unzip \
git \
software-properties-common \
build-essential \
gpg-agent && \
rm -rf /var/lib/apt/lists/*
# Install Python and Conan
RUN add-apt-repository -y ppa:deadsnakes/ppa && \
apt-get update -qq && \
apt-get install -y --no-install-recommends \
python3.12 \
python3.12-venv \
python3.12-dev && \
curl -sS https://bootstrap.pypa.io/get-pip.py | python3.12 && \
python3.12 -m pip install --upgrade pip setuptools && \
python3.12 -m pip install conan && \
rm -rf /var/lib/apt/lists/*
# Add GCC compiler
ARG GCC_VER="13"
RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test && \
apt-get update -qq && \
apt-get install -y --no-install-recommends \
gcc-${GCC_VER} \
g++-${GCC_VER} && \
update-alternatives --install /usr/bin/gcc gcc $(which gcc-${GCC_VER}) 100 && \
update-alternatives --install /usr/bin/g++ g++ $(which g++-${GCC_VER}) 100 && \
rm -rf /var/lib/apt/lists/*
# Add Clang compiler
ARG LLVM_VER="18"
RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - 2>/dev/null
RUN add-apt-repository -y "deb http://apt.llvm.org/${LINUX_DISTR}/ llvm-toolchain-${LINUX_DISTR}-${LLVM_VER} main" && \
apt-get update -qq && \
apt-get install -y --no-install-recommends \
clang-${LLVM_VER} \
clang-tools-${LLVM_VER} \
clangd-${LLVM_VER} \
lld-${LLVM_VER} \
lldb-${LLVM_VER} \
llvm-${LLVM_VER} \
llvm-${LLVM_VER}-dev \
llvm-${LLVM_VER}-runtime \
libc++-${LLVM_VER}-dev \
libc++abi-${LLVM_VER}-dev \
libclang-${LLVM_VER}-dev \
libclang-common-${LLVM_VER}-dev \
libfuzzer-${LLVM_VER}-dev \
clang-tidy-${LLVM_VER} && \
update-alternatives --install /usr/bin/clang clang $(which clang-${LLVM_VER}) 100 && \
update-alternatives --install /usr/bin/clang++ clang++ $(which clang++-${LLVM_VER}) 100 && \
update-alternatives --install /usr/bin/clang-tidy clang-tidy $(which clang-tidy-${LLVM_VER}) 1 && \
rm -rf /var/lib/apt/lists/*
# Add CMake
RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null \
| gpg --dearmor - | tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null && \
apt-add-repository "deb https://apt.kitware.com/${LINUX_FLAVOR}/ ${LINUX_DISTR} main" && \
apt-get update -qq && \
apt-get install -y --no-install-recommends \
cmake && \
rm -rf /var/lib/apt/lists/*
# Cleanup cached apt data we don't need anymore
RUN apt-get autoclean -y && \
apt-get autoremove -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# ====================== END OF CPP-Python environment ==================
FROM cpp-builder as CCL
ARG CMAKE_BUILD_TYPE="Release"
# Choose between clang/clang++ or gcc/g++
ENV CC="gcc"
ENV CXX="g++"
ENV BUILD_HOME=/home
WORKDIR $BUILD_HOME
COPY . /home
ENTRYPOINT ["sh", "/home/entrypoint.sh"]

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2024 Ivan
Copyright (c) 2024 CIHT CONCEPT, IRBorisov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

39
README.md Normal file
View File

@ -0,0 +1,39 @@
# 🏛️ ConceptCore
C++ library for manipulating concepts in formal language of advanced set theory
## 🌲 Structure
- ccl - ConceptCoreLibrary static C++ library
- coredll - ConceptCoreDLL binary for Windows embedding
- pyconcept - Python wrapper for some core RSLanguage and parsing functions
## 📦 Project build
Use Dockerfile to setup Clang / GCC build for Ubuntu development.
Use VS Solution for Windows development.
Use ccl/CMakeLists.txt for other platforms.
Windows development requires Bison installed and Visual Studio 2022+.
After changing grammar / syntax you can rebuild lexers and parser using scripts in 'scripts' folder.
If you want to only build pyconcept:
- Build CCL-Full.sln in Release mode.
- Make sure you have Python installed and executable path is present in environment variables.
- Run pyconcept\script\Build.ps1. Answer 'A' if execution policy prompts for input
- pyconcept wheel will be deposited in output\py folder
## 💝 Acknowledgements
This project is based on multiple projects and works listed below. If you notice any problems with licensing or missing acknowledgements please inform repository maintainer.
- [Re-flex](https://github.com/Genivia/RE-flex) provides clear way to generate lexical analyzers for ASCII and Unicode syntax variations
- [Bison](https://www.gnu.org/software/bison/) is used to generate language parser
- [nlohmann-json](https://github.com/nlohmann/json) is embedded as C++ JSON parser for conceptual schema persistence and high level JSON strings API
- [Clang-tidy](https://clang.llvm.org/extra/clang-tidy/) along with Visual Studio analyzer are used for C++ static code analysis
- [pybind11](https://github.com/pybind/pybind11) is used to generate Python module wrapper for C++ integration
- [CMake](https://cmake.org/) provides C++ projects build toolchain
- [conan](https://conan.io/) and NuGet are used to manage package dependencies
- Docker container is used to provide consistent build environment for Linux builds
- Microsoft Visual Studio 2022 and Visual Studio Code are used as IDE's and build environments for Windows builds

88
ccl/CMakeLists.txt Normal file
View File

@ -0,0 +1,88 @@
cmake_minimum_required(VERSION 3.23)
project (ConceptCoreLibrary VERSION 1.2.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
##
## Project options
##
option(CC_BuildTests "Include tests executable" TRUE)
option(CC_UseSanitizers "Use sanitizers" FALSE)
## Compiler options
include(cmake/CXXTargets.cmake)
## Import internal targets
add_subdirectory(cclCommons)
add_subdirectory(cclGraph)
add_subdirectory(cclLang)
add_subdirectory(rslang)
##
## Project Setup
##
add_library(${PROJECT_NAME})
set_target_properties(${PROJECT_NAME} PROPERTIES
OUTPUT_NAME ${PROJECT_NAME}
DEBUG_POSTFIX d
POSITION_INDEPENDENT_CODE ON
)
target_sources(${PROJECT_NAME}
PRIVATE
core/unity/CCL.cpp
)
target_include_directories(${PROJECT_NAME}
PUBLIC
cclCommons/include
cclGraph/include
cclLang/include
rslang/include
core/include
PRIVATE
core/import/include
core/header
)
target_link_libraries(${PROJECT_NAME}
INTERFACE
RSLang
cclLang
cclGraph
PRIVATE
ccl_CXXwarnings
ccl_CXXoptions
)
if(MSVC)
if (CMAKE_BUILD_TYPE EQUAL "DEBUG")
set(LIB_SUFFIX d)
else()
set(LIB_SUFFIX "")
endif ()
set_target_properties(${PROJECT_NAME}
PROPERTIES
COMPILE_PDB_NAME ${PROJECT_NAME}${LIB_SUFFIX}
)
get_target_property(output_dir ${PROJECT_NAME} BINARY_DIR)
install(FILES ${output_dir}/$<CONFIG>/${PROJECT_NAME}${LIB_SUFFIX}.pdb DESTINATION lib)
endif()
install(TARGETS ${PROJECT_NAME}
ARCHIVE
DESTINATION lib
LIBRARY
DESTINATION lib
INCLUDES
DESTINATION include
)
install(DIRECTORY core/include/ DESTINATION include)
if(CC_BuildTests)
enable_testing()
add_subdirectory("core/test")
endif()

View File

@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.23)
project (cclCommons VERSION 1.2.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
##
## Project options
##
option(CC_BuildTests "Include tests executable" TRUE)
option(CC_UseSanitizers "Use sanitizers" FALSE)
## Compiler options
include(../cmake/CXXTargets.cmake)
##
## Project Setup
##
install(DIRECTORY include/ DESTINATION include)
if(CC_BuildTests)
enable_testing()
add_subdirectory("test")
endif()

View File

@ -0,0 +1,9 @@
[requires]
gtest/1.14.0
[generators]
CMakeDeps
CMakeToolchain
[layout]
cmake_layout

View File

@ -0,0 +1,337 @@
#pragma once
#include <string>
#include <string_view>
#include <vector>
#include <unordered_map>
#include <numeric>
#include <algorithm>
#include <optional>
#include <functional>
#include <cassert>
#include <utility>
namespace ccl {
inline std::string operator""_c17(const char8_t* input, size_t /*size*/) {
// TODO: C++20 use constexpr version from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1423r0.html
return std::string{ reinterpret_cast<const char*>(input) };
}
template<typename T>
std::u8string to_u8string(const T& obj) {
auto str = std::to_string(obj);
return std::u8string{ begin(str), end(str) }; // TODO: do not copy!
}
template<>
inline std::u8string to_u8string<std::string>(const std::string& obj) {
return std::u8string{ begin(obj), end(obj) };
}
inline std::string u8to_string(const std::u8string& obj) {
return std::string{ begin(obj), end(obj) };
}
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 26481 ) // disable pointer arithmetic warning - it is ok to use it on string_view
#endif
//! Split string_view by specific symbol
inline std::vector<std::string_view> SplitBySymbol(std::string_view text, char delim = ',') {
std::vector<std::string_view> items{};
int32_t leftComma = 0;
int32_t rightComma = -1;
int32_t index = -1;
for (const auto& symbol : text) {
++index;
if (symbol != delim) {
continue;
}
leftComma = rightComma;
rightComma = index;
const auto start = leftComma + 1;
const auto length = rightComma - start;
items.emplace_back(text.data() + start, length);
}
items.emplace_back(text.data() + rightComma + 1, static_cast<int32_t>(text.size()) - rightComma - 1);
return items;
}
//! Trim whitespace
inline std::string_view TrimWhitespace(std::string_view text) {
if (empty(text)) {
return text;
}
size_t start = 0U;
size_t end = text.length() - 1;
while (std::isspace(text.at(start)) && ++start < end) {}
while (std::isspace(text.at(end)) && end != 0 && --end >= start) {}
const size_t length = start > end ? 0 : end - start + 1;
return { text.data() + start, length };
}
//! Check if text is integer (including negative integers)
inline bool IsInteger(std::string_view text) {
if (empty(text)) {
return false;
}
size_t pos = 0;
if (text.at(pos) == '-') {
++pos;
if (text.length() == 1) {
return false;
}
}
while (pos < text.length()) {
if (!std::isdigit(text.at(pos))) {
return false;
}
++pos;
}
return true;
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif
//! String position corresponds to CodePoint position
/*
Warning: codepoint position != array index in char* UTF8 string
*/
using StrPos = int32_t;
//! String range based on interval algebra
/*
Warning: lightweight class, doesnt provide any bounds checks!
For reference see https://en.wikipedia.org/wiki/Allen%27s_interval_algebra
Pre: start <= finish
*/
struct StrRange {
constexpr StrRange() noexcept = default;
explicit constexpr StrRange(StrPos start, StrPos end) noexcept
: start{ start }, finish{ end } {}
[[nodiscard]] static constexpr StrRange FromLength(StrPos start, StrPos len) noexcept {
return StrRange{ start, start + len };
}
StrPos start{ 0 };
StrPos finish{ 0 };
[[nodiscard]] constexpr StrPos length() const noexcept { return finish - start; }
[[nodiscard]] constexpr bool empty() const noexcept { return finish == start; }
[[nodiscard]] constexpr bool operator==(const StrRange& rhs) const noexcept {
return start == rhs.start && finish == rhs.finish;
}
[[nodiscard]] constexpr bool operator!=(const StrRange& rhs) const noexcept {
return !(*this == rhs);
}
[[nodiscard]] constexpr bool Contains(const StrPos pos) const noexcept {
return start <= pos && finish > pos;
}
[[nodiscard]] constexpr bool Contains(const StrRange& rhs) const noexcept {
if (std::empty(rhs)) {
return Contains(rhs.finish);
} else {
return start <= rhs.start && finish >= rhs.finish;
}
}
[[nodiscard]] constexpr bool SharesBorder(const StrRange& rhs) const noexcept {
return Meets(rhs) || rhs.Meets(*this);
}
[[nodiscard]] constexpr bool IsBefore(const StrRange& rhs) const noexcept {
return finish < rhs.start;
}
[[nodiscard]] constexpr bool IsAfter(const StrRange& rhs) const noexcept {
return start > rhs.finish;
}
[[nodiscard]] constexpr bool Meets(const StrRange& rhs) const noexcept {
return finish == rhs.start;
}
[[nodiscard]] constexpr bool Overlaps(const StrRange& rhs) const noexcept {
if (start == rhs.start) {
return true;
} else if (start < rhs.start) {
return finish > rhs.start;
} else {
return rhs.finish > start;
}
}
[[nodiscard]] constexpr bool Starts(const StrRange& rhs) const noexcept {
return start == rhs.start && finish < rhs.finish;
}
[[nodiscard]] constexpr bool Finishes(const StrRange& rhs) const noexcept {
return finish == rhs.finish && start > rhs.start;
}
[[nodiscard]] constexpr bool IsDuring(const StrRange& rhs) const noexcept {
return start > rhs.start && finish < rhs.finish;
}
//! Pre: assert(ammount >= 0)
constexpr StrRange& SetLength(StrPos ammount) noexcept {
finish = start + ammount;
return *this;
}
constexpr StrRange& Shift(StrPos ammount) noexcept {
start = start + ammount;
finish = finish + ammount;
return *this;
}
constexpr StrRange& CollapseEnd() noexcept {
start = finish;
return *this;
}
constexpr StrRange& CollapseStart() noexcept {
finish = start;
return *this;
}
[[nodiscard]] constexpr std::optional<StrRange> Intersect(const StrRange& rhs) const {
if (IsBefore(rhs) || IsAfter(rhs)) {
return std::nullopt;
} else {
return StrRange{ std::max(start, rhs.start), std::min(finish, rhs.finish) };
}
}
static constexpr StrRange Merge(const std::vector<StrRange>& base) {
if (std::empty(base)) {
return StrRange{};
} else {
return std::accumulate(next(begin(base)), end(base), *begin(base),
[](StrRange bounds, const StrRange& rng) noexcept {
bounds.start = std::min(rng.start, bounds.start);
bounds.finish = std::max(rng.finish, bounds.finish);
return bounds;
});
}
}
};
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 26446 ) // Note: do not warn about indexes
#endif
//! Evaluate codepoint size from first byte of UTF8 string
/*
Requires UTF8 conforming input
*/
[[nodiscard]] constexpr int8_t UTF8CharSize(unsigned const char firstByte) noexcept {
constexpr unsigned char kFirstBitMask = 128; // 1000000
constexpr unsigned char kThirdBitMask = 32; // 0010000
constexpr unsigned char kFourthBitMask = 16; // 0001000
if ((firstByte & kFirstBitMask) == 0) {
return 1;
} else if ((firstByte & kThirdBitMask) == 0) {
return 2;
} else if ((firstByte & kFourthBitMask) == 0) {
return 3;
} else {
return 4;
}
}
//! iterator for UTF8 string
/*
Warning: iterator returns first byte of each codepoint.
To access UTF8 symbol one should use BytePosition and SymbolSize functions
*/
class UTF8Iterator {
public:
// stl compatibility
using iterator_category = std::forward_iterator_tag;
using value_type = char;
using difference_type = std::ptrdiff_t;
using pointer = const char*;
using reference = const char&;
public:
constexpr explicit UTF8Iterator(const std::string_view data, const StrPos position = 0) noexcept
: data{ data } {
if (position != endPos) {
GotoCodepoint(position);
}
}
static constexpr StrPos endPos = -1;
private:
StrPos current{ endPos };
size_t bytePosition{ 0 };
std::string_view data;
public:
// Note: comparing iterators from different strings is unspecified behavior!
[[nodiscard]] bool operator==(const UTF8Iterator& rhs) const noexcept {
return current == rhs.current;
}
[[nodiscard]] bool operator!=(const UTF8Iterator& rhs) const noexcept {
return !(*this == rhs);
}
UTF8Iterator& operator++() noexcept {
bytePosition += SymbolSize(); // Note: range check not needed
++current;
if (size(data) <= bytePosition) {
current = endPos;
}
return *this;
}
[[nodiscard]] reference operator*() const noexcept { return data[bytePosition]; }
[[nodiscard]] size_t BytePosition() const noexcept { return bytePosition; }
[[nodiscard]] StrPos Position() const noexcept { return current; }
[[nodiscard]] size_t SymbolSize() const noexcept {
return static_cast<size_t>(UTF8CharSize(static_cast<unsigned char>(data[bytePosition])));
}
private:
void GotoCodepoint(const StrPos cpPosition) noexcept {
assert(cpPosition >= 0);
for (current = 0; current != cpPosition && size(data) > bytePosition; ++current) {
bytePosition += SymbolSize(); // Note: range check not needed
}
if (size(data) <= bytePosition) {
current = endPos;
}
};
};
#ifdef _MSC_VER
#pragma warning( pop )
#endif
[[nodiscard]] constexpr UTF8Iterator UTF8Begin(const std::string_view source) {
return UTF8Iterator{ source };
}
[[nodiscard]] constexpr UTF8Iterator UTF8End(const std::string_view source) {
return UTF8Iterator{ source, UTF8Iterator::endPos };
}
[[nodiscard]] inline std::string_view Substr(const std::string_view source, const StrRange range) noexcept {
// Note: add constexpr in C++20
const auto start = UTF8Iterator(source, range.start);
auto finish = UTF8Iterator(source, range.finish - 1);
if (start == UTF8End(source) || finish == UTF8End(source)) {
return {};
} else if (++finish == UTF8End(source)) {
return source.substr(start.BytePosition(), size(source) - start.BytePosition());
} else {
return source.substr(start.BytePosition(), finish.BytePosition() - start.BytePosition());
}
}
[[nodiscard]] inline StrPos SizeInCodePoints(const std::string_view utf8string) noexcept {
auto size = 0;
for (auto it = UTF8Begin(utf8string); it != UTF8End(utf8string); ++it, ++size) {}
return size;
}
} // namespace ccl

View File

@ -0,0 +1,32 @@
#pragma once
#include <optional>
#include <functional>
#include <string>
namespace ccl {
using StrSubstitutes = std::unordered_map<std::string, std::string>;
using StrTranslator = std::function<std::optional<std::string>(const std::string&)>;
inline StrTranslator CreateTranslator(const StrSubstitutes& substitutes) {
return [&substitutes](const std::string& oldVal) -> StrTranslator::result_type {
if (!substitutes.contains(oldVal)) {
return std::nullopt;
} else {
return substitutes.at(oldVal);
}
};
}
inline StrTranslator CreateTranslator(StrSubstitutes&& substitutes) {
return [substitutes = std::move(substitutes)](const std::string& oldVal) -> StrTranslator::result_type {
if (!substitutes.contains(oldVal)) {
return std::nullopt;
} else {
return substitutes.at(oldVal);
}
};
}
} // namespace ccl

View File

@ -0,0 +1,52 @@
#pragma once
#include "ccl/cclTypes.hpp"
#include <string>
#include <cstdint>
#include <vector>
#include <algorithm>
#include <numeric>
namespace ccl::change {
using Hash = uint32_t;
inline Hash HashString(const std::string& string) {
static constexpr auto BERNSTEIN_CONSTANT = 33U;
return std::accumulate(begin(string), end(string), Hash{ 0 },
[&](Hash hash, char symbol) noexcept
{ return hash * BERNSTEIN_CONSTANT + static_cast<uint32_t>(symbol); });
}
//! Usage types enumeration
enum class Usage : uint8_t {
notUsed = 0, // не используется
asElement = 1, // Наследуется
asOption = 2, // Используется в таблице отождествлений
};
//! Abstract modification info
struct Modification {};
//! Abstract modification message
class ModMessage : public types::Message {
const Modification* modInfo{ nullptr };
public:
ModMessage() = default;
explicit ModMessage(const Modification& modInfo) noexcept
: modInfo{ &modInfo } {}
[[nodiscard]] const Modification* GetModInfo() const noexcept { return modInfo; }
[[nodiscard]] uint32_t Type() const noexcept override { return modificationCode; }
};
//! Observable modifications base
class ObservableMods : public types::Observable {
protected:
void NotifyModification(const Modification& mod = {}) {
Notify(ModMessage{ mod });
}
};
} // namespace ccl::change

View File

@ -0,0 +1,371 @@
#pragma once
// Metaprogramming tools
#include <algorithm>
#include <iterator>
#include <functional>
#include <memory>
#include <type_traits>
#include <cstddef>
namespace ccl::meta {
//! Basic identity class for template overloading/specializations
template<typename T>
struct Identity { using type = T; };
//! Naive propagate_const implementation aimed at unique_ptr
template<typename T>
class PropagateConst {
// TODO: C++20 constexpr ctors
T pointer{ nullptr };
public:
~PropagateConst() noexcept = default;
PropagateConst() = default;
PropagateConst(const PropagateConst&) = delete;
PropagateConst& operator=(const PropagateConst&) = delete;
PropagateConst(PropagateConst&&) noexcept = default;
PropagateConst& operator=(PropagateConst&&) noexcept = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
PropagateConst(T&& val) noexcept
: pointer{ std::move(val) } {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
PropagateConst(std::nullptr_t) noexcept {}
template<typename Up,
typename std::enable_if_t<std::is_constructible_v<T, Up&&>>>
PropagateConst(Up&& up) // NOLINT(google-explicit-constructor, hicpp-explicit-conversions, bugprone-forwarding-reference-overload)
: pointer{ std::forward<Up>(up) } {}
template<typename Up,
typename std::enable_if_t<std::is_constructible_v<T, Up&&>>>
PropagateConst(PropagateConst<Up>&& up) // NOLINT(google-explicit-constructor, hicpp-explicit-conversions)
: pointer{ std::move(up.pointer) } {}
template<typename Up,
typename std::enable_if_t<std::is_constructible_v<T, Up&&>>>
constexpr PropagateConst& operator=(Up&& up) {
pointer = std::forward<Up>(up);
return *this;
}
template<typename Up,
typename std::enable_if_t<std::is_constructible_v<T, Up&&>>>
constexpr PropagateConst& operator=(PropagateConst<Up>&& up) {
pointer = std::move(up.pointer);
return *this;
}
public:
using ElementType = std::remove_reference_t<decltype(*std::declval<T>())>;
constexpr ElementType* get() {
return pointer.get();
}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
constexpr operator ElementType*() {
return get();
}
constexpr ElementType& operator*() {
return *get();
}
constexpr ElementType* operator->() {
return get();
}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
constexpr operator const ElementType*() const {
return get();
}
[[nodiscard]] constexpr const ElementType* get() const {
return pointer.get();
}
constexpr const ElementType& operator*() const {
return *get();
}
constexpr const ElementType* operator->() const {
return get();
}
constexpr void swap(PropagateConst<T>& second) noexcept {
std::swap(pointer, second.pointer);
}
constexpr bool operator==(const PropagateConst& rhs) const noexcept {
return pointer == rhs.pointer;
}
constexpr bool operator!=(const PropagateConst& rhs) const noexcept {
return pointer != rhs.pointer;
}
constexpr bool operator==(std::nullptr_t) const noexcept {
return pointer == nullptr;
}
constexpr bool operator!=(std::nullptr_t) const noexcept {
return pointer != nullptr;
}
};
//! Unique pointer propagating const wrapper alias
template<typename T>
using UniqueCPPtr = PropagateConst<std::unique_ptr<T>>;
//! Grouping multiple overloaded lambas
/*
see https://www.youtube.com/watch?v=XjOVebhUOY around 20:30
*/
template <typename ...Ops>
struct Overloads : Ops... {
using Ops::operator()...;
};
template <typename ...Ops>
Overloads(Ops...)->Overloads<Ops...>;
//! Apply function object to tuples created from elements container
template <typename Container,
typename Function,
typename Value = typename Container::value_type>
void ForEachPair(const Container& input, const Function& Fn) {
std::for_each(begin(input), end(input), [&input, &Fn](const Value& el1) {
std::for_each(begin(input), end(input), [&el1, &Fn](const Value& el2) {
Fn(el1, el2);
});
});
}
//! CRTP implementation
template <typename Base, template<typename> class crtpType>
struct crtp {
[[nodiscard]] Base& BaseT() noexcept { return static_cast<Base&>(*this); }
[[nodiscard]] const Base& BaseT() const noexcept { return static_cast<Base const&>(*this); }
};
//! Abstract strong type
/*
see https://github.com/joboccara/NamedType
*/
template <typename T>
struct Incrementable : crtp<T, Incrementable> {
T& operator+=(const T& other) { this->BaseT().get() += other.get(); return this->BaseT(); }
};
template <typename T>
struct PreIncrementable : crtp<T, PreIncrementable> {
T& operator++() { ++this->BaseT().get(); return this->BaseT(); }
};
template <typename T>
struct Addable : crtp<T, Addable> {
T operator+(const T& other) const { return T(this->BaseT().get() + other.get()); }
};
template <typename T>
struct Subtractable : crtp<T, Subtractable> {
T operator-(const T& other) const { return T(this->BaseT().get() - other.get()); }
};
template <typename T>
struct Multiplicable : crtp<T, Multiplicable> {
T operator*(const T& other) const { return T(this->BaseT().get() * other.get()); }
};
template <typename T>
struct Comparable : crtp<T, Comparable> {
bool operator==(const T& other) const noexcept { return this == &other || other.get() == this->BaseT().get(); }
bool operator!=(const T& other) const noexcept { return !(*this == other); }
};
template <typename T>
struct Orderable : crtp<T, Orderable> {
bool operator<(const T& other) const noexcept { return this->BaseT().get() < other.get(); }
bool operator>(const T& other) const noexcept { return other.get() < this->BaseT().get(); }
bool operator<=(const T& other) const noexcept { return !(other.get() < this->BaseT().get()); }
bool operator>=(const T& other) const noexcept { return !(*this < other); }
bool operator==(const T& other) const noexcept { return !(*this < other) && !(other.get() < this->BaseT().get()); }
bool operator!=(const T& other) const noexcept { return !(*this == other); }
};
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 26482 26446) // Note: ignore bounds warning when trying to address massive of static values
#endif
//! Iterable enumeration
template <typename T, T... args>
class EnumIter {
size_t pos{ 0 };
static constexpr T values[] = { args... }; // NOLINT: static C array
static constexpr size_t enumSize = sizeof...(args);
public:
EnumIter() = default;
explicit EnumIter(T val)
: pos(static_cast<size_t>(std::distance(&values[0], find(&values[0], &values[enumSize], val)))) {}
public:
// stl compatibility
using iterator_category = std::input_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = const T*;
using reference = const T&;
EnumIter end() noexcept { EnumIter result{}; result.pos = enumSize; return result; }
EnumIter begin() noexcept { return EnumIter{}; }
static constexpr size_t size() { return enumSize; }
const T& operator*() const noexcept { return values[pos]; }
EnumIter& operator--() noexcept { --pos; return *this; }
EnumIter& operator++() noexcept { ++pos; return *this; }
bool operator==(const EnumIter& rhs) const noexcept { return pos == rhs.pos; }
bool operator!=(const EnumIter& rhs) const noexcept { return pos != rhs.pos; }
};
#ifdef _MSC_VER
#pragma warning( pop )
#endif
//! Abstract mutating facet
/*
Note: facets are not movable and not copyable because of Core&.
Can use std::reference_wrapper instead if needed
*/
template<typename Core>
struct MutatorFacet {
Core& core;
protected:
~MutatorFacet() noexcept = default;
public:
MutatorFacet(const MutatorFacet&) = delete;
MutatorFacet& operator=(const MutatorFacet&) = delete;
explicit MutatorFacet(Core& core) noexcept
: core{ core } {}
};
//! Abstract constant facet
template<typename Core>
struct ConstFacet {
const Core& core;
protected:
~ConstFacet() noexcept = default;
public:
ConstFacet(const ConstFacet&) = delete;
ConstFacet& operator=(const ConstFacet&) = delete;
explicit ConstFacet(const Core& core) noexcept :
core{ core } {}
};
//! Abstract polymorphic iterator
template<typename Value>
class PolyFCIterator {
struct ImplConcept;
template<typename Iter> struct ImplModel;
std::unique_ptr<ImplConcept> impl{ nullptr };
public:
~PolyFCIterator() noexcept = default;
PolyFCIterator(const PolyFCIterator<Value>& rhs)
: impl{ rhs.impl->Clone() } {}
PolyFCIterator& operator=(const PolyFCIterator<Value>& rhs) {
if (this == &rhs) {
return *this;
}
impl = rhs.impl->Clone();
return *this;
}
PolyFCIterator(PolyFCIterator<Value>&&) noexcept = default;
PolyFCIterator& operator=(PolyFCIterator<Value>&&) noexcept = default;
template<typename Iter>
explicit PolyFCIterator(const Iter& iter)
: impl{ std::make_unique<ImplModel<Iter>>(iter) } {}
public:
// stl compatibility
using iterator_category = std::forward_iterator_tag;
using value_type = Value;
using difference_type = std::ptrdiff_t;
using pointer = const Value*;
using reference = const Value&;
private:
struct ImplConcept {
virtual ~ImplConcept() noexcept = default;
ImplConcept() = default;
ImplConcept(const ImplConcept&) = default;
ImplConcept& operator=(const ImplConcept&) = default;
ImplConcept(ImplConcept&&) noexcept = default;
ImplConcept& operator=(ImplConcept&&) noexcept = default;
virtual void Next() = 0;
[[nodiscard]] virtual const value_type& Deref() const = 0;
[[nodiscard]] virtual bool Equal(const void* other) const = 0;
[[nodiscard]] virtual std::unique_ptr<ImplConcept> Clone() const = 0;
[[nodiscard]] virtual const std::type_info& Type() const noexcept = 0;
[[nodiscard]] virtual const void* Address() const noexcept = 0;
};
template<typename Iter>
struct ImplModel : public ImplConcept {
Iter iter;
explicit ImplModel(Iter iter) noexcept
: iter{ std::move(iter) } {}
void Next() override {
std::advance(iter, 1);
}
[[nodiscard]] reference Deref() const override {
return *iter;
}
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 26440 ) // Note: false positive noexcept
#endif
[[nodiscard]] bool Equal(const void* rp) const override {
return iter == static_cast<const ImplModel*>(rp)->iter;
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif
[[nodiscard]] std::unique_ptr<ImplConcept> Clone() const override {
return std::make_unique<ImplModel>(*this);
}
[[nodiscard]] const std::type_info& Type() const noexcept override {
return typeid(iter);
}
[[nodiscard]] const void* Address() const noexcept override {
return this;
}
};
public:
reference operator*() const { return impl->Deref(); }
pointer operator->() const { return &impl->Deref(); }
PolyFCIterator<Value>& operator++() {
impl->Next();
return *this;
}
bool operator==(const PolyFCIterator<Value>& rhs) const {
return impl->Type() == rhs.impl->Type() && impl->Equal(rhs.impl->Address());
}
bool operator!=(const PolyFCIterator<Value>& r) const {
return !(*this == r);
}
};
} // namespace ccl::meta

View File

@ -0,0 +1,239 @@
#pragma once
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <vector>
#include <array>
#include <optional>
#include <stdexcept>
namespace ccl {
//! Comparison result for non-linear order
enum class Comparison : uint8_t {
LESS,
EQUAL,
GREATER,
INCOMPARABLE
};
//! Static fixed size BiMap with linear search access. Use preferably in constexpr context and only for small sizes
template <typename Key, typename Value, std::size_t Size>
struct StaticMap {
using DataType = std::array<std::pair<Key, Value>, Size>;
DataType data;
[[nodiscard]] constexpr bool ContainsKey(const Key& key) const noexcept {
const auto itr =
std::find_if(begin(data), end(data),
[&key](const auto& v) { return v.first == key; });
if (itr != end(data)) {
return true;
} else {
return false;
}
}
[[nodiscard]] constexpr bool ContainsValue(const Value& value) const noexcept {
const auto itr =
std::find_if(begin(data), end(data),
[&value](const auto& v) { return v.second == value; });
if (itr != end(data)) {
return true;
} else {
return false;
}
}
[[nodiscard]] constexpr Value AtKey(const Key& key) const {
const auto itr =
std::find_if(begin(data), end(data),
[&key](const auto& v) { return v.first == key; });
if (itr != end(data)) {
return itr->second;
} else {
throw std::range_error("Not Found");
}
}
[[nodiscard]] constexpr Key AtValue(const Value& value) const {
const auto itr =
std::find_if(begin(data), end(data),
[&value](const auto& v) { return v.second == value; });
if (itr != end(data)) {
return itr->first;
} else {
throw std::range_error("Not Found");
}
}
};
} // namespace ccl
namespace ccl::types {
//! Automatic counter guard
template<typename T>
class CounterGuard {
T* counter;
public:
~CounterGuard() noexcept {
if (counter != nullptr) {
--(*counter);
}
}
constexpr explicit CounterGuard(T& counterRef) noexcept
: counter(&counterRef) {
++(*counter);
}
CounterGuard(const CounterGuard&) = delete;
CounterGuard& operator=(const CounterGuard&) = delete;
CounterGuard(CounterGuard&&) noexcept = default;
CounterGuard& operator=(CounterGuard&&) noexcept = default;
void Release() {
if (counter != nullptr) {
--(*counter);
counter = nullptr;
}
}
};
// Note: possibly not thread-safe. If needed thread-safe - make counter atomic
class GuardableBool {
bool value;
uint16_t guardCounter{ 0 };
public:
constexpr explicit GuardableBool(const bool value)
: value{ value } {}
constexpr GuardableBool(const GuardableBool& rhs)
: value{ rhs.value } {}
GuardableBool& operator=(const GuardableBool& rhs) noexcept {
if (&rhs != this) {
value = rhs.value;
}
return *this;
}
GuardableBool(GuardableBool&&) noexcept = default;
GuardableBool& operator=(GuardableBool&&) noexcept = default;
public:
using Guard = CounterGuard<uint16_t>;
explicit(false) constexpr operator bool() const noexcept { return IsGuarded() != value; }
[[nodiscard]] constexpr bool IsGuarded() const noexcept { return guardCounter > 0; }
[[nodiscard]] Guard CreateGuard() noexcept { return Guard{ guardCounter }; }
};
//! Abstract message for observer
class Message {
public:
virtual ~Message() noexcept = default;
protected:
Message() = default;
Message(const Message&) = default;
Message& operator=(const Message&) = default;
Message(Message&&) noexcept = default;
Message& operator=(Message&&) noexcept = default;
public:
static constexpr auto modificationCode = 0x1000U;
static constexpr auto srcCode = 0x2000U;
[[nodiscard]] virtual uint32_t Type() const noexcept = 0;
};
//! Message with no payload
struct BasicMsg final : Message {
uint32_t msgID;
explicit BasicMsg(uint32_t msgID) noexcept
: msgID{ msgID } {}
[[nodiscard]] uint32_t Type() const noexcept override {
return msgID;
}
};
//! Observer base class
class Observable;
class Observer {
friend class Observable;
types::GuardableBool isObserving{ true };
public:
virtual ~Observer() noexcept = default;
protected:
Observer() = default;
Observer(const Observer&) = default;
Observer& operator=(const Observer&) = default;
Observer(Observer&&) noexcept = default;
Observer& operator=(Observer&&) noexcept = default;
protected:
[[nodiscard]] bool DndStatus() const noexcept {
return !isObserving;
}
[[nodiscard]] auto DndGuard() noexcept {
return isObserving.CreateGuard();
}
virtual void OnObserve(const Message& msg) = 0;
private:
void TryInvoking(const Message& msg) {
if (isObserving) {
OnObserve(msg);
}
}
};
//! Observable base class
class Observable {
std::vector<Observer*> observers{};
public:
void AddObserver(Observer& obs) {
observers.emplace_back(&obs);
}
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 26447 ) // Do not warn about using noexcept
#endif
void RemoveObserver(const Observer& obs) noexcept {
if (!empty(observers)) { // Note: After this check shouldnt throw (unless used in threaded environement)
observers.erase(std::remove(begin(observers), end(observers), &obs), end(observers));
}
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif
void ImportObserversFrom(Observable& rhs) {
if (this != &rhs) {
for (auto& obs : rhs.observers) {
AddObserver(*obs);
}
}
}
protected:
// Note: notifying dangling observer is UB! Observer should use move semantic
void Notify(uint32_t msgID) {
Notify(BasicMsg{ msgID });
}
void Notify(const Message& msg) {
for (auto& oberver : observers) {
oberver->TryInvoking(msg);
}
}
};
} // namespace ccl::types

View File

@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.23)
find_package(GTest REQUIRED)
add_executable(cclCommons_Tests)
target_sources(cclCommons_Tests
PRIVATE
unity/cclCommonsTest.cpp
)
target_include_directories(cclCommons_Tests
PRIVATE
../include
utils
)
target_link_libraries(cclCommons_Tests
PRIVATE
ccl_CXXwarnings
ccl_CXXoptions
GTest::gtest
GTest::gtest_main
)
include(GoogleTest)
gtest_discover_tests(cclCommons_Tests)

View File

@ -0,0 +1,211 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{53A380CF-B599-4170-89B1-642F1C3772E1}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<ProjectName>cclCommonsTest</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<WholeProgramOptimization>true</WholeProgramOptimization>
<VCToolsVersion />
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<WholeProgramOptimization>true</WholeProgramOptimization>
<VCToolsVersion />
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="Shared" />
<ImportGroup Label="PropertySheets">
<Import Project="..\..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.7\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets" Condition="Exists('..\..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.7\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets')" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>build\x86\$(Configuration)\</OutDir>
<IntDir>build\x86\$(Configuration)\</IntDir>
<EnableMicrosoftCodeAnalysis>false</EnableMicrosoftCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>build\x64\$(Configuration)\</OutDir>
<IntDir>build\x64\$(Configuration)\</IntDir>
<EnableMicrosoftCodeAnalysis>false</EnableMicrosoftCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>build\x86\$(Configuration)\</OutDir>
<IntDir>build\x86\$(Configuration)\</IntDir>
<GenerateManifest>false</GenerateManifest>
<EnableMicrosoftCodeAnalysis>false</EnableMicrosoftCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>build\x64\$(Configuration)\</OutDir>
<IntDir>build\x64\$(Configuration)\</IntDir>
<GenerateManifest>false</GenerateManifest>
<EnableMicrosoftCodeAnalysis>false</EnableMicrosoftCodeAnalysis>
</PropertyGroup>
<ItemGroup>
<ClCompile Include="src\testGuard.cpp" />
<ClCompile Include="src\testObserver.cpp" />
<ClCompile Include="src\testPropagateConst.cpp" />
<ClCompile Include="src\testStaticMap.cpp" />
<ClCompile Include="src\testStrings.cpp" />
<ClCompile Include="src\testSubstitutes.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\ccl\cclChange.hpp" />
<ClInclude Include="..\include\ccl\cclMeta.hpp" />
<ClInclude Include="..\include\ccl\cclTypes.hpp" />
<ClInclude Include="..\include\ccl\Strings.hpp" />
<ClInclude Include="..\include\ccl\Substitutes.hpp" />
<ClInclude Include="utils\FakeObserver.hpp" />
</ItemGroup>
<ItemDefinitionGroup />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level4</WarningLevel>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>utils;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level4</WarningLevel>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>utils;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
<OmitFramePointers>false</OmitFramePointers>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>utils;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<FunctionLevelLinking>true</FunctionLevelLinking>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
<ProjectReference>
<LinkLibraryDependencies>true</LinkLibraryDependencies>
</ProjectReference>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>
</PrecompiledHeaderFile>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>utils;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<FunctionLevelLinking>true</FunctionLevelLinking>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
<ProjectReference>
<LinkLibraryDependencies>true</LinkLibraryDependencies>
</ProjectReference>
</ItemDefinitionGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.7\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.7\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets'))" />
</Target>
</Project>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="src">
<UniqueIdentifier>{ed04b33d-515f-46b0-aad1-54dcbc55a5fd}</UniqueIdentifier>
</Filter>
<Filter Include="utils">
<UniqueIdentifier>{4c4173ff-1f15-4919-90b9-3d43cc164f84}</UniqueIdentifier>
</Filter>
<Filter Include="include">
<UniqueIdentifier>{be31e9f2-602c-4c10-b9ed-7407342449f4}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\testGuard.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\testObserver.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\testPropagateConst.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\testSubstitutes.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\testStrings.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\testStaticMap.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="utils\FakeObserver.hpp">
<Filter>utils</Filter>
</ClInclude>
<ClInclude Include="..\include\ccl\cclMeta.hpp">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\ccl\cclTypes.hpp">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\ccl\cclChange.hpp">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\ccl\Substitutes.hpp">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\ccl\Strings.hpp">
<Filter>include</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn" version="1.8.1.7" targetFramework="native" />
</packages>

View File

@ -0,0 +1,69 @@
#define GTEST_LANG_CXX11 1
#include "gtest/gtest.h"
#include "ccl/cclTypes.hpp"
namespace ct = ccl::types;
TEST(UTGuard, CounterIncrement) {
auto c = 0;
const auto guard = ct::CounterGuard(c);
EXPECT_EQ(c, 1);
}
TEST(UTGuard, CounterDecrementOnDestroy) {
auto c = 0;
{
const auto guard = ct::CounterGuard(c);
EXPECT_EQ(c, 1);
}
EXPECT_EQ(c, 0);
}
TEST(UTGuard, CounterDoubleGuard) {
auto c = 0;
{
const auto guard1 = ct::CounterGuard(c);
EXPECT_EQ(c, 1);
{
const auto guard2 = ct::CounterGuard(c);
EXPECT_EQ(c, 2);
}
EXPECT_EQ(c, 1);
}
EXPECT_EQ(c, 0);
}
TEST(UTGuard, CounterRelease) {
auto c = 0;
{
auto guard = ct::CounterGuard(c);
EXPECT_EQ(c, 1);
guard.Release();
EXPECT_EQ(c, 0);
}
EXPECT_EQ(c, 0);
}
TEST(UTGuard, GuardableDefault) {
const auto val = ct::GuardableBool{ true };
EXPECT_TRUE(val);
EXPECT_FALSE(val.IsGuarded());
const auto val2 = ct::GuardableBool{ false };
EXPECT_FALSE(val2);
EXPECT_FALSE(val2.IsGuarded());
}
TEST(UTGuard, GuardableSpawnGuard) {
auto val = ct::GuardableBool{ true };
const auto guard = val.CreateGuard();
EXPECT_FALSE(val);
EXPECT_TRUE(val.IsGuarded());
auto val2 = ct::GuardableBool{ false };
const auto guard2 = val2.CreateGuard();
EXPECT_TRUE(val2);
EXPECT_TRUE(val2.IsGuarded());
}

View File

@ -0,0 +1,83 @@
#define GTEST_LANG_CXX11 1
#include "gtest/gtest.h"
#include "FakeObserver.hpp"
namespace ct = ccl::types;
class UTObserver : public ::testing::Test {
public:
FakeObserver obs{};
FakeObservable object{};
};
TEST_F(UTObserver, BasicMod) {
auto basic = ct::BasicMsg{ 42U };
EXPECT_EQ(basic.Type(), 42U);
}
TEST_F(UTObserver, NoObservers) {
EXPECT_NO_THROW(object.Notify(42U));
EXPECT_NO_THROW(object.Notify(ct::BasicMsg{ 42U }));
}
TEST_F(UTObserver, AddObserver) {
object.AddObserver(obs);
object.Notify(42U);
EXPECT_EQ(ssize(obs.events), 1);
}
TEST_F(UTObserver, ImportObservers) {
object.AddObserver(obs);
FakeObservable anotherObj{};
anotherObj.ImportObserversFrom(object);
object.Notify(42U);
EXPECT_EQ(ssize(obs.events), 1);
anotherObj.Notify(42U);
EXPECT_EQ(ssize(obs.events), 2);
}
TEST_F(UTObserver, AddDoubleObserver) {
object.AddObserver(obs);
object.AddObserver(obs);
object.Notify(42U);
EXPECT_EQ(ssize(obs.events), 2);
}
TEST_F(UTObserver, RemoveObserver) {
object.AddObserver(obs);
object.RemoveObserver(obs);
object.Notify(42U);
EXPECT_EQ(ssize(obs.events), 0);
}
TEST_F(UTObserver, RemoveDoubleObserver) {
object.AddObserver(obs);
object.AddObserver(obs);
object.RemoveObserver(obs);
object.Notify(42U);
EXPECT_EQ(ssize(obs.events), 0);
}
TEST_F(UTObserver, Notify) {
object.AddObserver(obs);
object.Notify(42U);
EXPECT_EQ(ssize(obs.events), 1);
EXPECT_EQ(obs.events[0], 42U);
}
TEST_F(UTObserver, DndStatus) {
EXPECT_FALSE(obs.DndStatus());
auto guard = obs.DndGuard();
EXPECT_TRUE(obs.DndStatus());
}
TEST_F(UTObserver, DndBlockObserve) {
object.AddObserver(obs);
auto guard = obs.DndGuard();
object.Notify(42U);
EXPECT_EQ(ssize(obs.events), 0);
}

View File

@ -0,0 +1,126 @@
#define GTEST_LANG_CXX11 1
#include "gtest/gtest.h"
#include "ccl/cclMeta.hpp"
// helper classes
struct Base {
int32_t state{ 1337 };
virtual ~Base() = default;
virtual int32_t overloadedFunc() const {
return 42;
}
virtual int32_t overloadedFunc() {
return 43;
}
};
struct Derived1 : Base {
int32_t additionalState{ 42 };
int32_t overloadedFunc() const override {
return 1337;
}
};
struct Derived2 : Base {
std::string additionalState{ "str42" };
int32_t overloadedFunc() override {
return 1337;
}
};
TEST(UTPropagateConst, AccessValue) {
using ccl::meta::UniqueCPPtr;
UniqueCPPtr<int32_t> intCP{ std::make_unique<int32_t>(42) };
const auto& constRef = intCP;
EXPECT_EQ(intCP.operator*(), 42);
EXPECT_EQ(constRef.operator*(), 42);
EXPECT_EQ(intCP.get(), constRef.get());
EXPECT_EQ(intCP.operator->(), constRef.get());
EXPECT_EQ(static_cast<int32_t*>(intCP), constRef.get());
EXPECT_EQ(static_cast<const int32_t*>(constRef), constRef.get());
}
TEST(UTPropagateConst, Nullptr) {
using ccl::meta::UniqueCPPtr;
const UniqueCPPtr<int32_t> intPtr{};
EXPECT_TRUE(intPtr == nullptr);
EXPECT_FALSE(intPtr != nullptr);
EXPECT_EQ(intPtr, UniqueCPPtr<int32_t>(nullptr));
UniqueCPPtr<int32_t> notNull{ std::make_unique<int32_t>(42) };
EXPECT_FALSE(notNull == nullptr);
EXPECT_TRUE(notNull != nullptr);
notNull = nullptr;
EXPECT_TRUE(notNull == nullptr);
notNull = std::make_unique<int32_t>(0);
EXPECT_FALSE(notNull == nullptr);
}
TEST(UTPropagateConst, PointerEquality) {
using ccl::meta::UniqueCPPtr;
UniqueCPPtr<int32_t> int1{ std::make_unique<int32_t>(42) };
UniqueCPPtr<int32_t> int2{ std::make_unique<int32_t>(42) };
UniqueCPPtr<int32_t> int3{ std::make_unique<int32_t>(1337) };
const auto& constRef1 = int1;
const auto& constRef2 = int2;
const auto& constRef3 = int3;
EXPECT_EQ(int1, constRef1);
EXPECT_NE(int1, int2);
EXPECT_NE(int1, int3);
EXPECT_NE(constRef1, int3);
EXPECT_NE(constRef1, constRef3);
EXPECT_NE(int1, constRef2);
EXPECT_NE(constRef1, constRef2);
}
TEST(UTPropagateConst, Swap) {
using ccl::meta::UniqueCPPtr;
UniqueCPPtr<int32_t> int1{ std::make_unique<int32_t>(42) };
std::swap(int1, int1);
EXPECT_EQ(*int1, 42);
UniqueCPPtr<int32_t> int2{ std::make_unique<int32_t>(1337) };
std::swap(int1, int2);
EXPECT_EQ(*int2, 42);
EXPECT_EQ(*int1, 1337);
}
TEST(UTPropagateConst, Propagation) {
using ccl::meta::UniqueCPPtr;
UniqueCPPtr<Base> nonConstPtr{ std::make_unique<Base>() };
const auto& constRef = nonConstPtr;
EXPECT_EQ(nonConstPtr->overloadedFunc(), 43);
EXPECT_EQ(constRef->overloadedFunc(), 42);
}
TEST(UTPropagateConst, Polymorphism) {
using ccl::meta::UniqueCPPtr;
UniqueCPPtr<Base> basePtrToDerived1{}, basePtrToDerived2{};
basePtrToDerived1 = UniqueCPPtr<Base>{ std::make_unique<Derived1>() };
basePtrToDerived2 = UniqueCPPtr<Base>{ std::make_unique<Derived2>() };
ASSERT_FALSE(dynamic_cast<Derived1*>(basePtrToDerived1.get()) == nullptr);
EXPECT_TRUE(dynamic_cast<Derived2*>(basePtrToDerived1.get()) == nullptr);
EXPECT_EQ(dynamic_cast<Derived1*>(basePtrToDerived1.get())->additionalState, 42);
EXPECT_EQ(dynamic_cast<Derived2*>(basePtrToDerived2.get())->additionalState, "str42");
const auto& constRef1 = basePtrToDerived1;
EXPECT_EQ(basePtrToDerived1->overloadedFunc(), 43);
EXPECT_EQ(constRef1->overloadedFunc(), 1337);
const auto& constRef2 = basePtrToDerived2;
EXPECT_EQ(basePtrToDerived2->overloadedFunc(), 1337);
EXPECT_EQ(constRef2->overloadedFunc(), 42);
}

View File

@ -0,0 +1,49 @@
#define GTEST_LANG_CXX11 1
#include "gtest/gtest.h"
#include "ccl/cclTypes.hpp"
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 4834 6031 )
#endif
class UTStaticMap : public ::testing::Test {
protected:
using MapType = ccl::StaticMap<std::string_view, int32_t, 4>;
using ValueType = MapType::DataType::value_type;
static constexpr MapType testMap{
MapType::DataType({ ValueType{"A", 1}, ValueType{"B", 42}, ValueType{"C", 55}, ValueType{"Hello", 1337} })
};
};
TEST_F(UTStaticMap, ContainsKey) {
EXPECT_TRUE(testMap.ContainsKey("Hello"));
EXPECT_TRUE(testMap.ContainsKey("A"));
EXPECT_FALSE(testMap.ContainsKey(""));
EXPECT_FALSE(testMap.ContainsKey(" "));
EXPECT_FALSE(testMap.ContainsKey("invalid"));
}
TEST_F(UTStaticMap, ContainsValue) {
EXPECT_TRUE(testMap.ContainsValue(1));
EXPECT_TRUE(testMap.ContainsValue(1337));
EXPECT_FALSE(testMap.ContainsValue(-1));
EXPECT_FALSE(testMap.ContainsValue(0));
EXPECT_FALSE(testMap.ContainsValue(1338));
}
TEST_F(UTStaticMap, AtKey) {
EXPECT_EQ(testMap.AtKey("A"), 1);
EXPECT_EQ(testMap.AtKey("Hello"), 1337);
}
TEST_F(UTStaticMap, AtValue) {
EXPECT_EQ(testMap.AtValue(1), "A");
EXPECT_EQ(testMap.AtValue(1337), "Hello");
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

View File

@ -0,0 +1,384 @@
#define GTEST_LANG_CXX11 1
#include "gtest/gtest.h"
#include "ccl/Strings.hpp"
using ccl::operator""_c17;
class UTStrings : public ::testing::Test {
protected:
using StrRange = ccl::StrRange;
using UTF8Iterator = ccl::UTF8Iterator;
};
TEST_F(UTStrings, Length) {
EXPECT_TRUE(StrRange{}.length() == 0);
EXPECT_TRUE(StrRange(1, 1).length() == 0);
EXPECT_TRUE(StrRange(2, 4).length() == 2);
EXPECT_TRUE(StrRange(4, 2).length() == -2);
}
TEST_F(UTStrings, Shift) {
EXPECT_EQ(StrRange(4, 6), StrRange(2, 4).Shift(2));
EXPECT_EQ(StrRange(0, 2), StrRange(2, 4).Shift(-2));
}
TEST_F(UTStrings, Equals) {
EXPECT_EQ(StrRange(1, 1), StrRange(1, 1));
EXPECT_NE(StrRange(1, 2), StrRange(1, 1));
EXPECT_NE(StrRange(2, 1), StrRange(1, 1));
EXPECT_EQ(StrRange(2, 1), StrRange(2, 1));
EXPECT_EQ(StrRange(1, 2), StrRange(1, 2));
EXPECT_NE(StrRange(2, 1), StrRange(1, 2));
}
TEST_F(UTStrings, Collapse) {
EXPECT_EQ(StrRange(4, 4), StrRange(2, 4).CollapseEnd());
EXPECT_EQ(StrRange(2, 2), StrRange(2, 4).CollapseStart());
}
TEST_F(UTStrings, Contains) {
EXPECT_TRUE(StrRange(1, 3).Contains(1));
EXPECT_TRUE(StrRange(1, 3).Contains(2));
EXPECT_FALSE(StrRange(1, 3).Contains(3));
EXPECT_FALSE(StrRange(1, 3).Contains(4));
EXPECT_FALSE(StrRange(1, 3).Contains(0));
EXPECT_TRUE(StrRange(2, 5).Contains(StrRange(2, 5)));
EXPECT_FALSE(StrRange(2, 5).Contains(StrRange(0, 1)));
EXPECT_FALSE(StrRange(2, 5).Contains(StrRange(1, 2)));
EXPECT_FALSE(StrRange(2, 5).Contains(StrRange(1, 3)));
EXPECT_FALSE(StrRange(2, 5).Contains(StrRange(1, 5)));
EXPECT_FALSE(StrRange(2, 5).Contains(StrRange(1, 6)));
EXPECT_TRUE(StrRange(2, 5).Contains(StrRange(2, 3)));
EXPECT_FALSE(StrRange(2, 5).Contains(StrRange(2, 6)));
EXPECT_TRUE(StrRange(2, 5).Contains(StrRange(3, 4)));
EXPECT_TRUE(StrRange(2, 5).Contains(StrRange(3, 5)));
EXPECT_FALSE(StrRange(2, 5).Contains(StrRange(3, 6)));
EXPECT_FALSE(StrRange(2, 5).Contains(StrRange(5, 6)));
EXPECT_FALSE(StrRange(2, 5).Contains(StrRange(6, 7)));
}
TEST_F(UTStrings, SharesBorder) {
EXPECT_FALSE(StrRange(2, 5).SharesBorder(StrRange(2, 5)));
EXPECT_FALSE(StrRange(2, 5).SharesBorder(StrRange(0, 1)));
EXPECT_TRUE(StrRange(2, 5).SharesBorder(StrRange(1, 2)));
EXPECT_FALSE(StrRange(2, 5).SharesBorder(StrRange(1, 3)));
EXPECT_FALSE(StrRange(2, 5).SharesBorder(StrRange(1, 5)));
EXPECT_FALSE(StrRange(2, 5).SharesBorder(StrRange(1, 6)));
EXPECT_FALSE(StrRange(2, 5).SharesBorder(StrRange(2, 3)));
EXPECT_FALSE(StrRange(2, 5).SharesBorder(StrRange(2, 6)));
EXPECT_FALSE(StrRange(2, 5).SharesBorder(StrRange(3, 4)));
EXPECT_FALSE(StrRange(2, 5).SharesBorder(StrRange(3, 5)));
EXPECT_FALSE(StrRange(2, 5).SharesBorder(StrRange(3, 6)));
EXPECT_TRUE(StrRange(2, 5).SharesBorder(StrRange(5, 6)));
EXPECT_FALSE(StrRange(2, 5).SharesBorder(StrRange(6, 7)));
}
TEST_F(UTStrings, Overlaps) {
EXPECT_TRUE(StrRange(2, 5).Overlaps(StrRange(2, 5)));
EXPECT_FALSE(StrRange(2, 5).Overlaps(StrRange(0, 1)));
EXPECT_FALSE(StrRange(2, 5).Overlaps(StrRange(1, 2)));
EXPECT_TRUE(StrRange(2, 5).Overlaps(StrRange(1, 3)));
EXPECT_TRUE(StrRange(2, 5).Overlaps(StrRange(1, 5)));
EXPECT_TRUE(StrRange(2, 5).Overlaps(StrRange(1, 6)));
EXPECT_TRUE(StrRange(2, 5).Overlaps(StrRange(2, 3)));
EXPECT_TRUE(StrRange(2, 5).Overlaps(StrRange(2, 6)));
EXPECT_TRUE(StrRange(2, 5).Overlaps(StrRange(3, 4)));
EXPECT_TRUE(StrRange(2, 5).Overlaps(StrRange(3, 5)));
EXPECT_TRUE(StrRange(2, 5).Overlaps(StrRange(3, 6)));
EXPECT_FALSE(StrRange(2, 5).Overlaps(StrRange(5, 6)));
EXPECT_FALSE(StrRange(2, 5).Overlaps(StrRange(6, 7)));
}
TEST_F(UTStrings, IsBefore) {
EXPECT_FALSE(StrRange(2, 5).IsBefore(StrRange(2, 5)));
EXPECT_FALSE(StrRange(2, 5).IsBefore(StrRange(0, 1)));
EXPECT_FALSE(StrRange(2, 5).IsBefore(StrRange(1, 2)));
EXPECT_FALSE(StrRange(2, 5).IsBefore(StrRange(1, 3)));
EXPECT_FALSE(StrRange(2, 5).IsBefore(StrRange(1, 5)));
EXPECT_FALSE(StrRange(2, 5).IsBefore(StrRange(1, 6)));
EXPECT_FALSE(StrRange(2, 5).IsBefore(StrRange(2, 3)));
EXPECT_FALSE(StrRange(2, 5).IsBefore(StrRange(2, 6)));
EXPECT_FALSE(StrRange(2, 5).IsBefore(StrRange(3, 4)));
EXPECT_FALSE(StrRange(2, 5).IsBefore(StrRange(3, 5)));
EXPECT_FALSE(StrRange(2, 5).IsBefore(StrRange(3, 6)));
EXPECT_FALSE(StrRange(2, 5).IsBefore(StrRange(5, 6)));
EXPECT_TRUE(StrRange(2, 5).IsBefore(StrRange(6, 7)));
}
TEST_F(UTStrings, IsAfter) {
EXPECT_FALSE(StrRange(2, 5).IsAfter(StrRange(2, 5)));
EXPECT_TRUE(StrRange(2, 5).IsAfter(StrRange(0, 1)));
EXPECT_FALSE(StrRange(2, 5).IsAfter(StrRange(1, 2)));
EXPECT_FALSE(StrRange(2, 5).IsAfter(StrRange(1, 3)));
EXPECT_FALSE(StrRange(2, 5).IsAfter(StrRange(1, 5)));
EXPECT_FALSE(StrRange(2, 5).IsAfter(StrRange(1, 6)));
EXPECT_FALSE(StrRange(2, 5).IsAfter(StrRange(2, 3)));
EXPECT_FALSE(StrRange(2, 5).IsAfter(StrRange(2, 6)));
EXPECT_FALSE(StrRange(2, 5).IsAfter(StrRange(3, 4)));
EXPECT_FALSE(StrRange(2, 5).IsAfter(StrRange(3, 5)));
EXPECT_FALSE(StrRange(2, 5).IsAfter(StrRange(3, 6)));
EXPECT_FALSE(StrRange(2, 5).IsAfter(StrRange(5, 6)));
EXPECT_FALSE(StrRange(2, 5).IsAfter(StrRange(6, 7)));
}
TEST_F(UTStrings, Meets) {
EXPECT_FALSE(StrRange(2, 5).Meets(StrRange(2, 5)));
EXPECT_FALSE(StrRange(2, 5).Meets(StrRange(0, 1)));
EXPECT_FALSE(StrRange(2, 5).Meets(StrRange(1, 2)));
EXPECT_FALSE(StrRange(2, 5).Meets(StrRange(1, 3)));
EXPECT_FALSE(StrRange(2, 5).Meets(StrRange(1, 5)));
EXPECT_FALSE(StrRange(2, 5).Meets(StrRange(1, 6)));
EXPECT_FALSE(StrRange(2, 5).Meets(StrRange(2, 3)));
EXPECT_FALSE(StrRange(2, 5).Meets(StrRange(2, 6)));
EXPECT_FALSE(StrRange(2, 5).Meets(StrRange(3, 4)));
EXPECT_FALSE(StrRange(2, 5).Meets(StrRange(3, 5)));
EXPECT_FALSE(StrRange(2, 5).Meets(StrRange(3, 6)));
EXPECT_TRUE(StrRange(2, 5).Meets(StrRange(5, 6)));
EXPECT_FALSE(StrRange(2, 5).Meets(StrRange(6, 7)));
}
TEST_F(UTStrings, Starts) {
EXPECT_FALSE(StrRange(2, 5).Starts(StrRange(2, 5)));
EXPECT_FALSE(StrRange(2, 5).Starts(StrRange(0, 1)));
EXPECT_FALSE(StrRange(2, 5).Starts(StrRange(1, 2)));
EXPECT_FALSE(StrRange(2, 5).Starts(StrRange(1, 3)));
EXPECT_FALSE(StrRange(2, 5).Starts(StrRange(1, 5)));
EXPECT_FALSE(StrRange(2, 5).Starts(StrRange(1, 6)));
EXPECT_FALSE(StrRange(2, 5).Starts(StrRange(2, 3)));
EXPECT_TRUE(StrRange(2, 5).Starts(StrRange(2, 6)));
EXPECT_FALSE(StrRange(2, 5).Starts(StrRange(3, 4)));
EXPECT_FALSE(StrRange(2, 5).Starts(StrRange(3, 5)));
EXPECT_FALSE(StrRange(2, 5).Starts(StrRange(3, 6)));
EXPECT_FALSE(StrRange(2, 5).Starts(StrRange(5, 6)));
EXPECT_FALSE(StrRange(2, 5).Starts(StrRange(6, 7)));
}
TEST_F(UTStrings, Finishes) {
EXPECT_FALSE(StrRange(2, 5).Finishes(StrRange(2, 5)));
EXPECT_FALSE(StrRange(2, 5).Finishes(StrRange(0, 1)));
EXPECT_FALSE(StrRange(2, 5).Finishes(StrRange(1, 2)));
EXPECT_FALSE(StrRange(2, 5).Finishes(StrRange(1, 3)));
EXPECT_TRUE(StrRange(2, 5).Finishes(StrRange(1, 5)));
EXPECT_FALSE(StrRange(2, 5).Finishes(StrRange(1, 6)));
EXPECT_FALSE(StrRange(2, 5).Finishes(StrRange(2, 3)));
EXPECT_FALSE(StrRange(2, 5).Finishes(StrRange(2, 6)));
EXPECT_FALSE(StrRange(2, 5).Finishes(StrRange(3, 4)));
EXPECT_FALSE(StrRange(2, 5).Finishes(StrRange(3, 5)));
EXPECT_FALSE(StrRange(2, 5).Finishes(StrRange(3, 6)));
EXPECT_FALSE(StrRange(2, 5).Finishes(StrRange(5, 6)));
EXPECT_FALSE(StrRange(2, 5).Finishes(StrRange(6, 7)));
}
TEST_F(UTStrings, IsDuring) {
EXPECT_FALSE(StrRange(2, 5).IsDuring(StrRange(2, 5)));
EXPECT_FALSE(StrRange(2, 5).IsDuring(StrRange(0, 1)));
EXPECT_FALSE(StrRange(2, 5).IsDuring(StrRange(1, 2)));
EXPECT_FALSE(StrRange(2, 5).IsDuring(StrRange(1, 3)));
EXPECT_FALSE(StrRange(2, 5).IsDuring(StrRange(1, 5)));
EXPECT_TRUE(StrRange(2, 5).IsDuring(StrRange(1, 6)));
EXPECT_FALSE(StrRange(2, 5).IsDuring(StrRange(2, 3)));
EXPECT_FALSE(StrRange(2, 5).IsDuring(StrRange(2, 6)));
EXPECT_FALSE(StrRange(2, 5).IsDuring(StrRange(3, 4)));
EXPECT_FALSE(StrRange(2, 5).IsDuring(StrRange(3, 5)));
EXPECT_FALSE(StrRange(2, 5).IsDuring(StrRange(3, 6)));
EXPECT_FALSE(StrRange(2, 5).IsDuring(StrRange(5, 6)));
EXPECT_FALSE(StrRange(2, 5).IsDuring(StrRange(6, 7)));
}
TEST_F(UTStrings, Merge) {
EXPECT_EQ(StrRange::Merge({}), StrRange{});
EXPECT_EQ(StrRange::Merge({ StrRange(1, 2), StrRange(2, 4) }), StrRange(1, 4));
EXPECT_EQ(StrRange::Merge({ StrRange(2, 4), StrRange(1, 2) }), StrRange(1, 4));
EXPECT_EQ(StrRange::Merge({ StrRange(1, 2), StrRange(3, 4) }), StrRange(1, 4));
EXPECT_EQ(StrRange::Merge({ StrRange(1, 3), StrRange(2, 4) }), StrRange(1, 4));
}
TEST_F(UTStrings, Substr) {
EXPECT_EQ(ccl::Substr("", StrRange{ 0, 0 }), "");
EXPECT_EQ(ccl::Substr("012345", StrRange(0, 3)), "012");
EXPECT_EQ(ccl::Substr("012345", StrRange(0, 10)), "");
EXPECT_EQ(ccl::Substr("012345", StrRange(2, 3)), "2");
EXPECT_EQ(ccl::Substr(u8"ab\u212Ccd"_c17, StrRange(2, 4)), u8"\u212Cc"_c17);
}
TEST_F(UTStrings, CharSize) {
EXPECT_EQ(ccl::UTF8CharSize(static_cast<unsigned char>(std::string("a").at(0U))), 1);
EXPECT_EQ(ccl::UTF8CharSize(static_cast<unsigned char>(std::string("1").at(0U))), 1);
EXPECT_EQ(ccl::UTF8CharSize(static_cast<unsigned char>(u8"\u0435"_c17.at(0U))), 2);
EXPECT_EQ(ccl::UTF8CharSize(static_cast<unsigned char>(std::string("\xE2\x84\xAC").at(0U))), 3);
EXPECT_EQ(ccl::UTF8CharSize(static_cast<unsigned char>(std::string("\xF0\xA0\x9C\x8E").at(0U))), 4);
}
TEST_F(UTStrings, UTF8Iteration) {
EXPECT_EQ(ccl::UTF8End({}), UTF8Iterator({}));
const auto text = u8"\u212Cabc\u212Ccba\u212C"_c17;
auto it = UTF8Iterator(text);
EXPECT_EQ(*it, '\xE2');
EXPECT_EQ(it.BytePosition(), 0U);
EXPECT_EQ(it.Position(), 0);
EXPECT_EQ(it.SymbolSize(), 3U);
EXPECT_EQ(*++it, 'a');
EXPECT_EQ(it.BytePosition(), 3U);
EXPECT_EQ(it.Position(), 1);
EXPECT_EQ(it.SymbolSize(), 1U);
EXPECT_EQ(*++it, 'b');
EXPECT_EQ(it.BytePosition(), 4U);
EXPECT_EQ(it.Position(), 2);
EXPECT_EQ(*++it, 'c');
EXPECT_EQ(it.BytePosition(), 5U);
EXPECT_EQ(it.Position(), 3);
EXPECT_EQ(*++it, '\xE2');
EXPECT_EQ(it.BytePosition(), 6U);
EXPECT_EQ(it.Position(), 4);
EXPECT_EQ(*++it, 'c');
EXPECT_EQ(it.BytePosition(), 9U);
EXPECT_EQ(it.Position(), 5);
EXPECT_EQ(*++it, 'b');
EXPECT_EQ(it.BytePosition(), 10U);
EXPECT_EQ(it.Position(), 6);
EXPECT_EQ(*++it, 'a');
EXPECT_EQ(it.BytePosition(), 11U);
EXPECT_EQ(it.Position(), 7);
EXPECT_EQ(*++it, '\xE2');
EXPECT_EQ(it.BytePosition(), 12U);
EXPECT_EQ(it.Position(), 8);
EXPECT_EQ(++it, ccl::UTF8End(text));
}
TEST_F(UTStrings, IteratorCreation) {
EXPECT_EQ(UTF8Iterator("123", 42), ccl::UTF8End("123"));
EXPECT_EQ(UTF8Iterator("123", -1), ccl::UTF8End("123"));
EXPECT_EQ(UTF8Iterator("\xE2\x84\xAC", 1), ccl::UTF8End("\xE2\x84\xAC"));
}
TEST_F(UTStrings, SizeInCodePoints) {
EXPECT_EQ(ccl::SizeInCodePoints({}), 0);
EXPECT_EQ(ccl::SizeInCodePoints(""), 0);
EXPECT_EQ(ccl::SizeInCodePoints("42"), 2);
EXPECT_EQ(ccl::SizeInCodePoints(u8"\u212C42"_c17), 3);
}
TEST_F(UTStrings, SplitBySymbol) {
using ccl::SplitBySymbol;
{
const std::string str = "";
auto tokens = SplitBySymbol(str, ',');
EXPECT_TRUE(tokens.size() == 1);
EXPECT_TRUE(tokens[0] == "");
}
{
const std::string str = "A,B,C";
auto tokens = SplitBySymbol(str, ',');
EXPECT_TRUE(tokens.size() == 3);
EXPECT_TRUE(tokens[0] == "A");
EXPECT_TRUE(tokens[1] == "B");
EXPECT_TRUE(tokens[2] == "C");
}
{
const std::string str = ",B,C";
auto tokens = SplitBySymbol(str, ',');
EXPECT_TRUE(tokens.size() == 3);
EXPECT_TRUE(tokens[0] == "");
EXPECT_TRUE(tokens[1] == "B");
EXPECT_TRUE(tokens[2] == "C");
}
{
const std::string str = "A,B,";
auto tokens = SplitBySymbol(str, ',');
EXPECT_TRUE(tokens.size() == 3);
EXPECT_TRUE(tokens[0] == "A");
EXPECT_TRUE(tokens[1] == "B");
EXPECT_TRUE(tokens[2] == "");
}
{
const std::string str = "A";
auto tokens = SplitBySymbol(str, ',');
EXPECT_TRUE(tokens.size() == 1);
EXPECT_TRUE(tokens[0] == "A");
}
{
const std::string str = ",";
auto tokens = SplitBySymbol(str, ',');
EXPECT_TRUE(tokens.size() == 2);
EXPECT_TRUE(tokens[0] == "");
EXPECT_TRUE(tokens[1] == "");
}
{
const std::string str = ",,";
auto tokens = SplitBySymbol(str, ',');
EXPECT_TRUE(tokens.size() == 3);
EXPECT_TRUE(tokens[0] == "");
EXPECT_TRUE(tokens[1] == "");
EXPECT_TRUE(tokens[2] == "");
}
{
const std::string str = "A,";
auto tokens = SplitBySymbol(str, ',');
EXPECT_TRUE(tokens.size() == 2);
EXPECT_TRUE(tokens[0] == "A");
EXPECT_TRUE(tokens[1] == "");
}
{
const std::string str = ",B";
auto tokens = SplitBySymbol(str, ',');
EXPECT_TRUE(tokens.size() == 2);
EXPECT_TRUE(tokens[0] == "");
EXPECT_TRUE(tokens[1] == "B");
}
}
TEST_F(UTStrings, TrimWhitespace) {
using ccl::TrimWhitespace;
EXPECT_EQ(TrimWhitespace(""), "");
EXPECT_EQ(TrimWhitespace(" "), "");
EXPECT_EQ(TrimWhitespace(" "), "");
EXPECT_EQ(TrimWhitespace(" 1 "), "1");
EXPECT_EQ(TrimWhitespace(" 1 "), "1");
EXPECT_EQ(TrimWhitespace(" 1"), "1");
EXPECT_EQ(TrimWhitespace("1 "), "1");
EXPECT_EQ(TrimWhitespace(" 1 2 3 "), "1 2 3");
EXPECT_EQ(TrimWhitespace(" 1 2 3 "), "1 2 3");
EXPECT_EQ(TrimWhitespace("1 2 3 "), "1 2 3");
EXPECT_EQ(TrimWhitespace("1 2 3"), "1 2 3");
EXPECT_EQ(TrimWhitespace(" , "), ",");
}
TEST_F(UTStrings, IsInteger) {
using ccl::IsInteger;
EXPECT_FALSE(IsInteger(""));
EXPECT_FALSE(IsInteger(" "));
EXPECT_FALSE(IsInteger("-"));
EXPECT_FALSE(IsInteger("1-2"));
EXPECT_FALSE(IsInteger("-1-2"));
EXPECT_FALSE(IsInteger("-1-"));
EXPECT_TRUE(IsInteger("0"));
EXPECT_TRUE(IsInteger("00"));
EXPECT_TRUE(IsInteger("1"));
EXPECT_TRUE(IsInteger("-1"));
EXPECT_TRUE(IsInteger("1234"));
EXPECT_TRUE(IsInteger("-1234"));
EXPECT_TRUE(IsInteger("-9876543210"));
EXPECT_TRUE(IsInteger("007"));
}

View File

@ -0,0 +1,22 @@
#define GTEST_LANG_CXX11 1
#include "gtest/gtest.h"
#include "ccl/Substitutes.hpp"
TEST(UTSubstitutes, CreateTranslator) {
const ccl::StrSubstitutes subst{ {"X1", "X2"}, {"X2","X3"} };
const auto translator = ccl::CreateTranslator(subst);
ASSERT_TRUE(translator("X1").has_value());
ASSERT_FALSE(translator("X3").has_value());
EXPECT_EQ(translator("X1").value(), "X2");
EXPECT_EQ(translator("X2").value(), "X3");
}
TEST(UTSubstitutes, CreateMoveTranslator) {
const auto translator = ccl::CreateTranslator({ {"X1", "X2"}, {"X2","X3"} });
ASSERT_TRUE(translator("X1").has_value());
ASSERT_FALSE(translator("X3").has_value());
EXPECT_EQ(translator("X1").value(), "X2");
EXPECT_EQ(translator("X2").value(), "X3");
}

View File

@ -0,0 +1,12 @@
//! Unity build for cclCommons tests
#define GTEST_LANG_CXX11 1
#include "gtest/gtest.h"
#include "../src/testGuard.cpp"
#include "../src/testObserver.cpp"
#include "../src/testPropagateConst.cpp"
#include "../src/testStaticMap.cpp"
#include "../src/testStrings.cpp"
#include "../src/testSubstitutes.cpp"

View File

@ -0,0 +1,23 @@
#pragma once
#include "ccl/cclTypes.hpp"
#include <cstdint>
#include <list>
#include <memory>
struct FakeObserver : ccl::types::Observer {
std::vector<uint32_t> events{};
void OnObserve(const ccl::types::Message& msg) override {
events.emplace_back(msg.Type());
}
auto DndGuard() { return ccl::types::Observer::DndGuard(); }
[[nodiscard]] bool DndStatus() const noexcept { return Observer::DndStatus(); }
};
struct FakeObservable : ccl::types::Observable {
void Notify(const uint32_t msgID) { ccl::types::Observable::Notify(msgID); }
void Notify(const ccl::types::Message& msg) { ccl::types::Observable::Notify(msg); }
};

View File

@ -0,0 +1,75 @@
cmake_minimum_required(VERSION 3.23)
project (cclGraph VERSION 1.2.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
##
## Project options
##
option(CC_BuildTests "Include tests executable" TRUE)
option(CC_UseSanitizers "Use sanitizers" FALSE)
## Compiler options
include(../cmake/CXXTargets.cmake)
##
## Project Setup
##
add_library(${PROJECT_NAME})
set_target_properties(${PROJECT_NAME} PROPERTIES
OUTPUT_NAME ${PROJECT_NAME}
DEBUG_POSTFIX d
POSITION_INDEPENDENT_CODE ON
)
target_sources(${PROJECT_NAME}
PRIVATE
src/CGraph.cpp
)
target_include_directories(${PROJECT_NAME}
PUBLIC
../cclCommons/include
include
PRIVATE
header
import/include
)
target_link_libraries(${PROJECT_NAME}
PRIVATE
ccl_CXXwarnings
ccl_CXXoptions
)
if(MSVC)
if (CMAKE_BUILD_TYPE EQUAL "DEBUG")
set(LIB_SUFFIX d)
else()
set(LIB_SUFFIX "")
endif ()
set_target_properties(${PROJECT_NAME}
PROPERTIES
COMPILE_PDB_NAME ${PROJECT_NAME}${LIB_SUFFIX}
)
get_target_property(output_dir ${PROJECT_NAME} BINARY_DIR)
install(FILES ${output_dir}/$<CONFIG>/${PROJECT_NAME}${LIB_SUFFIX}.pdb DESTINATION lib)
endif()
install(TARGETS ${PROJECT_NAME}
ARCHIVE
DESTINATION lib
LIBRARY
DESTINATION lib
INCLUDES
DESTINATION include
)
install(DIRECTORY include/ DESTINATION include)
if(CC_BuildTests)
enable_testing()
add_subdirectory("test")
endif()

View File

@ -0,0 +1,193 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\ccl\Entity.hpp" />
<ClInclude Include="include\ccl\graph\CGraph.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\CGraph.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{7E1D5338-F819-4C96-B461-9EAAB8D02E1D}</ProjectGuid>
<ConfigurationType>StaticLibrary</ConfigurationType>
<Keyword>Win32Proj</Keyword>
<RootNamespace>cclGraph</RootNamespace>
<PlatformToolset>v143</PlatformToolset>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<TargetName>$(ProjectName)d</TargetName>
<OutDir>..\..\output\lib\x86\</OutDir>
<IntDir>build\x86\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<TargetName>$(ProjectName)d</TargetName>
<OutDir>..\..\output\lib\x64\</OutDir>
<IntDir>build\x64\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<TargetName>$(ProjectName)</TargetName>
<OutDir>..\..\output\lib\x86\</OutDir>
<IntDir>build\x86\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<TargetName>$(ProjectName)</TargetName>
<OutDir>..\..\output\lib\x64\</OutDir>
<IntDir>build\x64\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<AdditionalIncludeDirectories>include;header</AdditionalIncludeDirectories>
<ProgramDataBaseFileName>$(IntDir)$(ProjectName)d.pdb</ProgramDataBaseFileName>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PostBuildEvent>
<Command>xcopy /y /s /q /i include ..\..\output\include
xcopy /y /s /q /i ..\cclCommons\include ..\..\output\include</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<AdditionalIncludeDirectories>include</AdditionalIncludeDirectories>
<ProgramDataBaseFileName>$(IntDir)$(ProjectName)d.pdb</ProgramDataBaseFileName>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PostBuildEvent>
<Command>xcopy /y /s /q /i include ..\..\output\include
xcopy /y /s /q /i ..\cclCommons\include ..\..\output\include</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_LIB;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>
</PrecompiledHeaderFile>
<LanguageStandard>stdcpplatest</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<AdditionalIncludeDirectories>include;header</AdditionalIncludeDirectories>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PostBuildEvent>
<Command>xcopy /y /s /q /i include ..\..\output\include
xcopy /y /s /q /i ..\cclCommons\include ..\..\output\include</Command>
</PostBuildEvent>
<PreBuildEvent>
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_LIB;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<AdditionalIncludeDirectories>include</AdditionalIncludeDirectories>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PostBuildEvent>
<Command>xcopy /y /s /q /i include ..\..\output\include
xcopy /y /s /q /i ..\cclCommons\include ..\..\output\include</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<None Include="VSCustom.ruleset" />
<None Include="Jenkinsfile.groovy" />
</ItemGroup>
<ItemGroup>
<Filter Include="Graph">
<UniqueIdentifier>{f0a3dd37-e5ae-4a37-ab65-d574c8dfe252}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\ccl\graph\CGraph.h">
<Filter>Graph</Filter>
</ClInclude>
<ClInclude Include="include\ccl\Entity.hpp" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\CGraph.cpp">
<Filter>Graph</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,9 @@
[requires]
gtest/1.14.0
[generators]
CMakeDeps
CMakeToolchain
[layout]
cmake_layout

View File

@ -0,0 +1,100 @@
#pragma once
#include <algorithm>
#include <functional>
#include <optional>
#include <unordered_set>
#include <unordered_map>
#include <vector>
#include <string>
#include <cstdint>
namespace ccl {
//! Entity identifier
using EntityUID = uint32_t;
//! Unordered set of entities
using SetOfEntities = std::unordered_set<EntityUID>;
//! Ordered vector of entities
using VectorOfEntities = std::vector<EntityUID>;
class EntityTranslation {
using Container = std::unordered_map<EntityUID, EntityUID>;
Container relations{};
public:
using ConstIter = typename Container::const_iterator;
using size_type = typename Container::size_type;
[[nodiscard]] ConstIter begin() const noexcept {
return std::begin(relations);
}
[[nodiscard]] ConstIter end() const noexcept {
return std::end(relations);
}
[[nodiscard]] size_type size() const noexcept {
return std::size(relations);
}
[[nodiscard]] bool empty() const noexcept {
return std::empty(relations);
}
void Clear() noexcept {
relations.clear();
}
[[nodiscard]] bool ContainsValue(const EntityUID value) const {
return std::any_of(begin(), end(), [&](const auto& el) { return el.second == value; });
}
[[nodiscard]] bool ContainsKey(const EntityUID key) const {
return relations.contains(key);
}
[[nodiscard]] EntityUID operator()(const EntityUID key) const {
return relations.at(key);
}
void SubstituteValues(const EntityTranslation& substitutes) {
for (const auto& [key, value] : relations) {
if (substitutes.ContainsKey(value)) {
relations[key] = substitutes(value);
}
}
}
// Note: doesn't break iterators!
void ReplaceValue(const EntityUID& key, const EntityUID& newVal) {
relations[key] = newVal;
}
// Note: can break iterators!
void Insert(EntityUID key, EntityUID val) {
relations.emplace(key, val);
}
void Erase(EntityUID key) noexcept {
relations.erase(key);
}
void SuperposeWith(const EntityTranslation& second) {
SubstituteValues(second);
for (const auto& iter : second.relations) {
if (!relations.contains(iter.first)) {
relations.insert(iter);
}
}
}
[[nodiscard]] bool operator==(const EntityTranslation& second) const {
return relations == second.relations;
}
[[nodiscard]] bool operator!=(const EntityTranslation& second) const {
return relations != second.relations;
}
};
} // namespace ccl
namespace ccl::tag {
using Func_Name2UID = std::function<std::optional<EntityUID>(const std::string&)>;
using Func_UID2Name = std::function<std::optional<std::string>(EntityUID)>;
} // namespace ccl::tag

View File

@ -0,0 +1,95 @@
#pragma once
#include "ccl/Entity.hpp"
#include <unordered_map>
#include <unordered_set>
namespace ccl::graph {
using VertexIndex = int32_t;
//! Entity connections graph
class CGraph {
struct Vertex {
EntityUID uid{};
bool isValid{ true };
std::vector<VertexIndex> inputs{};
std::vector<VertexIndex> outputs{};
Vertex() = default;
explicit constexpr Vertex(const EntityUID uid) : uid{ uid } {}
};
std::vector<Vertex> graph{};
std::unordered_map<EntityUID, VertexIndex> verticies{};
public:
using UnorderedItems = SetOfEntities;
using OrderedItems = VectorOfEntities;
using ItemsGroup = std::vector<UnorderedItems>;
void Clear() noexcept;
[[nodiscard]] bool Contains(EntityUID item) const;
[[nodiscard]] bool ConnectionExists(EntityUID source, EntityUID dest) const;
[[nodiscard]] VertexIndex ItemsCount() const noexcept;
[[nodiscard]] VertexIndex ConnectionsCount() const;
void AddItem(EntityUID item);
void EraseItem(EntityUID item);
void AddConnection(EntityUID source, EntityUID dest);
void SetItemInputs(EntityUID item, const UnorderedItems& connectedItems);
[[nodiscard]] UnorderedItems InputsFor(EntityUID item) const;
[[nodiscard]] bool IsReachableFrom(EntityUID dest, EntityUID source) const;
[[nodiscard]] bool HasLoop() const;
[[nodiscard]] ItemsGroup GetAllLoopsItems() const;
[[nodiscard]] UnorderedItems ExpandOutputs(const UnorderedItems& input) const;
[[nodiscard]] UnorderedItems ExpandInputs(const UnorderedItems& input) const;
[[nodiscard]] OrderedItems TopologicalOrder() const;
[[nodiscard]] OrderedItems InverseTopologicalOrder() const;
[[nodiscard]] OrderedItems Sort(const UnorderedItems& input) const;
private:
[[nodiscard]] VertexIndex IndexFor(EntityUID uid) const;
VertexIndex AddInternal(EntityUID item);
VertexIndex EraseInternal(EntityUID item);
[[nodiscard]] bool HasEdge(VertexIndex source, VertexIndex destination) const;
[[nodiscard]] std::vector<VertexIndex> InternalOrder() const;
};
template<typename InputIt, typename OutputIt>
void TopologicalSort(const CGraph& graph, InputIt start, InputIt end, OutputIt inserter) {
if (start != end) {
for (const auto v : graph.TopologicalOrder()) {
if (std::find(start, end, v) != end) {
inserter = v;
}
}
}
}
//! Updatable entity connections graph
class UpdatableGraph : public CGraph {
std::function<UnorderedItems(EntityUID)> updater;
bool invalid{ false };
public:
explicit UpdatableGraph(std::function<UnorderedItems(EntityUID)> externalUpdater);
public:
void Invalidate() noexcept { invalid = true; }
void SetValid() noexcept { invalid = false; }
[[nodiscard]] bool IsBroken() const noexcept { return invalid; }
void UpdateFor(EntityUID item);
};
} // namespace ccl::graph

332
ccl/cclGraph/src/CGraph.cpp Normal file
View File

@ -0,0 +1,332 @@
#include "ccl/graph/CGraph.h"
#include <numeric>
#include <iterator>
// Disable warnings about using operator[]
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 26446 )
#endif
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wsign-conversion"
#endif
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
namespace ccl::graph {
void CGraph::Clear() noexcept {
graph.clear();
verticies.clear();
}
bool CGraph::Contains(const EntityUID item) const {
return verticies.contains(item);
}
VertexIndex CGraph::IndexFor(const EntityUID uid) const {
return verticies.at(uid);
}
void CGraph::AddItem(const EntityUID item) {
AddInternal(item);
}
VertexIndex CGraph::AddInternal(const EntityUID item) {
if (!Contains(item)) {
graph.emplace_back(item);
const auto index = static_cast<VertexIndex>(size(graph) - 1);
verticies.emplace(item, index);
return index;
} else {
return IndexFor(item);
}
}
bool CGraph::ConnectionExists(const EntityUID source, const EntityUID dest) const {
return Contains(source) && Contains(dest) &&
HasEdge(IndexFor(source), IndexFor(dest));
}
bool CGraph::HasEdge(const VertexIndex source, const VertexIndex destination) const {
const auto& outputs = graph[source].outputs;
return std::find(begin(outputs), end(outputs), destination) != end(outputs);
}
void CGraph::AddConnection(const EntityUID source, const EntityUID dest) {
if (!ConnectionExists(source, dest)) {
const auto srcID = AddInternal(source);
const auto destID = AddInternal(dest);
graph[srcID].outputs.push_back(destID);
graph[destID].inputs.push_back(srcID);
}
}
void CGraph::EraseItem(const EntityUID item) {
if (Contains(item)) {
EraseInternal(item);
}
}
VertexIndex CGraph::EraseInternal(const EntityUID item) {
const auto itemID = IndexFor(item);
for (const auto destination : graph[itemID].outputs) {
auto& inputs = graph[destination].inputs;
inputs.erase(std::find(begin(inputs), end(inputs), itemID));
}
for (const auto source : graph[itemID].inputs) {
auto& outputs = graph[source].outputs;
outputs.erase(std::find(begin(outputs), end(outputs), itemID));
}
graph[itemID].isValid = false;
graph[itemID].inputs.clear();
graph[itemID].outputs.clear();
verticies.erase(item);
return itemID;
}
VertexIndex CGraph::ItemsCount() const noexcept {
return static_cast<VertexIndex>(ssize(verticies));
}
void CGraph::SetItemInputs(const EntityUID item, const UnorderedItems& connectedItems) {
const auto itemID = AddInternal(item);
for (const auto source : graph[itemID].inputs) {
auto& outputs = graph[source].outputs;
outputs.erase(std::find(begin(outputs), end(outputs), itemID));
}
graph[itemID].inputs.clear();
for (const auto uid : connectedItems) {
const auto srcID = AddInternal(uid);
graph[itemID].inputs.push_back(srcID);
graph[srcID].outputs.push_back(itemID);
}
}
CGraph::UnorderedItems CGraph::InputsFor(const EntityUID item) const {
if (!Contains(item)) {
return UnorderedItems{};
} else {
UnorderedItems result{};
for (const auto index : graph[IndexFor(item)].inputs) {
result.emplace(graph[index].uid);
}
return result;
}
}
VertexIndex CGraph::ConnectionsCount() const {
return std::accumulate(begin(graph), end(graph), 0,
[](const VertexIndex val, const Vertex& item) noexcept { return val + static_cast<VertexIndex>(ssize(item.outputs)); });
}
bool CGraph::HasLoop() const {
std::vector<int8_t> status(size(graph), 0);
std::vector<VertexIndex> toVisit{};
for (VertexIndex index = 0; index < ssize(graph); ++index) {
if (status[index] > 1) {
continue;
}
toVisit.push_back(index);
while (!empty(toVisit)) {
const auto item = toVisit.back();
if (status[item] == 0) {
status[item] = 1;
for (const auto child : graph[item].outputs) {
if (status[child] == 1) {
return true;
} else if (status[child] == 0) {
toVisit.push_back(child);
}
}
} else {
toVisit.pop_back();
status[item] = 2;
}
}
}
return false;
}
bool CGraph::IsReachableFrom(const EntityUID dest, const EntityUID source) const {
if (ConnectionExists(source, dest)) {
return true;
} else if (source == dest) {
return false;
} else {
const auto reachables = ExpandOutputs({ source });
return std::find(begin(reachables), end(reachables), dest) != end(reachables);
}
}
CGraph::UnorderedItems CGraph::ExpandOutputs(const UnorderedItems& input) const {
std::vector<VertexIndex> toVisit{};
std::vector<bool> marked(size(graph), false);
for (const auto uid : input) {
if (Contains(uid)) {
const auto index = IndexFor(uid);
marked[index] = true;
toVisit.push_back(index);
}
}
UnorderedItems result{};
while (!empty(toVisit)) {
const auto item = toVisit.back();
toVisit.pop_back();
result.emplace(graph[item].uid);
for (const auto child : graph[item].outputs) {
if (!marked[child]) {
marked[child] = true;
toVisit.push_back(child);
}
}
}
return result;
}
CGraph::UnorderedItems CGraph::ExpandInputs(const UnorderedItems& input) const {
std::vector<VertexIndex> toVisit{};
std::vector<bool> marked(size(graph), false);
for (const auto uid : input) {
if (Contains(uid)) {
const auto index = IndexFor(uid);
marked[index] = true;
toVisit.push_back(index);
}
}
UnorderedItems result{};
while (!empty(toVisit)) {
const auto item = toVisit.back();
toVisit.pop_back();
result.emplace(graph[item].uid);
for (const auto child : graph[item].inputs) {
if (!marked[child]) {
marked[child] = true;
toVisit.push_back(child);
}
}
}
return result;
}
std::vector<VertexIndex> CGraph::InternalOrder() const {
std::vector<VertexIndex> outOrder{};
outOrder.reserve(size(verticies));
std::vector<VertexIndex> toVisit{};
std::vector<int8_t> status(size(graph), 0);
for (VertexIndex index = 0; index < ssize(graph); ++index) {
if (status[index] != 0 || !graph[index].isValid) {
continue;
}
toVisit.push_back(index);
while (!empty(toVisit)) {
const auto item = toVisit.back();
if (status[item] == 0) {
status[item] = 1;
for (const auto child : graph[item].outputs) {
if (status[child] == 0) {
toVisit.push_back(child);
}
}
} else {
toVisit.pop_back();
if (status[item] != 2) {
status[item] = 2;
outOrder.push_back(item);
}
}
}
}
return outOrder;
}
CGraph::OrderedItems CGraph::InverseTopologicalOrder() const {
OrderedItems outOrder{};
outOrder.reserve(size(verticies));
for (const auto index : InternalOrder()) {
outOrder.push_back(graph[index].uid);
}
return outOrder;
}
CGraph::OrderedItems CGraph::Sort(const UnorderedItems& input) const {
OrderedItems result{};
TopologicalSort(*this, begin(input), end(input), std::back_inserter(result));
return result;
}
CGraph::OrderedItems CGraph::TopologicalOrder() const {
auto result = InverseTopologicalOrder();
std::reverse(begin(result), end(result));
return result;
}
CGraph::ItemsGroup CGraph::GetAllLoopsItems() const {
ItemsGroup result{};
std::vector<bool> marked(size(graph), false);
std::vector<VertexIndex> toVisit{};
std::vector<VertexIndex> component{};
for (const auto index : InternalOrder()) {
if (marked[index]) {
continue;
}
component.clear();
toVisit.push_back(index);
marked[index] = true;
while (!empty(toVisit)) {
const auto item = toVisit.back();
toVisit.pop_back();
component.push_back(item);
for (const auto child : graph[item].outputs) {
if (!marked[child]) {
toVisit.push_back(child);
marked[child] = true;
}
}
}
if (size(component) != 1 || HasEdge(index, index)) {
result.push_back({});
result.back().reserve(size(component));
for (const auto item : component) {
result.back().emplace(graph[item].uid);
}
}
}
return result;
}
UpdatableGraph::UpdatableGraph(std::function<UnorderedItems(const EntityUID)> externalUpdater)
: updater{ std::move(externalUpdater) } {}
void UpdatableGraph::UpdateFor(const EntityUID item) {
if (!IsBroken()) {
SetItemInputs(item, updater(item));
}
}
} // namespace ccl::graph
#ifdef _MSC_VER
#pragma warning( pop )
#endif
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif

View File

@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.23)
find_package(GTest REQUIRED)
add_executable(cclGraph_Tests)
target_sources(cclGraph_Tests
PRIVATE
src/testConnectionsGraph.cpp
)
target_include_directories(cclGraph_Tests
PRIVATE
../header
../import/include
utils
)
target_link_libraries(cclGraph_Tests
PRIVATE
cclGraph
ccl_CXXwarnings
ccl_CXXoptions
GTest::gtest
GTest::gtest_main
)
include(GoogleTest)
gtest_discover_tests(cclGraph_Tests)

View File

@ -0,0 +1,215 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{5A2501C1-FEFB-4B14-A94D-E8F19ADEA239}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<ProjectName>cclGraphTest</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="Shared" />
<ImportGroup Label="PropertySheets">
<Import Project="..\..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.7\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets" Condition="Exists('..\..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.7\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets')" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>build\x86\$(Configuration)\</OutDir>
<IntDir>build\x86\$(Configuration)\</IntDir>
<EnableMicrosoftCodeAnalysis>false</EnableMicrosoftCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>build\x64\$(Configuration)\</OutDir>
<IntDir>build\x64\$(Configuration)\</IntDir>
<EnableMicrosoftCodeAnalysis>false</EnableMicrosoftCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>build\x86\$(Configuration)\</OutDir>
<IntDir>build\x86\$(Configuration)\</IntDir>
<GenerateManifest>false</GenerateManifest>
<EnableMicrosoftCodeAnalysis>false</EnableMicrosoftCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<GenerateManifest>false</GenerateManifest>
<OutDir>build\x64\$(Configuration)\</OutDir>
<IntDir>build\x64\$(Configuration)\</IntDir>
<EnableMicrosoftCodeAnalysis>false</EnableMicrosoftCodeAnalysis>
</PropertyGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
<Text Include="ImportGTest.txt" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\testConnectionsGraph.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\cclGraph.vcxproj">
<Project>{7e1d5338-f819-4c96-b461-9eaab8d02e1d}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemDefinitionGroup />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level4</WarningLevel>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\include;..\header;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>cclGraphd.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
<AdditionalLibraryDirectories>..\..\..\output\lib\x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level4</WarningLevel>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\include;..\header;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
<OmitFramePointers>false</OmitFramePointers>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>cclGraphd.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
<AdditionalLibraryDirectories>..\..\..\output\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\include;..\header;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<FunctionLevelLinking>true</FunctionLevelLinking>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<AdditionalDependencies>cclGraph.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\output\lib\x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
<ProjectReference>
<LinkLibraryDependencies>true</LinkLibraryDependencies>
</ProjectReference>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\include;..\header;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<FunctionLevelLinking>true</FunctionLevelLinking>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<AdditionalDependencies>cclGraph.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\output\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link>
<ProjectReference>
<LinkLibraryDependencies>true</LinkLibraryDependencies>
</ProjectReference>
</ItemDefinitionGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.7\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.7\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets'))" />
</Target>
</Project>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Text Include="ImportGTest.txt" />
<Text Include="CMakeLists.txt" />
</ItemGroup>
<ItemGroup>
<Filter Include="src">
<UniqueIdentifier>{ed04b33d-515f-46b0-aad1-54dcbc55a5fd}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\testConnectionsGraph.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn" version="1.8.1.7" targetFramework="native" />
</packages>

View File

@ -0,0 +1,350 @@
#define GTEST_LANG_CXX11 1
#include "gtest/gtest.h"
#include "ccl/graph/CGraph.h"
#include <vector>
class UTCGraph : public ::testing::Test {
protected:
using CGraph = ccl::graph::CGraph;
CGraph graph{};
const ccl::VectorOfEntities uids{ 0, 1, 42, 3, 44, 5, 6, 7, 8, 9 };
protected:
void PrepareGraphForExpansion();
};
void UTCGraph::PrepareGraphForExpansion() {
graph.AddConnection(uids[0], uids[0]);
graph.AddConnection(uids[0], uids[1]);
graph.AddConnection(uids[0], uids[2]);
graph.AddConnection(uids[1], uids[3]);
graph.AddConnection(uids[3], uids[1]);
graph.AddConnection(uids[3], uids[4]);
}
TEST_F(UTCGraph, ItemsCount) {
EXPECT_EQ(graph.ItemsCount(), 0);
graph.AddItem(uids[0]);
EXPECT_TRUE(graph.Contains(uids[0]));
EXPECT_EQ(graph.ItemsCount(), 1);
}
TEST_F(UTCGraph, AddDuplicateItem) {
graph.AddItem(uids[0]);
EXPECT_TRUE(graph.Contains(uids[0]));
graph.AddItem(uids[0]);
EXPECT_TRUE(graph.Contains(uids[0]));
EXPECT_EQ(graph.ItemsCount(), 1);
}
TEST_F(UTCGraph, ForeignItem) {
graph.AddItem(uids[0]);
EXPECT_FALSE(graph.Contains(uids[1]));
EXPECT_TRUE(empty(graph.InputsFor(uids[1])));
EXPECT_TRUE(empty(graph.ExpandOutputs({ uids[1] })));
EXPECT_TRUE(empty(graph.ExpandInputs({ uids[1] })));
EXPECT_FALSE(graph.ConnectionExists(uids[1], uids[1]));
EXPECT_FALSE(graph.ConnectionExists(uids[0], uids[1]));
EXPECT_FALSE(graph.ConnectionExists(uids[1], uids[0]));
EXPECT_FALSE(graph.IsReachableFrom(uids[1], uids[1]));
EXPECT_FALSE(graph.IsReachableFrom(uids[1], uids[0]));
EXPECT_FALSE(graph.IsReachableFrom(uids[0], uids[1]));
}
TEST_F(UTCGraph, EmptyGroupInput) {
PrepareGraphForExpansion();
EXPECT_TRUE(empty(graph.ExpandOutputs({})));
EXPECT_TRUE(empty(graph.ExpandInputs({})));
}
TEST_F(UTCGraph, Clear) {
graph.AddItem(uids[0]);
EXPECT_TRUE(graph.Contains(uids[0]));
graph.Clear();
EXPECT_FALSE(graph.Contains(uids[0]));
}
TEST_F(UTCGraph, CopyAndMove) {
graph.AddItem(uids[0]);
CGraph copy{ graph };
EXPECT_TRUE(copy.Contains(uids[0]));
copy.EraseItem(uids[0]);
EXPECT_FALSE(copy.Contains(uids[0]));
EXPECT_TRUE(graph.Contains(uids[0]));
copy = graph;
EXPECT_TRUE(copy.Contains(uids[0]));
CGraph moved(std::move(copy));
EXPECT_TRUE(moved.Contains(uids[0]));
CGraph secondMove{};
secondMove = std::move(moved);
EXPECT_TRUE(secondMove.Contains(uids[0]));
}
TEST_F(UTCGraph, AddConnection) {
graph.AddItem(uids[0]);
graph.AddItem(uids[1]);
EXPECT_EQ(ssize(graph.InputsFor(uids[0])), 0);
EXPECT_EQ(ssize(graph.InputsFor(uids[1])), 0);
graph.AddConnection(uids[0], uids[1]);
EXPECT_TRUE(graph.ConnectionExists(uids[0], uids[1]));
EXPECT_EQ(ssize(graph.InputsFor(uids[1])), 1);
EXPECT_EQ(ssize(graph.InputsFor(uids[0])), 0);
}
TEST_F(UTCGraph, ConnectionsCount) {
EXPECT_EQ(graph.ConnectionsCount(), 0);
graph.AddItem(uids[0]);
graph.AddItem(uids[1]);
graph.AddConnection(uids[0], uids[1]);
EXPECT_EQ(graph.ConnectionsCount(), 1);
}
TEST_F(UTCGraph, AddDoubleConnection) {
graph.AddConnection(uids[0], uids[1]);
graph.AddConnection(uids[0], uids[1]);
EXPECT_EQ(ssize(graph.InputsFor(uids[1])), 1);
EXPECT_EQ(graph.ConnectionsCount(), 1);
}
TEST_F(UTCGraph, AddSelfConnection) {
graph.AddConnection(uids[0], uids[0]);
EXPECT_EQ(ssize(graph.InputsFor(uids[0])), 1);
EXPECT_EQ(graph.ConnectionsCount(), 1);
}
TEST_F(UTCGraph, AddBiConnection) {
graph.AddConnection(uids[0], uids[1]);
graph.AddConnection(uids[1], uids[0]);
EXPECT_TRUE(graph.ConnectionExists(uids[0], uids[1]));
EXPECT_TRUE(graph.ConnectionExists(uids[1], uids[0]));
EXPECT_EQ(ssize(graph.InputsFor(uids[0])), 1);
EXPECT_EQ(ssize(graph.InputsFor(uids[1])), 1);
EXPECT_EQ(graph.ConnectionsCount(), 2);
}
TEST_F(UTCGraph, AddConnectionWithItems) {
EXPECT_EQ(graph.ItemsCount(), 0);
graph.AddConnection(uids[1], uids[0]);
EXPECT_EQ(ssize(graph.InputsFor(uids[0])), 1);
EXPECT_EQ(ssize(graph.InputsFor(uids[1])), 0);
EXPECT_EQ(graph.ConnectionsCount(), 1);
}
TEST_F(UTCGraph, CheckInvalidConnection) {
graph.AddConnection(uids[0], uids[1]);
EXPECT_TRUE(graph.ConnectionExists(uids[0], uids[1]));
EXPECT_FALSE(graph.ConnectionExists(uids[1], uids[0]));
EXPECT_FALSE(graph.ConnectionExists(uids[0], uids[0]));
EXPECT_FALSE(graph.ConnectionExists(uids[0], uids[2]));
EXPECT_FALSE(graph.ConnectionExists(uids[2], uids[0]));
EXPECT_FALSE(graph.ConnectionExists(uids[2], uids[3]));
}
TEST_F(UTCGraph, EraseItem) {
graph.AddItem(uids[0]);
graph.AddItem(uids[1]);
graph.EraseItem(uids[0]);
EXPECT_EQ(graph.ItemsCount(), 1);
EXPECT_TRUE(graph.Contains(uids[1]));
EXPECT_FALSE(graph.Contains(uids[0]));
EXPECT_EQ(graph.ConnectionsCount(), 0);
}
TEST_F(UTCGraph, EraseConnectedItem) {
graph.AddConnection(uids[0], uids[1]);
graph.AddConnection(uids[1], uids[0]);
graph.EraseItem(uids[0]);
EXPECT_EQ(graph.ItemsCount(), 1);
EXPECT_EQ(graph.ConnectionsCount(), 0);
EXPECT_EQ(ssize(graph.InputsFor(uids[1])), 0);
graph.EraseItem(uids[1]);
EXPECT_EQ(graph.ItemsCount(), 0);
EXPECT_EQ(graph.ConnectionsCount(), 0);
}
TEST_F(UTCGraph, SetConnections) {
graph.SetItemInputs(uids[0], { begin(uids), end(uids) });
EXPECT_EQ(graph.ItemsCount(), ssize(uids));
EXPECT_EQ(graph.ConnectionsCount(), ssize(uids));
}
TEST_F(UTCGraph, SetConnectionsWithPreexisting) {
graph.AddConnection(uids[0], uids[1]);
graph.AddConnection(uids[1], uids[0]);
graph.SetItemInputs(uids[0], { begin(uids), end(uids) });
EXPECT_TRUE(graph.ConnectionExists(uids[0], uids[1]));
EXPECT_EQ(graph.ItemsCount(), ssize(uids));
EXPECT_EQ(graph.ConnectionsCount(), ssize(uids) + 1);
}
TEST_F(UTCGraph, SetConnectionsReplacing) {
graph.AddConnection(uids[2], uids[0]);
graph.AddConnection(uids[0], uids[0]);
graph.SetItemInputs(uids[0], { uids[1] });
EXPECT_TRUE(graph.ConnectionExists(uids[1], uids[0]));
EXPECT_FALSE(graph.ConnectionExists(uids[2], uids[0]));
EXPECT_EQ(graph.ConnectionsCount(), 1);
}
TEST_F(UTCGraph, EraseAfterSetConnections) {
graph.AddConnection(uids[0], uids[1]);
graph.AddConnection(uids[1], uids[0]);
graph.SetItemInputs(uids[0], { uids[2], uids[3] });
graph.EraseItem(uids[1]);
EXPECT_EQ(graph.ConnectionsCount(), 2);
}
TEST_F(UTCGraph, SimpleLoop) {
EXPECT_FALSE(graph.HasLoop());
graph.AddConnection(uids[0], uids[0]);
EXPECT_TRUE(graph.HasLoop());
bool loopFoundCorrect = graph.GetAllLoopsItems() == CGraph::ItemsGroup{ { uids[0] } };
EXPECT_TRUE(loopFoundCorrect);
}
TEST_F(UTCGraph, ComplexGraph) {
graph.AddItem(uids[1]);
graph.AddItem(uids[0]);
graph.AddItem(uids[9]);
graph.AddItem(uids[3]);
graph.AddItem(uids[8]);
graph.AddItem(uids[5]);
graph.AddItem(uids[2]);
graph.AddItem(uids[6]);
graph.AddItem(uids[4]);
graph.AddItem(uids[7]);
graph.AddConnection(uids[2], uids[0]);
graph.AddConnection(uids[3], uids[0]);
graph.AddConnection(uids[3], uids[1]);
graph.AddConnection(uids[4], uids[3]);
graph.AddConnection(uids[4], uids[2]);
graph.AddConnection(uids[6], uids[3]);
graph.AddConnection(uids[6], uids[4]);
graph.AddConnection(uids[6], uids[2]);
graph.AddConnection(uids[7], uids[6]);
graph.AddConnection(uids[7], uids[5]);
graph.AddConnection(uids[5], uids[0]);
graph.AddConnection(uids[5], uids[3]);
graph.AddConnection(uids[8], uids[5]);
graph.AddConnection(uids[8], uids[7]);
graph.AddConnection(uids[9], uids[3]);
graph.AddConnection(uids[9], uids[5]);
graph.AddConnection(uids[9], uids[8]);
EXPECT_FALSE(graph.IsReachableFrom(uids[0], uids[1]));
EXPECT_FALSE(graph.IsReachableFrom(uids[1], uids[0]));
EXPECT_FALSE(graph.HasLoop());
}
TEST_F(UTCGraph, ComplexLoop) {
graph.AddConnection(uids[0], uids[1]);
graph.AddConnection(uids[1], uids[2]);
graph.AddConnection(uids[2], uids[0]);
graph.AddConnection(uids[2], uids[3]);
ASSERT_TRUE(graph.HasLoop());
const std::vector<ccl::SetOfEntities> loop{ { uids[0], uids[1], uids[2] } };
EXPECT_EQ(graph.GetAllLoopsItems(), loop);
}
TEST_F(UTCGraph, MultipleLoops) {
graph.AddConnection(uids[0], uids[1]);
graph.AddConnection(uids[1], uids[2]);
graph.AddConnection(uids[2], uids[0]);
graph.AddConnection(uids[2], uids[3]);
graph.AddConnection(uids[3], uids[3]);
ASSERT_TRUE(graph.HasLoop());
auto loopsItems = graph.GetAllLoopsItems();
ASSERT_EQ(ssize(loopsItems), 2);
const auto loop1 = CGraph::ItemsGroup::value_type{ uids[0], uids[1], uids[2] };
const auto loop2 = CGraph::ItemsGroup::value_type{ uids[3] };
EXPECT_TRUE((loopsItems[0] == loop1 && loopsItems[1] == loop2) ||
(loopsItems[1] == loop1 && loopsItems[0] == loop2));
}
TEST_F(UTCGraph, IsReachableFrom) {
PrepareGraphForExpansion();
EXPECT_TRUE(graph.IsReachableFrom(uids[0], uids[0]));
EXPECT_FALSE(graph.IsReachableFrom(uids[4], uids[4]));
EXPECT_TRUE(graph.IsReachableFrom(uids[1], uids[0]));
EXPECT_FALSE(graph.IsReachableFrom(uids[0], uids[1]));
EXPECT_TRUE(graph.IsReachableFrom(uids[4], uids[0]));
}
TEST_F(UTCGraph, ExpandOutputs) {
PrepareGraphForExpansion();
auto expansion = graph.ExpandOutputs({ uids[0] });
ccl::SetOfEntities testingVector{ uids[0], uids[1], uids[2], uids[3], uids[4] };
EXPECT_EQ(expansion, testingVector);
for (const auto id : expansion) {
if (id != uids[0]) {
EXPECT_TRUE(graph.IsReachableFrom(id, uids[0]));
}
}
}
TEST_F(UTCGraph, ExpandIntoReachableReverse) {
PrepareGraphForExpansion();
auto expansion = graph.ExpandInputs({ uids[4] });
ccl::SetOfEntities testingVector{ uids[0], uids[1], uids[3], uids[4] };
EXPECT_EQ(expansion, testingVector);
}
TEST_F(UTCGraph, InverseTopologicalOrder) {
graph.AddConnection(uids[0], uids[1]);
graph.AddConnection(uids[0], uids[2]);
graph.AddConnection(uids[2], uids[1]);
graph.AddConnection(uids[4], uids[3]);
graph.AddItem(uids[5]);
auto sorted = graph.InverseTopologicalOrder();
ASSERT_FALSE(empty(sorted));
for (auto i = 0U; i < size(sorted) - 1; ++i) {
for (auto j = i + 1; j < size(sorted); ++j) {
EXPECT_FALSE(graph.IsReachableFrom(sorted[j], sorted[i]));
}
}
}
TEST_F(UTCGraph, TopologicalOrder) {
EXPECT_TRUE(empty(graph.TopologicalOrder()));
graph.AddConnection(uids[0], uids[1]);
graph.AddConnection(uids[0], uids[2]);
graph.AddConnection(uids[2], uids[1]);
graph.AddConnection(uids[4], uids[3]);
graph.AddItem(uids[5]);
auto sorted = graph.TopologicalOrder();
ASSERT_FALSE(empty(sorted));
for (auto i = 0U; i < size(sorted) - 1; ++i) {
for (auto j = i + 1; j < size(sorted); ++j) {
EXPECT_FALSE(graph.IsReachableFrom(sorted[i], sorted[j]));
}
}
}
TEST_F(UTCGraph, InverseTopologicalOrderLoop) {
EXPECT_TRUE(empty(graph.InverseTopologicalOrder()));
graph.AddConnection(uids[0], uids[1]);
graph.AddConnection(uids[1], uids[2]);
graph.AddConnection(uids[2], uids[0]);
graph.AddConnection(uids[3], uids[0]);
auto sorted = graph.InverseTopologicalOrder();
EXPECT_TRUE(std::find(begin(sorted), end(sorted), uids[3]) >
std::find(begin(sorted), end(sorted), uids[0]));
}

View File

@ -0,0 +1,75 @@
cmake_minimum_required(VERSION 3.23)
project (cclLang VERSION 1.2.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
##
## Project options
##
option(CC_BuildTests "Include tests executable" TRUE)
option(CC_UseSanitizers "Use sanitizers" FALSE)
## Compiler options
include(../cmake/CXXTargets.cmake)
##
## Project Setup
##
add_library(${PROJECT_NAME})
set_target_properties(${PROJECT_NAME} PROPERTIES
OUTPUT_NAME ${PROJECT_NAME}
DEBUG_POSTFIX d
POSITION_INDEPENDENT_CODE ON
)
target_sources(${PROJECT_NAME}
PRIVATE
unity/cclLang.cpp
)
target_include_directories(${PROJECT_NAME}
PUBLIC
../cclCommons/include
include
PRIVATE
header
import/include
)
target_link_libraries(${PROJECT_NAME}
PRIVATE
ccl_CXXwarnings
ccl_CXXoptions
)
if(MSVC)
if (CMAKE_BUILD_TYPE EQUAL "DEBUG")
set(LIB_SUFFIX d)
else()
set(LIB_SUFFIX "")
endif ()
set_target_properties(${PROJECT_NAME}
PROPERTIES
COMPILE_PDB_NAME ${PROJECT_NAME}${LIB_SUFFIX}
)
get_target_property(output_dir ${PROJECT_NAME} BINARY_DIR)
install(FILES ${output_dir}/$<CONFIG>/${PROJECT_NAME}${LIB_SUFFIX}.pdb DESTINATION lib)
endif()
install(TARGETS ${PROJECT_NAME}
ARCHIVE
DESTINATION lib
LIBRARY
DESTINATION lib
INCLUDES
DESTINATION include
)
install(DIRECTORY include/ DESTINATION include)
if(CC_BuildTests)
enable_testing()
add_subdirectory("test")
endif()

209
ccl/cclLang/cclLang.vcxproj Normal file
View File

@ -0,0 +1,209 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\ccl\lang\EntityTermContext.hpp" />
<ClInclude Include="include\ccl\lang\LexicalTerm.h" />
<ClInclude Include="include\ccl\lang\Literals.h" />
<ClInclude Include="include\ccl\lang\ManagedText.h" />
<ClInclude Include="include\ccl\lang\Morphology.h" />
<ClInclude Include="include\ccl\lang\Reference.h" />
<ClInclude Include="include\ccl\lang\RefsManager.h" />
<ClInclude Include="include\ccl\lang\TextEnvironment.h" />
<ClInclude Include="include\ccl\lang\TextProcessor.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\LexicalTerm.cpp" />
<ClCompile Include="src\Literals.cpp" />
<ClCompile Include="src\ManagedText.cpp" />
<ClCompile Include="src\Morphology.cpp" />
<ClCompile Include="src\Reference.cpp" />
<ClCompile Include="src\RefsManager.cpp" />
<ClCompile Include="src\TextEnvironment.cpp" />
<ClCompile Include="src\TextProcessor.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ConfigurationType>StaticLibrary</ConfigurationType>
<ProjectGuid>{76B03803-56CC-47C2-A8F0-2241FCAF2898}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>cclLang</RootNamespace>
<PlatformToolset>v143</PlatformToolset>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<TargetName>$(ProjectName)d</TargetName>
<OutDir>..\..\output\lib\x86\</OutDir>
<IntDir>build\x86\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<TargetName>$(ProjectName)d</TargetName>
<OutDir>..\..\output\lib\x64\</OutDir>
<IntDir>build\x64\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<TargetName>$(ProjectName)</TargetName>
<OutDir>..\..\output\lib\x86\</OutDir>
<IntDir>build\x86\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<TargetName>$(ProjectName)</TargetName>
<OutDir>..\..\output\lib\x64\</OutDir>
<IntDir>build\x64\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<AdditionalIncludeDirectories>include;header;..\cclCommons\include</AdditionalIncludeDirectories>
<ProgramDataBaseFileName>$(IntDir)$(ProjectName)d.pdb</ProgramDataBaseFileName>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PostBuildEvent>
<Command>xcopy /y /s /q /i include ..\..\output\include
xcopy /y /s /q /i ..\cclCommons\include ..\..\output\include</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<AdditionalIncludeDirectories>include;header;..\cclCommons\include</AdditionalIncludeDirectories>
<ProgramDataBaseFileName>$(IntDir)$(ProjectName)d.pdb</ProgramDataBaseFileName>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
<OmitFramePointers>false</OmitFramePointers>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PostBuildEvent>
<Command>xcopy /y /s /q /i include ..\..\output\include
xcopy /y /s /q /i ..\cclCommons\include ..\..\output\include</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_LIB;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<AdditionalIncludeDirectories>include;header;..\cclCommons\include</AdditionalIncludeDirectories>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PostBuildEvent>
<Command>xcopy /y /s /q /i include ..\..\output\include
xcopy /y /s /q /i ..\cclCommons\include ..\..\output\include</Command>
</PostBuildEvent>
<PreBuildEvent>
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_LIB;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<AdditionalIncludeDirectories>include;header;..\cclCommons\include</AdditionalIncludeDirectories>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PostBuildEvent>
<Command>xcopy /y /s /q /i include ..\..\output\include
xcopy /y /s /q /i ..\cclCommons\include ..\..\output\include</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="lang">
<UniqueIdentifier>{3c85ab44-bb88-4509-be90-8f248199ac3f}</UniqueIdentifier>
</Filter>
<Filter Include="env">
<UniqueIdentifier>{5ea8ccaf-0a49-441e-8a3a-bb300790ffa4}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\ccl\lang\Reference.h">
<Filter>lang</Filter>
</ClInclude>
<ClInclude Include="include\ccl\lang\RefsManager.h">
<Filter>lang</Filter>
</ClInclude>
<ClInclude Include="include\ccl\lang\EntityTermContext.hpp">
<Filter>lang</Filter>
</ClInclude>
<ClInclude Include="include\ccl\lang\LexicalTerm.h">
<Filter>lang</Filter>
</ClInclude>
<ClInclude Include="include\ccl\lang\ManagedText.h">
<Filter>lang</Filter>
</ClInclude>
<ClInclude Include="include\ccl\lang\TextProcessor.h">
<Filter>env</Filter>
</ClInclude>
<ClInclude Include="include\ccl\lang\TextEnvironment.h">
<Filter>env</Filter>
</ClInclude>
<ClInclude Include="include\ccl\lang\Morphology.h">
<Filter>lang</Filter>
</ClInclude>
<ClInclude Include="include\ccl\lang\Literals.h">
<Filter>lang</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\Reference.cpp">
<Filter>lang</Filter>
</ClCompile>
<ClCompile Include="src\RefsManager.cpp">
<Filter>lang</Filter>
</ClCompile>
<ClCompile Include="src\LexicalTerm.cpp">
<Filter>lang</Filter>
</ClCompile>
<ClCompile Include="src\ManagedText.cpp">
<Filter>lang</Filter>
</ClCompile>
<ClCompile Include="src\TextProcessor.cpp">
<Filter>env</Filter>
</ClCompile>
<ClCompile Include="src\TextEnvironment.cpp">
<Filter>env</Filter>
</ClCompile>
<ClCompile Include="src\Morphology.cpp">
<Filter>lang</Filter>
</ClCompile>
<ClCompile Include="src\Literals.cpp">
<Filter>lang</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,9 @@
[requires]
gtest/1.14.0
[generators]
CMakeDeps
CMakeToolchain
[layout]
cmake_layout

View File

@ -0,0 +1,48 @@
#pragma once
#include "ccl/lang/Morphology.h"
#include <string>
#include <optional>
#include <vector>
namespace ccl::lang {
class LexicalTerm;
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 26440 ) // Note: do not warn if default implementation might be noexcept
#endif
//! Term context
class EntityTermContext {
public:
virtual ~EntityTermContext() noexcept = default;
protected:
EntityTermContext() = default;
EntityTermContext(const EntityTermContext&) = default;
EntityTermContext& operator=(const EntityTermContext&) = default;
EntityTermContext(EntityTermContext&&) noexcept = default;
EntityTermContext& operator=(EntityTermContext&&) noexcept = default;
public:
[[nodiscard]] virtual const LexicalTerm* At(const std::string& /*entity*/) const {
return nullptr;
}
[[nodiscard]] virtual bool Contains(const std::string& /*entity*/) const {
return false;
}
[[nodiscard]] virtual std::unordered_map<std::string, std::string>
MatchingTerms(const std::string& /*input*/) const {
return {};
}
};
#ifdef _MSC_VER
#pragma warning( pop )
#endif
} // namespace ccl::lang

View File

@ -0,0 +1,59 @@
#pragma once
#include "ccl/lang/Morphology.h"
#include "ccl/lang/ManagedText.h"
#include <unordered_map>
namespace ccl::lang {
class EntityTermContext;
//! Complex term with managed text and cached resolution
class LexicalTerm {
using Forms = std::unordered_map<Morphology, std::string>;
ManagedText text{};
Forms manualForms{};
mutable Forms cachedForms{};
public:
LexicalTerm() noexcept = default;
explicit LexicalTerm(std::string ref, std::string resolved) noexcept
: text{ ManagedText(std::move(ref), std::move(resolved)) } {}
explicit LexicalTerm(std::string ref) noexcept
: text{ std::move(ref) } {}
explicit LexicalTerm(ManagedText txt) noexcept
: text{ std::move(txt) } {}
public:
[[nodiscard]] const std::string& Nominal() const noexcept {
return text.Str();
}
[[nodiscard]] const ManagedText& Text() const noexcept {
return text;
}
void SetText(std::string_view ref, const EntityTermContext& cntxt);
void TranslateRefs(const StrTranslator& old2New, const EntityTermContext& cntxt);
void TranslateRaw(const StrTranslator& old2New);
[[nodiscard]] bool operator==(const LexicalTerm& t2) const noexcept;
[[nodiscard]] bool operator!=(const LexicalTerm& t2) const noexcept { return !(*this == t2); }
void UpdateFrom(const EntityTermContext& cntxt);
[[nodiscard]] bool MatchStr(const std::string& str) const;
[[nodiscard]] const std::string& GetForm(const Morphology& form) const;
[[nodiscard]] const std::string& GetNominalForm() const;
[[nodiscard]] const std::unordered_map<Morphology, std::string>& GetAllManual() const;
void SetForm(const Morphology& form, std::string_view formText);
[[nodiscard]] bool IsFormManual(const Morphology& form) const;
[[nodiscard]] const std::string& GetManualForm(const Morphology& form) const;
void ClearForms() noexcept;
};
} // namespace ccl::lang

View File

@ -0,0 +1,14 @@
#pragma once
#include "ccl/lang/Morphology.h"
#include "ccl/lang/Reference.h"
namespace ccl::lang {
//! Morphology from string of comma separated tags
Morphology operator""_form(const char* input, size_t /*size*/);
//! Reference from string
Reference operator""_ref(const char* input, size_t /*size*/);
} // namespace ccl::lang

View File

@ -0,0 +1,41 @@
#pragma once
#include "ccl/Substitutes.hpp"
#include "ccl/lang/Reference.h"
#include <unordered_set>
namespace ccl::lang {
class EntityTermContext;
//! Text with managed references
class ManagedText {
std::string rawText{};
mutable std::string cache{};
public:
ManagedText() noexcept = default;
explicit ManagedText(std::string stringWithRefs, std::string cache) noexcept
: rawText{ std::move(stringWithRefs) }, cache{ std::move(cache) } {}
explicit ManagedText(std::string stringWithRefs) noexcept
: ManagedText(std::move(stringWithRefs), std::string{}) {}
public:
[[nodiscard]] bool operator==(const ManagedText& str2) const noexcept;
[[nodiscard]] bool operator!=(const ManagedText& str2) const noexcept;
[[nodiscard]] bool empty() const noexcept;
[[nodiscard]] const std::string& Str() const noexcept;
[[nodiscard]] const std::string& Raw() const noexcept;
void SetRaw(std::string_view ref);
void InitFrom(std::string_view ref, const EntityTermContext& cntxt);
void UpdateFrom(const EntityTermContext& cntxt);
void TranslateRaw(const StrTranslator& old2New);
void TranslateRefs(const StrTranslator& old2New, const EntityTermContext& cntxt);
[[nodiscard]] std::unordered_set<std::string> Referals() const;
};
} // namespace ccl::lang

View File

@ -0,0 +1,191 @@
#pragma once
#include <cstdint>
#include <string>
#include <set>
#include "ccl/cclTypes.hpp"
//! Namespace for russian language specification
namespace ccl::lang {
//! All values of morphology attributes
enum class Grammem : uint8_t {
invalid = 0,
NOUN, NPRO, INFN, VERB, ADJF, ADJS, PRTF, PRTS, ADVB, GRND, COMP, PRED, NUMR,
CONJ, INTJ, PRCL, PREP, PNCT,
pres, past, futr,
per1, per2, per3,
sing, plur,
masc, femn, neut,
nomn, gent, datv, ablt, accs, loct
};
namespace detail {
static constexpr size_t GRAMMEM_LENGTH = 4U;
static constexpr std::array<std::string_view, 36> TAG_NAMES = {
"UNKN",
"NOUN", "NPRO", "INFN", "VERB", "ADJF", "ADJS",
"PRTF", "PRTS", "ADVB", "GRND", "COMP", "PRED",
"NUMR", "CONJ", "INTJ", "PRCL", "PREP", "PNCT",
"pres", "past", "futr",
"1per", "2per", "3per",
"sing", "plur",
"masc", "femn", "neut",
"nomn", "gent", "datv", "ablt", "accs", "loct"
};
static constexpr size_t GRAMMEM_COUNT = 36U;
using MapType = StaticMap<std::string_view, Grammem, GRAMMEM_COUNT>;
using MapValue = MapType::DataType::value_type;
static constexpr MapType TAG_MAP{
MapType::DataType({
MapValue{"UNKN", Grammem::invalid},
MapValue{"NOUN", Grammem::NOUN}, MapValue{"NPRO", Grammem::NPRO}, MapValue{"INFN", Grammem::INFN},
MapValue{"VERB", Grammem::VERB}, MapValue{"ADJF", Grammem::ADJF}, MapValue{"ADJS", Grammem::ADJS},
MapValue{"PRTF", Grammem::PRTF}, MapValue{"PRTS", Grammem::PRTS}, MapValue{"ADVB", Grammem::ADVB},
MapValue{"GRND", Grammem::GRND}, MapValue{"COMP", Grammem::COMP}, MapValue{"PRED", Grammem::PRED},
MapValue{"NUMR", Grammem::NUMR}, MapValue{"CONJ", Grammem::CONJ}, MapValue{"INTJ", Grammem::INTJ},
MapValue{"PRCL", Grammem::PRCL}, MapValue{"PREP", Grammem::PREP}, MapValue{"PNCT", Grammem::PNCT},
MapValue{"1per", Grammem::per1}, MapValue{"2per", Grammem::per2}, MapValue{"3per", Grammem::per3},
MapValue{"pres", Grammem::pres}, MapValue{"past", Grammem::past}, MapValue{"futr", Grammem::futr},
MapValue{"sing", Grammem::sing}, MapValue{"plur", Grammem::plur},
MapValue{"masc", Grammem::masc}, MapValue{"femn", Grammem::femn}, MapValue{"neut", Grammem::neut},
MapValue{"nomn", Grammem::nomn}, MapValue{"gent", Grammem::gent}, MapValue{"datv", Grammem::datv},
MapValue{"ablt", Grammem::ablt}, MapValue{"accs", Grammem::accs}, MapValue{"loct", Grammem::loct}
})
};
} // namespace detail
[[nodiscard]] inline Grammem Str2Grammem(std::string_view text) {
if (text.length() != detail::GRAMMEM_LENGTH || !detail::TAG_MAP.ContainsKey(text)) {
return Grammem::invalid;
} else {
return detail::TAG_MAP.AtKey(text);
}
}
[[nodiscard]] inline std::string_view Grammem2Str(Grammem gram) noexcept {
return detail::TAG_NAMES.at(static_cast<size_t>(gram));
}
//! Morphology parameters for a word or collation of words
struct Morphology {
std::set<Grammem> tags{};
Morphology() = default;
explicit Morphology(std::initializer_list<Grammem> init) noexcept // NOLINT
: tags{ init } {}
explicit Morphology(std::string_view tagsText);
explicit Morphology(const std::vector<std::string_view>& tags_);
[[nodiscard]] bool operator==(const Morphology& rval) const noexcept;
[[nodiscard]] bool operator!=(const Morphology& rval) const noexcept;
[[nodiscard]] size_t size() const noexcept;
[[nodiscard]] bool empty() const noexcept;
[[nodiscard]] bool Contains(Grammem gram) const noexcept;
[[nodiscard]] Grammem GetPOS() const;
[[nodiscard]] Grammem GetCase() const;
[[nodiscard]] Grammem GetNumber() const;
[[nodiscard]] Grammem GetGender() const;
[[nodiscard]] Grammem GetTense() const;
[[nodiscard]] Grammem GetPerson() const;
[[nodiscard]] std::string ToString() const noexcept;
};
//! Часть речи
static constexpr std::array<Grammem, 18> ALL_POS = {
Grammem::NOUN, // существительное | хомяк
Grammem::NPRO, // местоимение | он
Grammem::INFN, // глагол (инфинитив) | говорить
Grammem::VERB, // глагол (личная форма) | говорил
Grammem::ADJF, // прилагательное (полное) | хороший
Grammem::ADJS, // прилагательное (краткое) | хорош
Grammem::PRTF, // причастие (полное) | прочитавший, прочитанная
Grammem::PRTS, // причастие (краткое) | прочитана
Grammem::ADVB, // наречие | далеко
Grammem::GRND, // деепричастие | прочитав
Grammem::COMP, // компаратив | лучше, получше, выше
Grammem::PRED, // предикатив | некогда
Grammem::NUMR, // числительное | три
Grammem::CONJ, // союз | или
Grammem::INTJ, // междометие | ой
Grammem::PRCL, // частица | бы, же
Grammem::PREP, // предлог | в
Grammem::PNCT // знак пунктуации
};
//! Падеж
static constexpr std::array<Grammem, 6> ALL_CASES = {
Grammem::nomn, // именительный | Кто?, Что? | (хомяк) ест
Grammem::gent, // родительный | Кого? Чего? | у нас нет (хомяка)
Grammem::datv, // дательный | Кому? Чему? | сказать (хомяку) спасибо
Grammem::ablt, // винительный | Кого? Что? | тигр ест (хомяка)
Grammem::accs, // творительный | Кем? Чем? | зерно съедено (хомяком)
Grammem::loct // предложный | О ком? О чём? | хомяка несут в (коробке)
};
//! Число
static constexpr std::array<Grammem, 2> ALL_NUMBERS = {
Grammem::sing, // единственное
Grammem::plur // множественное
};
//! Род
static constexpr std::array<Grammem, 3> ALL_GENDERS = {
Grammem::masc, // мужской
Grammem::femn, // женский
Grammem::neut // средний
};
//! Время
static constexpr std::array<Grammem, 3> ALL_TENSES = {
Grammem::pres, // настоящее
Grammem::past, // прошлое
Grammem::futr // будущее
};
//! Лицо
static constexpr std::array<Grammem, 3> ALL_PERSONS = {
Grammem::per1, // первое (я, мы)
Grammem::per2, // второе (ты, вы)
Grammem::per3 // третье (он, она, оно, они)
};
//! Словоформы терминологической связки
static const std::array<Morphology, 12> TERM_FORMS = {
// TODO: replace static object for constexpr. Need to figure out constexpr Morphology
Morphology{ Grammem::sing, Grammem::nomn },
Morphology{ Grammem::sing, Grammem::gent },
Morphology{ Grammem::sing, Grammem::datv },
Morphology{ Grammem::sing, Grammem::ablt },
Morphology{ Grammem::sing, Grammem::accs },
Morphology{ Grammem::sing, Grammem::loct },
Morphology{ Grammem::plur, Grammem::nomn },
Morphology{ Grammem::plur, Grammem::gent },
Morphology{ Grammem::plur, Grammem::datv },
Morphology{ Grammem::plur, Grammem::ablt },
Morphology{ Grammem::plur, Grammem::accs },
Morphology{ Grammem::plur, Grammem::loct },
};
} // namespace ccl::lang
namespace std {
template <>
struct hash<ccl::lang::Morphology> {
uint32_t operator()(const ccl::lang::Morphology& form) const noexcept {
using std::hash;
uint32_t result = 0;
for (const auto tag : form.tags) {
result += static_cast<uint32_t>(tag);
}
return result + 0xFF * static_cast<uint32_t>(size(form.tags)); // NOLINT
}
};
} // namespace std

View File

@ -0,0 +1,94 @@
#pragma once
#include "ccl/Strings.hpp"
#include "ccl/cclMeta.hpp"
#include "ccl/Substitutes.hpp"
#include "ccl/lang/EntityTermContext.hpp"
#include <memory>
#include <variant>
#include <string_view>
namespace ccl::lang {
//! Reference types
enum class ReferenceType : uint8_t {
invalid = 0,
entity,
collaboration
};
//! Reference to entitity in a specific form
struct EntityRef {
std::string entity;
Morphology form;
explicit EntityRef(std::string entity, Morphology form) noexcept
: entity{ std::move(entity) }, form{ std::move(form)} {}
enum Fields : uint8_t {
TR_ENTITY = 0,
TR_TAGS = 1
};
static constexpr uint8_t fieldCount = 2;
[[nodiscard]] std::string ToString() const;
};
//! Reference to a text in collaboration with another reference
struct CollaborationRef {
std::string nominal;
int16_t offset;
explicit CollaborationRef(std::string text, int16_t offset) noexcept
: nominal{ std::move(text) }, offset{ offset } {}
enum Fields : uint8_t {
CR_OFFSET = 0,
CR_TEXT = 1
};
static constexpr uint8_t fieldCount = 2;
[[nodiscard]] std::string ToString() const;
};
//! Reference facade
class Reference {
using ReferenceData = std::variant<std::monostate, EntityRef, CollaborationRef>;
ReferenceData data{};
ReferenceType type{ ReferenceType::invalid };
public:
StrRange position{};
std::string resolvedText{};
public:
Reference() noexcept = default;
explicit Reference(EntityRef data, StrRange position_ = {}) noexcept
: data{ std::move(data) }, type{ ReferenceType::entity }, position{ position_ } {}
explicit Reference(CollaborationRef data, StrRange position_ = {}) noexcept
: data{ std::move(data) }, type{ ReferenceType::collaboration }, position{ position_ } {}
[[nodiscard]] static Reference Parse(std::string_view refStr);
[[nodiscard]] static std::vector<Reference> ExtractAll(std::string_view text);
//! Common API
[[nodiscard]] ReferenceType GetType() const noexcept;
[[nodiscard]] bool IsValid() const noexcept;
[[nodiscard]] bool IsEntity() const noexcept;
[[nodiscard]] bool IsCollaboration() const noexcept;
[[nodiscard]] std::string ToString() const;
//! Entity specific API
[[nodiscard]] std::string_view GetEntity() const;
[[nodiscard]] const Morphology& GetForm() const;
[[nodiscard]] bool TranslateEntity(const StrTranslator& old2New);
void ResolveEntity(const EntityTermContext& context);
//! Collaboration specific API
[[nodiscard]] int16_t GetOffset() const;
[[nodiscard]] const std::string& GetNominal() const;
void ResolveCollaboration(const Reference* master);
};
} // namespace ccl::lang

View File

@ -0,0 +1,84 @@
#pragma once
#include "ccl/lang/Reference.h"
namespace ccl::lang {
//! References management for a string
/*
* Represents vector of references for a contiguous text
* Vector is ordered by start position.
* Positions do not overlap.
* Positions are separated by at least 1 symbol.
* External iterator functor is used to inject position updates.
*/
class RefsManager {
std::vector<Reference> refs{};
const EntityTermContext* context{ nullptr };
public:
RefsManager() noexcept = default;
explicit RefsManager(const EntityTermContext& cntxt) noexcept;
[[nodiscard]] const auto& get() const noexcept { return refs; }
void clear() noexcept {
refs.clear();
}
void SetContext(const EntityTermContext& cntxt) noexcept;
//! Resolve references from \p text and calculate resolved positions
// Pre: context != nullptr
std::string Resolve(std::string_view text);
//! Add reference and update positions for other references
//! // Pre: context != nullptr
const Reference* Insert(Reference newRef, StrPos insWhere);
//! Update data positions using \p RangeIterator
/*
Note: returns false if position count doesnt match data count
or position length doesnt match data length
*/
template<typename RangeIterator>
bool UpdatePositions(RangeIterator nextPos);
//! Erase references in range or at position
std::optional<StrRange> EraseIn(StrRange range, bool expandRefs = false);
//! Get data representing given \p range
[[nodiscard]] const Reference* FirstIn(StrRange range) const;
//! Generate text with references from plain text using data positions for replacements
/*
Pre:
* length of \p normStr should be sufficient to replace all positions
* subRange.start < size(normstr)
*/
[[nodiscard]] std::string OutputRefs(const std::string& normStr) const;
[[nodiscard]] std::string OutputRefs(const std::string& normStr, StrRange subRange) const;
private:
using RefIter = std::vector<Reference>::iterator;
void ResolveAll();
void ResolveIt(RefIter target);
[[nodiscard]] const Reference* FindMaster(RefIter base, int16_t offset) const;
std::string GenerateResolved(std::string_view text);
};
template<typename RangeIterator>
bool RefsManager::UpdatePositions(RangeIterator nextPos) {
auto refIt = std::begin(refs);
auto position = nextPos(StrRange{ 0, 0 });
for (; position.has_value(); position = nextPos(*position), ++refIt) {
if (refIt == std::end(refs)) {
return false;
} else {
refIt->position = position.value();
}
}
return refIt == std::end(refs) && (!position.has_value() || !nextPos(position.value()).has_value());
}
} // namespace ccl::lang

View File

@ -0,0 +1,22 @@
#pragma once
#include "ccl/lang/TextProcessor.h"
#include <memory>
namespace ccl::lang {
class TextEnvironment final {
private:
std::unique_ptr<TextProcessor> processor{ std::make_unique<TextProcessor>() };
public:
bool skipResolving{ false };
public:
static void SetProcessor(std::unique_ptr<TextProcessor> newProcessor) noexcept;
[[nodiscard]] static TextProcessor& Processor() noexcept;
[[nodiscard]] static TextEnvironment& Instance() noexcept;
};
} // namespace ccl::lang

View File

@ -0,0 +1,40 @@
#pragma once
#include "ccl/lang/Morphology.h"
#include <optional>
namespace ccl::lang {
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 26440 ) // Note: do not warn if default implementation might be noexcept
#endif
//! Abstract text processor
class TextProcessor {
public:
virtual ~TextProcessor() noexcept = default;
TextProcessor() noexcept = default;
protected:
TextProcessor(const TextProcessor&) = default;
TextProcessor& operator=(const TextProcessor&) = default;
TextProcessor(TextProcessor&&) noexcept = default;
TextProcessor& operator=(TextProcessor&&) noexcept = default;
public:
[[nodiscard]] virtual std::string Inflect(const std::string& target, const Morphology& form) const;
[[nodiscard]] virtual std::string InflectDependant(const std::string& dependant, const std::string& main) const;
[[nodiscard]] virtual bool IsSubstr(const std::string& needle, const std::string& haystack) const;
private:
[[nodiscard]] static bool BasicSTDMatch(const std::string& needle, const std::string& haystack);
[[nodiscard]] static bool RegexMatch(const std::string& strRegex, const std::string& strHaystack);
};
#ifdef _MSC_VER
#pragma warning( pop )
#endif
} // namespace ccl::lang

View File

@ -0,0 +1,76 @@
#include "ccl/lang/LexicalTerm.h"
#include "ccl/lang/EntityTermContext.hpp"
#include "ccl/lang/TextEnvironment.h"
namespace ccl::lang {
void LexicalTerm::SetText(std::string_view ref, const EntityTermContext& cntxt) {
if (ref != text.Raw()) {
text.InitFrom(ref, cntxt);
ClearForms();
}
}
void LexicalTerm::TranslateRefs(const StrTranslator& old2New, const EntityTermContext& cntxt) {
text.TranslateRefs(old2New, cntxt);
cachedForms.clear();
}
void LexicalTerm::TranslateRaw(const StrTranslator& old2New) {
text.TranslateRaw(old2New);
cachedForms.clear();
}
void LexicalTerm::SetForm(const Morphology& form, std::string_view formText) {
manualForms[form] = formText;
cachedForms.erase(form);
}
bool LexicalTerm::operator==(const LexicalTerm& t2) const noexcept {
return text == t2.text;
}
void LexicalTerm::UpdateFrom(const EntityTermContext& cntxt) {
text.UpdateFrom(cntxt);
cachedForms.clear();
}
bool LexicalTerm::MatchStr(const std::string& str) const {
return TextEnvironment::Processor().IsSubstr(str, GetNominalForm());
}
void LexicalTerm::ClearForms() noexcept {
manualForms.clear();
cachedForms.clear();
}
bool LexicalTerm::IsFormManual(const Morphology& form) const {
return manualForms.contains(form);
}
const std::string& LexicalTerm::GetManualForm(const Morphology& form) const {
return manualForms.at(form);
}
const std::string& LexicalTerm::GetNominalForm() const {
return GetForm(Morphology{ Grammem::sing, Grammem::nomn });
}
const std::unordered_map<Morphology, std::string>& LexicalTerm::GetAllManual() const {
return manualForms;
}
const std::string& LexicalTerm::GetForm(const Morphology& form) const {
if (manualForms.contains(form)) {
return manualForms.at(form);
} else if (cachedForms.contains(form)) {
return cachedForms.at(form);
} else if (std::empty(form)) {
return GetNominalForm();
}
cachedForms.emplace(form, TextEnvironment::Processor().Inflect(text.Str(), form));
return cachedForms.at(form);
}
} // namespace ccl::lang

View File

@ -0,0 +1,13 @@
#include "ccl/lang/Literals.h"
namespace ccl::lang {
Morphology operator""_form(const char* input, size_t /*size*/) {
return Morphology(input);
}
Reference operator""_ref(const char* input, size_t size) {
return Reference::Parse(std::string_view(input, size));
}
} // namespace ccl::lang

View File

@ -0,0 +1,58 @@
#include "ccl/lang/ManagedText.h"
#include "ccl/lang/RefsManager.h"
#include "ccl/lang/EntityTermContext.hpp"
#include "ccl/lang/TextEnvironment.h"
namespace ccl::lang {
bool ManagedText::operator==(const ManagedText& str2) const noexcept { return rawText == str2.rawText; }
bool ManagedText::operator!=(const ManagedText& str2) const noexcept { return rawText != str2.rawText; }
bool ManagedText::empty() const noexcept { return std::empty(rawText); }
const std::string& ManagedText::Str() const noexcept { return std::empty(cache) ? rawText : cache; }
const std::string& ManagedText::Raw() const noexcept { return rawText; }
void ManagedText::InitFrom(std::string_view ref, const EntityTermContext& cntxt) {
rawText = ref;
UpdateFrom(cntxt);
}
void ManagedText::SetRaw(std::string_view ref) {
rawText = ref;
cache.clear();
}
void ManagedText::TranslateRefs(const StrTranslator& old2New, const EntityTermContext& cntxt) {
TranslateRaw(old2New);
UpdateFrom(cntxt);
}
void ManagedText::TranslateRaw(const StrTranslator& old2New) {
auto refs = Reference::ExtractAll(rawText);
for (auto ref = rbegin(refs); ref != rend(refs); ++ref) {
if (ref->IsEntity() && ref->TranslateEntity(old2New)) {
const auto start = UTF8Iterator(rawText, ref->position.start).BytePosition();
const auto oldLength = UTF8Iterator(rawText, ref->position.finish).BytePosition() - start;
rawText.replace(start, oldLength, ref->ToString());
}
}
}
void ManagedText::UpdateFrom(const EntityTermContext& cntxt) {
if (!TextEnvironment::Instance().skipResolving) {
cache = lang::RefsManager{ cntxt }.Resolve(rawText);
}
}
std::unordered_set<std::string> ManagedText::Referals() const {
const auto refs = Reference::ExtractAll(rawText);
std::unordered_set<std::string> result{};
for (const auto& ref : refs) {
if (ref.IsEntity()) {
result.emplace(ref.GetEntity());
}
}
return result;
}
} // namespace ccl::lang

View File

@ -0,0 +1,62 @@
#include "ccl/lang/Morphology.h"
#include <iterator>
#include "ccl/Strings.hpp"
namespace ccl::lang {
namespace {
template<typename TagsContainer, typename FilterContainer>
Grammem ExtractSingleIntersection(const TagsContainer& tags, const FilterContainer& filter) {
std::vector<Grammem> extracted_set{};
std::ranges::set_intersection(tags, filter, std::back_inserter(extracted_set));
if (size(extracted_set) != 1U) {
return Grammem::invalid;
}
else {
return extracted_set.at(0);
}
}
} // namespace
std::string Morphology::ToString() const noexcept {
if (empty()) {
return {};
}
return std::accumulate(next(begin(tags)), end(tags), std::string{ Grammem2Str(*begin(tags))},
[](std::string result, Grammem arg) {
result += ",";
result += Grammem2Str(arg);
return result;
}
);
}
Morphology::Morphology(std::string_view tagsText)
: Morphology(SplitBySymbol(tagsText, ',')) {}
Morphology::Morphology(const std::vector<std::string_view>& tags_) {
for (const auto tag : tags_) {
const auto gram = Str2Grammem(TrimWhitespace(tag));
if (gram != Grammem::invalid) {
tags.insert(gram);
}
}
}
bool Morphology::operator==(const Morphology& rval) const noexcept { return tags == rval.tags; }
bool Morphology::operator!=(const Morphology& rval) const noexcept { return !(*this == rval); }
bool Morphology::Contains(Grammem gram) const noexcept { return tags.contains(gram); }
size_t Morphology::size() const noexcept { return std::size(tags); }
bool Morphology::empty() const noexcept { return std::empty(tags); }
Grammem Morphology::GetPOS() const { return ExtractSingleIntersection(tags, ALL_POS); }
Grammem Morphology::GetCase() const { return ExtractSingleIntersection(tags, ALL_CASES); }
Grammem Morphology::GetNumber() const { return ExtractSingleIntersection(tags, ALL_NUMBERS); }
Grammem Morphology::GetGender() const { return ExtractSingleIntersection(tags, ALL_GENDERS); }
Grammem Morphology::GetTense() const { return ExtractSingleIntersection(tags, ALL_TENSES); }
Grammem Morphology::GetPerson() const { return ExtractSingleIntersection(tags, ALL_PERSONS); }
} // namespace ccl::lang

View File

@ -0,0 +1,214 @@
#include "ccl/lang/Reference.h"
// TODO: Use format library for GCC when available (GCC13+)
#if !defined(__GNUC__) && !defined(__clang__)
#include <format>
#endif
#include "ccl/lang/TextEnvironment.h"
#include "ccl/lang/LexicalTerm.h"
namespace ccl::lang {
namespace {
[[nodiscard]] const std::string& EmptyRefCheck(const std::string& result) {
static const std::string emptyOut{ R"(!Empty reference!)" };
if (empty(result)) {
return emptyOut;
} else {
return result;
}
}
[[nodiscard]] std::vector<std::string_view> SplitReference(std::string_view ref) {
static constexpr auto prefixLen = 2U; // prefix string: "@{"
static constexpr auto suffixLen = 1U; // suffix string: "}"
if (ref.size() <= prefixLen + suffixLen) {
return {};
} else {
return SplitBySymbol(ref.substr(prefixLen, ref.length() - prefixLen - suffixLen), '|');
}
}
[[nodiscard]] ReferenceType DeduceRefType(const std::vector<std::string_view>& tokens) noexcept {
if (size(tokens) < EntityRef::fieldCount || size(tokens) > EntityRef::fieldCount + 2) {
return ReferenceType::invalid;
}
const auto firstToken = tokens.at(0);
if (empty(firstToken)) {
return ReferenceType::invalid;
}
const auto& firstSymbol = firstToken.at(0);
if (isalpha(firstSymbol)) {
return ReferenceType::entity;
} else if (IsInteger(firstToken) && size(tokens) == CollaborationRef::fieldCount) {
return ReferenceType::collaboration;
}
return ReferenceType::invalid;
}
[[nodiscard]] Morphology ExtractMorpho(const std::vector<std::string_view>& tokens) noexcept {
if (size(tokens) == EntityRef::fieldCount) {
return Morphology{ tokens.at(EntityRef::TR_TAGS) };
} else {
auto tags = tokens;
tags.erase(begin(tags));
if (std::isdigit(tags.rbegin()->at(0))) {
tags.erase(prev(end(tags)));
}
return Morphology{ tags };
}
}
[[nodiscard]] UTF8Iterator ReferenceStart(const std::string_view refStr, const StrPos start) noexcept {
for (auto iter = UTF8Iterator(refStr, start); iter != UTF8End(refStr); ++iter) {
if (*iter == '@') {
if (++iter == UTF8End(refStr) || *iter == '{') {
return iter;
}
}
}
return UTF8End(refStr);
}
[[nodiscard]] UTF8Iterator ReferenceEnd(const std::string_view refStr, const UTF8Iterator start) noexcept {
auto bracketCount = 0;
for (auto iter = start; iter != UTF8End(refStr); ++iter) {
if (*iter == '{') {
++bracketCount;
} else if (*iter == '}') {
--bracketCount;
}
if (bracketCount == 0) {
return iter;
}
}
return UTF8End(refStr);
}
[[nodiscard]] std::optional<StrRange>
NextReference(const std::string_view refStr, const StrPos start = 0) noexcept {
if (const auto refStart = ReferenceStart(refStr, start); refStart == UTF8End(refStr)) {
return std::nullopt;
} else if (const auto refEnd = ReferenceEnd(refStr, refStart); refEnd == UTF8End(refStr)) {
return std::nullopt;
} else {
return StrRange{ refStart.Position() - 1, refEnd.Position() + 1 };
}
}
} // namespace
std::string EntityRef::ToString() const {
#if !defined(__GNUC__) && !defined(__clang__)
return std::format("@{{{}|{}}}", entity, form.ToString());
#else
return "@{" + entity + '|' + form.ToString() + '}';
#endif
}
std::string CollaborationRef::ToString() const {
#if !defined(__GNUC__) && !defined(__clang__)
return std::format("@{{{}|{}}}", offset, nominal);
#else
return "@{" + std::to_string(offset) + '|' + nominal + '}';
#endif
}
Reference Reference::Parse(std::string_view refStr) {
const auto tokens = SplitReference(refStr);
const auto type = DeduceRefType(tokens);
switch (type) {
case ReferenceType::entity: {
auto form = ExtractMorpho(tokens);
if (std::empty(form)) {
return {};
}
return Reference{ EntityRef{ std::string{ tokens.at(EntityRef::TR_ENTITY) }, std::move(form) } };
}
case ReferenceType::collaboration: {
return Reference{
CollaborationRef{ std::string{ tokens.at(CollaborationRef::CR_TEXT) },
static_cast<int16_t>(stoi(std::string{ tokens.at(CollaborationRef::CR_OFFSET) })) }
};
}
default:
case ReferenceType::invalid: return {};
}
}
std::vector<Reference> Reference::ExtractAll(const std::string_view text) {
std::vector<Reference> result{};
for (auto position = NextReference(text); position.has_value();
position = NextReference(text, position->finish)) {
if (auto ref = Reference::Parse(Substr(text, position.value())); ref.IsValid()) {
ref.position = position.value();
result.emplace_back(std::move(ref));
}
}
return result;
}
ReferenceType Reference::GetType() const noexcept { return type; }
bool Reference::IsValid() const noexcept { return GetType() != ReferenceType::invalid; }
bool Reference::IsEntity() const noexcept { return GetType() == ReferenceType::entity; }
bool Reference::IsCollaboration() const noexcept { return GetType() == ReferenceType::collaboration; }
std::string_view Reference::GetEntity() const { return std::get<EntityRef>(data).entity; }
const Morphology& Reference::GetForm() const { return std::get<EntityRef>(data).form; }
bool Reference::TranslateEntity(const StrTranslator& old2New) {
auto& refData = std::get<EntityRef>(data);
const auto newID = old2New(refData.entity);
if (!newID.has_value() || newID.value() == refData.entity) {
return false;
}
refData.entity = newID.value();
return true;
}
int16_t Reference::GetOffset() const { return std::get<CollaborationRef>(data).offset; }
const std::string& Reference::GetNominal() const { return std::get<CollaborationRef>(data).nominal; }
std::string Reference::ToString() const {
switch (type) {
case ReferenceType::entity: return std::get<EntityRef>(data).ToString();
case ReferenceType::collaboration: return std::get<CollaborationRef>(data).ToString();
default:
case ReferenceType::invalid: return "";
}
}
void Reference::ResolveCollaboration(const Reference* master) {
const auto& refData = std::get<CollaborationRef>(data);
if (empty(refData.nominal)) {
resolvedText = EmptyRefCheck(refData.nominal);
} else if(master == nullptr) {
#if !defined(__GNUC__) && !defined(__clang__)
resolvedText = std::format("!Invalid offset for {}: '{}'!", refData.nominal, refData.offset);
#else
resolvedText = "!Invalid offset for " + refData.nominal + ": '" + std::to_string(refData.offset) + "'!";
#endif
} else {
resolvedText = EmptyRefCheck(TextEnvironment::Processor().InflectDependant(refData.nominal, master->resolvedText));
}
}
void Reference::ResolveEntity(const EntityTermContext& context) {
auto& refData = std::get<EntityRef>(data);
if (empty(refData.entity)) {
resolvedText = EmptyRefCheck(refData.entity);
} else if (const auto* entity = context.At(refData.entity); entity == nullptr) {
#if !defined(__GNUC__) && !defined(__clang__)
resolvedText = std::format("!Cannot find entity: '{}'!", refData.entity);
#else
resolvedText = "!Cannot find entity: '" + refData.entity +"'!";
#endif
} else if (auto result = entity->GetForm(refData.form); std::empty(result)) {
resolvedText = EmptyRefCheck(entity->Nominal());
} else {
resolvedText = EmptyRefCheck(result);
}
}
} // namespace ccl::lang

View File

@ -0,0 +1,222 @@
#include "ccl/lang/RefsManager.h"
#include "ccl/lang/LexicalTerm.h"
namespace ccl::lang {
namespace {
void ShiftAllAfter(std::vector<Reference>& refs, std::vector<Reference>::iterator pos, StrPos shift) {
if (pos != end(refs)) {
for (auto shiftIt = next(pos); shiftIt != end(refs); ++shiftIt) {
shiftIt->position.Shift(shift);
}
}
}
} // namespace
const Reference* RefsManager::FirstIn(const StrRange range) const {
for (auto it = begin(refs); it != end(refs); ++it) {
if (range.IsAfter(it->position)) {
continue;
} else if (range.IsBefore(it->position)) {
return nullptr;
} else {
return &*it;
}
}
return nullptr;
}
RefsManager::RefsManager(const EntityTermContext& cntxt) noexcept :
context{ &cntxt } {}
void RefsManager::SetContext(const EntityTermContext& cntxt) noexcept {
context = &cntxt;
}
std::string RefsManager::Resolve(std::string_view text) {
refs = Reference::ExtractAll(text);
ResolveAll();
return GenerateResolved(text);
}
void RefsManager::ResolveAll() {
for (auto it = begin(refs); it != end(refs); ++it) {
if (it->IsEntity()) {
it->ResolveEntity(*context);
}
}
for (auto it = begin(refs); it != end(refs); ++it) {
if (it->IsCollaboration()) {
const auto* master = FindMaster(it, it->GetOffset());
it->ResolveCollaboration(master);
}
}
}
std::string RefsManager::GenerateResolved(std::string_view text) {
std::string resolvedString{};
StrPos difLen = 0;
auto current = UTF8Begin(text);
for (auto it = begin(refs); it != end(refs); ++it) {
if (current.Position() != it->position.start) {
resolvedString += text.substr(current.BytePosition(),
UTF8Iterator(text, it->position.start).BytePosition() - current.BytePosition());
}
current = UTF8Iterator(text, it->position.finish);
const auto resolvedRefText = it->resolvedText;
resolvedString += resolvedRefText;
const auto unresolvedLength = it->position.length();
const auto resolvedLength = SizeInCodePoints(resolvedRefText);
it->position = StrRange::FromLength(it->position.start + difLen, resolvedLength);
difLen += resolvedLength - unresolvedLength;
}
if (current != UTF8End(text)) {
resolvedString += text.substr(current.BytePosition(), size(text) - current.BytePosition());
}
return resolvedString;
}
const Reference* RefsManager::Insert(Reference newRef, const StrPos insWhere) {
assert(context != nullptr);
auto it = std::find_if(begin(refs), end(refs),
[&](const Reference& hold)
{ return hold.position.start >= insWhere; });
if (it != end(refs) &&
(it->position.Contains(insWhere) || it->position.finish == insWhere)) {
return nullptr;
} else if (it != begin(refs) && !empty(refs) &&
(prev(it)->position.Contains(insWhere) || prev(it)->position.finish == insWhere)) {
return nullptr;
}
it = refs.emplace(it, std::move(newRef));
ResolveIt(it);
const auto resultLen = SizeInCodePoints(it->resolvedText);
it->position = StrRange::FromLength(insWhere, resultLen);
ShiftAllAfter(refs, it, resultLen);
return &*it;
}
std::optional<StrRange> RefsManager::EraseIn(StrRange range, const bool expandRefs) {
bool checkFinish = false;
auto eraseStart = end(refs);
auto refIt = begin(refs);
for (; refIt != end(refs); ++refIt) {
const auto& refPosition = refIt->position;
if (refPosition.Meets(range)) {
checkFinish = true;
continue;
} else if (refPosition.IsBefore(range)) {
continue;
}
if (range.Meets(refPosition)) {
if (checkFinish) {
return std::nullopt;
} else {
break;
}
} else if (refPosition.IsAfter(range)) {
break;
}
if (refPosition.Contains(range) && expandRefs) {
range = refPosition;
}
if (range.Contains(refPosition)) {
if (eraseStart != end(refs)) {
continue;
} else {
eraseStart = refIt;
}
} else {
return std::nullopt;
}
}
ShiftAllAfter(refs, refIt, -range.length());
if (refIt != end(refs)) {
refIt->position.Shift(-range.length());
}
if (eraseStart != end(refs)) {
refs.erase(eraseStart, refIt);
}
return range;
}
std::string RefsManager::OutputRefs(const std::string& normStr) const {
return OutputRefs(normStr, StrRange{ 0, SizeInCodePoints(normStr) });
}
std::string RefsManager::OutputRefs(const std::string& normStr, const StrRange subRange) const {
std::string result{};
auto curPos = subRange.start;
auto finish = subRange.finish;
for (const auto& ref : refs) {
const auto intersection = subRange.Intersect(ref.position);
if (!intersection.has_value() || intersection->length() == 0) {
if (subRange.IsBefore(ref.position)) {
break;
}
} else if (intersection->length() * 2 < ref.position.length()) {
if (ref.position.finish >= finish) {
finish = std::min(finish, ref.position.start);
break;
} else {
curPos = std::max(curPos, ref.position.finish);
}
} else {
if (curPos < ref.position.start) {
result += Substr(normStr, StrRange{ curPos, ref.position.start });
}
result += ref.ToString();
curPos = ref.position.finish;
}
}
if (curPos < finish) {
result += Substr(normStr, StrRange{ curPos, finish });
}
return result;
}
void RefsManager::ResolveIt(RefIter target) {
if (target->IsEntity()) {
target->ResolveEntity(*context);
} else {
const auto* master = FindMaster(target, target->GetOffset());
target->ResolveCollaboration(master);
}
}
const Reference* RefsManager::FindMaster(const RefIter base, const int16_t offset) const {
if (offset == 0) {
return nullptr;
}
auto termCounter = abs(offset);
const auto boundary = offset > 0 ? prev(end(refs)) : begin(refs);
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 26496 ) // Note: false positive const for iterator
#endif
const auto sign = offset > 0 ? 1 : -1;
for (auto it = base; it != boundary; ) {
std::advance(it, sign);
if (it->IsEntity() && (--termCounter == 0)) {
return &*it;
}
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif
return nullptr;
}
} // namespace ccl::lang

View File

@ -0,0 +1,18 @@
#include "ccl/lang/TextEnvironment.h"
namespace ccl::lang {
void TextEnvironment::SetProcessor(std::unique_ptr<TextProcessor> newProcessor) noexcept {
Instance().processor = std::move(newProcessor);
}
TextProcessor& TextEnvironment::Processor() noexcept {
return *Instance().processor;
}
TextEnvironment& TextEnvironment::Instance() noexcept {
static TextEnvironment instance{};
return instance;
}
} // namespace ccl::lang

View File

@ -0,0 +1,55 @@
#include "ccl/lang/TextProcessor.h"
#include <regex>
namespace ccl::lang {
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 26440 ) // Note: do not warn if default implementation might be noexcept
#endif
std::string TextProcessor::Inflect(const std::string& target, const Morphology& /*morpho*/) const {
return target;
}
std::string TextProcessor::InflectDependant(const std::string& dependant, const std::string& /*main*/) const {
return dependant;
}
bool TextProcessor::IsSubstr(const std::string& needle, const std::string& haystack) const {
if (needle == haystack) {
return true;
} else if (empty(needle)) {
return true;
} else if (BasicSTDMatch(needle, haystack)) {
return true;
} else {
return RegexMatch(needle, haystack);
}
}
bool TextProcessor::BasicSTDMatch(const std::string& needle, const std::string& haystack) {
const auto it = std::search(
begin(haystack), end(haystack),
begin(needle), end(needle),
[](char ch1, char ch2) noexcept { return ::toupper(ch1) == ::toupper(ch2); }
);
return (it != end(haystack));
}
bool TextProcessor::RegexMatch(const std::string& strRegex, const std::string& strHaystack) {
try {
const std::regex testRegex(std::data(strRegex), std::size(strRegex));
return std::sregex_iterator(begin(strHaystack), end(strHaystack), testRegex) != std::sregex_iterator();
}
catch (const std::regex_error&) {
return false;
}
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif
} // namespace ccl::lang

View File

@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.23)
find_package(GTest REQUIRED)
add_executable(cclLang_Tests)
target_sources(cclLang_Tests
PRIVATE
unity/cclLangTest.cpp
)
target_include_directories(cclLang_Tests
PRIVATE
../header
../import/include
utils
)
target_link_libraries(cclLang_Tests
PRIVATE
cclLang
ccl_CXXwarnings
ccl_CXXoptions
GTest::gtest
GTest::gtest_main
)
include(GoogleTest)
gtest_discover_tests(cclLang_Tests)

View File

@ -0,0 +1,226 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{4754356B-DC01-4564-A035-270FFB72F6A0}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<ProjectName>cclLangTest</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<WholeProgramOptimization>true</WholeProgramOptimization>
<VCToolsVersion />
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<WholeProgramOptimization>true</WholeProgramOptimization>
<VCToolsVersion />
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="Shared" />
<ImportGroup Label="PropertySheets">
<Import Project="..\..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.7\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets" Condition="Exists('..\..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.7\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets')" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>build\x86\$(Configuration)\</OutDir>
<IntDir>build\x86\$(Configuration)\</IntDir>
<EnableMicrosoftCodeAnalysis>false</EnableMicrosoftCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>build\x64\$(Configuration)\</OutDir>
<IntDir>build\x64\$(Configuration)\</IntDir>
<EnableMicrosoftCodeAnalysis>false</EnableMicrosoftCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>build\x86\$(Configuration)\</OutDir>
<IntDir>build\x86\$(Configuration)\</IntDir>
<GenerateManifest>false</GenerateManifest>
<EnableMicrosoftCodeAnalysis>false</EnableMicrosoftCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<GenerateManifest>false</GenerateManifest>
<OutDir>build\x64\$(Configuration)\</OutDir>
<IntDir>build\x64\$(Configuration)\</IntDir>
<EnableMicrosoftCodeAnalysis>false</EnableMicrosoftCodeAnalysis>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\cclLang.vcxproj">
<Project>{76B03803-56CC-47C2-A8F0-2241FCAF2898}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\testLexicalTerm.cpp" />
<ClCompile Include="src\testManagedText.cpp" />
<ClCompile Include="src\testMorphology.cpp" />
<ClCompile Include="src\testReference.cpp" />
<ClCompile Include="src\testRefsManager.cpp" />
<ClCompile Include="src\testTextProcessor.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="utils\FakeTermContext.hpp" />
<ClInclude Include="utils\FakeTextProcessor.hpp" />
<ClInclude Include="utils\MockTextProcessor.hpp" />
<ClInclude Include="utils\RefHelper.hpp" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemDefinitionGroup />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level4</WarningLevel>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>utils;..\include;..\header;..\..\cclCommons\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>cclLangd.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
<AdditionalLibraryDirectories>..\..\..\output\lib\x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level4</WarningLevel>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>utils;..\include;..\header;..\..\cclCommons\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
<OmitFramePointers>false</OmitFramePointers>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>cclLangd.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
<AdditionalLibraryDirectories>..\..\..\output\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>
</PrecompiledHeaderFile>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>utils;..\include;..\header;..\..\cclCommons\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<FunctionLevelLinking>true</FunctionLevelLinking>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<AdditionalDependencies>cclLang.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\output\lib\x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
<ProjectReference>
<LinkLibraryDependencies>true</LinkLibraryDependencies>
</ProjectReference>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>utils;..\include;..\header;..\..\cclCommons\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<FunctionLevelLinking>true</FunctionLevelLinking>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<AdditionalDependencies>cclLang.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\output\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link>
<ProjectReference>
<LinkLibraryDependencies>true</LinkLibraryDependencies>
</ProjectReference>
</ItemDefinitionGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.7\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.7\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets'))" />
</Target>
</Project>

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="src">
<UniqueIdentifier>{ed04b33d-515f-46b0-aad1-54dcbc55a5fd}</UniqueIdentifier>
</Filter>
<Filter Include="utils">
<UniqueIdentifier>{e557a0c7-3116-47e0-9713-487c7e6cc101}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\testManagedText.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\testReference.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\testRefsManager.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\testLexicalTerm.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\testTextProcessor.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\testMorphology.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="utils\FakeTextProcessor.hpp">
<Filter>utils</Filter>
</ClInclude>
<ClInclude Include="utils\FakeTermContext.hpp">
<Filter>utils</Filter>
</ClInclude>
<ClInclude Include="utils\RefHelper.hpp">
<Filter>utils</Filter>
</ClInclude>
<ClInclude Include="utils\MockTextProcessor.hpp">
<Filter>utils</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn" version="1.8.1.7" targetFramework="native" />
</packages>

View File

@ -0,0 +1,132 @@
#define GTEST_LANG_CXX11 1
#include "gtest/gtest.h"
#include "RefHelper.hpp"
#include "FakeTextProcessor.hpp"
#include "FakeTermContext.hpp"
#include "ccl/Substitutes.hpp"
#include "ccl/lang/EntityTermContext.hpp"
#include "ccl/lang/Literals.h"
using ccl::lang::operator""_form;
class UTLexcicalTerm : public ::testing::Test {
protected:
FakeContext cntxt{};
protected:
using LexicalTerm = ccl::lang::LexicalTerm;
using ManagedText = ccl::lang::ManagedText;
using Morphology = ccl::lang::Morphology;
using TextEnvironment = ccl::lang::TextEnvironment;
void SetUp() override {
cntxt.Insert("X1", LexicalTerm{ "Test" });
}
void TearDown() override {
TextEnvironment::SetProcessor(std::make_unique<ccl::lang::TextProcessor>());
}
};
TEST_F(UTLexcicalTerm, DefaultParams) {
const LexicalTerm term{};
EXPECT_EQ(term, LexicalTerm());
EXPECT_EQ(term.Text(), ManagedText());
EXPECT_TRUE(term.MatchStr(""));
}
TEST_F(UTLexcicalTerm, Access) {
const LexicalTerm term{"raw", "cached"};
EXPECT_EQ(term.Nominal(), "cached");
EXPECT_EQ(term.Text().Str(), "cached");
EXPECT_EQ(term.Text().Raw(), "raw");
}
TEST_F(UTLexcicalTerm, SetText) {
LexicalTerm term{};
term.SetText("generic", cntxt);
EXPECT_EQ(term.Nominal(), "generic");
term.SetText("generic " + FakeTextProcessor::x1Ref, cntxt);
EXPECT_EQ(term.Nominal(), "generic Test");
}
TEST_F(UTLexcicalTerm, UpdateContext) {
auto term = LexicalTerm{ FakeTextProcessor::x1Ref };
EXPECT_EQ(FakeTextProcessor::x1Ref, term.Nominal());
term.UpdateFrom(cntxt);
EXPECT_EQ(term.Nominal(), "Test");
}
TEST_F(UTLexcicalTerm, SetForm) {
LexicalTerm term{"test", "test"};
EXPECT_EQ(term.Nominal(), "test");
EXPECT_EQ(term.GetForm(Morphology{}), "test");
term.SetForm(Morphology{}, "42");
EXPECT_EQ(term.Nominal(), "test");
EXPECT_EQ(term.GetForm(Morphology{}), "42");
term.SetForm("sing,ablt"_form, "43");
EXPECT_EQ(term.Nominal(), "test");
EXPECT_EQ(term.GetForm(Morphology{}), "42");
EXPECT_EQ(term.GetForm("sing,ablt"_form), "43");
}
TEST_F(UTLexcicalTerm, GetForm) {
const auto nominalForm = "sing,nomn"_form;
const auto manualForm = "sing,ablt"_form;
const auto cachedForm1 = "sing,datv"_form;
const auto cachedForm2 = "plur,datv"_form;
LexicalTerm term{ "42", "42" };
term.SetForm(manualForm, "43");
EXPECT_EQ(term.GetForm(nominalForm), "42");
EXPECT_EQ(term.GetForm(manualForm), "43");
EXPECT_EQ(term.GetForm(cachedForm1), "42");
EXPECT_EQ(term.GetForm(cachedForm1), "42");
term.ClearForms();
term.SetForm(nominalForm, "44");
EXPECT_EQ(term.GetForm(cachedForm1), "42");
EXPECT_EQ(term.GetForm(cachedForm2), "42");
}
TEST_F(UTLexcicalTerm, GetNominalForm) {
const auto nominalForm = "sing,nomn"_form;
LexicalTerm term{ "42", "42" };
term.SetForm(nominalForm, "43");
EXPECT_EQ(term.GetNominalForm(), "43");
}
TEST_F(UTLexcicalTerm, IsManualForm) {
LexicalTerm term{ "test", "test" };
EXPECT_FALSE(term.IsFormManual(Morphology{}));
term.SetForm({}, "42");
EXPECT_TRUE(term.IsFormManual(Morphology{}));
term.SetForm({}, "test");
EXPECT_TRUE(term.IsFormManual(Morphology{}));
}
TEST_F(UTLexcicalTerm, TranslateRefs) {
const auto manualForm = "sing,ablt"_form;
LexicalTerm term{ tccl::EntityRef("X2"), "cache" };
term.SetForm(manualForm, "man");
term.TranslateRefs(ccl::CreateTranslator({ { "X2", "X1" } }), cntxt);
EXPECT_EQ("man", term.GetForm(manualForm));
EXPECT_EQ("Test", term.Nominal());
EXPECT_EQ(tccl::EntityRef("X1"), term.Text().Raw());
}
TEST_F(UTLexcicalTerm, TranslateRaw) {
const auto manualForm = "sing,ablt"_form;
LexicalTerm term{ tccl::EntityRef("X2"), "cache" };
term.SetForm(manualForm, "man");
term.TranslateRaw(ccl::CreateTranslator({ { "X2", "X1" } }));
EXPECT_EQ("man", term.GetForm(manualForm));
EXPECT_EQ("cache", term.Nominal());
EXPECT_EQ(tccl::EntityRef("X1"), term.Text().Raw());
}

View File

@ -0,0 +1,129 @@
#define GTEST_LANG_CXX11 1
#include "gtest/gtest.h"
#include "RefHelper.hpp"
#include "FakeTermContext.hpp"
#include "ccl/lang/ManagedText.h"
#include "ccl/lang/Reference.h"
#include "ccl/lang/TextEnvironment.h"
class UTManagedText : public ::testing::Test {
protected:
using ManagedText = ccl::lang::ManagedText;
using LexicalTerm = ccl::lang::LexicalTerm;
protected:
UTManagedText();
FakeContext context{};
};
UTManagedText::UTManagedText() {
context.Insert("X1", LexicalTerm{ "Test" });
}
TEST_F(UTManagedText, EmptyCache) {
ManagedText text("123", std::string{});
EXPECT_EQ(text.Str(), "123");
EXPECT_EQ(text.Raw(), "123");
}
TEST_F(UTManagedText, Comparison) {
ManagedText t1{};
ManagedText t2{ "@test", "1test" };
ManagedText t3{ "@test", "2test" };
ManagedText t4{ "@@test", "1test" };
ManagedText t5{ "@@test" };
EXPECT_NE(t1, t2);
EXPECT_EQ(t2, t3);
EXPECT_NE(t2, t4);
EXPECT_NE(t3, t4);
EXPECT_EQ(t4, t5);
}
TEST_F(UTManagedText, Str) {
std::string text{ "test" };
auto modified = text + "123";
EXPECT_EQ(ManagedText(text).Str(), text);
EXPECT_EQ(ManagedText(text, modified).Str(), modified);
EXPECT_EQ(ManagedText(modified, text).Str(), text);
}
TEST_F(UTManagedText, SetRaw) {
ManagedText text{ "42", "43" };
text.SetRaw("44");
EXPECT_EQ(text.Raw(), "44");
EXPECT_EQ(text.Str(), "44");
}
TEST_F(UTManagedText, UpdateFrom) {
ManagedText text{ tccl::EntityRef("X1"), "cache" };
EXPECT_EQ(text.Str(), "cache");
text.UpdateFrom(context);
EXPECT_EQ(text.Str(), "Test");
}
TEST_F(UTManagedText, UpdateFromSkipResolution) {
ManagedText text{ tccl::EntityRef("X1"), "cache" };
EXPECT_EQ(text.Str(), "cache");
ccl::lang::TextEnvironment::Instance().skipResolving = true;
text.UpdateFrom(context);
ccl::lang::TextEnvironment::Instance().skipResolving = false;
EXPECT_EQ(text.Str(), "cache");
}
TEST_F(UTManagedText, TranslateRaw) {
ManagedText text{ tccl::EntityRef("X2"), "cache" };
text.TranslateRaw(ccl::CreateTranslator({ {"X2", "X1"} }));
EXPECT_EQ(text.Raw(), tccl::EntityRef("X1"));
EXPECT_EQ(text.Str(), "cache");
}
TEST_F(UTManagedText, Referals) {
const ManagedText input{ tccl::EntityRef("X1") };
const std::unordered_set<std::string> result{ "X1" };
EXPECT_EQ(input.Referals(), result);
}
TEST_F(UTManagedText, ReferalsInvalid) {
EXPECT_EQ(ssize(ManagedText("X1").Referals()), 0);
EXPECT_EQ(ssize(ManagedText("@{X1}").Referals()), 0);
}
TEST_F(UTManagedText, ReferalsMultiple) {
const ManagedText text{ "@{X2|nomn,sing} text @{abc|nomn,sing} X4 "
"@{-1|testing} @{X1|nomn,sing} @{X2,datv,sing}" };
EXPECT_EQ(text.Referals(), std::unordered_set<std::string>({ "X2", "abc", "X1" }));
}
TEST_F(UTManagedText, ReferalsRepeat) {
const ManagedText text{ tccl::EntityRef("X1") + " " + tccl::EntityRef("X1") };
EXPECT_EQ(ssize(text.Referals()), 1);
}
TEST_F(UTManagedText, TranslateRefs) {
{
ManagedText text{ tccl::EntityRef("X2"), "cache" };
text.TranslateRefs(ccl::CreateTranslator({ {"X2", "X1" } }), context);
EXPECT_EQ(text.Raw(), tccl::EntityRef("X1"));
EXPECT_EQ(text.Str(), "Test");
}
{
ManagedText text{ tccl::EntityRef("X1") + " " + tccl::EntityRef("X2") };
text.TranslateRefs(ccl::CreateTranslator({ { "X1", "X11" } }), context);
EXPECT_EQ(text.Raw(), tccl::EntityRef("X11") + " " + tccl::EntityRef("X2"));
}
{
ManagedText text{ tccl::EntityRef("X11") + " " + tccl::EntityRef("X2") };
text.TranslateRefs(ccl::CreateTranslator({ { "X11", "X1" } }), context);
EXPECT_EQ(text.Raw(), tccl::EntityRef("X1") + " " + tccl::EntityRef("X2"));
}
{
ManagedText text{ tccl::EntityRef("X11") + " \xE2\x84\xAC " + tccl::EntityRef("X21") + " \xE2\x84\xAC " + tccl::EntityRef("X3") };
auto translator = ccl::CreateTranslator({ { "X11", "X1" }, { "X21", "X2" }, { "X3", "X4" } });
text.TranslateRefs(translator, context);
EXPECT_EQ(text.Raw(), tccl::EntityRef("X1") + " \xE2\x84\xAC " + tccl::EntityRef("X2") + " \xE2\x84\xAC " + tccl::EntityRef("X4"));
}
}

View File

@ -0,0 +1,107 @@
#define GTEST_LANG_CXX11 1
#include "gtest/gtest.h"
#include "ccl/lang/Morphology.h"
#include "ccl/lang/Literals.h"
class UTMorphology : public ::testing::Test {
protected:
using Grammem = ccl::lang::Grammem;
using Morphology = ccl::lang::Morphology;
};
TEST_F(UTMorphology, Grammemes) {
EXPECT_EQ(ccl::lang::Str2Grammem(""), Grammem::invalid);
EXPECT_EQ(ccl::lang::Str2Grammem("invalid"), Grammem::invalid);
EXPECT_EQ(ccl::lang::Str2Grammem("PLUR"), Grammem::invalid);
EXPECT_EQ(ccl::lang::Str2Grammem("plur"), Grammem::plur);
EXPECT_EQ(ccl::lang::Str2Grammem("NOUN"), Grammem::NOUN);
EXPECT_EQ(ccl::lang::Grammem2Str(Grammem::invalid), "UNKN");
EXPECT_EQ(ccl::lang::Grammem2Str(Grammem::sing), "sing");
EXPECT_EQ(ccl::lang::Grammem2Str(Grammem::datv), "datv");
}
TEST_F(UTMorphology, Construction) {
using ccl::lang::operator""_form;
EXPECT_EQ(Morphology{}.ToString(), "");
EXPECT_EQ(ssize(Morphology{}.tags), 0);
const Morphology morpho{ Grammem::sing, Grammem::datv };
EXPECT_EQ(Morphology(morpho.ToString()), morpho);
EXPECT_EQ(Morphology("sing,datv"), morpho);
EXPECT_EQ(Morphology(" sing , datv "), morpho);
EXPECT_EQ(Morphology("datv,sing"), morpho);
EXPECT_EQ(Morphology("datv,sing,invalid_tag"), morpho);
EXPECT_EQ(Morphology("datv, sing, invalid_tag"), morpho);
EXPECT_EQ(""_form, Morphology{});
EXPECT_EQ("datv, sing, invalid_tag"_form, morpho);
}
TEST_F(UTMorphology, Comparison) {
EXPECT_EQ(Morphology(""), Morphology(""));
EXPECT_EQ(Morphology(" "), Morphology(""));
EXPECT_EQ(Morphology("sing,datv"), Morphology("datv,sing"));
EXPECT_EQ(Morphology("sing,datv"), Morphology("datv,sing"));
EXPECT_EQ(Morphology("sing"), Morphology("sing"));
EXPECT_NE(Morphology("sing,datv"), Morphology("sing"));
}
TEST_F(UTMorphology, GrammemAccess) {
const Morphology morpho0{};
EXPECT_EQ(morpho0.GetPOS(), Grammem::invalid);
EXPECT_EQ(morpho0.GetCase(), Grammem::invalid);
EXPECT_EQ(morpho0.GetNumber(), Grammem::invalid);
EXPECT_EQ(morpho0.GetGender(), Grammem::invalid);
EXPECT_EQ(morpho0.GetPerson(), Grammem::invalid);
EXPECT_EQ(morpho0.GetTense(), Grammem::invalid);
const Morphology morpho1{ Grammem::datv, Grammem::sing };
EXPECT_EQ(morpho1.GetPOS(), Grammem::invalid);
EXPECT_EQ(morpho1.GetCase(), Grammem::datv);
EXPECT_EQ(morpho1.GetNumber(), Grammem::sing);
EXPECT_EQ(morpho1.GetGender(), Grammem::invalid);
EXPECT_EQ(morpho1.GetPerson(), Grammem::invalid);
EXPECT_EQ(morpho1.GetTense(), Grammem::invalid);
const Morphology morpho2{ Grammem::NOUN, Grammem::femn, Grammem::sing, Grammem::datv };
EXPECT_EQ(morpho2.GetPOS(), Grammem::NOUN);
EXPECT_EQ(morpho2.GetCase(), Grammem::datv);
EXPECT_EQ(morpho2.GetNumber(), Grammem::sing);
EXPECT_EQ(morpho2.GetGender(), Grammem::femn);
EXPECT_EQ(morpho2.GetPerson(), Grammem::invalid);
EXPECT_EQ(morpho2.GetTense(), Grammem::invalid);
const Morphology morpho3{ Grammem::VERB, Grammem::sing, Grammem::per1, Grammem::pres };
EXPECT_EQ(morpho3.GetPOS(), Grammem::VERB);
EXPECT_EQ(morpho3.GetCase(), Grammem::invalid);
EXPECT_EQ(morpho3.GetNumber(), Grammem::sing);
EXPECT_EQ(morpho3.GetGender(), Grammem::invalid);
EXPECT_EQ(morpho3.GetPerson(), Grammem::per1);
EXPECT_EQ(morpho3.GetTense(), Grammem::pres);
}
TEST_F(UTMorphology, ContainerAPI) {
EXPECT_EQ(Morphology("").size(), 0U);
EXPECT_EQ(std::size(Morphology("")), 0U);
EXPECT_EQ(std::size(Morphology("datv,sing")), 2U);
EXPECT_TRUE(Morphology("").empty());
EXPECT_TRUE(std::empty(Morphology("")));
EXPECT_FALSE(std::empty(Morphology("NOUN")));
EXPECT_FALSE(Morphology("").Contains(Grammem::sing));
EXPECT_FALSE(Morphology("NOUN,datv").Contains(Grammem::sing));
EXPECT_TRUE(Morphology("sing,datv").Contains(Grammem::sing));
}
TEST_F(UTMorphology, ToString) {
EXPECT_EQ(Morphology("").ToString(), "");
EXPECT_EQ(Morphology(" datv ").ToString(), "datv");
const auto tags = Morphology("sing, datv").ToString();
EXPECT_TRUE(tags == "sing,datv" || tags == "datv,sing");
}

View File

@ -0,0 +1,278 @@
#define GTEST_LANG_CXX11 1
#include "gtest/gtest.h"
#include "FakeTermContext.hpp"
#include "MockTextProcessor.hpp"
#include "RefHelper.hpp"
#include "ccl/lang/TextEnvironment.h"
#include "ccl/lang/Reference.h"
#include "ccl/lang/Literals.h"
using ccl::lang::operator""_form;
using ccl::lang::operator""_ref;
class UTReference : public ::testing::Test {
protected:
using ReferenceType = ccl::lang::ReferenceType;
using Reference = ccl::lang::Reference;
using EntityRef = ccl::lang::EntityRef;
using CollaborationRef = ccl::lang::CollaborationRef;
using StrRange = ccl::StrRange;
using TextEnvironment = ccl::lang::TextEnvironment;
using TextProcessor = ccl::lang::TextProcessor;
protected:
static inline const std::string sampleCompanionRef { "@{-1|\u0442\u0435\u0441\u0442\u0438\u0440\u0443\u044e\u0449\u0438\u0439}" };
static inline const std::string sampleRef { "\u0442\u0435\u0441\u0442\u0438\u0440\u0443\u044e\u0449\u0438\u0439" };
static inline const std::string complexRefStr
{ "42 @{X1|nomn,sing} 43 @{-1|basic} 44 @{X1|nomn,sing} 45" };
static constexpr auto sampleOffset = -1;
static inline const Reference x1ref{ EntityRef{ "X1", "sing,nomn"_form } };
};
TEST_F(UTReference, Construction) {
{
const EntityRef term{ "X1", "sing,nomn"_form };
const Reference ref{ term, StrRange(1, 4) };
EXPECT_EQ(ref.position, StrRange(1, 4));
EXPECT_EQ(ref.resolvedText, "");
EXPECT_EQ(ref.GetType(), ReferenceType::entity);
EXPECT_EQ(ref.GetEntity(), term.entity);
EXPECT_EQ(ref.GetForm(), term.form);
}
{
const CollaborationRef collab{ "test", 4 };
const Reference ref{ collab, StrRange(2, 5) };
EXPECT_EQ(ref.position, StrRange(2, 5));
EXPECT_EQ(ref.resolvedText, "");
EXPECT_EQ(ref.GetType(), ReferenceType::collaboration);
EXPECT_EQ(ref.GetOffset(), collab.offset);
EXPECT_EQ(ref.GetNominal(), collab.nominal);
}
}
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 4834 ) // ignore not using return value
#endif
TEST_F(UTReference, ReferenceID) {
{
const Reference ref{ EntityRef{ "X1", "sing,nomn"_form } };
ASSERT_EQ(ref.GetType(), ReferenceType::entity);
EXPECT_TRUE(ref.IsEntity());
EXPECT_FALSE(ref.IsCollaboration());
EXPECT_EQ(ref.GetEntity(), "X1");
EXPECT_EQ(ref.GetForm(), "sing,nomn"_form);
}
{
const Reference ref{ CollaborationRef{ "text", 1337 }};
ASSERT_EQ(ref.GetType(), ReferenceType::collaboration);
EXPECT_FALSE(ref.IsEntity());
EXPECT_TRUE(ref.IsCollaboration());
EXPECT_EQ(ref.GetOffset(), 1337);
EXPECT_EQ(ref.GetNominal(), "text");
}
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif
TEST_F(UTReference, ToString) {
{
const EntityRef term{ "X1", "sing,nomn"_form };
EXPECT_EQ(term.ToString(), tccl::EntityRef("X1"));
const Reference ref{ term };
EXPECT_EQ(ref.ToString(), term.ToString());
}
{
const CollaborationRef collab{ sampleRef, sampleOffset };
EXPECT_EQ(collab.ToString(), sampleCompanionRef);
const Reference ref{ collab };
EXPECT_EQ(ref.ToString(), collab.ToString());
}
}
TEST_F(UTReference, ParseErrors) {
EXPECT_FALSE(Reference::Parse("").IsValid());
EXPECT_FALSE(Reference::Parse("@{}").IsValid());
EXPECT_FALSE(Reference::Parse("@{ }").IsValid());
EXPECT_FALSE(Reference::Parse("@{|}").IsValid());
EXPECT_FALSE(Reference::Parse("@{ | }").IsValid());
EXPECT_FALSE(Reference::Parse("@{ || }").IsValid());
EXPECT_FALSE(Reference::Parse("@{-1a|text}").IsValid());
EXPECT_FALSE(Reference::Parse("invalid").IsValid());
EXPECT_FALSE(Reference::Parse(tccl::EntityRef("X1") + " " + tccl::EntityRef("X2")).IsValid());
}
TEST_F(UTReference, ParseEntity) {
auto ref = Reference::Parse(tccl::EntityRef("X1"));
ASSERT_TRUE(ref.IsValid());
ASSERT_TRUE(ref.IsEntity());
EXPECT_EQ(ref.GetEntity(), "X1");
EXPECT_EQ(ref.GetForm(), "sing,nomn"_form);
}
TEST_F(UTReference, ParseEntityLegacy) {
const auto form = "sing,nomn"_form;
{
auto ref = Reference::Parse("@{X1|nomn|sing|0}");
ASSERT_TRUE(ref.IsValid());
ASSERT_TRUE(ref.IsEntity());
EXPECT_EQ(ref.GetEntity(), "X1");
EXPECT_EQ(ref.GetForm(), "sing,nomn"_form);
}
{
auto ref = Reference::Parse("@{X1|nomn|sing}");
ASSERT_TRUE(ref.IsValid());
ASSERT_TRUE(ref.IsEntity());
EXPECT_EQ(ref.GetEntity(), "X1");
EXPECT_EQ(ref.GetForm(), "sing,nomn"_form);
}
}
TEST_F(UTReference, ParseCollaboration) {
auto ref = Reference::Parse(sampleCompanionRef);
ASSERT_TRUE(ref.IsValid());
ASSERT_TRUE(ref.IsCollaboration());
EXPECT_EQ(ref.GetOffset(), sampleOffset);
EXPECT_EQ(ref.GetNominal(), sampleRef);
}
TEST_F(UTReference, ParseReferences) {
const auto parsed = Reference::ExtractAll(complexRefStr);
ASSERT_EQ(3u, std::size(parsed));
{
EXPECT_EQ(parsed.at(0).position, StrRange(3, 18));
ASSERT_TRUE(parsed.at(0).IsEntity());
const auto& ref = parsed.at(0);
EXPECT_EQ(ref.GetEntity(), "X1");
EXPECT_EQ(ref.GetForm(), "sing,nomn"_form);
}
{
EXPECT_EQ(parsed.at(1).position, StrRange(22, 33));
ASSERT_TRUE(parsed.at(1).IsCollaboration());
const auto& ref = parsed.at(1);
EXPECT_EQ(ref.GetNominal(), "basic");
EXPECT_EQ(ref.GetOffset(), -1);
}
{
EXPECT_EQ(parsed.at(2).position, StrRange(37, 52));
ASSERT_TRUE(parsed.at(2).IsEntity());
const auto& ref = parsed.at(2);
EXPECT_EQ(ref.GetEntity(), "X1");
EXPECT_EQ(ref.GetForm(), "sing,nomn"_form);
}
}
TEST_F(UTReference, ReferenceLiteral) {
const auto ref = "@{X1|nomn,plur}"_ref;
ASSERT_TRUE(ref.IsEntity());
EXPECT_EQ(ref.GetEntity(), "X1");
EXPECT_EQ(ref.GetForm(), "nomn,plur"_form);
}
TEST_F(UTReference, TranslateEntity) {
auto ref = x1ref;
EXPECT_FALSE(ref.TranslateEntity(ccl::CreateTranslator({ { "X42", "X43" } })));
EXPECT_TRUE(ref.TranslateEntity(ccl::CreateTranslator({ { "X1", "X2" }, { "X2", "X3" } })));
EXPECT_EQ(ref.GetEntity(), "X2");
}
TEST_F(UTReference, ResolveEntity) {
TextEnvironment::SetProcessor(std::make_unique<tccl::MockTextProcessor>());
auto& processor = static_cast<tccl::MockTextProcessor&>(TextEnvironment::Processor());
FakeContext context{};
auto emptyRef = Reference{ EntityRef{"", ""_form} };
emptyRef.ResolveEntity(context);
EXPECT_EQ(emptyRef.resolvedText, "!Empty reference!");
EXPECT_EQ(size(processor.log), 0U);
auto ref = x1ref;
ref.ResolveEntity(context);
EXPECT_EQ(ref.resolvedText, "!Cannot find entity: 'X1'!");
EXPECT_EQ(size(processor.log), 0U);
context.Insert("X1", ccl::lang::LexicalTerm{ "Test" });
ref.ResolveEntity(context);
EXPECT_EQ(ref.resolvedText, "Test");
ASSERT_EQ(size(processor.log), 1U);
EXPECT_EQ(processor.log.at(0).type, tccl::MockTextProcessor::CallType::INFLECT);
EXPECT_EQ(processor.log.at(0).targetText, "Test");
EXPECT_EQ(processor.log.at(0).form, x1ref.GetForm());
processor.ClearAll();
context.Clear();
context.Insert("X1", ccl::lang::LexicalTerm{ "" });
ref.resolvedText.clear();
ref.ResolveEntity(context);
EXPECT_EQ(ref.resolvedText, "!Empty reference!");
ASSERT_EQ(size(processor.log), 1U);
EXPECT_EQ(processor.log.at(0).type, tccl::MockTextProcessor::CallType::INFLECT);
EXPECT_EQ(processor.log.at(0).targetText, "");
EXPECT_EQ(processor.log.at(0).form, x1ref.GetForm());
TextEnvironment::SetProcessor(std::make_unique<TextProcessor>());
}
TEST_F(UTReference, ResolveCollaborationEmpty) {
TextEnvironment::SetProcessor(std::make_unique<tccl::MockTextProcessor>());
auto& processor = static_cast<tccl::MockTextProcessor&>(TextEnvironment::Processor());
auto emptyRef = Reference{ CollaborationRef{ "", 1 } };
emptyRef.ResolveCollaboration(nullptr);
EXPECT_EQ(emptyRef.resolvedText, "!Empty reference!");
EXPECT_EQ(size(processor.log), 0U);
auto master = Reference{ x1ref };
master.resolvedText = "";
emptyRef.resolvedText = "";
emptyRef.ResolveCollaboration(&master);
EXPECT_EQ(emptyRef.resolvedText, "!Empty reference!");
EXPECT_EQ(size(processor.log), 0U);
master.resolvedText = "test";
emptyRef.resolvedText = "";
emptyRef.ResolveCollaboration(&master);
EXPECT_EQ(emptyRef.resolvedText, "!Empty reference!");
EXPECT_EQ(size(processor.log), 0U);
TextEnvironment::SetProcessor(std::make_unique<TextProcessor>());
}
TEST_F(UTReference, ResolveCollaborationValid) {
TextEnvironment::SetProcessor(std::make_unique<tccl::MockTextProcessor>());
auto& processor = static_cast<tccl::MockTextProcessor&>(TextEnvironment::Processor());
auto collabRef = Reference{ CollaborationRef{ "companion", 1 } };
collabRef.ResolveCollaboration(nullptr);
EXPECT_EQ(collabRef.resolvedText, "!Invalid offset for companion: '1'!");
EXPECT_EQ(size(processor.log), 0U);
auto master = Reference{ x1ref };
master.resolvedText = "";
collabRef.resolvedText = "";
collabRef.ResolveCollaboration(&master);
EXPECT_EQ(collabRef.resolvedText, "companion");
ASSERT_EQ(size(processor.log), 1U);
EXPECT_EQ(processor.log.at(0).type, tccl::MockTextProcessor::CallType::DEPENDANT);
EXPECT_EQ(processor.log.at(0).targetText, "companion");
EXPECT_EQ(processor.log.at(0).mainText, "");
processor.ClearAll();
master.resolvedText = "test";
collabRef.resolvedText = "";
processor.AddCollaboration(collabRef.GetNominal(), master.resolvedText, "companion_inflected");
collabRef.ResolveCollaboration(&master);
EXPECT_EQ(collabRef.resolvedText, "companion_inflected");
ASSERT_EQ(size(processor.log), 1U);
EXPECT_EQ(processor.log.at(0).type, tccl::MockTextProcessor::CallType::DEPENDANT);
EXPECT_EQ(processor.log.at(0).targetText, "companion");
EXPECT_EQ(processor.log.at(0).mainText, "test");
TextEnvironment::SetProcessor(std::make_unique<TextProcessor>());
}

View File

@ -0,0 +1,304 @@
#define GTEST_LANG_CXX11 1
#include "gtest/gtest.h"
#include "MockTextProcessor.hpp"
#include "FakeTermContext.hpp"
#include "RefHelper.hpp"
#include "ccl/lang/TextEnvironment.h"
#include "ccl/lang/RefsManager.h"
#include "ccl/lang/Literals.h"
using ccl::lang::operator""_form;
using ccl::lang::operator""_ref;
struct FakeUpdater {
using StrRange = ccl::StrRange;
std::vector<StrRange> ranges;
FakeUpdater(std::vector<StrRange> ranges)
: ranges{ std::move(ranges) } {}
std::optional<StrRange> operator()(const StrRange& prev) {
if (prev == StrRange{ 0, 0 }) {
if (empty(ranges)) {
return std::nullopt;
} else {
return *begin(ranges);
}
}
if (auto it = std::find(begin(ranges), end(ranges), prev);
it == end(ranges)) {
return std::nullopt;
} else if (++it; it == end(ranges)) {
return std::nullopt;
} else {
return *it;
}
}
};
class UTRefsManager : public ::testing::Test {
protected:
using RefsManager = ccl::lang::RefsManager;
using Reference = ccl::lang::Reference;
using Morphology = ccl::lang::Morphology;
using LexicalTerm = ccl::lang::LexicalTerm;
using StrRange = ccl::StrRange;
using TextEnvironment = ccl::lang::TextEnvironment;
using TextProcessor = ccl::lang::TextProcessor;
FakeContext context{};
RefsManager refs{};
protected:
UTRefsManager();
static inline const std::string basicRefStr{"@{X1|sing,nomn}"};
static inline const std::string complexRefStr
{"42 @{X1|sing,nomn} 43 @{-1|basic} 44 @{X1|sing,nomn} 45"};
// refStr: 42 @{X1|sing,nomn} 43 @{-1|basic} 44 @{X1|sing,nomn} 45
// normStr: 42 Test 43 basic 44 Test 45
// ---------0000000000111111111122222222
// ---------0123456789012345678901234567
};
UTRefsManager::UTRefsManager() {
context.Insert("X1", LexicalTerm{ "Test" });
context.Insert("X2", LexicalTerm{ "Test2" });
refs.SetContext(context);
}
TEST_F(UTRefsManager, Constructor) {
const auto emptyMan = RefsManager{};
EXPECT_TRUE(empty(emptyMan.get()));
const auto manager = RefsManager{ context };
EXPECT_TRUE(empty(emptyMan.get()));
}
TEST_F(UTRefsManager, Clear) {
refs.clear();
EXPECT_EQ(size(refs.get()), 0U);
refs.SetContext(context);
EXPECT_EQ(size(refs.get()), 0U);
refs.Resolve(basicRefStr);
EXPECT_EQ(size(refs.get()), 1U);
refs.clear();
EXPECT_EQ(size(refs.get()), 0U);
}
TEST_F(UTRefsManager, ResolveValid) {
EXPECT_EQ(refs.Resolve(""), "");
EXPECT_EQ(std::size(refs.get()), 0U);
EXPECT_EQ(refs.Resolve("sample text"), "sample text");
EXPECT_EQ(std::size(refs.get()), 0U);
EXPECT_EQ(refs.Resolve(basicRefStr), "Test");
ASSERT_EQ(size(refs.get()), 1U);
EXPECT_EQ(begin(refs.get())->position, StrRange(0, 4));
EXPECT_EQ(refs.Resolve(complexRefStr), "42 Test 43 basic 44 Test 45");
ASSERT_EQ(size(refs.get()), 3U);
EXPECT_EQ(refs.get().at(0).position, StrRange(3, 7));
EXPECT_EQ(refs.get().at(1).position, StrRange(11, 16));
EXPECT_EQ(refs.get().at(2).position, StrRange(20, 24));
}
TEST_F(UTRefsManager, ResolveCompanionOffsets) {
TextEnvironment::SetProcessor(std::make_unique<tccl::MockTextProcessor>());
auto& processor = static_cast<tccl::MockTextProcessor&>(TextEnvironment::Processor());
EXPECT_EQ(refs.Resolve("@{-1|basic}"), "!Invalid offset for basic: '-1'!");
EXPECT_EQ(refs.Resolve("@{-1|basic} @{X1|sing,nomn}"), "!Invalid offset for basic: '-1'! Test");
EXPECT_EQ(refs.Resolve("@{X1|sing,nomn} @{-1|basic}"), "Test basic");
EXPECT_EQ(refs.Resolve("@{1|basic} @{X1|sing,nomn}"), "basic Test");
EXPECT_EQ(refs.Resolve("@{1|basic1} @{1|basic1} @{X1|sing,nomn}"), "basic1 basic1 Test");
EXPECT_EQ(refs.Resolve("@{2|basic1} @{1|basic1} @{X1|sing,nomn}"), "!Invalid offset for basic1: '2'! basic1 Test");
processor.AddCollaboration("basic", "Test", "basic_inflected");
EXPECT_EQ(refs.Resolve("@{1|basic} @{X1|sing,nomn}"), "basic_inflected Test");
TextEnvironment::SetProcessor(std::make_unique<TextProcessor>());
}
TEST_F(UTRefsManager, Insert) {
const auto newRef = "@{X2|sing,nomn}"_ref;
refs.Resolve("");
EXPECT_EQ(refs.Insert(newRef, 0)->resolvedText, "Test2");
ASSERT_EQ(size(refs.get()), 1U);
EXPECT_EQ(begin(refs.get())->position, StrRange(0, 5));
refs.Resolve(complexRefStr);
EXPECT_TRUE(refs.Insert(newRef, 3) == nullptr);
EXPECT_TRUE(refs.Insert(newRef, 4) == nullptr);
EXPECT_TRUE(refs.Insert(newRef, 6) == nullptr);
EXPECT_TRUE(refs.Insert(newRef, 7) == nullptr);
EXPECT_TRUE(refs.Insert(newRef, 20) == nullptr);
EXPECT_TRUE(refs.Insert(newRef, 21) == nullptr);
EXPECT_TRUE(refs.Insert(newRef, 23) == nullptr);
EXPECT_TRUE(refs.Insert(newRef, 24) == nullptr);
refs.Resolve(complexRefStr);
EXPECT_FALSE(refs.Insert(newRef, 2) == nullptr);
refs.Resolve(complexRefStr);
EXPECT_FALSE(refs.Insert(newRef, 19) == nullptr);
refs.Resolve(complexRefStr);
EXPECT_EQ(refs.Insert(newRef, 8)->resolvedText, "Test2");
ASSERT_EQ(size(refs.get()), 4U);
EXPECT_EQ(refs.get().at(0).position, StrRange(3, 7));
EXPECT_EQ(refs.get().at(1).position, StrRange(8, 13));
EXPECT_EQ(refs.get().at(2).position, StrRange(16, 21));
EXPECT_EQ(refs.get().at(3).position, StrRange(25, 29));
}
TEST_F(UTRefsManager, EraseRefsRange) {
EXPECT_TRUE(refs.EraseIn(StrRange{ 0, 0 }).has_value());
EXPECT_TRUE(refs.EraseIn(StrRange{ 0, 42 }).has_value());
refs.Resolve(complexRefStr);
EXPECT_FALSE(refs.EraseIn(StrRange{ 0, 4 }).has_value());
EXPECT_FALSE(refs.EraseIn(StrRange{ 0, 5 }).has_value());
EXPECT_FALSE(refs.EraseIn(StrRange{ 0, 6 }).has_value());
EXPECT_FALSE(refs.EraseIn(StrRange{ 3, 6 }).has_value());
EXPECT_FALSE(refs.EraseIn(StrRange{ 6, 7 }).has_value());
EXPECT_FALSE(refs.EraseIn(StrRange{ 4, 5 }).has_value());
EXPECT_FALSE(refs.EraseIn(StrRange{ 5, 5 }).has_value());
EXPECT_FALSE(refs.EraseIn(StrRange{ 7, 11 }).has_value());
refs.Resolve(complexRefStr);
EXPECT_EQ(refs.EraseIn(StrRange{ 42, 42 }).value(), StrRange(42, 42));
EXPECT_EQ(refs.EraseIn(StrRange{ 0, 27 }).value(), StrRange(0, 27));
EXPECT_EQ(std::size(refs.get()), 0U);
refs.Resolve(complexRefStr);
EXPECT_EQ(refs.EraseIn(StrRange{ 0, 3 }).value(), StrRange(0, 3));
ASSERT_EQ(std::size(refs.get()), 3U);
EXPECT_EQ(refs.get().at(0).position, StrRange(0, 4));
EXPECT_EQ(refs.get().at(1).position, StrRange(8, 13));
EXPECT_EQ(refs.get().at(2).position, StrRange(17, 21));
refs.Resolve(complexRefStr);
EXPECT_EQ(refs.EraseIn(StrRange{ 0, 7 }).value(), StrRange(0, 7));
ASSERT_EQ(std::size(refs.get()), 2U);
EXPECT_EQ(refs.get().at(0).position, StrRange(4, 9));
EXPECT_EQ(refs.get().at(1).position, StrRange(13, 17));
refs.Resolve(complexRefStr);
EXPECT_EQ(refs.EraseIn(StrRange{ 20, 24 }).value(), StrRange(20, 24));
ASSERT_EQ(size(refs.get()), 2U);
EXPECT_EQ(refs.get().at(0).position, StrRange(3, 7));
EXPECT_EQ(refs.get().at(1).position, StrRange(11, 16));
}
TEST_F(UTRefsManager, EraseRefsExpand) {
EXPECT_TRUE(refs.EraseIn(StrRange{ 0, 1 }, true).has_value());
refs.Resolve(complexRefStr);
refs.EraseIn((StrRange{ 7, 10 }));
EXPECT_FALSE(refs.EraseIn(StrRange{ 7, 8 }, true).has_value());
refs.Resolve(complexRefStr);
EXPECT_EQ(refs.EraseIn(StrRange{ 2, 3 }, true).value(), StrRange(2, 3));
ASSERT_EQ(size(refs.get()), 3U);
EXPECT_EQ(refs.get().at(0).position, StrRange(2, 6));
EXPECT_EQ(refs.get().at(1).position, StrRange(10, 15));
EXPECT_EQ(refs.get().at(2).position, StrRange(19, 23));
refs.Resolve(complexRefStr);
EXPECT_EQ(refs.EraseIn(StrRange{ 3, 4 }, true).value(), StrRange(3, 7));
ASSERT_EQ(size(refs.get()), 2U);
EXPECT_EQ(refs.get().at(0).position, StrRange(7, 12));
EXPECT_EQ(refs.get().at(1).position, StrRange(16, 20));
refs.Resolve(complexRefStr);
EXPECT_EQ(refs.EraseIn(StrRange{ 6, 7 }, true).value(), StrRange(3, 7));
ASSERT_EQ(size(refs.get()), 2U);
}
TEST_F(UTRefsManager, FirstRef) {
EXPECT_TRUE(refs.FirstIn(StrRange{ 0, 0 }) == nullptr);
EXPECT_TRUE(refs.FirstIn(StrRange{ 0, 42 }) == nullptr);
EXPECT_TRUE(refs.FirstIn(StrRange{ 42, 42 }) == nullptr);
refs.Resolve(complexRefStr);
EXPECT_TRUE(refs.FirstIn(StrRange{ 0, 0 }) == nullptr);
EXPECT_TRUE(refs.FirstIn(StrRange{ 0, 1 }) == nullptr);
EXPECT_TRUE(refs.FirstIn(StrRange{ 27, 27 }) == nullptr);
EXPECT_TRUE(refs.FirstIn(StrRange{ 28, 42 }) == nullptr);
EXPECT_EQ(refs.FirstIn(StrRange{ 0, 3 }), &refs.get()[0]);
EXPECT_EQ(refs.FirstIn(StrRange{ 3, 3 }), &refs.get()[0]);
EXPECT_EQ(refs.FirstIn(StrRange{ 3, 6 }), &refs.get()[0]);
EXPECT_EQ(refs.FirstIn(StrRange{ 3, 7 }), &refs.get()[0]);
EXPECT_EQ(refs.FirstIn(StrRange{ 0, 7 }), &refs.get()[0]);
EXPECT_EQ(refs.FirstIn(StrRange{ 6, 12 }), &refs.get()[0]);
EXPECT_EQ(refs.FirstIn(StrRange{ 0, 12 }), &refs.get()[0]);
EXPECT_EQ(refs.FirstIn(StrRange{ 12, 12 }), &refs.get()[1]);
}
TEST_F(UTRefsManager, UpdatePositions) {
EXPECT_TRUE(refs.UpdatePositions(FakeUpdater{ {} }));
EXPECT_FALSE(refs.UpdatePositions(FakeUpdater{ { StrRange{ 0, 1 } } }));
refs.Resolve(complexRefStr);
EXPECT_FALSE(refs.UpdatePositions(FakeUpdater{ {} }));
EXPECT_FALSE(refs.UpdatePositions(FakeUpdater{ { StrRange{ 0, 1 }, StrRange{ 2, 3 } } }));
EXPECT_FALSE(refs.UpdatePositions(FakeUpdater{
{ StrRange{ 0, 1 }, StrRange{ 2, 3 }, StrRange{ 4, 5 }, StrRange{ 6, 7 } } }));
refs.Resolve(complexRefStr);
EXPECT_TRUE(refs.UpdatePositions(FakeUpdater{ { StrRange{ 0, 1 }, StrRange{ 2, 3 }, StrRange{ 4, 5 } } }));
EXPECT_EQ(refs.get().at(0).position, StrRange(0, 1));
EXPECT_EQ(refs.get().at(1).position, StrRange(2, 3));
EXPECT_EQ(refs.get().at(2).position, StrRange(4, 5));
refs.Resolve(complexRefStr);
EXPECT_TRUE(refs.UpdatePositions(FakeUpdater{ { StrRange{ 0, 4 }, StrRange{ 5, 10 }, StrRange{ 11, 15 } } }));
EXPECT_EQ(refs.get().at(0).position, StrRange(0, 4));
EXPECT_EQ(refs.get().at(1).position, StrRange(5, 10));
EXPECT_EQ(refs.get().at(2).position, StrRange(11, 15));
}
TEST_F(UTRefsManager, OutputRefs) {
using ccl::operator""_c17;
EXPECT_TRUE(empty(refs.OutputRefs({}, {})));
EXPECT_TRUE(empty(refs.OutputRefs({}, StrRange{ 0, 10 })));
refs.Resolve({});
EXPECT_TRUE(empty(refs.OutputRefs({}, {})));
refs.Resolve(u8"\u212Cabc"_c17);
EXPECT_EQ(refs.OutputRefs(u8"\u212Cabc"_c17), u8"\u212Cabc"_c17);
const auto normStr = refs.Resolve(complexRefStr);
EXPECT_EQ(refs.OutputRefs(normStr), complexRefStr);
}
TEST_F(UTRefsManager, OutputRefsRange) {
const auto normStr = refs.Resolve(complexRefStr);
EXPECT_EQ(refs.OutputRefs(normStr, StrRange{ 0, 27 }), complexRefStr);
EXPECT_EQ(refs.OutputRefs(normStr, StrRange{ 0, 2 }), "42");
EXPECT_EQ(refs.OutputRefs(normStr, StrRange{ 0, 0 }), "");
EXPECT_EQ(refs.OutputRefs(normStr, StrRange{ 26, 27 }), "5");
EXPECT_EQ(refs.OutputRefs(normStr, StrRange{ 27, 28 }), "");
EXPECT_EQ(refs.OutputRefs(normStr, StrRange{ 0, 7 }), "42 @{X1|sing,nomn}");
EXPECT_EQ(refs.OutputRefs(normStr, StrRange{ 0, 6 }), "42 @{X1|sing,nomn}");
EXPECT_EQ(refs.OutputRefs(normStr, StrRange{ 0, 5 }), "42 @{X1|sing,nomn}");
EXPECT_EQ(refs.OutputRefs(normStr, StrRange{ 0, 4 }), "42 ");
EXPECT_EQ(refs.OutputRefs(normStr, StrRange{ 0, 3 }), "42 ");
EXPECT_EQ(refs.OutputRefs(normStr, StrRange{ 3, 10 }), "@{X1|sing,nomn} 43");
EXPECT_EQ(refs.OutputRefs(normStr, StrRange{ 4, 10 }), "@{X1|sing,nomn} 43");
EXPECT_EQ(refs.OutputRefs(normStr, StrRange{ 5, 10 }), "@{X1|sing,nomn} 43");
EXPECT_EQ(refs.OutputRefs(normStr, StrRange{ 6, 10 }), " 43");
EXPECT_EQ(refs.OutputRefs(normStr, StrRange{ 7, 10 }), " 43");
}

View File

@ -0,0 +1,38 @@
#define GTEST_LANG_CXX11 1
#include "gtest/gtest.h"
#include "ccl/lang/TextProcessor.h"
#include "ccl/lang/Literals.h"
using ccl::lang::operator""_form;
class UTTextProcessor : public ::testing::Test {
protected:
using TextProcessor = ccl::lang::TextProcessor;
using Morphology = ccl::lang::Morphology;
TextProcessor processor{};
};
TEST_F(UTTextProcessor, DefaultImplementations) {
EXPECT_EQ(processor.Inflect("invalid", Morphology{}), "invalid");
EXPECT_EQ(processor.Inflect("term", "sing,nomn"_form), "term");
EXPECT_EQ(processor.InflectDependant("", ""), "");
EXPECT_EQ(processor.InflectDependant("invalid", ""), "invalid");
}
TEST_F(UTTextProcessor, IsSubstr) {
EXPECT_TRUE(processor.IsSubstr("", ""));
EXPECT_TRUE(processor.IsSubstr("", "42"));
EXPECT_TRUE(processor.IsSubstr("4", "42"));
EXPECT_TRUE(processor.IsSubstr("2", "42"));
EXPECT_TRUE(processor.IsSubstr("42", "42"));
EXPECT_TRUE(processor.IsSubstr("test", "Test"));
EXPECT_TRUE(processor.IsSubstr("T?est", "Test"));
EXPECT_TRUE(processor.IsSubstr("T?est", "est"));
EXPECT_TRUE(processor.IsSubstr("?est", "?est"));
EXPECT_TRUE(processor.IsSubstr("?est", "?esT"));
EXPECT_FALSE(processor.IsSubstr("42", ""));
EXPECT_FALSE(processor.IsSubstr("[a-b][a", "abba"));
}

View File

@ -0,0 +1,12 @@
//! Unity build for cclLangTest
#define GTEST_LANG_CXX11 1
#include "gtest/gtest.h"
#include "../src/testLexicalTerm.cpp"
#include "../src/testManagedText.cpp"
#include "../src/testMorphology.cpp"
#include "../src/testReference.cpp"
#include "../src/testRefsManager.cpp"
#include "../src/testTextProcessor.cpp"

View File

@ -0,0 +1,25 @@
#pragma once
#include "ccl/lang/LexicalTerm.h"
#include "ccl/lang/EntityTermContext.hpp"
class FakeContext : public ccl::lang::EntityTermContext {
std::unordered_map<std::string, ccl::lang::LexicalTerm> terms{};
public:
void Clear() noexcept { terms.clear(); }
void Insert(const std::string& entity, const ccl::lang::LexicalTerm& term) {
terms.emplace(std::pair{ entity , term });
}
bool Contains(const std::string& entity) const override {
return terms.contains(entity);
}
const ccl::lang::LexicalTerm* At(const std::string& entity) const override {
if (!Contains(entity)) {
return nullptr;
} else {
return &terms.at(entity);
}
}
};

View File

@ -0,0 +1,8 @@
#pragma once
#include "ccl/lang/TextEnvironment.h"
struct FakeTextProcessor : ccl::lang::TextProcessor {
static inline std::string x1Ref = R"(@{X1|nomn|sing})";
};

View File

@ -0,0 +1,91 @@
#pragma once
#include "ccl/lang/TextProcessor.h"
namespace tccl {
using ccl::lang::Morphology;
using ccl::lang::TextProcessor;
struct MockTextProcessor : TextProcessor {
enum CallType : uint8_t {
INFLECT,
DEPENDANT
};
struct CallEntry {
CallType type;
std::string targetText;
std::optional<Morphology> form{};
std::optional<std::string> mainText{};
};
using InflectDB = std::vector<std::pair<std::pair<std::string, Morphology>, std::string>>;
using DependantDB = std::vector<std::pair<std::pair<std::string, std::string>, std::string>>;
mutable std::vector<CallEntry> log{};
mutable InflectDB inflections{};
mutable DependantDB substitutes{};
void ClearLog() noexcept {
log.clear();
}
void ClearAll() noexcept {
inflections.clear();
substitutes.clear();
log.clear();
}
void AddInflection(std::string target,Morphology morpho, std::string result) {
inflections.emplace_back(std::pair<std::string, Morphology>(std::move(target), std::move(morpho)), std::move(result));
}
void AddCollaboration(std::string dependant, std::string main, std::string result) {
substitutes.emplace_back(std::pair<std::string, std::string>(std::move(dependant), std::move(main)), std::move(result));
}
[[nodiscard]] std::string Inflect(const std::string& target, const Morphology& form) const override {
log.emplace_back(CallEntry{INFLECT, target, form, {}});
const auto itr =
std::find_if(begin(inflections), end(inflections),
[&target, &form](const auto& v) { return v.first.first == target && v.first.second == form; });
if (itr != end(inflections)) {
return itr->second;
} else {
return target;
}
}
[[nodiscard]] std::string InflectDependant(const std::string& dependant, const std::string& main) const override {
log.emplace_back(CallEntry{DEPENDANT, dependant, std::nullopt, main});
const auto itr =
std::find_if(begin(substitutes), end(substitutes),
[&dependant, &main](const auto& v) { return v.first.first == dependant && v.first.second == main; });
if (itr != end(substitutes)) {
return itr->second;
} else {
return dependant;
}
}
//std::string ApplyForm(const std::string& nominal, ccl::lang::Termform termForm) const override {
// callLog.emplace_back(CallEntry{ APPLYFORM, nominal, termForm });
// if (std::empty(nominal)) {
// return nominal;
// } else {
// return "RESOLVED_TERM";
// }
//}
//std::string ApplyFormGender(const std::string& word,
// ccl::lang::Termform form,
// ccl::lang::Gender gender) const override {
// callLog.emplace_back(CallEntry{ APPLYGENDER, word, form, gender });
// if (std::empty(word)) {
// return word;
// } else {
// return "RESOLVED_COMPANION";
// }
//}
};
} // namespace tccl

View File

@ -0,0 +1,13 @@
#pragma once
#include "ccl/lang/Reference.h"
namespace tccl {
inline std::string EntityRef(const std::string& cstName,
const ccl::lang::Morphology form = ccl::lang::Morphology{ ccl::lang::Grammem::nomn, ccl::lang::Grammem::sing }) {
const ccl::lang::EntityRef ref{ cstName, form };
return ref.ToString();
}
} // namespace tccl

View File

@ -0,0 +1,10 @@
//! Unity build for cclLang
#include "../src/LexicalTerm.cpp"
#include "../src/ManagedText.cpp"
#include "../src/Reference.cpp"
#include "../src/RefsManager.cpp"
#include "../src/Morphology.cpp"
#include "../src/TextEnvironment.cpp"
#include "../src/TextProcessor.cpp"
#include "../src/Literals.cpp"

View File

@ -0,0 +1,70 @@
cmake_minimum_required(VERSION 3.23)
if(TARGET ccl_CXXwarnings)
return()
endif()
##
## Compiler options
##
add_library(ccl_CXXwarnings INTERFACE)
add_library(ccl_CXXoptions INTERFACE)
target_compile_features(ccl_CXXoptions INTERFACE cxx_std_20)
if(CC_UseSanitizers AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(ccl_CXXoptions
INTERFACE
-fsanitize=address
-fsanitize=undefined
)
target_link_libraries(ccl_CXXoptions
INTERFACE
-fsanitize=address
-fsanitize=undefined
)
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(ccl_CXXwarnings
INTERFACE
-Wall
-Werror
-Wextra
-Wextra-semi-stmt
-Wshadow
-Wconversion
-Wold-style-cast
-Wunused
-Woverloaded-virtual
-Wnull-dereference
-Wdouble-promotion
-Wno-invalid-token-paste
-Wno-unknown-pragmas
-Wno-unused-variable
-Wformat=2
)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(ccl_CXXwarnings
INTERFACE
-Wall
-Werror
-Wmisleading-indentation
-Wduplicated-cond
-Wno-unknown-pragmas
-Wduplicated-branches
-Wuseless-cast
-Wlogical-op
-fexceptions
)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(ccl_CXXoptions
INTERFACE
/Zi
)
target_compile_options(ccl_CXXwarnings
INTERFACE
/permissive-
/W4
)
endif()

9
ccl/conanfile.txt Normal file
View File

@ -0,0 +1,9 @@
[requires]
gtest/1.14.0
[generators]
CMakeDeps
CMakeToolchain
[layout]
cmake_layout

View File

@ -0,0 +1,259 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{B0ABA27B-9D39-4B48-9977-AFF20925B309}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<ConfigurationType>StaticLibrary</ConfigurationType>
<RootNamespace>ConceptLibrary</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>ConceptCoreLibrary</ProjectName>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>..\..\output\lib\x86\</OutDir>
<IntDir>build\x86\$(Configuration)\</IntDir>
<TargetName>$(ProjectName)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>..\..\output\lib\x86\</OutDir>
<IntDir>build\x86\$(Configuration)\</IntDir>
<TargetName>$(ProjectName)d</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>..\..\output\lib\x64\</OutDir>
<IntDir>build\x64\$(Configuration)\</IntDir>
<TargetName>$(ProjectName)d</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>..\..\output\lib\x64\</OutDir>
<IntDir>build\x64\$(Configuration)\</IntDir>
<TargetName>$(ProjectName)</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<AdditionalIncludeDirectories>include;header;..\RSlang\include;..\cclGraph\include;..\cclCommons\include;..\cclLang\include</AdditionalIncludeDirectories>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_LIB;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<ProgramDataBaseFileName>$(OutDir)$(ProjectName).pdb</ProgramDataBaseFileName>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PostBuildEvent>
<Command>xcopy /y /s /q /i include ..\..\output\include</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<AdditionalIncludeDirectories>include;header;..\RSlang\include;..\cclGraph\include;..\cclCommons\include;..\cclLang\include</AdditionalIncludeDirectories>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<ProgramDataBaseFileName>$(OutDir)$(ProjectName)d.pdb</ProgramDataBaseFileName>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<BrowseInformationFile>$(IntDir)</BrowseInformationFile>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<TreatWarningAsError>true</TreatWarningAsError>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PostBuildEvent>
<Command>xcopy /y /s /q /i include ..\..\output\include</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AdditionalIncludeDirectories>include;header;..\RSlang\include;..\cclGraph\include;..\cclCommons\include;..\cclLang\include</AdditionalIncludeDirectories>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<SupportJustMyCode>true</SupportJustMyCode>
<WarningLevel>Level4</WarningLevel>
<TreatWarningAsError>true</TreatWarningAsError>
<SDLCheck>true</SDLCheck>
<Optimization>Disabled</Optimization>
<OmitFramePointers>false</OmitFramePointers>
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<ConformanceMode>true</ConformanceMode>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<ProgramDataBaseFileName>$(OutDir)$(ProjectName)d.pdb</ProgramDataBaseFileName>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<PostBuildEvent>
<Command>xcopy /y /s /q /i include ..\..\output\include</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AdditionalIncludeDirectories>include;header;..\RSlang\include;..\cclGraph\include;..\cclCommons\include;..\cclLang\include</AdditionalIncludeDirectories>
<WarningLevel>Level4</WarningLevel>
<TreatWarningAsError>true</TreatWarningAsError>
<SDLCheck>true</SDLCheck>
<IntrinsicFunctions>true</IntrinsicFunctions>
<OmitFramePointers>false</OmitFramePointers>
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<ConformanceMode>true</ConformanceMode>
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
<ProgramDataBaseFileName>$(OutDir)$(ProjectName).pdb</ProgramDataBaseFileName>
<DisableLanguageExtensions>true</DisableLanguageExtensions>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<PostBuildEvent>
<Command>xcopy /y /s /q /i include ..\..\output\include</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\api\RSFormJA.cpp" />
<ClCompile Include="src\env\cclEnvironment.cpp" />
<ClCompile Include="src\JSON.cpp" />
<ClCompile Include="src\ops\EquationOptions.cpp" />
<ClCompile Include="src\ops\RSAggregator.cpp" />
<ClCompile Include="src\ops\RSEquationProcessor.cpp" />
<ClCompile Include="src\ops\RSOperations.cpp" />
<ClCompile Include="src\oss\OSSchema.cpp" />
<ClCompile Include="src\oss\ossGraphFacet.cpp" />
<ClCompile Include="src\oss\ossGridFacet.cpp" />
<ClCompile Include="src\oss\ossOperationsFacet.cpp" />
<ClCompile Include="src\oss\ossSourceFacet.cpp" />
<ClCompile Include="src\oss\RSSynthesProcessor.cpp" />
<ClCompile Include="src\semantic\rscore\ConceptRecord.cpp" />
<ClCompile Include="src\semantic\rscore\CstList.cpp" />
<ClCompile Include="src\semantic\rscore\IdentityManager.cpp" />
<ClCompile Include="src\semantic\rscore\RSCore.cpp" />
<ClCompile Include="src\semantic\rsform\RSForm.cpp" />
<ClCompile Include="src\semantic\rsform\rsModificationFacet.cpp" />
<ClCompile Include="src\semantic\rsform\rsOperationFacet.cpp" />
<ClCompile Include="src\semantic\rsmodel\InterpretationStorage.cpp" />
<ClCompile Include="src\semantic\rsmodel\rsCalculationFacet.cpp" />
<ClCompile Include="src\semantic\rsmodel\RSModel.cpp" />
<ClCompile Include="src\semantic\rsmodel\rsValuesFacet.cpp" />
<ClCompile Include="src\semantic\schema\RSConcept.cpp" />
<ClCompile Include="src\semantic\schema\Schema.cpp" />
<ClCompile Include="src\semantic\thesaurus\TextConcept.cpp" />
<ClCompile Include="src\semantic\thesaurus\Thesaurus.cpp" />
<ClCompile Include="src\tools\CstNameGenerator.cpp" />
<ClCompile Include="src\tools\EntityGenerator.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\ccl\api\RSFormJA.h" />
<ClInclude Include="include\ccl\env\cclEnvironment.h" />
<ClInclude Include="include\ccl\env\SourceManager.hpp" />
<ClInclude Include="include\ccl\Operation.hpp" />
<ClInclude Include="include\ccl\ops\EquationOptions.h" />
<ClInclude Include="include\ccl\ops\RSAggregator.h" />
<ClInclude Include="include\ccl\ops\RSEquationProcessor.h" />
<ClInclude Include="include\ccl\ops\RSOperations.h" />
<ClInclude Include="include\ccl\oss\OperationProcessor.hpp" />
<ClInclude Include="include\ccl\oss\OSSchema.h" />
<ClInclude Include="include\ccl\oss\ossGraphFacet.h" />
<ClInclude Include="include\ccl\oss\ossGridFacet.h" />
<ClInclude Include="include\ccl\oss\ossOperationsFacet.h" />
<ClInclude Include="include\ccl\oss\ossSourceFacet.h" />
<ClInclude Include="include\ccl\oss\Pict.hpp" />
<ClInclude Include="include\ccl\oss\RSSynthesProcessor.h" />
<ClInclude Include="include\ccl\semantic\ConceptRecord.h" />
<ClInclude Include="include\ccl\semantic\CstFilters.hpp" />
<ClInclude Include="include\ccl\semantic\CstList.h" />
<ClInclude Include="include\ccl\semantic\CstType.hpp" />
<ClInclude Include="include\ccl\semantic\IdentityManager.h" />
<ClInclude Include="include\ccl\semantic\InterpretationStorage.h" />
<ClInclude Include="include\ccl\semantic\rsCalculationFacet.h" />
<ClInclude Include="include\ccl\semantic\RSConcept.h" />
<ClInclude Include="include\ccl\semantic\RSCore.h" />
<ClInclude Include="include\ccl\semantic\RSForm.h" />
<ClInclude Include="include\ccl\semantic\RSModel.h" />
<ClInclude Include="include\ccl\semantic\rsModificationFacet.h" />
<ClInclude Include="include\ccl\semantic\rsOperationFacet.h" />
<ClInclude Include="include\ccl\semantic\rsValuesFacet.h" />
<ClInclude Include="include\ccl\semantic\Schema.h" />
<ClInclude Include="include\ccl\semantic\TextConcept.h" />
<ClInclude Include="include\ccl\semantic\TextData.hpp" />
<ClInclude Include="include\ccl\semantic\Thesaurus.h" />
<ClInclude Include="include\ccl\Source.hpp" />
<ClInclude Include="include\ccl\tools\CstNameGenerator.h" />
<ClInclude Include="include\ccl\tools\EntityGenerator.h" />
<ClInclude Include="include\ccl\tools\EnumJSON.hpp" />
<ClInclude Include="include\ccl\tools\JSON.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\cclGraph\cclGraph.vcxproj">
<Project>{7e1d5338-f819-4c96-b461-9eaab8d02e1d}</Project>
</ProjectReference>
<ProjectReference Include="..\cclLang\cclLang.vcxproj">
<Project>{76b03803-56cc-47c2-a8f0-2241fcaf2898}</Project>
</ProjectReference>
<ProjectReference Include="..\RSlang\RSlang.vcxproj">
<Project>{a8529c63-42f5-43e6-97b8-2ec83f23e1f9}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>

View File

@ -0,0 +1,252 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="02 env">
<UniqueIdentifier>{86d3c4ce-8b73-446b-ae23-3c51e5321065}</UniqueIdentifier>
</Filter>
<Filter Include="01 tools">
<UniqueIdentifier>{bb109e85-4730-40c3-8cf6-4cf4a3c7111d}</UniqueIdentifier>
</Filter>
<Filter Include="03 semantic">
<UniqueIdentifier>{afefed49-4a72-4a1d-87f0-26c3b8f3f1e9}</UniqueIdentifier>
</Filter>
<Filter Include="04 ops">
<UniqueIdentifier>{5c6e9f8c-3112-47da-9118-ecfbaa836009}</UniqueIdentifier>
</Filter>
<Filter Include="05 oss">
<UniqueIdentifier>{dafdb290-2760-4574-bf2e-86b94008efe7}</UniqueIdentifier>
</Filter>
<Filter Include="03 semantic\03 RSCore">
<UniqueIdentifier>{1d5f943e-0673-47a8-b4ab-ef179e4c3dd1}</UniqueIdentifier>
</Filter>
<Filter Include="03 semantic\04 RSForm">
<UniqueIdentifier>{2bd129f8-604d-4b83-affd-8f322bbff657}</UniqueIdentifier>
</Filter>
<Filter Include="03 semantic\05 RSModel">
<UniqueIdentifier>{5de97bf2-566e-46c1-9b21-9ceacac19749}</UniqueIdentifier>
</Filter>
<Filter Include="03 semantic\01 Schema">
<UniqueIdentifier>{0a673bb0-fb75-4cc4-8fd7-1b1d033a699a}</UniqueIdentifier>
</Filter>
<Filter Include="03 semantic\02 Thesaurus">
<UniqueIdentifier>{166b33ed-84a5-4040-8398-983a541cc3db}</UniqueIdentifier>
</Filter>
<Filter Include="04 ops\01 RSOps">
<UniqueIdentifier>{3b955deb-294b-4000-bb01-4a043fdfbf38}</UniqueIdentifier>
</Filter>
<Filter Include="06 API">
<UniqueIdentifier>{b2207c2a-30f0-424c-bfe2-f81eeca2949d}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\env\cclEnvironment.cpp">
<Filter>02 env</Filter>
</ClCompile>
<ClCompile Include="src\tools\EntityGenerator.cpp">
<Filter>01 tools</Filter>
</ClCompile>
<ClCompile Include="src\tools\CstNameGenerator.cpp">
<Filter>01 tools</Filter>
</ClCompile>
<ClCompile Include="src\ops\RSAggregator.cpp">
<Filter>04 ops\01 RSOps</Filter>
</ClCompile>
<ClCompile Include="src\ops\RSOperations.cpp">
<Filter>04 ops\01 RSOps</Filter>
</ClCompile>
<ClCompile Include="src\oss\ossGraphFacet.cpp">
<Filter>05 oss</Filter>
</ClCompile>
<ClCompile Include="src\oss\ossGridFacet.cpp">
<Filter>05 oss</Filter>
</ClCompile>
<ClCompile Include="src\oss\ossOperationsFacet.cpp">
<Filter>05 oss</Filter>
</ClCompile>
<ClCompile Include="src\oss\ossSourceFacet.cpp">
<Filter>05 oss</Filter>
</ClCompile>
<ClCompile Include="src\oss\RSSynthesProcessor.cpp">
<Filter>05 oss</Filter>
</ClCompile>
<ClCompile Include="src\oss\OSSchema.cpp">
<Filter>05 oss</Filter>
</ClCompile>
<ClCompile Include="src\ops\EquationOptions.cpp">
<Filter>04 ops</Filter>
</ClCompile>
<ClCompile Include="src\ops\RSEquationProcessor.cpp">
<Filter>04 ops\01 RSOps</Filter>
</ClCompile>
<ClCompile Include="src\JSON.cpp">
<Filter>01 tools</Filter>
</ClCompile>
<ClCompile Include="src\semantic\rsform\RSForm.cpp">
<Filter>03 semantic\04 RSForm</Filter>
</ClCompile>
<ClCompile Include="src\semantic\rsform\rsModificationFacet.cpp">
<Filter>03 semantic\04 RSForm</Filter>
</ClCompile>
<ClCompile Include="src\semantic\rsmodel\InterpretationStorage.cpp">
<Filter>03 semantic\05 RSModel</Filter>
</ClCompile>
<ClCompile Include="src\semantic\rsmodel\rsCalculationFacet.cpp">
<Filter>03 semantic\05 RSModel</Filter>
</ClCompile>
<ClCompile Include="src\semantic\rsmodel\RSModel.cpp">
<Filter>03 semantic\05 RSModel</Filter>
</ClCompile>
<ClCompile Include="src\semantic\rsmodel\rsValuesFacet.cpp">
<Filter>03 semantic\05 RSModel</Filter>
</ClCompile>
<ClCompile Include="src\semantic\rsform\rsOperationFacet.cpp">
<Filter>03 semantic\04 RSForm</Filter>
</ClCompile>
<ClCompile Include="src\semantic\rscore\RSCore.cpp">
<Filter>03 semantic\03 RSCore</Filter>
</ClCompile>
<ClCompile Include="src\semantic\rscore\ConceptRecord.cpp">
<Filter>03 semantic\03 RSCore</Filter>
</ClCompile>
<ClCompile Include="src\semantic\rscore\CstList.cpp">
<Filter>03 semantic\03 RSCore</Filter>
</ClCompile>
<ClCompile Include="src\semantic\schema\RSConcept.cpp">
<Filter>03 semantic\01 Schema</Filter>
</ClCompile>
<ClCompile Include="src\semantic\schema\Schema.cpp">
<Filter>03 semantic\01 Schema</Filter>
</ClCompile>
<ClCompile Include="src\semantic\thesaurus\TextConcept.cpp">
<Filter>03 semantic\02 Thesaurus</Filter>
</ClCompile>
<ClCompile Include="src\semantic\thesaurus\Thesaurus.cpp">
<Filter>03 semantic\02 Thesaurus</Filter>
</ClCompile>
<ClCompile Include="src\semantic\rscore\IdentityManager.cpp">
<Filter>03 semantic\03 RSCore</Filter>
</ClCompile>
<ClCompile Include="src\api\RSFormJA.cpp">
<Filter>06 API</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\ccl\env\SourceManager.hpp">
<Filter>02 env</Filter>
</ClInclude>
<ClInclude Include="include\ccl\env\cclEnvironment.h">
<Filter>02 env</Filter>
</ClInclude>
<ClInclude Include="include\ccl\tools\EntityGenerator.h">
<Filter>01 tools</Filter>
</ClInclude>
<ClInclude Include="include\ccl\tools\CstNameGenerator.h">
<Filter>01 tools</Filter>
</ClInclude>
<ClInclude Include="include\ccl\ops\RSAggregator.h">
<Filter>04 ops\01 RSOps</Filter>
</ClInclude>
<ClInclude Include="include\ccl\ops\RSOperations.h">
<Filter>04 ops\01 RSOps</Filter>
</ClInclude>
<ClInclude Include="include\ccl\oss\OSSchema.h">
<Filter>05 oss</Filter>
</ClInclude>
<ClInclude Include="include\ccl\oss\ossGraphFacet.h">
<Filter>05 oss</Filter>
</ClInclude>
<ClInclude Include="include\ccl\oss\ossGridFacet.h">
<Filter>05 oss</Filter>
</ClInclude>
<ClInclude Include="include\ccl\oss\ossOperationsFacet.h">
<Filter>05 oss</Filter>
</ClInclude>
<ClInclude Include="include\ccl\oss\ossSourceFacet.h">
<Filter>05 oss</Filter>
</ClInclude>
<ClInclude Include="include\ccl\oss\Pict.hpp">
<Filter>05 oss</Filter>
</ClInclude>
<ClInclude Include="include\ccl\oss\OperationProcessor.hpp">
<Filter>05 oss</Filter>
</ClInclude>
<ClInclude Include="include\ccl\oss\RSSynthesProcessor.h">
<Filter>05 oss</Filter>
</ClInclude>
<ClInclude Include="include\ccl\ops\EquationOptions.h">
<Filter>04 ops</Filter>
</ClInclude>
<ClInclude Include="include\ccl\tools\JSON.h">
<Filter>01 tools</Filter>
</ClInclude>
<ClInclude Include="include\ccl\tools\EnumJSON.hpp">
<Filter>01 tools</Filter>
</ClInclude>
<ClInclude Include="include\ccl\Operation.hpp">
<Filter>03 semantic</Filter>
</ClInclude>
<ClInclude Include="include\ccl\Source.hpp">
<Filter>03 semantic</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\RSForm.h">
<Filter>03 semantic\04 RSForm</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\rsModificationFacet.h">
<Filter>03 semantic\04 RSForm</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\rsOperationFacet.h">
<Filter>03 semantic\04 RSForm</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\InterpretationStorage.h">
<Filter>03 semantic\05 RSModel</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\rsCalculationFacet.h">
<Filter>03 semantic\05 RSModel</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\RSModel.h">
<Filter>03 semantic\05 RSModel</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\rsValuesFacet.h">
<Filter>03 semantic\05 RSModel</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\TextData.hpp">
<Filter>03 semantic\05 RSModel</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\RSCore.h">
<Filter>03 semantic\03 RSCore</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\TextConcept.h">
<Filter>03 semantic\02 Thesaurus</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\RSConcept.h">
<Filter>03 semantic\01 Schema</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\Schema.h">
<Filter>03 semantic\01 Schema</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\Thesaurus.h">
<Filter>03 semantic\02 Thesaurus</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\CstList.h">
<Filter>03 semantic\03 RSCore</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\CstFilters.hpp">
<Filter>03 semantic\03 RSCore</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\ConceptRecord.h">
<Filter>03 semantic\03 RSCore</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\CstType.hpp">
<Filter>03 semantic\01 Schema</Filter>
</ClInclude>
<ClInclude Include="include\ccl\semantic\IdentityManager.h">
<Filter>03 semantic\03 RSCore</Filter>
</ClInclude>
<ClInclude Include="include\ccl\ops\RSEquationProcessor.h">
<Filter>04 ops\01 RSOps</Filter>
</ClInclude>
<ClInclude Include="include\ccl\api\RSFormJA.h">
<Filter>06 API</Filter>
</ClInclude>
</ItemGroup>
</Project>

3
ccl/core/RunCoverage.bat Normal file
View File

@ -0,0 +1,3 @@
OpenCppCoverage --sources "%~dp0src" --sources "%~dp0header" --sources "%~dp0include" --modules "%~dp0build\Debug\ConceptCoreLibraryd.lib" --modules "%~dp0test\build\Debug\cclTest.exe" --excluded_line_regex "\s*\} else \{.*" --excluded_line_regex "\s*\}\s*" --excluded_line_regex "\s*\sthrow *" --excluded_line_regex "\sassert\(.*" --excluded_sources "rsparserimpl.cpp" --excluded_sources "rsparserimpl.y" --excluded_sources "stack.hh" -- %~dp0test\build\Debug\CCLTest.exe
pause

View File

@ -0,0 +1,85 @@
#pragma once
#include "ccl/Entity.hpp"
#include "ccl/cclMeta.hpp"
#include "ccl/Source.hpp"
namespace ccl::ops {
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 26440 ) // Note: do not warn if default implementation might be noexcept
#endif
//! Operation status enumeration
enum class Status : uint32_t {
undefined = 2,
defined = 3,
done = 4,
outdated = 5, // Требуется пересинтез
broken = 7, // Требуется переопределение операции
};
//! Operation type enumeration
enum class Type : uint32_t {
tba = 0,
rsMerge = 4, // Слияние двух РС-форм в одну
rsSynt = 8, // Обобщенный синтез
};
static constexpr bool HasOptions(Type type) noexcept { return type == Type::rsSynt; }
//! Abstract options of operation
class Options {
public:
virtual ~Options() noexcept = default;
protected:
Options() = default;
Options(const Options&) = default;
Options& operator=(const Options&) = default;
Options(Options&&) noexcept = default;
Options& operator=(Options&&) noexcept = default;
public:
[[nodiscard]] virtual bool IsEqualTo(const Options& /*opt2*/) const = 0;
};
//! Abstract translation data
using TranslationData = std::vector<EntityTranslation>;
using Args = std::vector<const src::DataStream*>;
using ArgsInfo = std::vector<src::DataType>;
//! Operation signature
struct Signature {
src::DataType result{};
ArgsInfo args{};
};
//! Operation call
struct Call {
Type type{ Type::tba };
const Options* params{ nullptr };
Args args{};
[[nodiscard]] ArgsInfo Info() const {
auto result = ArgsInfo{};
for (const auto& arg : args) {
result.emplace_back(arg == nullptr ? src::DataType::tba : arg->Type());
}
return result;
}
};
//! Operation result
struct Result {
meta::UniqueCPPtr<src::DataStream> value{ nullptr };
meta::UniqueCPPtr<TranslationData> translation{ nullptr };
};
#ifdef _MSC_VER
#pragma warning( pop )
#endif
} // namespace ccl::ops

View File

@ -0,0 +1,196 @@
#pragma once
#include "ccl/Entity.hpp"
#include "ccl/cclChange.hpp"
#include "ccl/cclMeta.hpp"
namespace ccl::src {
//! Data type enumeration
enum class DataType : uint32_t { tba = 0, rsSchema };
//! Source type enumeration
enum class SrcType : uint32_t { tba = 0, rsDoc };
//! Abstract data stream
class DataStream {
public:
virtual ~DataStream() noexcept = default;
DataStream& operator=(const DataStream&) = delete;
DataStream& operator=(DataStream&&) = delete;
protected:
DataStream() = default;
DataStream(const DataStream&) = default;
DataStream(DataStream&&) noexcept = default;
public:
enum class Attribute : uint32_t {
title, alias, comment
};
using ValueType = std::string;
struct AttrValue {
Attribute type{};
ValueType value{};
};
[[nodiscard]] virtual DataType Type() const noexcept = 0;
};
template<
typename T,
typename = std::enable_if_t<std::is_base_of_v<DataStream, T>>
>
const T* CastData(const DataStream* data) noexcept {
return dynamic_cast<const T*>(data);
}
//! Abstract data source
class Source {
bool isClaimed{ false };
public:
virtual ~Source() noexcept = default;
protected:
Source() = default;
Source(const Source&) = default;
Source& operator=(const Source&) noexcept = default;
Source& operator=(Source&&) noexcept = default;
Source(Source&&) = default;
public:
[[nodiscard]] virtual change::Hash CoreHash() const = 0;
[[nodiscard]] virtual change::Hash FullHash() const = 0;
[[nodiscard]] virtual SrcType Type() const noexcept = 0;
virtual bool WriteData(meta::UniqueCPPtr<DataStream> /*data*/) = 0;
[[nodiscard]] virtual const DataStream* ReadData() const = 0;
[[nodiscard]] virtual DataStream* AccessData() = 0;
[[nodiscard]] bool IsClaimed() const noexcept { return isClaimed; }
void Claim() noexcept { isClaimed = true; }
void ReleaseClaim() noexcept { isClaimed = false; }
};
//! Source descriptor
struct Descriptor {
SrcType type{ SrcType::tba };
std::u8string name{};
explicit Descriptor(SrcType type = SrcType::tba, std::u8string name = {}) noexcept
: type{ type }, name{ std::move(name) } {}
bool operator==(const Descriptor& rhs) const noexcept { return type == rhs.type && name == rhs.name; }
bool operator!=(const Descriptor& rhs) const noexcept { return !(*this == rhs); }
};
//! Source handle
struct Handle {
Source* src{ nullptr };
Descriptor desc{};
change::Hash coreHash{ 0 };
change::Hash fullHash{ 0 };
public:
Handle() = default;
explicit Handle(SrcType type) noexcept
: desc{ type, std::u8string{} } {}
public:
[[nodiscard]] bool empty() const noexcept {
return src == nullptr && std::empty(desc.name);
}
void UpdateHashes() {
coreHash = src == nullptr ? coreHash : src->CoreHash();
fullHash = src == nullptr ? fullHash : src->FullHash();
}
void DiscardSrc() noexcept {
src = nullptr;
desc.name.clear();
coreHash = 0;
fullHash = 0;
}
};
//! Abstract source callback
class SourceCallback {
public:
virtual ~SourceCallback() noexcept = default;
protected:
SourceCallback() = default;
SourceCallback(const SourceCallback&) = default;
SourceCallback& operator=(const SourceCallback&) = default;
SourceCallback(SourceCallback&&) noexcept = default;
SourceCallback& operator=(SourceCallback&&) noexcept = default;
public:
[[nodiscard]] virtual change::Usage QueryEntityIsUsed(const src::Source& /*from*/, EntityUID /*entity*/) const = 0;
[[nodiscard]] virtual bool IsConnectedWith(const src::Source& /*src*/) const = 0;
};
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 26440 ) // Note: do not warn if default implementation might be noexcept
#endif
//! Callback manager base class
class CallbackManager {
std::vector<SourceCallback*> callbacks{};
public:
bool AddCallback(SourceCallback& newCB) {
if (std::find(begin(callbacks), end(callbacks), &newCB) != end(callbacks)) {
return false;
} else {
callbacks.emplace_back(&newCB);
return true;
}
}
void RemoveCallback(const SourceCallback& cb) {
if (const auto item = std::find(begin(callbacks), end(callbacks), &cb);
item != end(callbacks)) {
callbacks.erase(item);
}
}
void ImportCallbacksFrom(CallbackManager& rhs) {
if (this != &rhs) {
for (auto& cb : rhs.callbacks) {
AddCallback(*cb);
}
}
}
[[nodiscard]] bool QuerySrcUse(const src::Source& src) const {
return std::any_of(begin(callbacks), end(callbacks),
[&](const auto& callback) {
return callback->IsConnectedWith(src);
});
}
[[nodiscard]] change::Usage QueryEntityUse(const src::Source& from, EntityUID entity) const {
change::Usage result = change::Usage::notUsed;
for (const auto& callback : callbacks) {
const auto curStatus = callback->QueryEntityIsUsed(from, entity);
if (static_cast<uint8_t>(result) < static_cast<uint8_t>(curStatus)) {
result = curStatus;
}
}
return result;
}
};
#ifdef _MSC_VER
#pragma warning( pop )
#endif
} // namespace ccl::src

View File

@ -0,0 +1,30 @@
#pragma once
#include "ccl/semantic/RSForm.h"
namespace ccl::api {
static constexpr int JSON_IDENT = 4;
std::string ParseExpression(const std::string& expression, rslang::Syntax syntaxHint = rslang::Syntax::UNDEF);
//! JSON accessor wrapper for RSForm object
class RSFormJA {
private:
std::unique_ptr<semantic::RSForm> schema{};
private:
RSFormJA() noexcept = default;
public:
[[nodiscard]] const semantic::RSForm& data() const noexcept;
[[nodiscard]] semantic::RSForm& data() noexcept;
[[nodiscard]] static RSFormJA FromData(semantic::RSForm&& data);
[[nodiscard]] static RSFormJA FromJSON(std::string_view json);
[[nodiscard]] std::string ToJSON() const;
[[nodiscard]] std::string ToMinimalJSON() const;
[[nodiscard]] std::string CheckExpression(const std::string& text, rslang::Syntax syntaxHint = rslang::Syntax::UNDEF) const;
};
} // namespace ccl::api

View File

@ -0,0 +1,80 @@
#pragma once
#include "ccl/Source.hpp"
namespace ccl {
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 26440 ) // Note: do not warn if default implementation might be noexcept
#endif
//! Abstract source manager
class SourceManager : public src::CallbackManager, public types::Observable {
public:
SourceManager() = default;
virtual ~SourceManager() noexcept = default;
SourceManager(const SourceManager&) = delete;
SourceManager& operator=(const SourceManager&) = delete;
public:
struct SrcMessage : types::Message {
explicit SrcMessage(src::Descriptor srcID) noexcept
: srcID{ std::move(srcID) } {}
[[nodiscard]] uint32_t Type() const noexcept override { return srcCode; }
src::Descriptor srcID;
};
struct SrcOpened : SrcMessage { using SrcMessage::SrcMessage; };
struct SrcClosed : SrcMessage { using SrcMessage::SrcMessage; };
struct SrcChanged : SrcMessage { using SrcMessage::SrcMessage; };
public:
void OnSourceOpen(const src::Source& src) { Notify(SrcOpened{ GetDescriptor(src) }); }
void OnSourceClose(const src::Source& src) { Notify(SrcClosed{ GetDescriptor(src) }); }
void OnSourceChange(const src::Source& src) { Notify(SrcChanged{ GetDescriptor(src) }); }
[[nodiscard]] virtual bool TestDomain(const src::Descriptor& /*global*/,
const std::u8string& /*domain*/) const {
return false;
}
[[nodiscard]] virtual src::Descriptor Convert2Local(const src::Descriptor& /*global*/,
const std::u8string& /*domain*/) const {
return src::Descriptor{};
}
[[nodiscard]] virtual src::Descriptor Convert2Global(const src::Descriptor& /*local*/,
const std::u8string& /*domain*/) const {
return src::Descriptor{};
}
[[nodiscard]] virtual src::Descriptor CreateLocalDesc(src::SrcType type, std::u8string localName) const {
return src::Descriptor{ type, localName };
}
[[nodiscard]] virtual src::Source* Find(const src::Descriptor& /*desc*/) {
return nullptr;
}
[[nodiscard]] virtual src::Descriptor GetDescriptor(const src::Source& /*src*/) const
{ return src::Descriptor{};
}
virtual bool ChangeDescriptor(const src::Descriptor& /*desc*/, const src::Descriptor& /*newDesc*/) {
return false;
}
[[nodiscard]] virtual src::Source* CreateNew(const src::Descriptor& /*desc*/) {
return nullptr;
}
[[nodiscard]] virtual src::Source* Open(const src::Descriptor& /*desc*/) {
return nullptr;
}
virtual void Close(src::Source& /*src*/) {}
virtual bool SaveState(src::Source& /*src*/) {
return false;
}
virtual void Discard(const src::Descriptor& /*desc*/) {}
};
#ifdef _MSC_VER
#pragma warning( pop )
#endif
} // namespace ccl

View File

@ -0,0 +1,24 @@
#pragma once
#include "ccl/env/SourceManager.hpp"
#include <random>
namespace ccl {
//! Enivroment configuration - !Global state!
class Environment final {
std::unique_ptr<SourceManager> sources{ std::make_unique<SourceManager>() };
public:
[[nodiscard]] static Environment& Instance() noexcept;
[[nodiscard]] static SourceManager& Sources() noexcept { return *Instance().sources; }
[[nodiscard]] static std::random_device& RNG();
[[nodiscard]] static std::string_view VersionInfo() noexcept;
void SetSourceManager(std::unique_ptr<SourceManager> newSources);
};
} // namespace ccl

View File

@ -0,0 +1,67 @@
#pragma once
#include "ccl/Operation.hpp"
#include "ccl/Entity.hpp"
namespace ccl::ops {
//! Options for single entity equation
struct Equation {
enum class Mode : uint8_t {
keepHier = 1,
keepDel = 2,
createNew = 3,
};
Equation::Mode mode{ Equation::Mode::keepHier };
std::string arg{};
Equation() = default;
explicit Equation(Equation::Mode mode, std::string arg) noexcept
: mode{ mode }, arg{ std::move(arg) } {}
bool operator==(const Equation& rhs) const noexcept {
return mode == rhs.mode && arg == rhs.arg;
}
bool operator!=(const Equation& rhs) const noexcept {
return mode != rhs.mode || arg != rhs.arg;
}
};
//! Options for equation operation
class EquationOptions : public Options {
public:
using Props = std::unordered_map<EntityUID, Equation>;
private:
EntityTranslation translation{};
Props properties{};
public:
EquationOptions() = default;
explicit EquationOptions(EntityUID key, EntityUID val, const Equation& params = {});
public:
using ConstIter = EntityTranslation::ConstIter;
using size_type = EntityTranslation::size_type;
[[nodiscard]] bool IsEqualTo(const Options& opt2) const override;
[[nodiscard]] ConstIter begin() const noexcept { return std::begin(translation); }
[[nodiscard]] ConstIter end() const noexcept { return std::end(translation); }
[[nodiscard]] size_type size() const noexcept { return std::size(translation); }
[[nodiscard]] bool empty() const noexcept { return std::empty(translation); }
[[nodiscard]] const Equation& PropsFor(EntityUID key) const;
[[nodiscard]] bool ContainsValue(EntityUID value) const;
[[nodiscard]] bool ContainsKey(EntityUID key) const;
void Insert(EntityUID key, EntityUID val, const Equation& params = {});
void Erase(EntityUID key) noexcept;
bool SwapKeyVal(EntityUID key);
void SubstituteValues(const EntityTranslation& idSubstitutes);
};
} // namespace ccl::ops

View File

@ -0,0 +1,33 @@
#pragma once
#include "ccl/semantic/RSForm.h"
namespace ccl::ops {
//! Aggregation manager for versions of RSForm object
class RSAggregator {
semantic::RSForm& output;
const semantic::RSForm* prevOutput{ nullptr };
EntityTranslation translation{};
StrSubstitutes nameSubstitutes{};
VectorOfEntities insertedCsts{};
public:
explicit RSAggregator(semantic::RSForm& newShema)
: output{ newShema } {}
public:
std::optional<EntityTranslation> Merge(const semantic::RSForm& previous, const EntityTranslation& oldToNew);
private:
void Clear() noexcept;
bool PrepareSubstitutes();
void TransferNew();
void TransferIheritedData();
void UpdateReferences();
[[nodiscard]] std::optional<EntityUID> TransferCst(EntityUID target, semantic::ListIterator insertWhere);
};
} // namespace ccl::ops

View File

@ -0,0 +1,44 @@
#pragma once
#include "ccl/rslang/TypeContext.hpp"
#include "ccl/rslang/Typification.h"
#include "ccl/ops/EquationOptions.h"
#include "ccl/Substitutes.hpp"
namespace ccl::semantic { class RSForm; }
namespace ccl::ops {
//! Constituents equation processor
class RSEquationProcessor {
semantic::RSForm& schema;
EntityTranslation translation{};
mutable const EquationOptions* equations{ nullptr };
mutable StrSubstitutes nameSubstitutes{};
public:
explicit RSEquationProcessor(semantic::RSForm& schema);
public:
[[nodiscard]] bool Evaluate(const EquationOptions& options) const;
[[nodiscard]] bool Execute(const EquationOptions& options);
[[nodiscard]] const EntityTranslation& Translation() const noexcept { return translation; }
private:
void Clear() noexcept;
void ClearMutable() const noexcept;
bool ResolveCstAndPrecheck() const;
bool PrecheckFor(EntityUID key, EntityUID value) const;
bool CheckNonBasicEquations() const;
void ChangeEquatedCsts();
void RemoveEquatedCsts();
void UpdateExpressions();
[[nodiscard]] rslang::ExpressionType Evaluate(EntityUID uid) const;
};
} // namespace ccl::ops

View File

@ -0,0 +1,116 @@
#pragma once
#include "ccl/semantic/RSForm.h"
#include "ccl/Operation.hpp"
#include "ccl/ops/EquationOptions.h"
namespace ccl::ops {
//! Binary synthesis of RSForms
class BinarySynthes {
const semantic::RSForm& operand1;
const semantic::RSForm& operand2;
EquationOptions equations;
bool isCorrect{ false };
std::unique_ptr<semantic::RSForm> resultSchema{ nullptr };
TranslationData translations{};
public:
BinarySynthes(const semantic::RSForm& ks1, const semantic::RSForm& ks2, EquationOptions equations);
public:
std::unique_ptr<semantic::RSForm> Execute();
[[nodiscard]] const TranslationData& Translations() noexcept { return translations; }
[[nodiscard]] const EquationOptions& Equations() const noexcept;
[[nodiscard]] bool IsCorrectlyDefined() const noexcept;
private:
void ResetResult();
void PrecreateResult();
[[nodiscard]] EquationOptions TranslateEquations() const;
};
//! Operation: Maximize part of RSForm
class OpMaxPart {
const semantic::RSForm& schema;
SetOfEntities arguments;
public:
OpMaxPart(const semantic::RSForm& ks, SetOfEntities args)
: schema{ ks }, arguments{ std::move(args) } {}
public:
std::unique_ptr<semantic::RSForm> Execute();
[[nodiscard]] bool IsCorrectlyDefined() const;
private:
[[nodiscard]] VectorOfEntities GetAllCstMaxPart() const;
[[nodiscard]] bool CheckCst(EntityUID target, const SetOfEntities& selList) const;
};
//! Operation: Extract basis from RSForm
class OpExtractBasis {
const semantic::RSForm& schema;
SetOfEntities arguments;
public:
OpExtractBasis(const semantic::RSForm& ks, SetOfEntities args)
: schema{ ks }, arguments{ std::move(args) } {}
public:
std::unique_ptr<semantic::RSForm> Execute();
[[nodiscard]] bool IsCorrectlyDefined() const;
};
//! Operation: relativation
class OpRelativation {
const semantic::RSForm& operand;
const SetOfEntities ignoreOperand;
const EntityUID targetID;
bool isApplicable{ false };
std::unique_ptr<semantic::RSForm> resultSchema{ nullptr };
SetOfEntities ignoreResult{};
EntityUID baseCst;
std::string baseStr{};
VectorOfEntities processOrder{};
std::string baseID{};
std::string termID{};
std::unordered_map<EntityUID, std::string> types{};
std::unordered_set<std::string> moifiedFuncs{};
StrSubstitutes substitutes{};
public:
OpRelativation(const semantic::RSForm& ks, EntityUID target, const SetOfEntities& ignore = {});
public:
std::unique_ptr<semantic::RSForm> Execute();
[[nodiscard]] static bool IsCorrectlyDefined(const semantic::RSForm& schema, EntityUID target,
const SetOfEntities& ignore = {});
[[nodiscard]] bool IsCorrectlyDefined() const noexcept;
private:
void ResetState() noexcept;
void SetupCore();
[[nodiscard]] SetOfEntities ExtractCore();
void CreateProcessingOrder(const SetOfEntities& core);
[[nodiscard]] EntityUID PromoteBaseToStruct(EntityUID target);
[[nodiscard]] semantic::ConceptRecord CreateStructFrom(EntityUID base, const std::string& newName);
void CreateUniqueIDs();
void CreateFunctions();
[[nodiscard]] std::string GenerateTermFunc() const;
void ModifyExpressions();
[[nodiscard]] std::string UpdatedExpression(std::string_view input) const;
void UpdatedFunctionCalls(std::string& input) const;
[[nodiscard]] std::string UpdatedFunction(std::string_view input) const;
};
} // namespace ccl::ops

View File

@ -0,0 +1,137 @@
#pragma once
#include "ccl/tools/EntityGenerator.h"
#include "ossGraphFacet.h"
#include "ossGridFacet.h"
#include "ossOperationsFacet.h"
#include "ossSourceFacet.h"
namespace ccl::oss {
class IteratorPict;
struct GridPosition;
struct OperationHandle;
//! Operational synthesis schema
class OSSchema final : public change::ObservableMods, public types::Observer {
friend class IteratorPict;
friend class ossGridFacet;
friend class ossSourceFacet;
friend class ossOperationsFacet;
public:
std::string title{};
std::string comment{};
private:
using Storage = std::unordered_map<PictID, Pict>;
Storage storage{};
tools::EntityGenerator idGen{};
std::unique_ptr<ossGridFacet> grid;
std::unique_ptr<ossGraphFacet> graph;
std::unique_ptr<ossSourceFacet> sources;
std::unique_ptr<ossOperationsFacet> ops;
class Callback;
std::unique_ptr<Callback> callback{};
public:
~OSSchema() noexcept override;
OSSchema();
OSSchema(const OSSchema&) = delete;
OSSchema& operator=(const OSSchema&) = delete;
public:
struct ErasePictMod : change::ModMessage {
PictID pid;
explicit ErasePictMod(const PictID pid) noexcept
: pid{ pid } {}
};
struct SrcStatusMod : change::ModMessage {};
struct SrcStateMod : change::ModMessage {};
public:
using size_type = Storage::size_type;
void OnObserve(const types::Message& msg) override;
[[nodiscard]] ossGridFacet& Grid() noexcept;
[[nodiscard]] const ossGridFacet& Grid() const noexcept;
[[nodiscard]] ossGraphFacet& Graph() noexcept;
[[nodiscard]] const ossGraphFacet& Graph() const noexcept;
[[nodiscard]] ossSourceFacet& Src() noexcept;
[[nodiscard]] const ossSourceFacet& Src() const noexcept;
[[nodiscard]] ossOperationsFacet& Ops() noexcept;
[[nodiscard]] const ossOperationsFacet& Ops() const noexcept;
[[nodiscard]] size_type size() const noexcept;
[[nodiscard]] bool empty() const noexcept;
[[nodiscard]] bool Contains(PictID pict) const;
[[nodiscard]] IteratorPict begin() const noexcept;
[[nodiscard]] IteratorPict end() const noexcept;
[[nodiscard]] PictPtr operator()(PictID pid) const;
PictPtr InsertBase();
PictPtr InsertOperation(PictID operand1, PictID operand2);
bool Erase(PictID target);
void SetPictTitle(PictID target, const std::string& newValue);
void SetPictAlias(PictID target, const std::string& newValue);
void SetPictComment(PictID target, const std::string& newValue);
void SetPictLink(PictID target, const MediaLink& lnk);
const Pict& LoadPict(Pict&& pict, GridPosition pos, const src::Handle& handle, std::unique_ptr<OperationHandle> params);
private:
Pict* Access(PictID pid);
void InsertInternal(const Pict& pict, GridPosition pos,
const src::Handle& srcHandle,
std::unique_ptr<OperationHandle> opHandle);
void OnCoreChange(PictID changedPict);
//! OSS callback
class Callback : public src::SourceCallback {
OSSchema& oss;
public:
explicit Callback(OSSchema& oss) noexcept
: oss{ oss } {}
public:
[[nodiscard]] change::Usage QueryEntityIsUsed(const src::Source& from, EntityUID entity) const override;
[[nodiscard]] bool IsConnectedWith(const src::Source& src) const override;
};
};
//! OSS iterator
class IteratorPict {
friend class OSSchema;
using Container = OSSchema::Storage;
Container::const_iterator iterator;
private:
explicit IteratorPict(Container::const_iterator iter) noexcept;
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = Pict;
using difference_type = std::ptrdiff_t;
using pointer = const Pict*;
using reference = const Pict&;
[[nodiscard]] bool operator==(const IteratorPict& iter2) const noexcept;
[[nodiscard]] bool operator!=(const IteratorPict& iter2) const noexcept { return !(*this == iter2); }
IteratorPict& operator++() noexcept;
[[nodiscard]] pointer operator->() const noexcept;
[[nodiscard]] reference operator*() const noexcept;
};
} // namespace ccl::oss

View File

@ -0,0 +1,45 @@
#pragma once
#include "ccl/Entity.hpp"
#include "ccl/Operation.hpp"
namespace ccl::oss {
//! Abstract OSS operations processor
class OperationsProcessor {
public:
virtual ~OperationsProcessor() noexcept = default;
OperationsProcessor() = default;
protected:
OperationsProcessor(const OperationsProcessor&) = default;
OperationsProcessor& operator=(const OperationsProcessor&) = default;
OperationsProcessor(OperationsProcessor&&) noexcept = default;
OperationsProcessor& operator=(OperationsProcessor&&) noexcept = default;
public:
[[nodiscard]] virtual std::optional<src::DataType>
ResultingTypeFor(ops::Type /*operation*/, const ops::ArgsInfo& /*args*/) const = 0;
[[nodiscard]] virtual bool CheckCall(const ops::Call& /*call*/) const = 0;
[[nodiscard]] virtual std::optional<ops::Result> Execute(const ops::Call& /*call*/) const = 0;
[[nodiscard]] virtual EntityTranslation
CreateVersionTranslation(ops::Type /*operation*/,
const ops::TranslationData& /*oldT*/,
const ops::TranslationData& /*newT*/) const = 0;
[[nodiscard]] virtual bool CheckTranslations(const ops::TranslationData& /*translations*/,
const src::DataStream& /*data*/) const = 0;
[[nodiscard]] bool CanProcess(ops::Type operation,
const ops::ArgsInfo& args) const {
return ResultingTypeFor(operation, args).has_value();
}
[[nodiscard]] bool CanProcess(const ops::Call& def) const {
return CanProcess(def.type, def.Info());
}
};
} // namespace ccl::oss

View File

@ -0,0 +1,43 @@
#pragma once
#include "ccl/Source.hpp"
namespace ccl::oss {
using PictID = uint32_t;
//! Media hyperlink data
struct MediaLink {
std::string address{};
std::string subAddr{};
[[nodiscard]] bool empty() const noexcept {
return std::empty(address);
}
bool operator==(const MediaLink& rhs) const noexcept {
return address == rhs.address && subAddr == rhs.subAddr;
}
bool operator!=(const MediaLink& rhs) const noexcept {
return address != rhs.address || subAddr != rhs.subAddr;
}
};
//! Pictogramm data structure
struct Pict {
PictID uid{ 0 };
src::DataType dataType{ src::DataType::rsSchema };
std::string title{};
std::string alias{};
std::string comment{};
MediaLink lnk;
Pict() = default;
explicit Pict(const PictID uid) noexcept
: uid{ uid } {}
};
using PictPtr = const Pict*;
} // namespace ccl::oss

View File

@ -0,0 +1,27 @@
#pragma once
#include "ccl/oss/OperationProcessor.hpp"
namespace ccl::oss {
//! RSForm synthesis processor for OSS
struct RSSProcessor : OperationsProcessor {
[[nodiscard]] std::optional<src::DataType>
ResultingTypeFor(ops::Type /*operation*/,
const ops::ArgsInfo& args) const override;
[[nodiscard]] bool CheckCall(const ops::Call& call) const override;
[[nodiscard]] std::optional<ops::Result>
Execute(const ops::Call& call) const override;
[[nodiscard]] EntityTranslation
CreateVersionTranslation(ops::Type /*operation*/,
const ops::TranslationData& oldT,
const ops::TranslationData& newT) const override;
[[nodiscard]] bool CheckTranslations(const ops::TranslationData& translations,
const src::DataStream& data) const override;
};
} // namespace ccl::oss

View File

@ -0,0 +1,44 @@
#pragma once
#include "ccl/oss/Pict.hpp"
#include "ccl/cclMeta.hpp"
namespace ccl::oss {
class OSSchema;
class ossGraphFacet final : public meta::ConstFacet<OSSchema> {
friend class OSSchema;
using AdjacencyList = std::vector<std::vector<size_t>>;
AdjacencyList graph{};
std::vector<PictID> items{};
public:
explicit ossGraphFacet(const OSSchema& core) noexcept
: ConstFacet<OSSchema>(core) {}
public:
[[nodiscard]] std::vector<PictID> ParentsOf(PictID pid) const;
[[nodiscard]] std::vector<PictID> ChildrenOf(PictID pid) const;
[[nodiscard]] std::optional<size_t> ParentIndex(PictID parent, PictID child) const;
[[nodiscard]] std::vector<std::pair<PictID, PictID>> EdgeList() const;
[[nodiscard]] std::vector<PictID> ExecuteOrder() const;
// Note: use only for Serialization!
bool LoadParent(PictID child, PictID parent);
private:
void AddItem(PictID item, const std::vector<PictID>& connectedItems);
void Erase(PictID item);
size_t InsertKeyValue(PictID item);
[[nodiscard]] size_t Item2ID(PictID item);
[[nodiscard]] std::vector<size_t> Item2ID(const std::vector<PictID>& input);
[[nodiscard]] std::optional<size_t> FindItemIndex(PictID item) const;
[[nodiscard]] std::vector<PictID> Index2PIDs(const std::vector<size_t>& input) const;
};
} // namespace ccl::oss

View File

@ -0,0 +1,83 @@
#pragma once
#include "ccl/oss/Pict.hpp"
#include "ccl/cclMeta.hpp"
namespace ccl::oss {
class OSSchema;
using GridIndex = int32_t; // TODO: change to int16_t after serialization fix
//! Decartian grid position
struct GridPosition {
GridIndex row{ 0 };
GridIndex column{ 0 };
constexpr GridPosition() = default;
constexpr GridPosition(GridIndex row, GridIndex column) noexcept
: row{ row }, column{ column } {}
bool operator==(const GridPosition& other) const noexcept {
return row == other.row && column == other.column;
}
bool operator!=(const GridPosition& other) const noexcept {
return row != other.row || column != other.column;
}
[[nodiscard]] constexpr double EffectiveColumn() const {
return column + row / 2.0; // NOLINT(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers)
}
};
} // namespace ccl::oss
namespace std {
template <>
struct hash<ccl::oss::GridPosition> {
uint32_t operator()(const ccl::oss::GridPosition& k) const noexcept {
using std::hash;
return static_cast<uint32_t>(hash<int32_t>()(k.row) ^ (hash<int32_t>()(k.column) << 1U));
}
};
} // namespace std
namespace ccl::oss {
using Grid = std::unordered_map<GridPosition, PictID>;
//! OSS grid positions facet
class ossGridFacet final : public meta::MutatorFacet<OSSchema> {
friend class OSSchema;
Grid grid{};
public:
static constexpr GridPosition origin{ 0, 0 };
public:
explicit ossGridFacet(OSSchema& oss) noexcept // NOLINT(bugprone-exception-escape)
: MutatorFacet<OSSchema>(oss) {}
public:
[[nodiscard]] const Grid& data() const noexcept;
[[nodiscard]] std::optional<PictID> operator()(GridPosition pos) const;
[[nodiscard]] std::optional<GridPosition> operator()(PictID pid) const;
[[nodiscard]] GridIndex MaxColumnIn(GridIndex row) const;
[[nodiscard]] GridIndex CountRows() const;
bool ShiftPict(PictID pid, int32_t shift);
void LoadPosition(PictID pid, GridPosition pos);
private:
[[nodiscard]] GridPosition ChildPosFor(PictID parent1, PictID parent2) const;
[[nodiscard]] GridPosition ClosestFreePos(GridPosition start = origin) const;
void SetPosFor(PictID pid, GridPosition pos);
void Erase(PictID pid);
};
} // namespace ccl::oss

View File

@ -0,0 +1,87 @@
#pragma once
#include "ccl/oss/OperationProcessor.hpp"
#include "ccl/oss/Pict.hpp"
#include "ccl/Source.hpp"
#include "ccl/cclMeta.hpp"
namespace ccl::oss {
class ossSourceFacet;
class OSSchema;
//! OSS operation handle
struct OperationHandle final {
ops::Type type{ ops::Type::tba };
meta::UniqueCPPtr<ops::Options> options{ nullptr };
meta::UniqueCPPtr<ops::TranslationData> translations{ nullptr };
bool broken{ false };
bool outdated{ false };
void Reset() noexcept;
void InitOperation(ops::Type operation,
meta::UniqueCPPtr<ops::Options> newOptions) noexcept;
};
//! OSS operations facet
class ossOperationsFacet final : public meta::MutatorFacet<OSSchema> {
friend class OSSchema;
public:
std::string ossPath{};
private:
using Operations = std::unordered_map<PictID, std::unique_ptr<OperationHandle>>;
using Processors = std::vector<std::unique_ptr<OperationsProcessor>>;
using ProcessorMapping = std::unordered_map<ops::Type, OperationsProcessor*>;
Operations operations{};
Processors procs;
ProcessorMapping opProcs;
public:
explicit ossOperationsFacet(OSSchema& oss);
public:
[[nodiscard]] const OperationHandle* operator()(PictID pid) const;
[[nodiscard]] ops::Status StatusOf(PictID pid) const;
[[nodiscard]] bool HasOperation(PictID pid) const { return operator()(pid) != nullptr; }
[[nodiscard]] std::unordered_set<ops::Type> VariantsFor(PictID pid) const;
[[nodiscard]] bool IsExecutable(PictID pid);
[[nodiscard]] bool IsTranslatable(PictID pid);
bool InitFor(PictID pid, ops::Type operation,
std::unique_ptr<ops::Options> newOptions = nullptr);
bool Execute(PictID pid, bool autoDiscard = false);
void ExecuteAll();
[[nodiscard]] change::Usage QueryEntityUsage(const src::Source& src, EntityUID cstID) const;
private:
OperationHandle* Access(PictID pid);
[[nodiscard]] bool IsOperable(PictID pid) const;
[[nodiscard]] bool CheckTranslations(PictID pid);
void Erase(PictID pid) noexcept;
bool CheckOperation(PictID pid);
[[nodiscard]] ops::Signature SignaFor(PictID pid) const;
[[nodiscard]] ops::Call CallFor(PictID pid) const;
bool RunOperation(PictID pid, bool autoDiscard);
bool PrepareParents(PictID pid);
[[nodiscard]] ops::Result CreateNewResult(PictID pid);
[[nodiscard]] std::optional<EntityTranslation> AggregateVersions(PictID pid, ops::Result& opResult) const;
bool SaveOperationResult(PictID pid, const EntityTranslation& old2New, ops::Result&& opResult);
[[nodiscard]] change::Usage CstUsage(EntityUID cstID, PictID pid) const;
void UpdateChild(PictID child, size_t childIndex, const EntityTranslation& oldToNew);
void UpdateTranslations(PictID child, size_t childIndex, const EntityTranslation& oldToNew);
void UpdateOptions(PictID child, size_t childIndex, const EntityTranslation& oldToNew);
};
} // namespace ccl::oss

View File

@ -0,0 +1,79 @@
#pragma once
#include "ccl/oss/Pict.hpp"
#include "ccl/Source.hpp"
#include "ccl/cclMeta.hpp"
namespace ccl::oss {
class OSSchema;
class ossOperationsFacet;
//! OSS source management facet
class ossSourceFacet final : public meta::MutatorFacet<OSSchema> {
friend class OSSchema;
friend class ossOperationsFacet;
public:
std::u8string ossDomain{};
private:
using Container = std::unordered_map<PictID, src::Handle>;
Container sources{};
bool disableImport{ false };
static constexpr bool loadData = true;
public:
~ossSourceFacet() noexcept;
ossSourceFacet(const ossSourceFacet&) = delete;
ossSourceFacet& operator=(const ossSourceFacet&) = delete;
explicit ossSourceFacet(OSSchema& oss);
public:
[[nodiscard]] const src::Handle* operator()(PictID pid) const;
[[nodiscard]] bool IsConnectedWith(const src::Source& src) const;
[[nodiscard]] bool IsAssociatedWith(const src::Descriptor& localDesc) const;
[[nodiscard]] std::optional<PictID> Src2PID(const src::Source& src) const;
[[nodiscard]] src::Source* OpenSrc(PictID pid);
[[nodiscard]] src::Source* ActiveSrc(PictID pid);
bool Rename(PictID pid, const std::u8string& newLocalName);
void Discard(PictID pid);
bool ConnectPict2Src(PictID pid, src::Source& src);
bool ConnectSrc2Pict(PictID pid, src::Source& src);
[[nodiscard]] const src::DataStream* DataFor(PictID pid);
void ReconnectAll();
private:
void Erase(PictID pid);
void CloseAll() noexcept;
bool UpdateSync(PictID pid);
bool InputData(PictID pid, meta::UniqueCPPtr<src::DataStream> data);
bool Import(src::Source& src);
void UpdateOnSrcChange(PictID pid);
void Disconnect(src::Source& src);
bool ForceConnection(PictID pid, src::Source& src, bool importFromSrc);
void ConnectInternal(PictID pid, src::Source& src, bool importFromSrc);
void SyncPict(PictID pid, bool importFromSrc);
void UpdateHashes(PictID pid);
void SyncData(PictID pid, src::Handle& handle, bool importFromSrc);
static void EnableTracking(src::DataStream& data);
[[nodiscard]] static std::optional<src::DataStream::ValueType>
ReadAttribute(src::DataStream::Attribute attr, const src::DataStream& data) noexcept;
static bool WriteAttribute(const src::DataStream::AttrValue& val, src::DataStream& data);
[[nodiscard]] src::Descriptor GlobalDesc(const src::Descriptor& local) const;
};
} // namespace ccl::oss

View File

@ -0,0 +1,31 @@
#pragma once
#include "ccl/semantic/RSConcept.h"
#include "ccl/semantic/TextConcept.h"
namespace ccl::semantic {
//! Constituent of RSCore
class ConceptRecord {
public:
EntityUID uid{};
std::string alias{};
CstType type{ CstType::base };
std::string rs{};
std::string convention{};
lang::LexicalTerm term{};
lang::ManagedText definition{};
public:
explicit ConceptRecord() = default;
explicit ConceptRecord(const RSConcept& rsPart, const TextConcept& textPart);
public:
RSConcept SpawnRS() const;
TextConcept SpawnText() const;
};
} // namespace ccl::semantic

Some files were not shown because too many files have changed in this diff Show More