From f9d498771206b3876e574fcabb665fd80a05eaac Mon Sep 17 00:00:00 2001 From: Farzon Lotfi <1802579+farzonl@users.noreply.github.com> Date: Mon, 22 Aug 2022 12:57:39 -0400 Subject: [PATCH] Feature/binding (#3) * move syntax code to its own directory * fix build system to take objects * Added BoundBinary, converted the evaluator from SyntaxTree, parenthesisnodes remain todo * add operator overloading for valuetype, fix boolean bug in lexer, and add boolean binary expressions * delete evaluator from SyntaxTree, fix tests. update Evaluator to support binary boolean operations * turn paren case back on, fix logical errors, add testcases, and fix nested binaryExpresssion cases --- .clang-tidy | 44 +++++ .github/workflows/clang-format.yml | 22 +++ .github/workflows/cmake-debug.yml | 136 +++++++++++++++ .github/workflows/cmake-release.yml | 148 ++++++++++++++++ .github/workflows/license-check.yml | 25 +++ .gitignore | 5 +- CMakeLists.txt | 11 +- Dockerfile | 8 +- LICENSE | 2 +- README.md | 22 ++- depsCache.json | 5 + fuzz/CMakeLists.txt | 6 +- fuzz/fuzzTest.cpp | 8 +- scripts/license-add.sh | 1 + scripts/license-check.sh | 28 +++ scripts/runGcov.sh | 4 + src/cli/main.cpp | 43 ++++- src/lib/Binding/Binder.cpp | 83 +++++++++ src/lib/Binding/Binder.h | 31 ++++ src/lib/Binding/BoundBinaryExpressionNode.cpp | 104 ++++++++++++ src/lib/Binding/BoundBinaryExpressionNode.h | 78 +++++++++ src/lib/Binding/BoundExpressionNode.h | 19 +++ .../Binding/BoundLiteralExpressionNode.cpp | 18 ++ src/lib/Binding/BoundLiteralExpressionNode.h | 25 +++ src/lib/Binding/BoundUnaryExpressionNode.cpp | 67 ++++++++ src/lib/Binding/BoundUnaryExpressionNode.h | 64 +++++++ src/lib/Binding/CMakeLists.txt | 20 +++ src/lib/Binding/ValueType.cpp | 5 + src/lib/Binding/ValueType.h | 160 ++++++++++++++++++ src/lib/CMakeLists.txt | 47 +++-- src/lib/Evaluator.cpp | 66 ++++++++ src/lib/Evaluator.h | 23 +++ src/lib/ExpressionNode.cpp | 3 - src/lib/ExpressionNode.h | 7 - src/lib/LiteralExpression.cpp | 7 - src/lib/LiteralExpression.h | 11 -- src/lib/{ => Syntax}/BinaryExpressionNode.cpp | 4 + src/lib/{ => Syntax}/BinaryExpressionNode.h | 7 + src/lib/Syntax/CMakeLists.txt | 27 +++ src/lib/Syntax/ExpressionNode.cpp | 7 + src/lib/Syntax/ExpressionNode.h | 13 ++ src/lib/{ => Syntax}/Lexer.cpp | 32 ++-- src/lib/{ => Syntax}/Lexer.h | 8 +- src/lib/Syntax/LiteralExpressionNode.cpp | 12 ++ src/lib/Syntax/LiteralExpressionNode.h | 17 ++ .../ParenthesizedExpressionNode.cpp | 4 + .../ParenthesizedExpressionNode.h | 6 + src/lib/{ => Syntax}/Parser.cpp | 10 +- src/lib/{ => Syntax}/Parser.h | 6 + src/lib/{ => Syntax}/SyntaxNode.cpp | 4 + src/lib/{ => Syntax}/SyntaxNode.h | 6 + src/lib/{ => Syntax}/SyntaxToken.cpp | 10 +- src/lib/Syntax/SyntaxToken.h | 32 ++++ src/lib/Syntax/SyntaxTree.cpp | 43 +++++ src/lib/{ => Syntax}/SyntaxTree.h | 8 +- src/lib/{ => Syntax}/SyntaxType.cpp | 4 + src/lib/{ => Syntax}/SyntaxType.h | 16 ++ src/lib/{ => Syntax}/UnaryExpressionNode.cpp | 4 + src/lib/{ => Syntax}/UnaryExpressionNode.h | 6 + src/lib/SyntaxToken.h | 86 ---------- src/lib/SyntaxTree.cpp | 91 ---------- src/lib/Version/appName.txt | 1 + src/lib/Version/version.txt | 1 + src/lib/WarfCoreStatic.cpp | 6 + test/CMakeLists.txt | 1 - test/test.cpp | 153 +++++++++++++---- 66 files changed, 1671 insertions(+), 310 deletions(-) create mode 100644 .clang-tidy create mode 100644 .github/workflows/clang-format.yml create mode 100644 .github/workflows/cmake-debug.yml create mode 100644 .github/workflows/cmake-release.yml create mode 100644 .github/workflows/license-check.yml create mode 100644 depsCache.json create mode 100755 scripts/license-add.sh create mode 100755 scripts/license-check.sh create mode 100755 scripts/runGcov.sh create mode 100644 src/lib/Binding/Binder.cpp create mode 100644 src/lib/Binding/Binder.h create mode 100644 src/lib/Binding/BoundBinaryExpressionNode.cpp create mode 100644 src/lib/Binding/BoundBinaryExpressionNode.h create mode 100644 src/lib/Binding/BoundExpressionNode.h create mode 100644 src/lib/Binding/BoundLiteralExpressionNode.cpp create mode 100644 src/lib/Binding/BoundLiteralExpressionNode.h create mode 100644 src/lib/Binding/BoundUnaryExpressionNode.cpp create mode 100644 src/lib/Binding/BoundUnaryExpressionNode.h create mode 100644 src/lib/Binding/CMakeLists.txt create mode 100644 src/lib/Binding/ValueType.cpp create mode 100644 src/lib/Binding/ValueType.h create mode 100644 src/lib/Evaluator.cpp create mode 100644 src/lib/Evaluator.h delete mode 100644 src/lib/ExpressionNode.cpp delete mode 100644 src/lib/ExpressionNode.h delete mode 100644 src/lib/LiteralExpression.cpp delete mode 100644 src/lib/LiteralExpression.h rename src/lib/{ => Syntax}/BinaryExpressionNode.cpp (81%) rename src/lib/{ => Syntax}/BinaryExpressionNode.h (74%) create mode 100644 src/lib/Syntax/CMakeLists.txt create mode 100644 src/lib/Syntax/ExpressionNode.cpp create mode 100644 src/lib/Syntax/ExpressionNode.h rename src/lib/{ => Syntax}/Lexer.cpp (89%) rename src/lib/{ => Syntax}/Lexer.h (72%) create mode 100644 src/lib/Syntax/LiteralExpressionNode.cpp create mode 100644 src/lib/Syntax/LiteralExpressionNode.h rename src/lib/{ => Syntax}/ParenthesizedExpressionNode.cpp (78%) rename src/lib/{ => Syntax}/ParenthesizedExpressionNode.h (77%) rename src/lib/{ => Syntax}/Parser.cpp (91%) rename src/lib/{ => Syntax}/Parser.h (83%) rename src/lib/{ => Syntax}/SyntaxNode.cpp (55%) rename src/lib/{ => Syntax}/SyntaxNode.h (65%) rename src/lib/{ => Syntax}/SyntaxToken.cpp (57%) create mode 100644 src/lib/Syntax/SyntaxToken.h create mode 100644 src/lib/Syntax/SyntaxTree.cpp rename src/lib/{ => Syntax}/SyntaxTree.h (78%) rename src/lib/{ => Syntax}/SyntaxType.cpp (86%) rename src/lib/{ => Syntax}/SyntaxType.h (94%) rename src/lib/{ => Syntax}/UnaryExpressionNode.cpp (77%) rename src/lib/{ => Syntax}/UnaryExpressionNode.h (71%) delete mode 100644 src/lib/SyntaxToken.h delete mode 100644 src/lib/SyntaxTree.cpp create mode 100644 src/lib/Version/appName.txt create mode 100644 src/lib/Version/version.txt create mode 100644 src/lib/WarfCoreStatic.cpp diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..c3baf98 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,44 @@ +#clang-tidy copied from chromium +--- + Checks: '-*, + bugprone-argument-comment, + bugprone-dangling-handle, + bugprone-inaccurate-erase, + bugprone-string-constructor, + bugprone-string-integer-assignment, + bugprone-unused-raii, + bugprone-use-after-move, + google-build-explicit-make-pair, + #google-explicit-constructor, + #google-readability-casting, + modernize-avoid-bind, + modernize-concat-nested-namespaces, + #modernize-loop-convert, + #modernize-make-shared, + #modernize-make-unique, + #modernize-redundant-void-arg, + #modernize-replace-random-shuffle, + #modernize-shrink-to-fit, + modernize-use-bool-literals, + #modernize-use-default-member-init, + #modernize-use-emplace, + #modernize-use-equals-default, + #modernize-use-equals-delete, + #modernize-use-noexcept, + #modernize-use-nullptr, + #modernize-use-override, + #modernize-use-transparent-functors, + #readability-redundant-member-init' + CheckOptions: + - key: bugprone-dangling-handle.HandleClasses + value: ::std::basic_string_view;::std::span;::absl::string_view;::base::BasicStringPiece;::base::span + - key: bugprone-string-constructor.StringNames + value: ::std::basic_string;::std::basic_string_view;::base::BasicStringPiece + - key: modernize-use-default-member-init.UseAssignment + value: 1 + # This relaxes modernize-use-emplace in some cases; we might want to make it + # more aggressive in the future. See discussion on + # https://groups.google.com/a/chromium.org/g/cxx/c/noMMTNYiM0w . + - key: modernize-use-emplace.IgnoreImplicitConstructors + value: 1 +... \ No newline at end of file diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml new file mode 100644 index 0000000..92b388c --- /dev/null +++ b/.github/workflows/clang-format.yml @@ -0,0 +1,22 @@ +name: clang-format Check +on: + pull_request: + branches: + - '**' +jobs: + formatting-check: + name: Formatting Check + runs-on: ubuntu-latest + strategy: + matrix: + path: + - 'src' + - 'fuzz' + - 'test' + steps: + - uses: actions/checkout@v2 + - name: Run clang-format style check for C/C++ programs. + uses: jidicula/clang-format-action@v4.8.0 + with: + clang-format-version: '14' + check-path: ${{ matrix.path }} \ No newline at end of file diff --git a/.github/workflows/cmake-debug.yml b/.github/workflows/cmake-debug.yml new file mode 100644 index 0000000..57ac23a --- /dev/null +++ b/.github/workflows/cmake-debug.yml @@ -0,0 +1,136 @@ +name: CMake Debug + +on: + push: + branches: + - master + paths: + - src/** + - test/** + - fuzz/** + - .github/workflows/cmake.yml + pull_request: + branches: + - '**' + paths: + - src/** + - test/** + - fuzz/** + - .github/workflows/cmake.yml +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Debug + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + include: + - os: ubuntu-latest + artifact_dlib_ext: .so + artifact_staticlib_ext: .a + - os: windows-latest + artifact_exec_ext: .exe + artifact_dlib_ext: .dll + artifact_staticlib_ext: .lib + # Note: I wanted to use env.BUILD_TYPE, but it isn't taking + #artifact_out_dir: ${{ BUILD_TYPE }}/ + artifact_out_dir: Debug/ + artifact_os_name: Windows + artifact_arch: x86_64 + - os: macos-latest + artifact_dlib_ext: .dylib + artifact_staticlib_ext: .a + steps: + - uses: actions/checkout@v2 + - name: Create Build Environment + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{github.workspace}}/build + + - name: Cache C++ dependencies in Packages Directory + uses: actions/cache@v3 + with: + path: | + packages + key: ${{ runner.OS }}-c++-packages-cache-Debug-${{ hashFiles('depsCache.json') }} + restore-keys: | + ${{ runner.OS }}-c++-packages-cache-Debug- + + - name: Configure CMake + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash + working-directory: ${{github.workspace}}/build + # Note the current convention is to use the -S and -B options here to specify source + # and build directories, but this is only available with CMake 3.13 and higher. + # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE + + - name: Build + working-directory: ${{github.workspace}}/build + shell: bash + # Execute the build. You can specify a specific target with "--target " + run: cmake --build . --config $BUILD_TYPE + - name: Set variables (Mac\Linux) + if: matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest' + run: | + APP=$(cat $GITHUB_WORKSPACE/src/lib/Version/appName.txt) + VER=$(cat $GITHUB_WORKSPACE/src/lib/Version/version.txt) + echo "VERSION=$VER" >> $GITHUB_ENV + echo "APPNAME=$APP" >> $GITHUB_ENV + - name: Set variables (Windows) + if: matrix.os == 'windows-latest' + run: | + $APP = type .\src\lib\Version\appName.txt + $VER = type .\src\lib\Version\version.txt + echo "VERSION=$VER" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + echo "APPNAME=$APP" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + - name: Test Unix-like + if: matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest' + working-directory: ${{github.workspace}}/build + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ./test/${{ env.APPNAME }}Lang_TEST + + - name: Test Windows + if: matrix.os == 'windows-latest' + working-directory: ${{github.workspace}}/build + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ./test/${{ matrix.artifact_out_dir}}${{ env.APPNAME }}Lang_TEST${{ matrix.artifact_exec_ext }} + + #- name: Linux Code Coverage + # if: matrix.os == 'ubuntu-latest' + # working-directory: ${{github.workspace}} + # shell: bash + # # Execute tests defined by the CMake configuration. + # # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + # run: ./scripts/runGcov.sh + - name: Prepare Binaries for upload (Mac\Linux) + if: matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest' + shell: bash + run: | + mkdir ${{github.workspace}}/artifacts + cp build/src/lib/lib${{ env.APPNAME }}Core${{ matrix.artifact_staticlib_ext }} ${{github.workspace}}/artifacts + cp build/src/cli/${{ env.APPNAME }} ${{github.workspace}}/artifacts + pushd ${{github.workspace}} + zip -r ${{ env.APPNAME }}-$(uname -s)-$(uname -m).zip artifacts + popd + - name: Prepare Binaries for upload (windows) + if: matrix.os == 'windows-latest' + shell: powershell + run: | + [system.io.directory]::CreateDirectory("${{github.workspace}}/artifacts") + Copy-Item "build/src/lib/${{ matrix.artifact_out_dir}}${{ env.APPNAME }}Core${{ matrix.artifact_staticlib_ext }}" -Destination "${{github.workspace}}/artifacts" + Copy-Item "build/src/cli/${{ matrix.artifact_out_dir }}${{ env.APPNAME }}${{ matrix.artifact_exec_ext }}" -Destination "${{github.workspace}}/artifacts" + Compress-Archive -Path ${{github.workspace}}/artifacts/* -DestinationPath ${{ env.APPNAME }}-${{matrix.artifact_os_name}}-${{matrix.artifact_arch}}.zip + - name: 'Upload Pull Request Artifact' + uses: actions/upload-artifact@v3 + if: startsWith(github.ref, 'refs/pull/') + with: + name: ${{ env.APPNAME }} Pull Request Artifacts + path: ${{ env.APPNAME }}-*.zip + retention-days: 5 diff --git a/.github/workflows/cmake-release.yml b/.github/workflows/cmake-release.yml new file mode 100644 index 0000000..c218ed3 --- /dev/null +++ b/.github/workflows/cmake-release.yml @@ -0,0 +1,148 @@ +name: CMake Release + +on: + push: + branches: + - master + paths: + - src/** + - test/** + - fuzz/** + - .github/workflows/cmake.yml + pull_request: + branches: + - '**' + paths: + - src/** + - test/** + - fuzz/** + - .github/workflows/cmake.yml +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + include: + - os: ubuntu-latest + artifact_dlib_ext: .so + artifact_staticlib_ext: .a + - os: windows-latest + artifact_exec_ext: .exe + artifact_dlib_ext: .dll + artifact_staticlib_ext: .lib + # Note: I wanted to use env.BUILD_TYPE, but it isn't taking + #artifact_out_dir: ${{ BUILD_TYPE }}/ + artifact_out_dir: Release/ + artifact_os_name: Windows + artifact_arch: x86_64 + - os: macos-latest + artifact_dlib_ext: .dylib + artifact_staticlib_ext: .a + steps: + - uses: actions/checkout@v2 + # - run: | + # sudo apt install clang-14 lldb-14 lld-14 libfuzzer-14-dev + # if: matrix.os == 'ubuntu-latest' + # - run: brew install llvm + # if: matrix.os == 'macOS-latest' + - name: Create Build Environment + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{github.workspace}}/build + + - name: Cache C++ dependencies in Packages Directory + uses: actions/cache@v3 + with: + path: | + packages + key: ${{ runner.OS }}-c++-packages-cache-Release-${{ hashFiles('depsCache.json') }} + restore-keys: | + ${{ runner.OS }}-c++-packages-cache-Release- + + - name: Configure CMake + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash + working-directory: ${{github.workspace}}/build + # Note the current convention is to use the -S and -B options here to specify source + # and build directories, but this is only available with CMake 3.13 and higher. + # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE + - name: Build + working-directory: ${{github.workspace}}/build + shell: bash + # Execute the build. You can specify a specific target with "--target " + run: cmake --build . --config $BUILD_TYPE + - name: Set variables (Mac\Linux) + if: matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest' + run: | + APP=$(cat $GITHUB_WORKSPACE/src/lib/Version/appName.txt) + VER=$(cat $GITHUB_WORKSPACE/src/lib/Version/version.txt) + echo "VERSION=$VER" >> $GITHUB_ENV + echo "APPNAME=$APP" >> $GITHUB_ENV + - name: Set variables (Windows) + if: matrix.os == 'windows-latest' + run: | + $APP = type .\src\lib\Version\appName.txt + $VER = type .\src\lib\Version\version.txt + echo "VERSION=$VER" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + echo "APPNAME=$APP" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + - name: Test Unix-like + if: matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest' + working-directory: ${{github.workspace}}/build + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ./test/${{ env.APPNAME }}Lang_TEST + + - name: Test Windows + if: matrix.os == 'windows-latest' + working-directory: ${{github.workspace}}/build + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ./test/${{ matrix.artifact_out_dir}}${{ env.APPNAME }}Lang_TEST${{ matrix.artifact_exec_ext }} + + #- name: Linux Code Coverage + # if: matrix.os == 'ubuntu-latest' + # working-directory: ${{github.workspace}} + # shell: bash + # # Execute tests defined by the CMake configuration. + # # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + # run: ./scripts/runGcov.sh + - name: Prepare Binaries for upload (Mac\Linux) + if: matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest' + shell: bash + run: | + mkdir ${{github.workspace}}/artifacts + cp build/src/lib/lib${{ env.APPNAME }}Core${{ matrix.artifact_staticlib_ext }} ${{github.workspace}}/artifacts + cp build/src/cli/${{ env.APPNAME }} ${{github.workspace}}/artifacts + + pushd ${{github.workspace}} + zip -r ${{ env.APPNAME }}-$(uname -s)-$(uname -m).zip artifacts + popd + - name: Prepare Binaries for upload (windows) + if: matrix.os == 'windows-latest' + shell: powershell + run: | + [system.io.directory]::CreateDirectory("${{github.workspace}}/artifacts") + Copy-Item "build/src/lib/${{ matrix.artifact_out_dir}}${{ env.APPNAME }}Core${{ matrix.artifact_staticlib_ext }}" -Destination "${{github.workspace}}/artifacts" + Copy-Item "build/src/cli/${{ matrix.artifact_out_dir }}${{ env.APPNAME }}${{ matrix.artifact_exec_ext }}" -Destination "${{github.workspace}}/artifacts" + Compress-Archive -Path ${{github.workspace}}/artifacts/* -DestinationPath ${{ env.APPNAME }}-${{matrix.artifact_os_name}}-${{matrix.artifact_arch}}.zip + - name: 'Upload Pull Request Artifact' + uses: actions/upload-artifact@v3 + if: startsWith(github.ref, 'refs/pull/') + with: + name: ${{ env.APPNAME }} Pull Request Artifacts + path: ${{ env.APPNAME }}-*.zip + retention-days: 5 + - name: Upload binaries to Release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/heads/master') + with: + tag_name: ${{ env.APPNAME }}-${{ env.VERSION }} + files: | + ${{ env.APPNAME }}-*.zip diff --git a/.github/workflows/license-check.yml b/.github/workflows/license-check.yml new file mode 100644 index 0000000..b68c20b --- /dev/null +++ b/.github/workflows/license-check.yml @@ -0,0 +1,25 @@ +name: License Check + +on: + push: + branches: + - master + pull_request: + branches: + - '**' + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: true + steps: + - uses: actions/checkout@v2 + - + name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.19 + - run: go install github.com/google/addlicense@latest + - name: Run license check + run: ${{github.workspace}}/scripts/license-check.sh diff --git a/.gitignore b/.gitignore index 302518d..a8f2740 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,7 @@ build/ packages/ # MacOS -.DS_Store \ No newline at end of file +.DS_Store + +# vs code +.vscode/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index ee2d1bd..666a9bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,10 +12,15 @@ endif() endfunction(download_file) -cmake_minimum_required(VERSION 3.10) - +cmake_minimum_required(VERSION 3.22) set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) add_subdirectory (src) -add_subdirectory (fuzz) add_subdirectory (test) + +include(CheckCXXCompilerFlag) +CHECK_CXX_COMPILER_FLAG("-fsanitize=address,fuzzer" HAVE_SANITIZE_FUZZER) +if(HAVE_SANITIZE_FUZZER) + add_subdirectory (fuzz) +endif() diff --git a/Dockerfile b/Dockerfile index 04ba7fe..5aa978c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM ubuntu:22.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update \ @@ -11,9 +11,9 @@ WORKDIR root COPY . . -#RUN cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-coverage" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -#RUN make -C./build +RUN cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-coverage" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ +RUN make -C./build # -#RUN ./build/test/WarfLang_TEST +RUN ./build/test/WarfLang_TEST # #RUN ./build/fuzz/WarfLang_FUZZ \ No newline at end of file diff --git a/LICENSE b/LICENSE index ecfe836..fe7149b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2021, F. Lotfi & D. Kane +Copyright (c) 2021, F. Lotfi All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index eb5e87b..56a9a66 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,29 @@ ninja -C./build - Using MSBUILD ```powershell cmake -B build -msbuild build\exp-tree.sln -t:Build -p:Configuration=Release +msbuild build\WarfLang.sln -t:Build -p:Configuration=Release ``` +## Setup Clang Tidy +### On MacOS +```bash +ln -s "$(brew --prefix llvm)/share/clang/run-clang-tidy.py" "/usr/local/bin/run-clang-tidy.py" +ln -s "$(brew --prefix llvm)/bin/clang-apply-replacements" "/usr/local/bin/clang-apply-replacements" +ln -s "$(brew --prefix llvm)/share/clang/run-clang-tidy.py" "/usr/local/bin/run-clang-tidy.py" +``` + +## Building the fuzzer +### On MacOS +```bash +cmake -G Ninja -B build -DCMAKE_C_COMPILER=$(brew --prefix llvm)/bin/clang \ + -DCMAKE_CXX_COMPILER=$(brew --prefix llvm)/bin/clang++ +``` + +### Run Clang Tidy +After running cmake, the build dir will have a `compile_commands.json` file. Thats all you need to run `run-clang-tidy.py`. +```bash +run-clang-tidy.py -p build/ -header-filter='.*' -fix -format +``` ## Docker Build & Run - build: `docker build -t warflang:latest .` - run: `docker run --name test_vm -it warflang:latest` \ No newline at end of file diff --git a/depsCache.json b/depsCache.json new file mode 100644 index 0000000..c578a87 --- /dev/null +++ b/depsCache.json @@ -0,0 +1,5 @@ +{ + "doctest" : { + "Src" : "1494db8a28035bcf6caeea817c4b2b65" + } +} \ No newline at end of file diff --git a/fuzz/CMakeLists.txt b/fuzz/CMakeLists.txt index 487ec57..735ebd0 100644 --- a/fuzz/CMakeLists.txt +++ b/fuzz/CMakeLists.txt @@ -1,5 +1,4 @@ project(${CMAKE_PROJECT_NAME}_FUZZ) -cmake_minimum_required(VERSION 3.10) set(CMAKE_C_COMPILER:FILEPATH /usr/local/opt/llvm/bin/clang) set(CMAKE_CXX_COMPILER:FILEPATH /usr/local/opt/llvm/bin/clang++) @@ -7,6 +6,11 @@ set(CMAKE_CXX_COMPILER:FILEPATH /usr/local/opt/llvm/bin/clang++) #/usr/local/opt/llvm/lib/clang/12.0.1/lib/darwin/libclang_rt.fuzzer_osx.a #/usr/local/opt/llvm/lib/clang/12.0.1/lib/darwin/libclang_rt.asan_osx_dynamic.dylib +#/usr/local/opt/llvm/lib/clang/13.0.1/lib/darwin/libclang_rt.asan_osx_dynamic.dylib + +#/usr/local/opt/llvm/lib/clang/14.0.6/lib/darwin/libclang_rt.fuzzer_osx.a +#/usr/local/opt/llvm/lib/clang/14.0.6/lib/darwin/libclang_rt.asan_osx_dynamic.dylib + include_directories(${CMAKE_SOURCE_DIR}/src/lib) add_executable(${PROJECT_NAME} fuzzTest.cpp) diff --git a/fuzz/fuzzTest.cpp b/fuzz/fuzzTest.cpp index 78ddc13..eb3f9ba 100644 --- a/fuzz/fuzzTest.cpp +++ b/fuzz/fuzzTest.cpp @@ -1,5 +1,9 @@ -#include "Lexer.h" -#include "Parser.h" +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "Syntax/Lexer.h" +#include "Syntax/Parser.h" void LexerFuzzTest(std::string &line) { Lexer lex(line); diff --git a/scripts/license-add.sh b/scripts/license-add.sh new file mode 100755 index 0000000..620ea98 --- /dev/null +++ b/scripts/license-add.sh @@ -0,0 +1 @@ +find src test fuzz \( -name \*.h -o -name \*.cpp \) -exec ~/go/bin/addlicense -c "F. Lotfi" -l bsd {} \; \ No newline at end of file diff --git a/scripts/license-check.sh b/scripts/license-check.sh new file mode 100755 index 0000000..7c922de --- /dev/null +++ b/scripts/license-check.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# sudo apt-get install golang or brew install golang +# go get -u github.com/google/addlicense + + +exit_if_error() { + local exit_code=$1 + shift + [[ $exit_code ]] && # do nothing if no error code passed + ((exit_code != 0)) && { # do nothing if error code is 0 + printf 'ERROR: %s\n' "$@" >&2 # we can use better logging here + exit "$exit_code" # we could also check to make sure + # error code is numeric when passed + } +} + +~/go/bin/addlicense -check src +exit_if_error $? "license check failed in src." + +~/go/bin/addlicense -check test +exit_if_error $? "license check failed in test." + +~/go/bin/addlicense -check fuzz +exit_if_error $? "license check failed in fuzz." + +echo "license check successfully exited!" +exit 0 \ No newline at end of file diff --git a/scripts/runGcov.sh b/scripts/runGcov.sh new file mode 100755 index 0000000..6df8bf1 --- /dev/null +++ b/scripts/runGcov.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export CODECOV_TOKEN="" +find src test \( -name \*.h -o -name \*.cpp \) -exec gcov -style=file -i build/{} \; +bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/src/cli/main.cpp b/src/cli/main.cpp index df01372..40f56e1 100644 --- a/src/cli/main.cpp +++ b/src/cli/main.cpp @@ -1,22 +1,49 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. -#include "SyntaxTree.h" - +#include "Binding/Binder.h" +#include "Evaluator.h" +#include "Syntax/SyntaxTree.h" int main(int argc, char **argv) { std::cout << "warfLang 1.0" << std::endl; + bool showTree = false; while (true) { try { std::string line = ""; std::cout << ">>> "; std::getline(std::cin, line); - + if (line == "#showTree") { + showTree = !showTree; + std::cout << (showTree ? "Showing parse trees." + : "Not showing parse trees") + << std::endl; + continue; + } auto syntaxTree = SyntaxTree::Parse(line); - syntaxTree->PrintTree(); - for (auto error : syntaxTree->Errors()) { - std::cerr << error << std::endl; + auto binder = std::make_unique(); + auto boundExpression = binder->BindExpression(syntaxTree->Root()); + + if (showTree) { + syntaxTree->PrintTree(); } - if (syntaxTree->Errors().size() == 0) { - std::cout << syntaxTree->Evaluate() << std::endl; + + if (binder->Diagnostics().empty()) { + auto eval = std::make_unique(std::move(boundExpression)); + if (eval->Errors().size() == 0) { + Value result = eval->Evaluate(); + std::cout << result << std::endl; + } else { + for (auto error : eval->Errors()) { + std::cerr << error << std::endl; + } + } + } else { + for (auto error : binder->Diagnostics()) { + std::cerr << error << std::endl; + } } + } catch (std::runtime_error &error) { std::cerr << error.what() << std::endl; std::cin.get(); diff --git a/src/lib/Binding/Binder.cpp b/src/lib/Binding/Binder.cpp new file mode 100644 index 0000000..43babd1 --- /dev/null +++ b/src/lib/Binding/Binder.cpp @@ -0,0 +1,83 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "Binder.h" +#include "BoundBinaryExpressionNode.h" +#include "BoundLiteralExpressionNode.h" +#include "BoundUnaryExpressionNode.h" +#include "Syntax/BinaryExpressionNode.h" +#include "Syntax/LiteralExpressionNode.h" +#include "Syntax/ParenthesizedExpressionNode.h" +#include "Syntax/UnaryExpressionNode.h" + +#include + +std::unique_ptr +Binder::BindExpression(ExpressionNode *node) { + + if (LiteralExpressionNode *literal = + dynamic_cast(node)) { + return std::move(BindLiteralExpression(literal)); + } + if (UnaryExpressionNode *unaryExpression = + dynamic_cast(node)) { + return std::move(BindUnaryExpression(unaryExpression)); + } + if (BinaryExpressionNode *binaryExpression = + dynamic_cast(node)) { + return std::move(BindBinaryExpression(binaryExpression)); + } + if (ParenthesizedExpressionNode *parenthesizedExpression = + dynamic_cast(node)) { + return std::move(BindExpression(parenthesizedExpression->Expression())); + } + std::stringstream diagmsg; + diagmsg << "Unexpected syntax " << SyntaxTokenToStrMap.at(node->Type()); + mDiagnostics.push_back(diagmsg.str()); + throw diagmsg.str(); + return nullptr; +} + +std::unique_ptr +Binder::BindLiteralExpression(LiteralExpressionNode *literal) { + return std::make_unique(literal); +} + +std::unique_ptr +Binder::BindUnaryExpression(UnaryExpressionNode *unary) { + auto boundOperand = BindExpression(unary->Operand()); + const std::shared_ptr boundOperator = + BoundUnaryOperator::Bind(unary->Operator()->Type(), + boundOperand->GetType()); + if (boundOperator == BoundUnaryOperator::GetBindFailure()) { + + std::stringstream diagmsg; + diagmsg << "Unary operator " + << SyntaxTokenToStrMap.at(unary->Operator()->Type()) + << " is not defined for type " << boundOperand->GetType() << "."; + mDiagnostics.push_back(diagmsg.str()); + return boundOperand; + } + return std::make_unique(boundOperator, + std::move(boundOperand)); +} + +std::unique_ptr +Binder::BindBinaryExpression(BinaryExpressionNode *binary) { + auto boundLeft = BindExpression(binary->Left()); + auto boundRight = BindExpression(binary->Right()); + const std::shared_ptr boundOperator = + BoundBinaryOperator::Bind(binary->Operator()->Type(), + boundLeft->GetType(), boundRight->GetType()); + if (boundOperator == BoundBinaryOperator::GetBindFailure()) { + std::stringstream diagmsg; + diagmsg << "Binary operator " << binary->Operator()->Text() + << " is not defined for types " << boundLeft->GetType() << " and " + << boundRight->GetType() << "." << std::endl; + mDiagnostics.push_back(diagmsg.str()); + return boundLeft; + } + return std::make_unique( + std::move(boundLeft), boundOperator, std::move(boundRight)); +} diff --git a/src/lib/Binding/Binder.h b/src/lib/Binding/Binder.h new file mode 100644 index 0000000..9b819e7 --- /dev/null +++ b/src/lib/Binding/Binder.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +#pragma once + +#include + +#include "BoundExpressionNode.h" +#include "Syntax/ExpressionNode.h" + +class LiteralExpressionNode; +class UnaryExpressionNode; +class BinaryExpressionNode; + +class Binder { +public: + std::unique_ptr BindExpression(ExpressionNode *syntax); + const std::vector &Diagnostics() const { return mDiagnostics; } + +private: + std::vector mDiagnostics; + std::unique_ptr + BindLiteralExpression(LiteralExpressionNode *literal); + std::unique_ptr + BindUnaryExpression(UnaryExpressionNode *unary); + std::unique_ptr + BindBinaryExpression(BinaryExpressionNode *binary); +}; \ No newline at end of file diff --git a/src/lib/Binding/BoundBinaryExpressionNode.cpp b/src/lib/Binding/BoundBinaryExpressionNode.cpp new file mode 100644 index 0000000..8f2861c --- /dev/null +++ b/src/lib/Binding/BoundBinaryExpressionNode.cpp @@ -0,0 +1,104 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "BoundBinaryExpressionNode.h" + +const std::shared_ptr BoundBinaryOperator::sOperators[] = { + std::make_shared(SyntaxType::UnknownToken, + BoundBinaryOperatorType::Addition, + Type::Unknown), + std::make_shared( + SyntaxType::PlusToken, BoundBinaryOperatorType::Addition, Type::Number), + std::make_shared(SyntaxType::MinusToken, + BoundBinaryOperatorType::Subtraction, + Type::Number), + std::make_shared( + SyntaxType::StarToken, BoundBinaryOperatorType::Multiplication, + Type::Number), + std::make_shared(SyntaxType::SlashToken, + BoundBinaryOperatorType::Division, + Type::Number), + std::make_shared( + SyntaxType::EqualsEqualsToken, BoundBinaryOperatorType::Equals, + Type::Number, Type::Number, Type::Boolean), + std::make_shared( + SyntaxType::EqualsEqualsToken, BoundBinaryOperatorType::Equals, + Type::Boolean, Type::Boolean, Type::Boolean), + std::make_shared( + SyntaxType::BangEqualsToken, BoundBinaryOperatorType::NotEquals, + Type::Number, Type::Number, Type::Boolean), + std::make_shared( + SyntaxType::BangEqualsToken, BoundBinaryOperatorType::NotEquals, + Type::Boolean, Type::Boolean, Type::Boolean), + std::make_shared(SyntaxType::AmpersandAmpersandToken, + BoundBinaryOperatorType::LogicalAnd, + Type::Boolean), + std::make_shared(SyntaxType::PipePipeToken, + BoundBinaryOperatorType::LogicalOr, + Type::Boolean), +}; + +BoundBinaryOperator::BoundBinaryOperator(SyntaxType syntaxType, + BoundBinaryOperatorType boundType, + Type type) + : mSyntaxType(syntaxType), mBoundType(boundType), mLeftOperandType(type), + mRightOperandType(type), mEvalType(type) {} + +BoundBinaryOperator::BoundBinaryOperator(SyntaxType syntaxType, + BoundBinaryOperatorType boundType, + Type leftOperandType, + Type rightOperandType, Type evalType) + : mSyntaxType(syntaxType), mBoundType(boundType), + mLeftOperandType(leftOperandType), mRightOperandType(rightOperandType), + mEvalType(evalType) {} + +const std::shared_ptr +BoundBinaryOperator::GetBindFailure() { + return sOperators[0]; +} + +const std::shared_ptr +BoundBinaryOperator::Bind(SyntaxType syntaxType, Type leftOperandType, + Type rightOperandType) { + for (std::shared_ptr op : + BoundBinaryOperator::sOperators) { + if (op->GetSyntaxType() == syntaxType && + op->LeftOperandType() == leftOperandType && + op->RightOperandType() == rightOperandType) { + return op; + } + } + std::cerr << "Unexpected binary operator: " << syntaxType << std::endl; + return GetBindFailure(); +} + +SyntaxType BoundBinaryOperator::GetSyntaxType() { return mSyntaxType; } + +BoundBinaryOperatorType BoundBinaryOperator::BoundType() { return mBoundType; } +Type BoundBinaryOperator::LeftOperandType() { return mLeftOperandType; } + +Type BoundBinaryOperator::RightOperandType() { return mRightOperandType; } + +Type BoundBinaryOperator::GetType() { return mEvalType; } + +BoundBinaryExpressionNode::BoundBinaryExpressionNode( + std::unique_ptr left, + const std::shared_ptr op, + std::unique_ptr right) + : BoundExpressionNode(), mLeft(std::move(left)), mOperator(op), + mRight(std::move(right)) {} + +BoundNodeType BoundBinaryExpressionNode::NodeType() { + return mLeft->NodeType(); +} + +Type BoundBinaryExpressionNode::GetType() { return mOperator->GetType(); } + +BoundExpressionNode *BoundBinaryExpressionNode::Left() { return mLeft.get(); } + +BoundExpressionNode *BoundBinaryExpressionNode::Right() { return mRight.get(); } + +BoundBinaryOperatorType BoundBinaryExpressionNode::OperatorType() { + return mOperator->BoundType(); +} \ No newline at end of file diff --git a/src/lib/Binding/BoundBinaryExpressionNode.h b/src/lib/Binding/BoundBinaryExpressionNode.h new file mode 100644 index 0000000..5ca4b2c --- /dev/null +++ b/src/lib/Binding/BoundBinaryExpressionNode.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +#pragma once +#include "BoundExpressionNode.h" +#include "Syntax/SyntaxType.h" +#include + +enum class BoundBinaryOperatorType { + Addition, + Subtraction, + Multiplication, + Division, + Equals, + NotEquals, + LogicalAnd, + LogicalOr +}; + +static const std::unordered_map + BoundBinaryTypeStrMap = { + {BoundBinaryOperatorType::Addition, "Addition"}, + {BoundBinaryOperatorType::Subtraction, "Subtraction"}, + {BoundBinaryOperatorType::Multiplication, "Multiplication"}, + {BoundBinaryOperatorType::Division, "Division"}, + {BoundBinaryOperatorType::Equals, "Equals"}, + {BoundBinaryOperatorType::NotEquals, "NotEquals"}, + {BoundBinaryOperatorType::LogicalAnd, "LogicalAnd"}, + {BoundBinaryOperatorType::LogicalOr, "LogicalOr"}, +}; + +class BoundBinaryOperator { +public: + static const std::shared_ptr + Bind(SyntaxType syntaxType, Type leftOperandType, Type rightOperandType); + SyntaxType GetSyntaxType(); + BoundBinaryOperatorType BoundType(); + Type LeftOperandType(); // expected Type + Type RightOperandType(); // expected Type + Type GetType(); // resulting Type + static const std::shared_ptr GetBindFailure(); + // TODO see if there is a way to make shared_ptr a friend function + BoundBinaryOperator(SyntaxType syntaxType, BoundBinaryOperatorType boundType, + Type type); + BoundBinaryOperator(SyntaxType syntaxType, BoundBinaryOperatorType boundType, + Type leftOperandType, Type rightOperandType, + Type evalType); + +private: + SyntaxType mSyntaxType; + BoundBinaryOperatorType mBoundType; + Type mLeftOperandType; + Type mRightOperandType; + Type mEvalType; + static const std::shared_ptr sOperators[]; + BoundBinaryOperator() = delete; + friend class BoundBinaryExpressionNode; +}; + +class BoundBinaryExpressionNode : public BoundExpressionNode { +public: + BoundBinaryExpressionNode(std::unique_ptr left, + const std::shared_ptr op, + std::unique_ptr right); + virtual BoundNodeType NodeType() override; + virtual Type GetType() override; + BoundExpressionNode *Left(); + BoundExpressionNode *Right(); + BoundBinaryOperatorType OperatorType(); + +private: + std::unique_ptr mLeft; + std::unique_ptr mRight; + const std::shared_ptr mOperator; +}; \ No newline at end of file diff --git a/src/lib/Binding/BoundExpressionNode.h b/src/lib/Binding/BoundExpressionNode.h new file mode 100644 index 0000000..cecb709 --- /dev/null +++ b/src/lib/Binding/BoundExpressionNode.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +#pragma once + +#include "ValueType.h" + +enum class BoundNodeType { Unknown, UnaryExpression, LiteralExpression }; + +class BoundExpressionNode { +public: + BoundExpressionNode() = default; + virtual BoundNodeType NodeType() = 0; + virtual Type GetType() = 0; + virtual ~BoundExpressionNode() {} +}; \ No newline at end of file diff --git a/src/lib/Binding/BoundLiteralExpressionNode.cpp b/src/lib/Binding/BoundLiteralExpressionNode.cpp new file mode 100644 index 0000000..c82d635 --- /dev/null +++ b/src/lib/Binding/BoundLiteralExpressionNode.cpp @@ -0,0 +1,18 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "BoundLiteralExpressionNode.h" +#include "ValueType.h" + +BoundLiteralExpressionNode::BoundLiteralExpressionNode( + LiteralExpressionNode *literal) + : mValue(const_cast(literal->LiteralToken()->GetValue())) {} + +BoundNodeType BoundLiteralExpressionNode::NodeType() { + return BoundNodeType::LiteralExpression; +} + +Type BoundLiteralExpressionNode::GetType() { return mValue.GetType(); } + +Value BoundLiteralExpressionNode::GetValue() { return mValue; } \ No newline at end of file diff --git a/src/lib/Binding/BoundLiteralExpressionNode.h b/src/lib/Binding/BoundLiteralExpressionNode.h new file mode 100644 index 0000000..3a94605 --- /dev/null +++ b/src/lib/Binding/BoundLiteralExpressionNode.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +#pragma once + +#include + +#include "BoundExpressionNode.h" +#include "Syntax/LiteralExpressionNode.h" +#include "Syntax/SyntaxType.h" + +class BoundLiteralExpressionNode : public BoundExpressionNode { +public: + BoundLiteralExpressionNode(LiteralExpressionNode *literal); + // Value GetValue(); + virtual BoundNodeType NodeType() override; + virtual Type GetType() override; + Value GetValue(); + +private: + Value mValue; +}; \ No newline at end of file diff --git a/src/lib/Binding/BoundUnaryExpressionNode.cpp b/src/lib/Binding/BoundUnaryExpressionNode.cpp new file mode 100644 index 0000000..4a23422 --- /dev/null +++ b/src/lib/Binding/BoundUnaryExpressionNode.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "BoundUnaryExpressionNode.h" + +const std::shared_ptr BoundUnaryOperator::sOperators[] = { + std::make_shared(SyntaxType::UnknownToken, + BoundUnaryOperatorType::Identity, + Type::Unknown), + std::make_shared( + SyntaxType::BangToken, BoundUnaryOperatorType::LogicalNegation, + Type::Boolean), + std::make_shared( + SyntaxType::MinusToken, BoundUnaryOperatorType::Negation, Type::Number), + std::make_shared( + SyntaxType::PlusToken, BoundUnaryOperatorType::Identity, Type::Number), +}; + +BoundUnaryOperator::BoundUnaryOperator(SyntaxType syntaxType, + BoundUnaryOperatorType unaryType, + Type operandValueType) + : mSyntaxType(syntaxType), mUnaryType(unaryType), + mOperandType(operandValueType) {} + +const std::shared_ptr BoundUnaryOperator::GetBindFailure() { + return sOperators[0]; +} + +const std::shared_ptr +BoundUnaryOperator::Bind(SyntaxType syntaxType, Type operandType) { + for (std::shared_ptr op : + BoundUnaryOperator::sOperators) { + if (op->GetSyntaxType() == syntaxType && op->OperandType() == operandType) { + return op; + } + } + std::cerr << "Unexpected unary operator: " << syntaxType << std::endl; + return GetBindFailure(); +} + +SyntaxType BoundUnaryOperator::GetSyntaxType() { return mSyntaxType; } + +Type BoundUnaryOperator::OperandType() { return mOperandType; } + +Type BoundUnaryOperator::EvalType() { return mEvalType; } + +BoundUnaryOperatorType BoundUnaryOperator::UnaryType() { return mUnaryType; } + +BoundUnaryExpressionNode::BoundUnaryExpressionNode( + const std::shared_ptr op, + std::unique_ptr operand) + : BoundExpressionNode(), mOperator(op), mOperand(std::move(operand)) {} + +BoundNodeType BoundUnaryExpressionNode::NodeType() { + return BoundNodeType::UnaryExpression; +} + +BoundExpressionNode *BoundUnaryExpressionNode::Operand() { + return mOperand.get(); +} + +Type BoundUnaryExpressionNode::GetType() { return mOperand->GetType(); } + +BoundUnaryOperatorType BoundUnaryExpressionNode::OperatorType() { + return mOperator->UnaryType(); +} diff --git a/src/lib/Binding/BoundUnaryExpressionNode.h b/src/lib/Binding/BoundUnaryExpressionNode.h new file mode 100644 index 0000000..a40a3d2 --- /dev/null +++ b/src/lib/Binding/BoundUnaryExpressionNode.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +#pragma once + +#include + +#include "BoundExpressionNode.h" +#include "Syntax/SyntaxType.h" +#include "ValueType.h" + +enum class BoundUnaryOperatorType { + Identity, + Negation, + LogicalNegation, +}; + +static const std::unordered_map + BoundUnaryTypeStrMap = { + {BoundUnaryOperatorType::Identity, "Identity"}, + {BoundUnaryOperatorType::Negation, "Negation"}, + {BoundUnaryOperatorType::LogicalNegation, "LogicalNegation"}, +}; + +class BoundUnaryOperator { +public: + static const std::shared_ptr Bind(SyntaxType syntaxType, + Type operandType); + SyntaxType GetSyntaxType(); + BoundUnaryOperatorType UnaryType(); + Type OperandType(); // expected Type + Type EvalType(); // resulting Type + static const std::shared_ptr GetBindFailure(); + BoundUnaryOperator(SyntaxType syntaxType, BoundUnaryOperatorType unaryType, + Type operandValueType); + +private: + SyntaxType mSyntaxType; + BoundUnaryOperatorType mUnaryType; + Type mOperandType; + Type mEvalType; + + static const std::shared_ptr sOperators[]; + BoundUnaryOperator() = delete; + friend class BoundUnaryExpressionNode; +}; + +class BoundUnaryExpressionNode : public BoundExpressionNode { +public: + BoundUnaryExpressionNode(const std::shared_ptr op, + std::unique_ptr operand); + virtual BoundNodeType NodeType() override; + virtual Type GetType() override; + BoundExpressionNode *Operand(); + BoundUnaryOperatorType OperatorType(); + virtual ~BoundUnaryExpressionNode() {} + +private: + const std::shared_ptr mOperator; + std::unique_ptr mOperand; +}; \ No newline at end of file diff --git a/src/lib/Binding/CMakeLists.txt b/src/lib/Binding/CMakeLists.txt new file mode 100644 index 0000000..e6c2683 --- /dev/null +++ b/src/lib/Binding/CMakeLists.txt @@ -0,0 +1,20 @@ +include_directories(${WARF_CORE_SOURCE_DIR}) + +set(SourceFiles + Binder.cpp + BoundBinaryExpressionNode.cpp + BoundLiteralExpressionNode.cpp + BoundUnaryExpressionNode.cpp + ValueType.cpp + +) +set(HeaderFiles + Binder.h + BoundBinaryExpressionNode.h + BoundExpressionNode.h + BoundLiteralExpressionNode.h + BoundUnaryExpressionNode.h + ValueType.h +) + +add_library (WarfCore.Binding OBJECT ${SourceFiles} ${HeaderFiles}) \ No newline at end of file diff --git a/src/lib/Binding/ValueType.cpp b/src/lib/Binding/ValueType.cpp new file mode 100644 index 0000000..6ff5aef --- /dev/null +++ b/src/lib/Binding/ValueType.cpp @@ -0,0 +1,5 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "ValueType.h" \ No newline at end of file diff --git a/src/lib/Binding/ValueType.h b/src/lib/Binding/ValueType.h new file mode 100644 index 0000000..f654286 --- /dev/null +++ b/src/lib/Binding/ValueType.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +#pragma once + +#include +#include +#include +#include + +#include "Syntax/SyntaxType.h" + +enum class Type { Unknown, Number, Boolean }; + +class Value { + union uValue { + bool boolean; + int32_t integer; + }; + Type type; + uValue val; + +public: + Value() : type(Type::Unknown), val() {} + + Value &operator=(int32_t i) { + val.integer = i; + type = Type::Number; + return *this; + } + Value &operator=(bool b) { + val.boolean = b; + type = Type::Boolean; + return *this; + } + + Value operator+(const Value &v) { + Value value; + value = this->asInt() + v.asInt(); + return value; + } + + Value operator-(const Value &v) { + Value value; + value = this->asInt() - v.asInt(); + return value; + } + + Value operator-() { + Value value; + value = -this->asInt(); + return value; + } + + Value operator*(const Value &v) { + Value value; + value = this->asInt() * v.asInt(); + return value; + } + + Value operator/(const Value &v) { + Value value; + value = this->asInt() / v.asInt(); + return value; + } + + Value operator==(const Value &v) { + Value value; + value.type = Type::Boolean; + if (this->isInt() && v.isInt()) { + value.val.boolean = this->val.integer == v.val.integer; + } else if (this->isBool() && v.isBool()) { + value.val.boolean = this->val.boolean == v.val.boolean; + } else { + value.val.boolean = false; + } + return value; + } + + Value operator!=(const Value &v) { + Value value; + value.type = Type::Boolean; + if (this->isInt() && v.isInt()) { + value.val.boolean = this->val.integer != v.val.integer; + } else if (this->isBool() && v.isBool()) { + value.val.boolean = this->val.boolean != v.val.boolean; + } else { + value.val.boolean = false; + } + return value; + } + + Value operator&&(const Value &v) { + Value value; + value.type = Type::Boolean; + value.val.boolean = this->asBool() && v.asBool(); + return value; + } + + Value operator||(const Value &v) { + Value value; + value.type = Type::Boolean; + value.val.boolean = this->asBool() || v.asBool(); + return value; + } + + Value operator!() { + Value value; + value.type = Type::Boolean; + value.val.boolean = !this->asBool(); + return value; + } + + Type GetType() { return type; } + + int asInt() const { + assert(type == Type::Number); + return val.integer; + } + bool asBool() const { + assert(type == Type::Boolean); + return val.boolean; + } + bool isInt() const { return type == Type::Number; } + bool isBool() const { return type == Type::Boolean; } + friend class SyntaxToken; + friend std::ostream &operator<<(std::ostream &out, const Value v); +}; + +inline std::ostream &operator<<(std::ostream &out, const Value v) { + switch (v.type) { + case Type::Boolean: + out << boolToNameMap.at(v.asBool()); + break; + case Type::Number: + out << v.asInt(); + break; + default: + throw std::runtime_error("Unsupported Literal Value."); + } + return out; +} + +inline std::ostream &operator<<(std::ostream &out, const Type t) { + switch (t) { + case Type::Boolean: + out << "Boolean"; + break; + case Type::Number: + out << "Number"; + break; + default: + throw std::runtime_error("Unsupported Literal type."); + ; + } + return out; +} \ No newline at end of file diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 181d4cf..1832ed9 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -1,5 +1,4 @@ project(WarfCore) -cmake_minimum_required(VERSION 3.10) if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++latest /W4") @@ -11,28 +10,28 @@ else() set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g") endif() -set(SourceFiles Lexer.cpp - SyntaxToken.cpp - SyntaxType.cpp - SyntaxNode.cpp - ExpressionNode.cpp - LiteralExpression.cpp - BinaryExpressionNode.cpp - UnaryExpressionNode.cpp - ParenthesizedExpressionNode.cpp - Parser.cpp - SyntaxTree.cpp +# PathTracer Src dir +set(WARF_CORE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +set(SourceFiles Evaluator.cpp +) + +set(HeaderFiles Evaluator.h ) -set(HeaderFiles Lexer.h - SyntaxToken.h - SyntaxType.h - SyntaxNode.h - ExpressionNode.h - LiteralExpression.h - BinaryExpressionNode.h - UnaryExpressionNode.h - ParenthesizedExpressionNode.h - Parser.h - SyntaxTree.h + +add_library (${PROJECT_NAME} STATIC + WarfCoreStatic.cpp + ${SourceFiles} ${HeaderFiles} + $ + $ ) -add_library (${PROJECT_NAME} STATIC ${SourceFiles} ${HeaderFiles}) \ No newline at end of file + +target_include_directories(${PROJECT_NAME} + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + # Add include_directories here + Syntax + Binding +) + +add_subdirectory(Syntax) +add_subdirectory(Binding) \ No newline at end of file diff --git a/src/lib/Evaluator.cpp b/src/lib/Evaluator.cpp new file mode 100644 index 0000000..a0934b9 --- /dev/null +++ b/src/lib/Evaluator.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "Evaluator.h" +#include "Binding/BoundBinaryExpressionNode.h" +#include "Binding/BoundLiteralExpressionNode.h" +#include "Binding/BoundUnaryExpressionNode.h" + +BoundExpressionNode *Evaluator::Root() const { return mRootExpression.get(); } +Value Evaluator::Evaluate() { return EvaluateRec(mRootExpression.get()); } + +Evaluator::Evaluator(std::unique_ptr root) + : mRootExpression(std::move(root)) {} + +Value Evaluator::EvaluateRec(BoundExpressionNode *node) { + if (BoundLiteralExpressionNode *literal = + dynamic_cast(node)) { + return literal->GetValue(); + } + if (BoundUnaryExpressionNode *unaryExpression = + dynamic_cast(node)) { + auto opType = unaryExpression->OperatorType(); + auto operand = EvaluateRec(unaryExpression->Operand()); + switch (opType) { + case BoundUnaryOperatorType::Identity: + return operand; + case BoundUnaryOperatorType::Negation: + return -operand; + case BoundUnaryOperatorType::LogicalNegation: + return !operand; + default: + mVecErrors.push_back("EvaluatorError: Unexpected unary operator: " + + BoundUnaryTypeStrMap.at(opType)); + } + } + if (BoundBinaryExpressionNode *binaryExpression = + dynamic_cast(node)) { + auto left = EvaluateRec(binaryExpression->Left()); + auto right = EvaluateRec(binaryExpression->Right()); + auto opType = binaryExpression->OperatorType(); + switch (opType) { + case BoundBinaryOperatorType ::Addition: + return left + right; + case BoundBinaryOperatorType ::Subtraction: + return left - right; + case BoundBinaryOperatorType ::Multiplication: + return left * right; + case BoundBinaryOperatorType ::Division: + return left / right; + case BoundBinaryOperatorType::Equals: + return left == right; + case BoundBinaryOperatorType::NotEquals: + return left != right; + case BoundBinaryOperatorType::LogicalAnd: + return left && right; + case BoundBinaryOperatorType::LogicalOr: + return left || right; + default: + mVecErrors.push_back("EvaluatorError: Unexpected binary operator: " + + BoundBinaryTypeStrMap.at(opType)); + return Value(); + } + } + return Value(); +} \ No newline at end of file diff --git a/src/lib/Evaluator.h b/src/lib/Evaluator.h new file mode 100644 index 0000000..004be62 --- /dev/null +++ b/src/lib/Evaluator.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +#pragma once + +#include "Binding/BoundBinaryExpressionNode.h" +#include + +class Evaluator { +public: + Evaluator(std::unique_ptr root); + Value Evaluate(); + BoundExpressionNode *Root() const; + const std::vector &Errors() const { return mVecErrors; } + +private: + Value EvaluateRec(BoundExpressionNode *node); + std::unique_ptr mRootExpression; + std::vector mVecErrors; +}; \ No newline at end of file diff --git a/src/lib/ExpressionNode.cpp b/src/lib/ExpressionNode.cpp deleted file mode 100644 index a9cde0c..0000000 --- a/src/lib/ExpressionNode.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "ExpressionNode.h" - -ExpressionNode::ExpressionNode(SyntaxType type) : SyntaxNode(type) {} \ No newline at end of file diff --git a/src/lib/ExpressionNode.h b/src/lib/ExpressionNode.h deleted file mode 100644 index 6e28e99..0000000 --- a/src/lib/ExpressionNode.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include "SyntaxNode.h" - -class ExpressionNode : public SyntaxNode { -public: - ExpressionNode(SyntaxType type); -}; \ No newline at end of file diff --git a/src/lib/LiteralExpression.cpp b/src/lib/LiteralExpression.cpp deleted file mode 100644 index b179e58..0000000 --- a/src/lib/LiteralExpression.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "LiteralExpression.h" - -LiteralExpression::LiteralExpression(std::shared_ptr literalToken) - : ExpressionNode(SyntaxType::LiteralExpression), - mLiteralToken(literalToken) { - mVecExpressionNodes.push_back(literalToken.get()); -} diff --git a/src/lib/LiteralExpression.h b/src/lib/LiteralExpression.h deleted file mode 100644 index ff22de3..0000000 --- a/src/lib/LiteralExpression.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include "ExpressionNode.h" -#include "SyntaxToken.h" - -class LiteralExpression : public ExpressionNode { - std::shared_ptr mLiteralToken; - -public: - LiteralExpression(std::shared_ptr literalToken); - std::shared_ptr LiteralToken() { return mLiteralToken; } -}; \ No newline at end of file diff --git a/src/lib/BinaryExpressionNode.cpp b/src/lib/Syntax/BinaryExpressionNode.cpp similarity index 81% rename from src/lib/BinaryExpressionNode.cpp rename to src/lib/Syntax/BinaryExpressionNode.cpp index 1305c21..b359aca 100644 --- a/src/lib/BinaryExpressionNode.cpp +++ b/src/lib/Syntax/BinaryExpressionNode.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + #include "BinaryExpressionNode.h" BinaryExpressionNode::BinaryExpressionNode( diff --git a/src/lib/BinaryExpressionNode.h b/src/lib/Syntax/BinaryExpressionNode.h similarity index 74% rename from src/lib/BinaryExpressionNode.h rename to src/lib/Syntax/BinaryExpressionNode.h index 7a940d9..4c6c53d 100644 --- a/src/lib/BinaryExpressionNode.h +++ b/src/lib/Syntax/BinaryExpressionNode.h @@ -1,6 +1,13 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + #pragma once #include "ExpressionNode.h" #include "SyntaxToken.h" +#include class BinaryExpressionNode : public ExpressionNode { std::unique_ptr mLeft; diff --git a/src/lib/Syntax/CMakeLists.txt b/src/lib/Syntax/CMakeLists.txt new file mode 100644 index 0000000..4c9bbd4 --- /dev/null +++ b/src/lib/Syntax/CMakeLists.txt @@ -0,0 +1,27 @@ +include_directories(${WARF_CORE_SOURCE_DIR}) + +set(SourceFiles Lexer.cpp + SyntaxToken.cpp + SyntaxType.cpp + SyntaxNode.cpp + ExpressionNode.cpp + LiteralExpressionNode.cpp + BinaryExpressionNode.cpp + UnaryExpressionNode.cpp + ParenthesizedExpressionNode.cpp + Parser.cpp + SyntaxTree.cpp +) +set(HeaderFiles Lexer.h + SyntaxToken.h + SyntaxType.h + SyntaxNode.h + ExpressionNode.h + LiteralExpressionNode.h + BinaryExpressionNode.h + UnaryExpressionNode.h + ParenthesizedExpressionNode.h + Parser.h + SyntaxTree.h +) +add_library (WarfCore.Syntax OBJECT ${SourceFiles} ${HeaderFiles}) \ No newline at end of file diff --git a/src/lib/Syntax/ExpressionNode.cpp b/src/lib/Syntax/ExpressionNode.cpp new file mode 100644 index 0000000..743ab65 --- /dev/null +++ b/src/lib/Syntax/ExpressionNode.cpp @@ -0,0 +1,7 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "ExpressionNode.h" + +ExpressionNode::ExpressionNode(SyntaxType type) : SyntaxNode(type) {} \ No newline at end of file diff --git a/src/lib/Syntax/ExpressionNode.h b/src/lib/Syntax/ExpressionNode.h new file mode 100644 index 0000000..7979a0d --- /dev/null +++ b/src/lib/Syntax/ExpressionNode.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +#pragma once +#include "SyntaxNode.h" + +class ExpressionNode : public SyntaxNode { +public: + ExpressionNode(SyntaxType type); +}; \ No newline at end of file diff --git a/src/lib/Lexer.cpp b/src/lib/Syntax/Lexer.cpp similarity index 89% rename from src/lib/Lexer.cpp rename to src/lib/Syntax/Lexer.cpp index cedfbf5..204f14d 100644 --- a/src/lib/Lexer.cpp +++ b/src/lib/Syntax/Lexer.cpp @@ -1,4 +1,9 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + #include "Lexer.h" +#include "SyntaxType.h" #include #include #include @@ -176,28 +181,25 @@ void Lexer::ReadToken(SyntaxType &type) { } } void Lexer::ParseBool(SyntaxType &type) { - // true - // false + const char *startPointer = mText.c_str() + mPosition; - bool bValue; - if (*startPointer == 't') { - bValue = true; - } else if (*startPointer == 'f') { - bValue = false; + std::string trueStr = boolToNameMap.at(true); + std::string falseStr = boolToNameMap.at(false); + if (*startPointer == 't' && + strncmp(startPointer, trueStr.c_str(), trueStr.size()) == 0) { + mValue = true; + mPosition += trueStr.size(); + } else if (*startPointer == 'f' && + strncmp(startPointer, falseStr.c_str(), falseStr.size()) == 0) { + mValue = false; + mPosition += falseStr.size(); } else { std::stringstream errorStream; errorStream << "LexerError: bad character input: " << CurrentToken(); mVecErrors.push_back(errorStream.str()); return; } - std::string boolStr = boolToNameMap.at(bValue); - std::string boolValueAsStr = mText.substr(mPosition, boolStr.size()); - // note only set the value if found. - if (boolStrToValueMap.find(boolStr) != boolStrToValueMap.end()) { - mValue = static_cast(bValue); - type = SyntaxType::BooleanToken; - mPosition += boolStr.size(); - } + type = SyntaxType::BooleanToken; } void Lexer::ParseNumber(SyntaxType &type) { diff --git a/src/lib/Lexer.h b/src/lib/Syntax/Lexer.h similarity index 72% rename from src/lib/Lexer.h rename to src/lib/Syntax/Lexer.h index 8bc7fd1..a32119d 100644 --- a/src/lib/Lexer.h +++ b/src/lib/Syntax/Lexer.h @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + #pragma once #include "SyntaxToken.h" #include @@ -8,7 +14,7 @@ class Lexer { private: std::string mText; int32_t mPosition; - ValueType mValue; + Value mValue; std::vector mVecErrors; void ReadToken(SyntaxType &type); void ParseNumber(SyntaxType &type); diff --git a/src/lib/Syntax/LiteralExpressionNode.cpp b/src/lib/Syntax/LiteralExpressionNode.cpp new file mode 100644 index 0000000..d38f03d --- /dev/null +++ b/src/lib/Syntax/LiteralExpressionNode.cpp @@ -0,0 +1,12 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "LiteralExpressionNode.h" + +LiteralExpressionNode::LiteralExpressionNode( + std::shared_ptr literalToken) + : ExpressionNode(SyntaxType::LiteralExpression), + mLiteralToken(literalToken) { + mVecExpressionNodes.push_back(literalToken.get()); +} diff --git a/src/lib/Syntax/LiteralExpressionNode.h b/src/lib/Syntax/LiteralExpressionNode.h new file mode 100644 index 0000000..4b2d218 --- /dev/null +++ b/src/lib/Syntax/LiteralExpressionNode.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +#pragma once +#include "ExpressionNode.h" +#include "SyntaxToken.h" + +class LiteralExpressionNode : public ExpressionNode { + std::shared_ptr mLiteralToken; + +public: + LiteralExpressionNode(std::shared_ptr literalToken); + std::shared_ptr LiteralToken() { return mLiteralToken; } +}; \ No newline at end of file diff --git a/src/lib/ParenthesizedExpressionNode.cpp b/src/lib/Syntax/ParenthesizedExpressionNode.cpp similarity index 78% rename from src/lib/ParenthesizedExpressionNode.cpp rename to src/lib/Syntax/ParenthesizedExpressionNode.cpp index 0489fec..35b97fc 100644 --- a/src/lib/ParenthesizedExpressionNode.cpp +++ b/src/lib/Syntax/ParenthesizedExpressionNode.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + #include "ParenthesizedExpressionNode.h" ParenthesizedExpressionNode::ParenthesizedExpressionNode( diff --git a/src/lib/ParenthesizedExpressionNode.h b/src/lib/Syntax/ParenthesizedExpressionNode.h similarity index 77% rename from src/lib/ParenthesizedExpressionNode.h rename to src/lib/Syntax/ParenthesizedExpressionNode.h index 905faa3..6c8aad3 100644 --- a/src/lib/ParenthesizedExpressionNode.h +++ b/src/lib/Syntax/ParenthesizedExpressionNode.h @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + #pragma once #include "ExpressionNode.h" #include "SyntaxToken.h" diff --git a/src/lib/Parser.cpp b/src/lib/Syntax/Parser.cpp similarity index 91% rename from src/lib/Parser.cpp rename to src/lib/Syntax/Parser.cpp index fdf7e54..ca2f9c8 100644 --- a/src/lib/Parser.cpp +++ b/src/lib/Syntax/Parser.cpp @@ -1,9 +1,13 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + #include //#include // for copy() and assign() //#include // for back_inserter #include "BinaryExpressionNode.h" #include "Lexer.h" -#include "LiteralExpression.h" +#include "LiteralExpressionNode.h" #include "ParenthesizedExpressionNode.h" #include "Parser.h" #include "UnaryExpressionNode.h" @@ -92,11 +96,11 @@ std::unique_ptr Parser::ParsePrimaryExpression() { } if (Current()->Type() == SyntaxType::BooleanToken) { auto boolToken = Match(SyntaxType::BooleanToken); - return std::make_unique(boolToken); + return std::make_unique(boolToken); } auto numberToken = Match(SyntaxType::NumberToken); - return std::make_unique(numberToken); + return std::make_unique(numberToken); } std::unique_ptr Parser::Parse() { diff --git a/src/lib/Parser.h b/src/lib/Syntax/Parser.h similarity index 83% rename from src/lib/Parser.h rename to src/lib/Syntax/Parser.h index 25fdffe..798b7d9 100644 --- a/src/lib/Parser.h +++ b/src/lib/Syntax/Parser.h @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + #pragma once #include diff --git a/src/lib/SyntaxNode.cpp b/src/lib/Syntax/SyntaxNode.cpp similarity index 55% rename from src/lib/SyntaxNode.cpp rename to src/lib/Syntax/SyntaxNode.cpp index 5760aa0..3912f19 100644 --- a/src/lib/SyntaxNode.cpp +++ b/src/lib/Syntax/SyntaxNode.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + #include "SyntaxNode.h" SyntaxNode::SyntaxNode(SyntaxType type) : mType(type), mVecExpressionNodes() {} diff --git a/src/lib/SyntaxNode.h b/src/lib/Syntax/SyntaxNode.h similarity index 65% rename from src/lib/SyntaxNode.h rename to src/lib/Syntax/SyntaxNode.h index a53093f..7bb9bc2 100644 --- a/src/lib/SyntaxNode.h +++ b/src/lib/Syntax/SyntaxNode.h @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + #pragma once #include #include diff --git a/src/lib/SyntaxToken.cpp b/src/lib/Syntax/SyntaxToken.cpp similarity index 57% rename from src/lib/SyntaxToken.cpp rename to src/lib/Syntax/SyntaxToken.cpp index 6167bcb..b374007 100644 --- a/src/lib/SyntaxToken.cpp +++ b/src/lib/Syntax/SyntaxToken.cpp @@ -1,8 +1,12 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + #include "SyntaxToken.h" -const ValueType &SyntaxToken::Value() const { return mValue; } +const Value &SyntaxToken::GetValue() const { return mValue; } -bool SyntaxToken::HasValue() const { return mValue.type != Type::UnknownType; } +bool SyntaxToken::HasValue() const { return mValue.type != Type::Unknown; } int SyntaxToken::Position() const { return mPosition; } @@ -11,5 +15,5 @@ std::string SyntaxToken::Text() const { return mText; } SyntaxToken::SyntaxToken(SyntaxType synType, int pos, std::string text) : SyntaxNode(synType), mPosition(pos), mText(text) {} -SyntaxToken::SyntaxToken(SyntaxType synType, int pos, ValueType value) +SyntaxToken::SyntaxToken(SyntaxType synType, int pos, Value value) : SyntaxNode(synType), mPosition(pos), mText(), mValue(value) {} \ No newline at end of file diff --git a/src/lib/Syntax/SyntaxToken.h b/src/lib/Syntax/SyntaxToken.h new file mode 100644 index 0000000..52a8d1e --- /dev/null +++ b/src/lib/Syntax/SyntaxToken.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +#pragma once +#include +#include +#include +#include + +#include "Binding/ValueType.h" +#include "SyntaxNode.h" +#include "SyntaxType.h" + +enum class Errors { LexError, ParseError }; + +class SyntaxToken : public SyntaxNode { +private: + int mPosition; + std::string mText; + Value mValue; + +public: + bool HasValue() const; + const Value &GetValue() const; + int Position() const; + std::string Text() const; + SyntaxToken(SyntaxType synType, int pos, std::string text); + SyntaxToken(SyntaxType synType, int pos, Value value); +}; \ No newline at end of file diff --git a/src/lib/Syntax/SyntaxTree.cpp b/src/lib/Syntax/SyntaxTree.cpp new file mode 100644 index 0000000..31d5d72 --- /dev/null +++ b/src/lib/Syntax/SyntaxTree.cpp @@ -0,0 +1,43 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "SyntaxTree.h" +#include "BinaryExpressionNode.h" +#include "LiteralExpressionNode.h" +#include "ParenthesizedExpressionNode.h" +#include "Parser.h" +#include "UnaryExpressionNode.h" +#include + +SyntaxTree::SyntaxTree(std::vector &vecErrors, + std::unique_ptr root) + : mVecErrors(), mRootExpression(std::move(root)) { + mVecErrors.swap(vecErrors); +} + +ExpressionNode *SyntaxTree::Root() const { return mRootExpression.get(); } + +std::unique_ptr SyntaxTree::Parse(std::string text) { + Parser parser(text); + return parser.Parse(); +} + +void SyntaxTree::PrintTreeRec(SyntaxNode *sNode, std::string indent, + bool isLast) { + std::string marker = isLast ? "L--" : "|--"; + std::cout << indent << marker << sNode->Type() << std::endl; + SyntaxToken *derived = dynamic_cast(sNode); + if (derived && derived->HasValue()) { + std::cout << indent << " " << derived->GetValue() << std::endl; + } + + indent += isLast ? " " : "| "; + auto vecChildren = sNode->GetChildren(); + for (auto child : vecChildren) { + bool isLastChild = (child == vecChildren[vecChildren.size() - 1]); + PrintTreeRec(child, indent, isLastChild); + } +} + +void SyntaxTree::PrintTree() { PrintTreeRec(this->mRootExpression.get()); } \ No newline at end of file diff --git a/src/lib/SyntaxTree.h b/src/lib/Syntax/SyntaxTree.h similarity index 78% rename from src/lib/SyntaxTree.h rename to src/lib/Syntax/SyntaxTree.h index 0cd9d8d..c2ebf91 100644 --- a/src/lib/SyntaxTree.h +++ b/src/lib/Syntax/SyntaxTree.h @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + #pragma once #include "ExpressionNode.h" #include @@ -6,7 +12,6 @@ class SyntaxTree { std::vector mVecErrors; std::unique_ptr mRootExpression; - int EvaluateRec(ExpressionNode *node); void PrintTreeRec(SyntaxNode *sNode, std::string indent = "", bool isLast = true); @@ -16,6 +21,5 @@ class SyntaxTree { const std::vector &Errors() const { return mVecErrors; } ExpressionNode *Root() const; void PrintTree(); - int Evaluate(); static std::unique_ptr Parse(std::string text); }; diff --git a/src/lib/SyntaxType.cpp b/src/lib/Syntax/SyntaxType.cpp similarity index 86% rename from src/lib/SyntaxType.cpp rename to src/lib/Syntax/SyntaxType.cpp index e9dafdc..402ad3c 100644 --- a/src/lib/SyntaxType.cpp +++ b/src/lib/Syntax/SyntaxType.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + #include "SyntaxType.h" int SyntaxOrder::GetUnaryOperatorPrecedence(SyntaxType type) { diff --git a/src/lib/SyntaxType.h b/src/lib/Syntax/SyntaxType.h similarity index 94% rename from src/lib/SyntaxType.h rename to src/lib/Syntax/SyntaxType.h index 30ae94a..ea2a64a 100644 --- a/src/lib/SyntaxType.h +++ b/src/lib/Syntax/SyntaxType.h @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + #pragma once #include #include @@ -50,6 +56,16 @@ enum class SyntaxType { LiteralExpression }; +static const std::unordered_map boolToNameMap = { + {true, "true"}, + {false, "false"}, +}; + +static const std::unordered_map boolStrToValueMap = { + {"true", true}, + {"false", false}, +}; + struct SyntaxOrder { static int GetUnaryOperatorPrecedence(SyntaxType type); static int GetBinaryOperatorPrecedence(SyntaxType type); diff --git a/src/lib/UnaryExpressionNode.cpp b/src/lib/Syntax/UnaryExpressionNode.cpp similarity index 77% rename from src/lib/UnaryExpressionNode.cpp rename to src/lib/Syntax/UnaryExpressionNode.cpp index a8239bd..58d09b0 100644 --- a/src/lib/UnaryExpressionNode.cpp +++ b/src/lib/Syntax/UnaryExpressionNode.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + #include "UnaryExpressionNode.h" UnaryExpressionNode::UnaryExpressionNode( diff --git a/src/lib/UnaryExpressionNode.h b/src/lib/Syntax/UnaryExpressionNode.h similarity index 71% rename from src/lib/UnaryExpressionNode.h rename to src/lib/Syntax/UnaryExpressionNode.h index d990982..6154ccd 100644 --- a/src/lib/UnaryExpressionNode.h +++ b/src/lib/Syntax/UnaryExpressionNode.h @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2022 F. Lotfi All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + #pragma once #include "ExpressionNode.h" #include "SyntaxToken.h" diff --git a/src/lib/SyntaxToken.h b/src/lib/SyntaxToken.h deleted file mode 100644 index d510cd1..0000000 --- a/src/lib/SyntaxToken.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once -#include -#include -#include -#include - -#include "SyntaxNode.h" -#include "SyntaxType.h" - -enum class Type { UnknownType, IntegerType, BooleanType }; - -enum class Errors { LexError, ParseError }; - -static const std::unordered_map boolToNameMap = { - {true, "true"}, - {false, "false"}, -}; - -static const std::unordered_map boolStrToValueMap = { - {"true", true}, - {"false", false}, -}; - -class ValueType { - union Value { - bool boolean; - int32_t integer; - }; - Type type; - Value val; - -public: - ValueType() : type(Type::UnknownType), val() {} - ValueType &operator=(int32_t i) { - val.integer = i; - type = Type::IntegerType; - return *this; - } - ValueType &operator=(bool b) { - val.boolean = b; - type = Type::BooleanType; - return *this; - } - int asInt() const { - assert(type == Type::IntegerType); - return val.integer; - } - bool asBool() const { - assert(type == Type::BooleanType); - return val.boolean; - } - bool isInt() const { return type == Type::IntegerType; } - bool isBool() const { return type == Type::BooleanType; } - friend class SyntaxToken; - friend std::ostream &operator<<(std::ostream &out, const ValueType v); -}; - -inline std::ostream &operator<<(std::ostream &out, const ValueType v) { - switch (v.type) { - case Type::BooleanType: - out << boolToNameMap.at(v.asBool()); - break; - case Type::IntegerType: - out << v.asInt(); - break; - default: - std::cerr << "Literal type is Unknown or not supported." << std::endl; - throw; - } - return out; -} - -class SyntaxToken : public SyntaxNode { -private: - int mPosition; - std::string mText; - ValueType mValue; - -public: - bool HasValue() const; - const ValueType &Value() const; - int Position() const; - std::string Text() const; - SyntaxToken(SyntaxType synType, int pos, std::string text); - SyntaxToken(SyntaxType synType, int pos, ValueType value); -}; \ No newline at end of file diff --git a/src/lib/SyntaxTree.cpp b/src/lib/SyntaxTree.cpp deleted file mode 100644 index 9de5523..0000000 --- a/src/lib/SyntaxTree.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "SyntaxTree.h" -#include "BinaryExpressionNode.h" -#include "LiteralExpression.h" -#include "ParenthesizedExpressionNode.h" -#include "Parser.h" -#include "UnaryExpressionNode.h" -#include - -SyntaxTree::SyntaxTree(std::vector &vecErrors, - std::unique_ptr root) - : mVecErrors(), mRootExpression(std::move(root)) { - mVecErrors.swap(vecErrors); -} - -ExpressionNode *SyntaxTree::Root() const { return mRootExpression.get(); } - -int SyntaxTree::EvaluateRec(ExpressionNode *node) { - if (LiteralExpression *literal = dynamic_cast(node)) { - return literal->LiteralToken()->HasValue() - ? literal->LiteralToken()->Value().asInt() - : 0; - } - if (UnaryExpressionNode *unaryExpression = - dynamic_cast(node)) { - auto opType = unaryExpression->Operator()->Type(); - auto operand = EvaluateRec(unaryExpression->Operand()); - switch (opType) { - case SyntaxType::MinusToken: - return -operand; - case SyntaxType::PlusToken: - return operand; - default: - mVecErrors.push_back("EvaluatorError: Unexpected unary operator: " + - SyntaxTokenToStrMap.at(opType)); - } - } - if (BinaryExpressionNode *binaryExpression = - dynamic_cast(node)) { - auto left = EvaluateRec(binaryExpression->Left()); - auto right = EvaluateRec(binaryExpression->Right()); - auto opType = binaryExpression->Operator()->Type(); - switch (opType) { - case SyntaxType::PlusToken: - return left + right; - case SyntaxType::MinusToken: - return left - right; - case SyntaxType::StarToken: - return left * right; - case SyntaxType::SlashToken: - return left / right; - default: - mVecErrors.push_back("EvaluatorError: Unexpected binary operator: " + - SyntaxTokenToStrMap.at(opType)); - return 0; - } - } - if (ParenthesizedExpressionNode *parenExpression = - dynamic_cast(node)) { - return EvaluateRec(parenExpression->Expression()); - } - - mVecErrors.push_back("EvaluatorError: Unexpected node: " + - SyntaxTypeStrMap.at(node->Type())); - return 0; -} - -int SyntaxTree::Evaluate() { return EvaluateRec(mRootExpression.get()); } - -std::unique_ptr SyntaxTree::Parse(std::string text) { - Parser parser(text); - return parser.Parse(); -} - -void SyntaxTree::PrintTreeRec(SyntaxNode *sNode, std::string indent, - bool isLast) { - std::string marker = isLast ? "L--" : "|--"; - std::cout << indent << marker << sNode->Type() << std::endl; - SyntaxToken *derived = dynamic_cast(sNode); - if (derived && derived->HasValue()) { - std::cout << indent << " " << derived->Value() << std::endl; - } - - indent += isLast ? " " : "| "; - auto vecChildren = sNode->GetChildren(); - for (auto child : vecChildren) { - bool isLastChild = (child == vecChildren[vecChildren.size() - 1]); - PrintTreeRec(child, indent, isLastChild); - } -} - -void SyntaxTree::PrintTree() { PrintTreeRec(this->mRootExpression.get()); } \ No newline at end of file diff --git a/src/lib/Version/appName.txt b/src/lib/Version/appName.txt new file mode 100644 index 0000000..617b4ce --- /dev/null +++ b/src/lib/Version/appName.txt @@ -0,0 +1 @@ +Warf \ No newline at end of file diff --git a/src/lib/Version/version.txt b/src/lib/Version/version.txt new file mode 100644 index 0000000..8a9ecc2 --- /dev/null +++ b/src/lib/Version/version.txt @@ -0,0 +1 @@ +0.0.1 \ No newline at end of file diff --git a/src/lib/WarfCoreStatic.cpp b/src/lib/WarfCoreStatic.cpp new file mode 100644 index 0000000..0cc97d3 --- /dev/null +++ b/src/lib/WarfCoreStatic.cpp @@ -0,0 +1,6 @@ +// Copyright (c) 2021 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// dummy file for build system to make sure libWarfCoreStatic.a builds. +void EMPTY_FUNCTION() {} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5551da7..f3eb3b4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,4 @@ project(${CMAKE_PROJECT_NAME}_TEST) -cmake_minimum_required(VERSION 3.10) download_file(https://raw.githubusercontent.com/onqtam/doctest/2.4.6/doctest/doctest.h ${CMAKE_SOURCE_DIR}/packages/doctest/doctest.h diff --git a/test/test.cpp b/test/test.cpp index df164e4..f00dc65 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,54 +1,137 @@ +// Copyright (c) 2022 F. Lotfi All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -#include "SyntaxTree.h" +#include "Binding/Binder.h" +#include "Evaluator.h" +#include "Syntax/SyntaxTree.h" #include +Value testCaseHelper(std::string s) { + auto syntaxTree = SyntaxTree::Parse(s); + auto binder = std::make_unique(); + auto boundExpression = binder->BindExpression(syntaxTree->Root()); + auto eval = std::make_unique(std::move(boundExpression)); + return eval->Evaluate(); +} + TEST_CASE("Binary Expression") { - SUBCASE("negative numbers") { - auto syntaxTree = SyntaxTree::Parse("-1"); - REQUIRE(-1 == syntaxTree->Evaluate()); - } - SUBCASE("Simple Addition") { - auto syntaxTree = SyntaxTree::Parse("1+3"); - REQUIRE(4 == syntaxTree->Evaluate()); - } - SUBCASE("Simple Subtraction") { - auto syntaxTree = SyntaxTree::Parse("4-3"); - REQUIRE(1 == syntaxTree->Evaluate()); - } + SUBCASE("negative numbers") { REQUIRE(-1 == testCaseHelper("-1").asInt()); } + SUBCASE("Simple Addition") { REQUIRE(4 == testCaseHelper("1 + 3").asInt()); } + SUBCASE("Simple Subtraction") { REQUIRE(1 == testCaseHelper("4-3").asInt()); } SUBCASE("Simple Negative Number") { - auto syntaxTree = SyntaxTree::Parse("3-4"); - REQUIRE(-1 == syntaxTree->Evaluate()); + REQUIRE(-1 == testCaseHelper("3-4").asInt()); } SUBCASE("Simple Multiplication") { - auto syntaxTree = SyntaxTree::Parse("4*3"); - REQUIRE(12 == syntaxTree->Evaluate()); - } - SUBCASE("Multiplication of Negative") { - auto syntaxTree = SyntaxTree::Parse("4*-5"); - REQUIRE(-20 == syntaxTree->Evaluate()); + REQUIRE(12 == testCaseHelper("4*3").asInt()); } SUBCASE("Multiplication of Negative") { - auto syntaxTree = SyntaxTree::Parse("4*(-5)"); - REQUIRE(-20 == syntaxTree->Evaluate()); + REQUIRE(-20 == testCaseHelper("4*-5").asInt()); } - SUBCASE("Multiplication of Negative") { - auto syntaxTree = SyntaxTree::Parse("4*(3-5)"); - REQUIRE(-8 == syntaxTree->Evaluate()); + SUBCASE("Multiplication of Negative Parentheses") { + REQUIRE(-20 == testCaseHelper("4*(-5)").asInt()); } - SUBCASE("Simple Division") { - auto syntaxTree = SyntaxTree::Parse("9/3"); - REQUIRE(3 == syntaxTree->Evaluate()); + SUBCASE("Multiplication of Negative Created in Parentheses") { + REQUIRE(-8 == testCaseHelper("4*(3-5)").asInt()); } + SUBCASE("Simple Division") { REQUIRE(3 == testCaseHelper("9/3").asInt()); } SUBCASE("Simple PMDAS Ordering") { - auto syntaxTree = SyntaxTree::Parse("4*1+3"); - REQUIRE(7 == syntaxTree->Evaluate()); + REQUIRE(7 == testCaseHelper("4*1+3").asInt()); } SUBCASE("Simple Parentheses") { - auto syntaxTree = SyntaxTree::Parse("4*(1+3)"); - REQUIRE(16 == syntaxTree->Evaluate()); + REQUIRE(16 == testCaseHelper("4*(1+3)").asInt()); } SUBCASE("Negative Parentheses") { - auto syntaxTree = SyntaxTree::Parse("-(1+3)"); - REQUIRE(-4 == syntaxTree->Evaluate()); + REQUIRE(-4 == testCaseHelper("-(1+3)").asInt()); + } +} + +TEST_CASE("Boolean Expression") { + SUBCASE("types defined") { + REQUIRE(testCaseHelper("true").asBool()); + REQUIRE_FALSE(testCaseHelper("false").asBool()); + } + SUBCASE("Unary Expression Boolean Logical Negation") { + REQUIRE(testCaseHelper("!false").asBool()); + REQUIRE_FALSE(testCaseHelper("!true").asBool()); + } + SUBCASE("Binary Expression Boolean Logical OR") { + REQUIRE(testCaseHelper("false || true").asBool()); + REQUIRE(testCaseHelper("true || false").asBool()); + REQUIRE(testCaseHelper("true || true").asBool()); + REQUIRE_FALSE(testCaseHelper("false || false").asBool()); + + // Logical negation on the right hand + REQUIRE_FALSE(testCaseHelper("false || !true").asBool()); + REQUIRE(testCaseHelper("true || !false").asBool()); + REQUIRE(testCaseHelper("false || !false").asBool()); + + // Logical negation on the left hand + REQUIRE(testCaseHelper("!false || true").asBool()); + REQUIRE_FALSE(testCaseHelper("!true || false").asBool()); + REQUIRE(testCaseHelper("!true || true").asBool()); + } + SUBCASE("Binary Expression Boolean Logical AND") { + REQUIRE_FALSE(testCaseHelper("false && true").asBool()); + REQUIRE_FALSE(testCaseHelper("true && false").asBool()); + REQUIRE_FALSE(testCaseHelper("false && false").asBool()); + REQUIRE(testCaseHelper("true && true").asBool()); + + // Logical negation on the right hand + REQUIRE(testCaseHelper("true && !false").asBool()); + REQUIRE_FALSE(testCaseHelper("false && !false").asBool()); + REQUIRE_FALSE(testCaseHelper("false && !true").asBool()); + + // Logical negation on the left hand + REQUIRE(testCaseHelper("!false && true").asBool()); + REQUIRE_FALSE(testCaseHelper("!true && false").asBool()); + REQUIRE_FALSE(testCaseHelper("!true && true").asBool()); + } + SUBCASE("Binary Expression Boolean Equality") { + REQUIRE(testCaseHelper("true == true").asBool()); + REQUIRE(testCaseHelper("false == false").asBool()); + REQUIRE(testCaseHelper("1 == 1").asBool()); + REQUIRE(testCaseHelper("255 == 255").asBool()); + + REQUIRE_FALSE(testCaseHelper("true == false").asBool()); + REQUIRE_FALSE(testCaseHelper("!true == true").asBool()); + REQUIRE_FALSE(testCaseHelper("false == true").asBool()); + REQUIRE_FALSE(testCaseHelper("1 == 2").asBool()); + } + + SUBCASE("Binary Expression Boolean inEquality") { + REQUIRE_FALSE(testCaseHelper("true != true").asBool()); + REQUIRE_FALSE(testCaseHelper("false != false").asBool()); + REQUIRE_FALSE(testCaseHelper("1 != 1").asBool()); + REQUIRE_FALSE(testCaseHelper("255 != 255").asBool()); + + REQUIRE(testCaseHelper("true != false").asBool()); + REQUIRE(testCaseHelper("!true != true").asBool()); + REQUIRE(testCaseHelper("false != true").asBool()); + REQUIRE(testCaseHelper("1 != 2").asBool()); + REQUIRE(testCaseHelper("1 != 111").asBool()); + } + + SUBCASE("Nested Binary Expression Boolean Logical AND") { + REQUIRE(testCaseHelper("1==1 && true").asBool()); + REQUIRE(testCaseHelper("1==1 && 2==2").asBool()); + + REQUIRE_FALSE(testCaseHelper("1==1 && false").asBool()); + REQUIRE_FALSE(testCaseHelper("1==1 && 2==3").asBool()); + REQUIRE_FALSE(testCaseHelper("false && 2==2").asBool()); + REQUIRE_FALSE(testCaseHelper("1==2 && 2==2").asBool()); + REQUIRE_FALSE(testCaseHelper("1==2 && 2==3").asBool()); + } + + SUBCASE("Nested Binary Expression Boolean Logical OR") { + REQUIRE(testCaseHelper("1==1 || true").asBool()); + REQUIRE(testCaseHelper("1==1 || 2==2").asBool()); + REQUIRE(testCaseHelper("1==1 || false").asBool()); + REQUIRE(testCaseHelper("1==1 || 2==3").asBool()); + REQUIRE(testCaseHelper("false || 2==2").asBool()); + REQUIRE(testCaseHelper("1==2 || 2==2").asBool()); + + REQUIRE_FALSE(testCaseHelper("1==2 || 2==3").asBool()); } } \ No newline at end of file