diff --git a/.github/workflows/ax.yml b/.github/workflows/ax.yml index 2af99c9264..1d80168419 100644 --- a/.github/workflows/ax.yml +++ b/.github/workflows/ax.yml @@ -23,99 +23,69 @@ on: - cron: '0 0 * * 2' jobs: - linux-ax-full-vfx: + linux-ax-vfx: runs-on: ubuntu-16.04 - name: linux-ax-full-vfx:${{ matrix.image }}-cxx:${{ matrix.compiler }}-${{ matrix.build }} + name: linux-ax-vfx:${{ matrix.image }}-cxx:${{ matrix.compiler }}-${{ matrix.build }} container: image: aswf/ci-openvdb:${{ matrix.image }} env: CXX: ${{ matrix.compiler }} - LLVM_DIR: /github/home/llvm-8.0.0-${{ matrix.compiler }} strategy: matrix: - image: ['2019', '2020'] - compiler: ['clang++'] + image: ['2019-clang8', '2020-clang7', '2021-clang10'] + compiler: ['clang++', 'g++'] build: ['Release'] # Extra builds include: - - image: '2020' - compiler: 'g++' - build: 'Release' - - image: '2020' + - image: '2020-clang7' compiler: 'clang++' build: 'Debug' fail-fast: false steps: - uses: actions/checkout@v2 - # Setup - - name: llvm_cache - id: llvm_cache - uses: actions/cache@v2 - with: - key: ${{ env.LLVM_DIR }} - path: ${{ env.LLVM_DIR }} - - name: install_llvm - if: steps.llvm_cache.outputs.cache-hit != 'true' - run: ./ci/install_llvm.sh 8.0.0 ${{ env.LLVM_DIR }} - - name: install_libedit - run: yum -y install libedit-devel # Build - name: build run: | ./ci/build.sh ${{ matrix.build }} None ON None "core,axcore,axbin,axtest" \ - -DOPENVDB_CXX_STRICT=ON \ - -DLLVM_DIR=${{ env.LLVM_DIR }} + -DOPENVDB_CXX_STRICT=ON # Tests - name: test run: cd build && ctest -V - name: test_doxygen_examples run: ./ci/extract_test_examples.sh - linux-ax-vfx: + linux-ax-standalone-vfx: runs-on: ubuntu-16.04 - name: linux-ax-vfx:${{ matrix.image }}-cxx:${{ matrix.compiler }}-llvm:${{ matrix.llvm }}-${{ matrix.build }} + name: linux-ax-standalone-vfx:${{ matrix.image }}-cxx:${{ matrix.compiler }}-llvm:${{ matrix.llvm }}-${{ matrix.build }} container: - image: aswf/ci-vfxall:${{ matrix.image }} + image: aswf/ci-openvdb:${{ matrix.image }} env: CXX: ${{ matrix.compiler }} - LLVM_DIR: /github/home/llvm-${{ matrix.llvm }}-${{ matrix.compiler }} strategy: matrix: - image: ['2019', '2020'] + image: ['2019-clang6', '2019-clang7', '2019-clang8', '2019-clang9', '2020-clang7', '2021-clang10'] compiler: ['clang++'] build: ['Release'] - llvm: ['6.0.0','7.0.0','8.0.0','9.0.0','10.0.0'] # Extra builds include: - - image: '2020' + - image: '2020-clang7' compiler: 'g++' build: 'Release' - llvm: '8.0.0' - - image: '2020' + - image: '2020-clang7' compiler: 'clang++' build: 'Debug' - llvm: '8.0.0' fail-fast: false steps: - uses: actions/checkout@v2 - # Setup - - name: llvm_cache - id: llvm_cache - uses: actions/cache@v2 - with: - key: ${{ env.LLVM_DIR }} - path: ${{ env.LLVM_DIR }} - - name: install_llvm - if: steps.llvm_cache.outputs.cache-hit != 'true' - run: ./ci/install_llvm.sh ${{ matrix.llvm }} ${{ env.LLVM_DIR }} - - name: install_libedit - run: yum -y install libedit-devel # Build + - name: vdb + run: | + ./ci/build.sh ${{ matrix.build }} None ON None "core" -DOPENVDB_CXX_STRICT=ON + rm -rf build - name: build run: | ./ci/build.sh ${{ matrix.build }} None ON None "axcore,axbin,axtest" \ - -DOPENVDB_CXX_STRICT=ON \ - -DLLVM_DIR=${{ env.LLVM_DIR }} + -DOPENVDB_CXX_STRICT=ON # Tests - name: test run: cd build && ctest -V @@ -131,7 +101,7 @@ jobs: matrix: compiler: ['clang++'] build: ['Release'] - llvm: ['6','7','8','9','10'] + llvm: ['7','8','9','10'] # Extra builds include: - compiler: 'g++' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cd567c898c..d67bb967f3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -139,19 +139,19 @@ jobs: - uses: actions/checkout@v2 - name: install shell: bash + # brew boost-python3 installs a "Keg-only" version of python which is + # not installed to PATH. We must manually provide the location of the + # required python installation to CMake through a hint variable which + # is exported in install_macos.sh run: ./ci/install_macos.sh - name: install shell: bash run: ./ci/install_blosc.sh 1.5.0 - name: build shell: bash - # brew boost-python installs a "Keg-only" version of python which is - # not insatlled to PATH. Until this becomes the default, we must - # manually provide the location of the require python installation - # See https://formulae.brew.sh/formula/python@3.8 # Also need to disable compiler warnings for ABI 6 and above due to # the version of clang installed - run: ./ci/build.sh Release 7 ON SSE42 "core,python,bin,test" -DPython_ROOT_DIR=/usr/local/opt/python@3.8 -DOPENVDB_CXX_STRICT=OFF + run: ./ci/build.sh Release 7 ON SSE42 "core,python,bin,test" -DOPENVDB_CXX_STRICT=OFF - name: test shell: bash run: ./ci/test.sh diff --git a/CHANGES b/CHANGES index 9f0ea728e5..f30fea7ec8 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,7 @@ Version 7.1.1 - In Development packed geometry were also being converted. - Fixed a bug where a Houdini SOP's verb would not be correctly associated with the corresponding node if the node's internal name was changed. + - Fixed bug where OpenVDB Convert SOP could revert the name attribute. Bug fixes: - Fixed a bug which could cause recursive compile time instantiations of diff --git a/ci/install_macos.sh b/ci/install_macos.sh index 4213c78bad..52bd65428c 100755 --- a/ci/install_macos.sh +++ b/ci/install_macos.sh @@ -13,3 +13,13 @@ brew install cppunit brew install tbb brew install zlib brew install glfw +brew install jq # for trivial parsing of brew json + +# Alias python version installed by boost-python3 to path +py_version=$(brew info boost-python3 --json | \ + jq -cr '.[].dependencies[] | select(. | startswith("python"))') +echo "Using python $py_version" +# export for subsequent action steps (note, not exported for this env) +echo "Python_ROOT_DIR=/usr/local/opt/$py_version" >> $GITHUB_ENV +echo "/usr/local/opt/$py_version/bin" >> $GITHUB_PATH + diff --git a/doc/changes.txt b/doc/changes.txt index a85e5d6e5a..04a9513048 100644 --- a/doc/changes.txt +++ b/doc/changes.txt @@ -18,6 +18,7 @@ Houdini: packed geometry were also being converted. - Fixed a bug where a Houdini SOP's verb would not be correctly associated with the corresponding node if the node's internal name was changed. +- Fixed bug where OpenVDB Convert SOP could revert the name attribute. @par Bug Fixes: diff --git a/openvdb_ax/CMakeLists.txt b/openvdb_ax/CMakeLists.txt index babc2e83a0..cc17906fec 100644 --- a/openvdb_ax/CMakeLists.txt +++ b/openvdb_ax/CMakeLists.txt @@ -46,13 +46,12 @@ if(OPENVDB_BUILD_AX_GRAMMAR) find_package(FLEX REQUIRED) find_package(BISON 3.0 REQUIRED) - # @note CMake lists don't seem to work with these options - SET(BISON_COMPILE_FLAGS "${BISON_COMPILE_FLAGS} -Werror") + set(BISON_COMPILE_FLAGS "${BISON_COMPILE_FLAGS} -Werror") if(OPENVDB_AX_GRAMMAR_NO_LINES) # Suppress #line directives - SET(FLEX_COMPILE_FLAGS "${FLEX_COMPILE_FLAGS} -L") - SET(BISON_COMPILE_FLAGS "${BISON_COMPILE_FLAGS} -l") + set(FLEX_COMPILE_FLAGS "${FLEX_COMPILE_FLAGS} -L") + set(BISON_COMPILE_FLAGS "${BISON_COMPILE_FLAGS} -l") endif() FLEX_TARGET(openvdb_ax_lexer openvdb_ax/grammar/axlexer.l ${OPENVDB_AX_GRAMMAR_DIR}/axlexer.cc @@ -69,16 +68,7 @@ if(OPENVDB_BUILD_AX_GRAMMAR) COMMENT "Re-generate the AX language files." DEPENDS ${OPENVDB_AX_GRAMMAR_DIR}/axlexer.cc - ${OPENVDB_AX_GRAMMAR_DIR}/axparser.cc - ) -else() - file(COPY - openvdb_ax/grammar/generated/axlexer.cc - openvdb_ax/grammar/generated/axparser.cc - openvdb_ax/grammar/generated/axparser.h - DESTINATION - ${OPENVDB_AX_GRAMMAR_DIR} - ) + ${OPENVDB_AX_GRAMMAR_DIR}/axparser.cc) endif() ######################################################################### @@ -123,9 +113,7 @@ find_package(Boost REQUIRED COMPONENTS random) ######################################################################### set(OPENVDB_AX_LIBRARY_SOURCE_FILES - ${OPENVDB_AX_GRAMMAR_DIR}/axlexer.cc - ${OPENVDB_AX_GRAMMAR_DIR}/axparser.cc - openvdb_ax/ast/AST.cc + openvdb_ax/ast/Parse.cc openvdb_ax/ast/PrintTree.cc openvdb_ax/ast/Scanners.cc openvdb_ax/ax.cc @@ -140,11 +128,23 @@ set(OPENVDB_AX_LIBRARY_SOURCE_FILES openvdb_ax/compiler/Compiler.cc openvdb_ax/compiler/PointExecutable.cc openvdb_ax/compiler/VolumeExecutable.cc + openvdb_ax/compiler/Logger.cc openvdb_ax/math/OpenSimplexNoise.cc - ) +) + +if(OPENVDB_BUILD_AX_GRAMMAR) + list(APPEND OPENVDB_AX_LIBRARY_SOURCE_FILES + ${OPENVDB_AX_GRAMMAR_DIR}/axlexer.cc + ${OPENVDB_AX_GRAMMAR_DIR}/axparser.cc) +else() + list(APPEND OPENVDB_AX_LIBRARY_SOURCE_FILES + openvdb_ax/grammar/generated/axlexer.cc + openvdb_ax/grammar/generated/axparser.cc) +endif() set(OPENVDB_AX_AST_INCLUDE_FILES openvdb_ax/ast/AST.h + openvdb_ax/ast/Parse.h openvdb_ax/ast/Literals.h openvdb_ax/ast/PrintTree.h openvdb_ax/ast/Scanners.h @@ -174,6 +174,7 @@ set(OPENVDB_AX_COMPILER_INCLUDE_FILES openvdb_ax/compiler/PointExecutable.h openvdb_ax/compiler/AttributeRegistry.h openvdb_ax/compiler/VolumeExecutable.h + openvdb_ax/compiler/Logger.h ) # @todo CMake >= 3.12, use an object library to consolidate shared/static @@ -216,18 +217,24 @@ set(OPENVDB_AX_CORE_DEPENDENT_LIBS if(OPENVDB_AX_STATIC) target_compile_definitions(openvdb_ax_static PUBLIC ${LLVM_DEFINITIONS}) - if(NOT MATRIX_SUPPORT) - target_compile_definitions(openvdb_ax_static PRIVATE -DOPENVDB_AX_NO_MATRIX) + if(OPENVDB_HAS_MATRIX_SUPPORT) + target_compile_definitions(openvdb_ax_shared PUBLIC -DOPENVDB_HAS_MATRIX_SUPPORT) endif() - target_include_directories(openvdb_ax_static - PUBLIC . - PRIVATE ${OPENVDB_AX_GRAMMAR_DIR} - ) + target_include_directories(openvdb_ax_static PUBLIC . ) target_include_directories(openvdb_ax_static SYSTEM PUBLIC ${LLVM_INCLUDE_DIRS} ) + if(OPENVDB_BUILD_AX_GRAMMAR) + target_include_directories(openvdb_ax_static + PRIVATE ${OPENVDB_AX_GRAMMAR_DIR} + ) + target_compile_definitions(openvdb_ax_static + PRIVATE -DOPENVDB_AX_REGENERATE_GRAMMAR + ) + endif() + set_target_properties(openvdb_ax_static PROPERTIES OUTPUT_NAME openvdb_ax ) @@ -242,18 +249,24 @@ endif() if(OPENVDB_AX_SHARED) target_compile_definitions(openvdb_ax_shared PUBLIC ${LLVM_DEFINITIONS}) - if(NOT MATRIX_SUPPORT) - target_compile_definitions(openvdb_ax_shared PRIVATE -DOPENVDB_AX_NO_MATRIX) + if(OPENVDB_HAS_MATRIX_SUPPORT) + target_compile_definitions(openvdb_ax_shared PUBLIC -DOPENVDB_HAS_MATRIX_SUPPORT) endif() - target_include_directories(openvdb_ax_shared - PUBLIC . - PRIVATE ${OPENVDB_AX_GRAMMAR_DIR} - ) + target_include_directories(openvdb_ax_shared PUBLIC . ) target_include_directories(openvdb_ax_shared SYSTEM PUBLIC ${LLVM_INCLUDE_DIRS} ) + if(OPENVDB_BUILD_AX_GRAMMAR) + target_include_directories(openvdb_ax_shared + PRIVATE ${OPENVDB_AX_GRAMMAR_DIR} + ) + target_compile_definitions(openvdb_ax_shared + PRIVATE -DOPENVDB_AX_REGENERATE_GRAMMAR + ) + endif() + # @TODO - Use a similar system to vdb to extract the version number from version.h set_target_properties( @@ -289,7 +302,7 @@ if(OPENVDB_AX_SHARED) endif() endif() -install(FILES openvdb_ax/ax.h openvdb_ax/Exceptions.h openvdb_ax/version.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb_ax/) +install(FILES openvdb_ax/ax.h openvdb_ax/Exceptions.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb_ax/) install(FILES ${OPENVDB_AX_AST_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb_ax/ast) install(FILES ${OPENVDB_AX_CODEGEN_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb_ax/codegen) install(FILES ${OPENVDB_AX_COMPILER_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb_ax/compiler) diff --git a/openvdb_ax/openvdb_ax/Exceptions.h b/openvdb_ax/openvdb_ax/Exceptions.h index 875966d318..d6dd186f49 100644 --- a/openvdb_ax/openvdb_ax/Exceptions.h +++ b/openvdb_ax/openvdb_ax/Exceptions.h @@ -1,7 +1,7 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -/// @file openvdb_ax/Exceptions.h +/// @file Exceptions.h /// /// @authors Nick Avramoussis, Richard Jones /// @@ -11,6 +11,7 @@ #ifndef OPENVDB_AX_EXCEPTIONS_HAS_BEEN_INCLUDED #define OPENVDB_AX_EXCEPTIONS_HAS_BEEN_INCLUDED +#include #include #include @@ -20,14 +21,6 @@ namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { -#define OPENVDB_LLVM_EXCEPTION(_classname) \ -class OPENVDB_API _classname: public Exception \ -{ \ -public: \ - _classname() noexcept: Exception( #_classname ) {} \ - explicit _classname(const std::string& msg) noexcept: Exception( #_classname , &msg) {} \ -} - #define OPENVDB_AX_EXCEPTION(_classname) \ class OPENVDB_API _classname: public Exception \ { \ @@ -36,46 +29,14 @@ public: \ explicit _classname(const std::string& msg) noexcept: Exception( #_classname , &msg) {} \ } -/// @todo The compiler has two levels of runtime errors - The first are internal errors -/// produced either from bad API usage, bad compilation or critical faults. The -/// second are user errors produced from incorrect or unsupported usage of the -/// language. We should be able to catch and determine the instance of the error -/// easily, most likely achieving this with another level of inheritance from -/// openvdb::Exception. We should also introduce naming conventions for these -/// exceptions. - -// Runtime internal usage errors - -OPENVDB_LLVM_EXCEPTION(LLVMContextError); -OPENVDB_LLVM_EXCEPTION(LLVMInitialisationError); -OPENVDB_LLVM_EXCEPTION(LLVMIRError); -OPENVDB_LLVM_EXCEPTION(LLVMModuleError); -OPENVDB_LLVM_EXCEPTION(LLVMTargetError); -OPENVDB_LLVM_EXCEPTION(LLVMTokenError); - -// Runtime user errors - -OPENVDB_LLVM_EXCEPTION(LLVMSyntaxError); -OPENVDB_LLVM_EXCEPTION(LLVMArrayError); -OPENVDB_LLVM_EXCEPTION(LLVMBinaryOperationError); -OPENVDB_LLVM_EXCEPTION(LLVMCastError); -OPENVDB_LLVM_EXCEPTION(LLVMLoopError); -OPENVDB_LLVM_EXCEPTION(LLVMKeywordError); -OPENVDB_LLVM_EXCEPTION(LLVMDeclarationError); -OPENVDB_LLVM_EXCEPTION(LLVMFunctionError); -OPENVDB_LLVM_EXCEPTION(LLVMTypeError); -OPENVDB_LLVM_EXCEPTION(LLVMUnaryOperationError); - -// Runtime AX usage errors - -OPENVDB_AX_EXCEPTION(AXCompilerError); +// @note: Compilation errors due to invalid AX code should be collected using a separate logging system. +// These errors are only thrown upon encountering fatal errors within the compiler/executables themselves +OPENVDB_AX_EXCEPTION(AXTokenError); OPENVDB_AX_EXCEPTION(AXSyntaxError); - -// Runtime AX execution errors - +OPENVDB_AX_EXCEPTION(AXCodeGenError); +OPENVDB_AX_EXCEPTION(AXCompilerError); OPENVDB_AX_EXCEPTION(AXExecutionError); -#undef OPENVDB_LLVM_EXCEPTION #undef OPENVDB_AX_EXCEPTION } // namespace OPENVDB_VERSION_NAME diff --git a/openvdb_ax/openvdb_ax/ast/AST.cc b/openvdb_ax/openvdb_ax/ast/AST.cc deleted file mode 100644 index 4be5a72d5f..0000000000 --- a/openvdb_ax/openvdb_ax/ast/AST.cc +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright Contributors to the OpenVDB Project -// SPDX-License-Identifier: MPL-2.0 - -/// @file ast/AST.cc - -#include "AST.h" - -#include "../Exceptions.h" -// @note We need to include this to get access to axlloc. Should look to -// re-work this so we don't have to. -#include "axparser.h" /*generated by bison*/ - -#include -#include - -namespace { -// Declare this at file scope to ensure thread-safe initialization. -tbb::mutex sInitMutex; -std::string sLastParsingError; -} - -using YY_BUFFER_STATE = struct yy_buffer_state*; -extern int axparse(openvdb::ax::ast::Tree**); -extern YY_BUFFER_STATE ax_scan_string(const char * str); -extern void ax_delete_buffer(YY_BUFFER_STATE buffer); - -/// On a parsing error we store the error string in a global -/// variable so that we can access it later. Note that this does -/// not get called for invalid character lexical errors - we -/// immediate throw in the lexer in this case -extern void axerror (openvdb::ax::ast::Tree**, char const *s) { - std::ostringstream os; - os << axlloc.first_line << ":" << axlloc.first_column - << " '" << s << "'"; - sLastParsingError = os.str(); -} - -openvdb::ax::ast::Tree::Ptr -openvdb::ax::ast::parse(const char* code) -{ - tbb::mutex::scoped_lock lock(sInitMutex); - - // reset all locations - axlloc.first_line = axlloc.last_line = 1; - axlloc.first_column = axlloc.last_column = 0; - - YY_BUFFER_STATE buffer = ax_scan_string(code); - - openvdb::ax::ast::Tree* tree(nullptr); - const int result = axparse(&tree); - - openvdb::ax::ast::Tree::Ptr ptr(tree); - - ax_delete_buffer(buffer); - - if (result) { - OPENVDB_THROW(openvdb::LLVMSyntaxError, sLastParsingError) - } - - assert(ptr); - return ptr; -} - diff --git a/openvdb_ax/openvdb_ax/ast/AST.h b/openvdb_ax/openvdb_ax/ast/AST.h index 727de0c559..0fc3b51f7b 100644 --- a/openvdb_ax/openvdb_ax/ast/AST.h +++ b/openvdb_ax/openvdb_ax/ast/AST.h @@ -7,8 +7,7 @@ /// /// @brief Provides the definition for every abstract and concrete derived /// class which represent a particular abstract syntax tree (AST) node -/// type. Also provides access to the parser for generating a fully -/// constructed AST from a given string. +/// type. /// /// AST nodes represents a particular branch of a complete AST. Concrete /// nodes can be thought of as leaf node types which hold semantic @@ -28,9 +27,10 @@ #ifndef OPENVDB_AX_AST_HAS_BEEN_INCLUDED #define OPENVDB_AX_AST_HAS_BEEN_INCLUDED -#include "Tokens.h" #include "Literals.h" -#include "../version.h" +#include "Tokens.h" + +#include #include #include @@ -50,16 +50,6 @@ namespace ast { /// They are always returned from the parser. struct Tree; -/// @brief Construct an abstract syntax tree from a code snippet. If the code -/// is not well formed, as defined b the AX grammar, a runtime exception -/// will be thrown with the first lexer or parsing error. -/// -/// @return A shared pointer to a valid AST. -/// -/// @param code The code to parse -std::shared_ptr parse(const char* code); - - //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// @@ -2401,14 +2391,14 @@ struct Value : public ValueBase const Type mValue; }; +// fwd declaration for backwards compatibility, see Parse.h/.cc for definition +openvdb::ax::ast::Tree::Ptr parse(const char* code); + } // namespace ast } // namespace ax -} +} // namespace OPENVDB_VERSION_NAME } // namespace openvdb -extern FILE* yyin; -extern int yyparse(openvdb::ax::ast::Tree** tree); - #endif // OPENVDB_AX_AST_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/ast/Literals.h b/openvdb_ax/openvdb_ax/ast/Literals.h index dad49fc9c0..615f894752 100644 --- a/openvdb_ax/openvdb_ax/ast/Literals.h +++ b/openvdb_ax/openvdb_ax/ast/Literals.h @@ -11,7 +11,7 @@ #ifndef OPENVDB_AX_AST_LITERALS_HAS_BEEN_INCLUDED #define OPENVDB_AX_AST_LITERALS_HAS_BEEN_INCLUDED -#include "../version.h" +#include #include #include diff --git a/openvdb_ax/openvdb_ax/ast/Parse.cc b/openvdb_ax/openvdb_ax/ast/Parse.cc new file mode 100644 index 0000000000..f319b4ef65 --- /dev/null +++ b/openvdb_ax/openvdb_ax/ast/Parse.cc @@ -0,0 +1,76 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "Parse.h" +#include "../Exceptions.h" + +// if OPENVDB_AX_REGENERATE_GRAMMAR is defined, we've re-generated the +// grammar - include path should be set up to pull in from the temp dir +// @note We need to include this to get access to axlloc. Should look to +// re-work this so we don't have to (would require a reentrant parser) +#ifdef OPENVDB_AX_REGENERATE_GRAMMAR +#include "axparser.h" +#else +#include "../grammar/generated/axparser.h" +#endif + +#include +#include +#include + +namespace { +// Declare this at file scope to ensure thread-safe initialization. +tbb::mutex sInitMutex; +} + +openvdb::ax::Logger* axlog = nullptr; +using YY_BUFFER_STATE = struct yy_buffer_state*; +extern int axparse(openvdb::ax::ast::Tree**); +extern YY_BUFFER_STATE ax_scan_string(const char * str); +extern void ax_delete_buffer(YY_BUFFER_STATE buffer); +extern void axerror (openvdb::ax::ast::Tree**, char const *s) { + //@todo: add check for memory exhaustion + assert(axlog); + axlog->error(/*starts with 'syntax error, '*/s + 14, + {axlloc.first_line, axlloc.first_column}); +} + +openvdb::ax::ast::Tree::ConstPtr +openvdb::ax::ast::parse(const char* code, openvdb::ax::Logger& logger) +{ + tbb::mutex::scoped_lock lock(sInitMutex); + axlog = &logger; // for lexer errs + logger.setSourceCode(code); + + // reset all locations + axlloc.first_line = axlloc.last_line = 1; + axlloc.first_column = axlloc.last_column = 1; + + YY_BUFFER_STATE buffer = ax_scan_string(code); + + openvdb::ax::ast::Tree* tree(nullptr); + axparse(&tree); + axlog = nullptr; + + openvdb::ax::ast::Tree::ConstPtr ptr(const_cast(tree)); + + ax_delete_buffer(buffer); + + logger.setSourceTree(ptr); + return ptr; +} + + +openvdb::ax::ast::Tree::Ptr +openvdb::ax::ast::parse(const char* code) +{ + openvdb::ax::Logger logger( + [](const std::string& error) { + OPENVDB_THROW(openvdb::AXSyntaxError, error); + }); + + openvdb::ax::ast::Tree::ConstPtr constTree = openvdb::ax::ast::parse(code, logger); + + return std::const_pointer_cast(constTree); +} + diff --git a/openvdb_ax/openvdb_ax/ast/Parse.h b/openvdb_ax/openvdb_ax/ast/Parse.h new file mode 100644 index 0000000000..fe992ed387 --- /dev/null +++ b/openvdb_ax/openvdb_ax/ast/Parse.h @@ -0,0 +1,56 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file ast/Parse.h +/// +/// @authors Nick Avramoussis, Richard Jones +/// +/// @brief Parsing methods for creating abstract syntax trees out of AX code +/// + +#ifndef OPENVDB_AX_PARSE_HAS_BEEN_INCLUDED +#define OPENVDB_AX_PARSE_HAS_BEEN_INCLUDED + +#include "AST.h" +#include "../compiler/Logger.h" + +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +namespace ax { +namespace ast { + +/// @brief Construct an abstract syntax tree from a code snippet. If the code is +/// not well formed, as defined by the AX grammar, this will simply return +/// nullptr, with the logger collecting the errors. +/// @note The returned AST is const as the logger uses this to determine line +/// and column numbers of errors/warnings in later stages. If you need to +/// modify the tree, take a copy. +/// +/// @return A shared pointer to a valid const AST, or nullptr if errored. +/// +/// @param code The code to parse +/// @param logger The logger to collect syntax errors +/// +openvdb::ax::ast::Tree::ConstPtr parse(const char* code, ax::Logger& logger); + +/// @brief Construct an abstract syntax tree from a code snippet. +/// A runtime exception will be thrown with the first syntax error. +/// +/// @return A shared pointer to a valid AST. +/// +/// @param code The code to parse +/// +openvdb::ax::ast::Tree::Ptr parse(const char* code); + +} // namespace ast +} // namespace ax + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_AX_AST_HAS_BEEN_INCLUDED + diff --git a/openvdb_ax/openvdb_ax/ast/PrintTree.cc b/openvdb_ax/openvdb_ax/ast/PrintTree.cc index 7e38a72bce..904f15cce7 100644 --- a/openvdb_ax/openvdb_ax/ast/PrintTree.cc +++ b/openvdb_ax/openvdb_ax/ast/PrintTree.cc @@ -3,8 +3,8 @@ /// @file ast/PrintTree.cc -#include "PrintTree.h" #include "AST.h" +#include "PrintTree.h" #include "Tokens.h" #include "Visitor.h" @@ -897,7 +897,7 @@ void reprint(const ast::Node& node, std::ostream& os, const char* indent) } // namespace ast } // namespace ax -} +} // namespace OPENVDB_VERSION_NAME } // namespace openvdb diff --git a/openvdb_ax/openvdb_ax/ast/PrintTree.h b/openvdb_ax/openvdb_ax/ast/PrintTree.h index a04302d093..e372d2e65f 100644 --- a/openvdb_ax/openvdb_ax/ast/PrintTree.h +++ b/openvdb_ax/openvdb_ax/ast/PrintTree.h @@ -12,9 +12,9 @@ #ifndef OPENVDB_AX_AST_PRINT_TREE_HAS_BEEN_INCLUDED #define OPENVDB_AX_AST_PRINT_TREE_HAS_BEEN_INCLUDED -#include "../version.h" +#include -#include +#include namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE @@ -52,7 +52,7 @@ void reprint(const ast::Node& node, } // namespace ast } // namespace ax -} +} // namespace OPENVDB_VERSION_NAME } // namespace openvdb #endif // OPENVDB_AX_AST_PRINT_TREE_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/ast/Scanners.cc b/openvdb_ax/openvdb_ax/ast/Scanners.cc index 6de6123101..1d60136626 100644 --- a/openvdb_ax/openvdb_ax/ast/Scanners.cc +++ b/openvdb_ax/openvdb_ax/ast/Scanners.cc @@ -561,9 +561,9 @@ void linearize(const ast::Node& node, std::vector& list) collectNodeType(node, list); } -} -} -} -} +} // namespace ast +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb_ax/openvdb_ax/ast/Scanners.h b/openvdb_ax/openvdb_ax/ast/Scanners.h index 349cd7f639..855a6cf1c8 100644 --- a/openvdb_ax/openvdb_ax/ast/Scanners.h +++ b/openvdb_ax/openvdb_ax/ast/Scanners.h @@ -15,6 +15,8 @@ #include "AST.h" #include "Visitor.h" +#include + #include namespace openvdb { @@ -193,10 +195,10 @@ inline void visitNodeType(const ast::Node& node, const OpT& op) visitOp.traverse(&node); } -} -} -} -} +} // namespace ast +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_COMPILER_AST_SCANNERS_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/ast/Tokens.h b/openvdb_ax/openvdb_ax/ast/Tokens.h index 179c2dd4cb..aaa85eb008 100644 --- a/openvdb_ax/openvdb_ax/ast/Tokens.h +++ b/openvdb_ax/openvdb_ax/ast/Tokens.h @@ -12,9 +12,11 @@ #ifndef OPENVDB_AX_AST_TOKENS_HAS_BEEN_INCLUDED #define OPENVDB_AX_AST_TOKENS_HAS_BEEN_INCLUDED -#include -#include "../version.h" #include "../Exceptions.h" + +#include +#include + #include namespace openvdb { @@ -274,7 +276,7 @@ inline OperatorToken operatorTokenFromName(const std::string& name) if (name == "&=") return BITANDEQUALS; if (name == "^=") return BITXOREQUALS; if (name == "|=") return BITOREQUALS; - OPENVDB_THROW(AXSyntaxError, "Unsupported op \"" + name + "\""); + OPENVDB_THROW(AXTokenError, "Unsupported op \"" + name + "\""); } inline std::string operatorNameFromToken(const OperatorToken token) @@ -312,7 +314,7 @@ inline std::string operatorNameFromToken(const OperatorToken token) case BITXOREQUALS : return "^="; case BITOREQUALS : return "|="; default : - OPENVDB_THROW(AXSyntaxError, "Unsupported op"); + OPENVDB_THROW(AXTokenError, "Unsupported op"); } } @@ -330,7 +332,7 @@ inline std::string loopNameFromToken(const LoopToken loop) case DO : return "do"; case WHILE : return "while"; default : - OPENVDB_THROW(AXSyntaxError, "Unsupported loop"); + OPENVDB_THROW(AXTokenError, "Unsupported loop"); } } @@ -348,17 +350,17 @@ inline std::string keywordNameFromToken(const KeywordToken keyw) case BREAK : return "break"; case CONTINUE : return "continue"; default : - OPENVDB_THROW(AXSyntaxError, "Unsupported keyword"); + OPENVDB_THROW(AXTokenError, "Unsupported keyword"); } } -} +} // namespace tokens -} -} -} -} +} // namespace ast +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_AST_TOKENS_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/ast/Visitor.h b/openvdb_ax/openvdb_ax/ast/Visitor.h index 820b5fc092..4d30c3b30e 100644 --- a/openvdb_ax/openvdb_ax/ast/Visitor.h +++ b/openvdb_ax/openvdb_ax/ast/Visitor.h @@ -14,11 +14,11 @@ #ifndef OPENVDB_AX_AST_VISITOR_HAS_BEEN_INCLUDED #define OPENVDB_AX_AST_VISITOR_HAS_BEEN_INCLUDED -#include "Tokens.h" +#include "AST.h" #include "Literals.h" +#include "Tokens.h" -#include "../version.h" -#include "../ast/AST.h" +#include #include @@ -465,7 +465,7 @@ struct Visitor } // namespace ast } // namespace ax -} +} // namespace OPENVDB_VERSION_NAME } // namespace openvdb #endif // OPENVDB_AX_AST_VISITOR_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/ax.cc b/openvdb_ax/openvdb_ax/ax.cc index 6b8f56f01e..e678fb9152 100644 --- a/openvdb_ax/openvdb_ax/ax.cc +++ b/openvdb_ax/openvdb_ax/ax.cc @@ -3,10 +3,20 @@ #include "ax.h" #include "ast/AST.h" +#include "compiler/Logger.h" #include "compiler/Compiler.h" #include "compiler/PointExecutable.h" #include "compiler/VolumeExecutable.h" +#include +#include +#include // version numbers +#include // InitializeNativeTarget +#include // llvm_shutdown +#include // LLVMLinkInMCJIT + +#include + namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { @@ -17,19 +27,22 @@ namespace ax { void run(const char* ax, openvdb::GridBase& grid) { + // Construct a logger that will output errors to cerr and suppress warnings + openvdb::ax::Logger logger; // Construct a generic compiler openvdb::ax::Compiler compiler; // Parse the provided code and produce an abstract syntax tree // @note Throws with parser errors if invalid. Parsable code does not // necessarily equate to compilable code - const openvdb::ax::ast::Tree::ConstPtr ast = openvdb::ax::ast::parse(ax); + const openvdb::ax::ast::Tree::ConstPtr + ast = openvdb::ax::ast::parse(ax, logger); if (grid.isType()) { // Compile for Point support and produce an executable // @note Throws compiler errors on invalid code. On success, returns // the executable which can be used multiple times on any inputs const openvdb::ax::PointExecutable::Ptr exe = - compiler.compile(*ast); + compiler.compile(*ast, logger); // Execute on the provided points // @note Throws on invalid point inputs such as mismatching types exe->execute(static_cast(grid)); @@ -39,7 +52,7 @@ void run(const char* ax, openvdb::GridBase& grid) // @note Throws compiler errors on invalid code. On success, returns // the executable which can be used multiple times on any inputs const openvdb::ax::VolumeExecutable::Ptr exe = - compiler.compile(*ast); + compiler.compile(*ast, logger); // Execute on the provided numerical grid // @note Throws on invalid grid inputs such as mismatching types exe->execute(grid); @@ -58,19 +71,21 @@ void run(const char* ax, openvdb::GridPtrVec& grids) break; } } - + // Construct a logger that will output errors to cerr and suppress warnings + openvdb::ax::Logger logger; // Construct a generic compiler openvdb::ax::Compiler compiler; // Parse the provided code and produce an abstract syntax tree // @note Throws with parser errors if invalid. Parsable code does not // necessarily equate to compilable code - const openvdb::ax::ast::Tree::ConstPtr ast = openvdb::ax::ast::parse(ax); + const openvdb::ax::ast::Tree::ConstPtr + ast = openvdb::ax::ast::parse(ax, logger); if (points) { // Compile for Point support and produce an executable // @note Throws compiler errors on invalid code. On success, returns // the executable which can be used multiple times on any inputs const openvdb::ax::PointExecutable::Ptr exe = - compiler.compile(*ast); + compiler.compile(*ast, logger); // Execute on the provided points individually // @note Throws on invalid point inputs such as mismatching types for (auto& grid : grids) { @@ -82,13 +97,107 @@ void run(const char* ax, openvdb::GridPtrVec& grids) // @note Throws compiler errors on invalid code. On success, returns // the executable which can be used multiple times on any inputs const openvdb::ax::VolumeExecutable::Ptr exe = - compiler.compile(*ast); + compiler.compile(*ast, logger); // Execute on the provided volumes // @note Throws on invalid grid inputs such as mismatching types exe->execute(grids); } } +namespace { +// Declare this at file scope to ensure thread-safe initialization. +tbb::mutex sInitMutex; +bool sIsInitialized = false; +bool sShutdown = false; +} + +bool isInitialized() +{ + tbb::mutex::scoped_lock lock(sInitMutex); + return sIsInitialized; } + +void initialize() +{ + tbb::mutex::scoped_lock lock(sInitMutex); + if (sIsInitialized) return; + + if (sShutdown) { + OPENVDB_THROW(AXCompilerError, + "Unable to re-initialize LLVM target after uninitialize has been called."); + } + + // Init JIT + if (llvm::InitializeNativeTarget() || + llvm::InitializeNativeTargetAsmPrinter() || + llvm::InitializeNativeTargetAsmParser()) + { + OPENVDB_THROW(AXCompilerError, + "Failed to initialize LLVM target for JIT"); + } + + // required on some systems + LLVMLinkInMCJIT(); + + // Initialize passes + llvm::PassRegistry& registry = *llvm::PassRegistry::getPassRegistry(); + llvm::initializeCore(registry); + llvm::initializeScalarOpts(registry); + llvm::initializeObjCARCOpts(registry); + llvm::initializeVectorization(registry); + llvm::initializeIPO(registry); + llvm::initializeAnalysis(registry); + llvm::initializeTransformUtils(registry); + llvm::initializeInstCombine(registry); +#if LLVM_VERSION_MAJOR > 6 + llvm::initializeAggressiveInstCombine(registry); +#endif + llvm::initializeInstrumentation(registry); + llvm::initializeTarget(registry); + // For codegen passes, only passes that do IR to IR transformation are + // supported. + llvm::initializeExpandMemCmpPassPass(registry); + llvm::initializeScalarizeMaskedMemIntrinPass(registry); + llvm::initializeCodeGenPreparePass(registry); + llvm::initializeAtomicExpandPass(registry); + llvm::initializeRewriteSymbolsLegacyPassPass(registry); + llvm::initializeWinEHPreparePass(registry); + llvm::initializeDwarfEHPreparePass(registry); + llvm::initializeSafeStackLegacyPassPass(registry); + llvm::initializeSjLjEHPreparePass(registry); + llvm::initializePreISelIntrinsicLoweringLegacyPassPass(registry); + llvm::initializeGlobalMergePass(registry); +#if LLVM_VERSION_MAJOR > 6 + llvm::initializeIndirectBrExpandPassPass(registry); +#endif +#if LLVM_VERSION_MAJOR > 7 + llvm::initializeInterleavedLoadCombinePass(registry); +#endif + llvm::initializeInterleavedAccessPass(registry); + llvm::initializeEntryExitInstrumenterPass(registry); + llvm::initializePostInlineEntryExitInstrumenterPass(registry); + llvm::initializeUnreachableBlockElimLegacyPassPass(registry); + llvm::initializeExpandReductionsPass(registry); +#if LLVM_VERSION_MAJOR > 6 + llvm::initializeWasmEHPreparePass(registry); +#endif + llvm::initializeWriteBitcodePassPass(registry); + + sIsInitialized = true; } + +void uninitialize() +{ + tbb::mutex::scoped_lock lock(sInitMutex); + if (!sIsInitialized) return; + + // @todo consider replacing with storage to Support/InitLLVM + llvm::llvm_shutdown(); + + sIsInitialized = false; + sShutdown = true; } + +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb_ax/openvdb_ax/ax.h b/openvdb_ax/openvdb_ax/ax.h index 2a51393a39..aeee4d4649 100644 --- a/openvdb_ax/openvdb_ax/ax.h +++ b/openvdb_ax/openvdb_ax/ax.h @@ -19,6 +19,7 @@ #define OPENVDB_AX_AX_HAS_BEEN_INCLUDED #include +#include namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE @@ -29,8 +30,6 @@ namespace ax { /// @details Must be called before any AX compilation or execution is performed. /// Can be safely called from multiple threads. Cannot be called after /// uninitialize has been called. -/// @note The implementation for the initialize methods can be found in -/// compiler/Compiler.cc void initialize(); /// @brief Check to see if OpenVDB AX components have been initialized. @@ -88,8 +87,8 @@ void run(const char* ax, openvdb::GridBase& grid); /// @param grids The grids to which to apply the compiled AX function void run(const char* ax, openvdb::GridPtrVec& grids); -} -} -} +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_AX_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/cmd/openvdb_ax.cc b/openvdb_ax/openvdb_ax/cmd/openvdb_ax.cc index 4c172f8cd8..83d37fe4d6 100644 --- a/openvdb_ax/openvdb_ax/cmd/openvdb_ax.cc +++ b/openvdb_ax/openvdb_ax/cmd/openvdb_ax.cc @@ -9,17 +9,19 @@ /// run and analyze AX code. /// -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../codegen/Functions.h" -#include "../compiler/Compiler.h" -#include "../compiler/AttributeRegistry.h" -#include "../compiler/CompilerOptions.h" -#include "../compiler/PointExecutable.h" -#include "../compiler/VolumeExecutable.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include #include #include @@ -42,6 +44,8 @@ void usage [[noreturn]] (int exitStatus = EXIT_FAILURE) " -f file.txt execute text file containing a code snippet on the input.vdb file\n" << " -v verbose (print timing and diagnostics)\n" << " --opt level set an optimization level on the generated IR [NONE, O0, O1, O2, Os, Oz, O3]\n" << + " --werror set warnings as errors\n" << + " --max-errors n sets the maximum number of error messages to n, a value of 0 (default) allows all error messages\n" << " analyze parse the provided code and enter analysis mode\n" << " --ast-print descriptive print the abstract syntax tree generated\n" << " --re-print re-interpret print of the provided code after ast traversal\n" << @@ -67,6 +71,10 @@ struct ProgOptions Mode mMode = Execute; + // Compilation options + size_t mMaxErrors = 0; + bool mWarningsAsErrors = false; + // Execute options std::unique_ptr mInputCode = nullptr; std::string mInputVDBFile = ""; @@ -313,6 +321,10 @@ main(int argc, char *argv[]) loadSnippetFile(argv[i], *opts.mInputCode); } else if (parser.check(i, "-v", 0)) { opts.mVerbose = true; + } else if (parser.check(i, "--max-errors")) { + opts.mMaxErrors = atoi(argv[++i]); + } else if (parser.check(i, "--werror", 0)) { + opts.mWarningsAsErrors = true; } else if (parser.check(i, "--list", 0)) { opts.mFunctionList = true; opts.mInitCompile = true; // need to intialize llvm @@ -382,7 +394,7 @@ main(int argc, char *argv[]) } if (opts.mVerbose) { - axlog("OpenVDB AX " << openvdb::ax::getLibraryVersionString() << '\n'); + axlog("OpenVDB AX " << openvdb::getLibraryVersionString() << '\n'); axlog("----------------\n"); axlog("Inputs\n"); axlog(" mode : " << modeString(opts.mMode)); @@ -491,13 +503,27 @@ main(int argc, char *argv[]) return EXIT_SUCCESS; } + // set up logger + + openvdb::ax::Logger + logs([](const std::string& msg) { std::cerr << msg << std::endl; }, + [](const std::string& msg) { std::cerr << msg << std::endl; }); + logs.setMaxErrors(opts.mMaxErrors); + logs.setWarningsAsErrors(opts.mWarningsAsErrors); + logs.setPrintLines(true); + logs.setNumberedOutput(true); + // parse axtimer(); axlog("[INFO] Parsing input code" << std::flush); + const openvdb::ax::ast::Tree::ConstPtr syntaxTree = - openvdb::ax::ast::parse(opts.mInputCode->c_str()); + openvdb::ax::ast::parse(opts.mInputCode->c_str(), logs); axlog(": " << axtime() << '\n'); + if (!syntaxTree) { + return EXIT_FAILURE; + } if (opts.mMode == ProgOptions::Analyze) { axlog("[INFO] Running analysis options\n" << std::flush); @@ -561,22 +587,24 @@ main(int argc, char *argv[]) opts.mCompileFor == ProgOptions::Compilation::Points) { axtimer(); axlog("[INFO] Compiling for VDB Points\n" << std::flush); - std::vector warnings; - try { compiler->compile(*syntaxTree, customData, &warnings); } + try { + compiler->compile(*syntaxTree, logs, customData); + if (logs.hasError()) { + axlog("[INFO] Compilation error(s)!\n"); + psuccess = false; + } + } catch (std::exception& e) { psuccess = false; - axlog("[INFO] Compilation error!\n"); + axlog("[INFO] Fatal error!\n"); OPENVDB_LOG_ERROR(e.what()); } + const bool hasWarning = logs.hasWarning(); if (psuccess) { axlog("[INFO] | Compilation successful"); - if (!warnings.empty()) axlog(" with warnings"); - axlog('\n' << std::flush) + if (hasWarning) axlog(" with warning(s)\n"); } axlog("[INFO] | " << axtime() << '\n' << std::flush); - for (const std::string& warning : warnings) { - OPENVDB_LOG_WARN(warning); - } } bool vsuccess = true; @@ -585,22 +613,24 @@ main(int argc, char *argv[]) opts.mCompileFor == ProgOptions::Compilation::Volumes) { axtimer(); axlog("[INFO] Compiling for VDB Volumes\n" << std::flush); - std::vector warnings; - try { compiler->compile(*syntaxTree, customData, &warnings); } + try { + compiler->compile(*syntaxTree, logs, customData); + if (logs.hasError()) { + axlog("[INFO] Compilation error(s)!\n"); + vsuccess = false; + } + } catch (std::exception& e) { vsuccess = false; - axlog("[INFO] Compilation error!\n"); + axlog("[INFO] Fatal error!\n"); OPENVDB_LOG_ERROR(e.what()); } + const bool hasWarning = logs.hasWarning(); if (vsuccess) { axlog("[INFO] | Compilation successful"); - if (!warnings.empty()) axlog(" with warnings"); - axlog('\n'); + if (hasWarning) axlog(" with warning(s)\n"); } axlog("[INFO] | " << axtime() << '\n' << std::flush); - for (const std::string& warning : warnings) { - OPENVDB_LOG_WARN(warning); - } } return ((vsuccess && psuccess) ? EXIT_SUCCESS : EXIT_FAILURE); @@ -613,23 +643,29 @@ main(int argc, char *argv[]) axlog("[INFO] VDB PointDataGrids Found\n" << std::flush); - std::vector warnings; - openvdb::ax::PointExecutable::Ptr pointExecutable; + openvdb::ax::PointExecutable::Ptr pointExe; axtimer(); try { axlog("[INFO] Compiling for VDB Points\n" << std::flush); - pointExecutable = compiler->compile(*syntaxTree, customData, &warnings); + pointExe = compiler->compile(*syntaxTree, logs, customData); } catch (std::exception& e) { - OPENVDB_LOG_FATAL("Compilation error!\nErrors:\n" << e.what()); + OPENVDB_LOG_FATAL("Fatal error!\nErrors:\n" << e.what()); return EXIT_FAILURE; } - for (const std::string& warning : warnings) { - OPENVDB_LOG_WARN(warning); + if (pointExe) { + axlog("[INFO] | Compilation successful"); + if (logs.hasWarning()) { + axlog(" with warning(s)\n"); + } + } + else { + if (logs.hasError()) { + axlog("[INFO] Compilation error(s)!\n"); + } + return EXIT_FAILURE; } - - axlog("[INFO] | Compilation success.\n"); axlog("[INFO] | " << axtime() << '\n' << std::flush); size_t total = 0, count = 1; @@ -650,7 +686,7 @@ main(int argc, char *argv[]) ++count; try { - pointExecutable->execute(*points); + pointExe->execute(*points); if (openvdb::ax::ast::callsFunction(*syntaxTree, "deletepoint")) { openvdb::points::deleteFromGroup(points->tree(), "dead", false, false); } @@ -672,23 +708,26 @@ main(int argc, char *argv[]) axlog("[INFO] VDB Volumes Found\n" << std::flush); - std::vector warnings; - openvdb::ax::VolumeExecutable::Ptr volumeExecutable; - - axtimer(); + openvdb::ax::VolumeExecutable::Ptr volumeExe; try { - axlog("[INFO] Compiling for VDB Volumes\n" << std::flush); - volumeExecutable = compiler->compile(*syntaxTree, customData, &warnings); + axlog("[INFO] Compiling for VDB Points\n" << std::flush); + volumeExe = compiler->compile(*syntaxTree, logs, customData); } catch (std::exception& e) { - OPENVDB_LOG_FATAL("Compilation error!\nErrors:\n" << e.what()); + OPENVDB_LOG_FATAL("Fatal error!\nErrors:\n" << e.what()); return EXIT_FAILURE; } - for (const std::string& warning : warnings) { - OPENVDB_LOG_WARN(warning); + if (volumeExe) { + axlog("[INFO] | Compilation successful"); + if (logs.hasWarning()) { + axlog(" with warning(s)\n"); } + } + else { + if (logs.hasError()) { + axlog("[INFO] Compilation error(s)!\n"); + } + return EXIT_FAILURE; } - - axlog("[INFO] | Compilation success.\n"); axlog("[INFO] | " << axtime() << '\n' << std::flush); if (opts.mVerbose) { @@ -703,7 +742,7 @@ main(int argc, char *argv[]) axlog(std::flush); } - try { volumeExecutable->execute(*grids); } + try { volumeExe->execute(*grids); } catch (std::exception& e) { OPENVDB_LOG_FATAL("Execution error!\nErrors:\n" << e.what()); return EXIT_FAILURE; diff --git a/openvdb_ax/openvdb_ax/codegen/ComputeGenerator.cc b/openvdb_ax/openvdb_ax/codegen/ComputeGenerator.cc index 65d4e9d31e..5049b75916 100644 --- a/openvdb_ax/openvdb_ax/codegen/ComputeGenerator.cc +++ b/openvdb_ax/openvdb_ax/codegen/ComputeGenerator.cc @@ -4,7 +4,6 @@ /// @file codegen/ComputeGenerator.cc #include "ComputeGenerator.h" - #include "FunctionRegistry.h" #include "FunctionTypes.h" #include "Types.h" @@ -35,6 +34,50 @@ namespace OPENVDB_VERSION_NAME { namespace ax { namespace codegen { +namespace { + +inline void +printType(const llvm::Type* type, llvm::raw_os_ostream& stream, const bool axTypes) +{ + const ast::tokens::CoreType token = + axTypes ? tokenFromLLVMType(type) : ast::tokens::UNKNOWN; + if (token == ast::tokens::UNKNOWN) type->print(stream); + else stream << ast::tokens::typeStringFromToken(token); +} + +inline void +printTypes(llvm::raw_os_ostream& stream, + const std::vector& types, + const std::vector& names = {}, + const std::string sep = "; ", + const bool axTypes = true) +{ + if (types.empty()) return; + auto typeIter = types.cbegin(); + std::vector::const_iterator nameIter; + if (!names.empty()) nameIter = names.cbegin(); + + for (; typeIter != types.cend() - 1; ++typeIter) { + printType(*typeIter, stream, axTypes); + if (!names.empty() && nameIter != names.cend()) { + if (*nameIter && (*nameIter)[0] != '\0') { + stream << ' ' << *nameIter; + } + ++nameIter; + } + stream << sep; + } + + printType(*typeIter, stream, axTypes); + if (!names.empty() && nameIter != names.cend()) { + if (*nameIter && (*nameIter)[0] != '\0') { + stream << ' ' << *nameIter; + } + } +} + +} + const std::array& ComputeKernel::getArgumentKeys() { @@ -55,7 +98,7 @@ std::string ComputeKernel::getDefaultName() { return "ax.compute"; } ComputeGenerator::ComputeGenerator(llvm::Module& module, const FunctionOptions& options, FunctionRegistry& functionRegistry, - std::vector* const warnings) + Logger& logger) : mModule(module) , mContext(module.getContext()) , mBuilder(llvm::IRBuilder<>(module.getContext())) @@ -63,9 +106,9 @@ ComputeGenerator::ComputeGenerator(llvm::Module& module, , mBreakContinueStack() , mScopeIndex(1) , mSymbolTables() - , mWarnings(warnings) , mFunction(nullptr) , mOptions(options) + , mLog(logger) , mFunctionRegistry(functionRegistry) {} bool ComputeGenerator::generate(const ast::Tree& tree) @@ -92,7 +135,9 @@ bool ComputeGenerator::generate(const ast::Tree& tree) "entry_" + ComputeKernel::getDefaultName(), mFunction); mBuilder.SetInsertPoint(entry); - return this->traverse(&tree); + // if traverse is false, log should have error, but can error + // without stopping traversal, so check both + return this->traverse(&tree) && !mLog.hasError(); } bool ComputeGenerator::visit(const ast::CommaOperator* comma) @@ -299,8 +344,7 @@ bool ComputeGenerator::visit(const ast::TernaryOperator* tern) } } else { - OPENVDB_THROW(LLVMCastError, - "Unsupported implicit cast in ternary operation."); + if (!mLog.error("unsupported implicit cast in ternary operation.", tern)) return false; } } } @@ -336,6 +380,11 @@ bool ComputeGenerator::visit(const ast::Loop* loop) llvm::BasicBlock* postBodyBlock = conditionBlock; const ast::tokens::LoopToken loopType = loop->loopType(); + assert((loopType == ast::tokens::LoopToken::FOR || + loopType == ast::tokens::LoopToken::WHILE || + loopType == ast::tokens::LoopToken::DO) && + "Unsupported loop type"); + if (loopType == ast::tokens::LoopToken::FOR) { // init -> condition -> body -> iter -> condition ... continue @@ -363,9 +412,6 @@ bool ComputeGenerator::visit(const ast::Loop* loop) // condition -> body -> condition ... continue mBuilder.CreateBr(conditionBlock); } - else { // unrecognised loop type - OPENVDB_THROW(LLVMCastError, "Unsupported loop type."); - } // store the destinations for break and continue mBreakContinueStack.push({postLoopBlock, postBodyBlock}); @@ -401,6 +447,11 @@ bool ComputeGenerator::visit(const ast::Loop* loop) bool ComputeGenerator::visit(const ast::Keyword* node) { const ast::tokens::KeywordToken keyw = node->keyword(); + assert((keyw == ast::tokens::KeywordToken::RETURN || + keyw == ast::tokens::KeywordToken::BREAK || + keyw == ast::tokens::KeywordToken::CONTINUE) && + "Unsupported keyword"); + if (keyw == ast::tokens::KeywordToken::RETURN) { mBuilder.CreateRetVoid(); } @@ -417,26 +468,23 @@ bool ComputeGenerator::visit(const ast::Keyword* node) parentLoop = child->parent(); } if (!parentLoop) { - OPENVDB_THROW(LLVMSyntaxError, "Keyword \"" + - ast::tokens::keywordNameFromToken(keyw) + "\" used outside of loop."); + if (!mLog.error("keyword \"" + ast::tokens::keywordNameFromToken(keyw) + + "\" used outside of loop.", node)) return false; } + else { + const std::pair + breakContinue = mBreakContinueStack.top(); - const std::pair - breakContinue = mBreakContinueStack.top(); - - if (keyw == ast::tokens::KeywordToken::BREAK) { - assert(breakContinue.first); - mBuilder.CreateBr(breakContinue.first); - } - else if (keyw == ast::tokens::KeywordToken::CONTINUE) { - assert(breakContinue.second); - mBuilder.CreateBr(breakContinue.second); + if (keyw == ast::tokens::KeywordToken::BREAK) { + assert(breakContinue.first); + mBuilder.CreateBr(breakContinue.first); + } + else if (keyw == ast::tokens::KeywordToken::CONTINUE) { + assert(breakContinue.second); + mBuilder.CreateBr(breakContinue.second); + } } } - else { - OPENVDB_THROW(LLVMKeywordError, "Unsupported keyword '\"" + - ast::tokens::keywordNameFromToken(keyw) + "\"'" ); - } llvm::BasicBlock* nullBlock = llvm::BasicBlock::Create(mContext, "null", mFunction); // insert all remaining instructions in scope into a null block @@ -449,10 +497,10 @@ bool ComputeGenerator::visit(const ast::BinaryOperator* node) { llvm::Value* rhs = mValues.top(); mValues.pop(); llvm::Value* lhs = mValues.top(); mValues.pop(); - llvm::Value* result = this->binaryExpression(lhs, rhs, node->operation()); + llvm::Value* result = nullptr; + if (!this->binaryExpression(result, lhs, rhs, node->operation(), node)) return false; - assert(result); - mValues.push(result); + if (result) mValues.push(result); return true; } @@ -467,8 +515,9 @@ bool ComputeGenerator::visit(const ast::UnaryOperator* node) if (token != ast::tokens::MINUS && token != ast::tokens::BITNOT && token != ast::tokens::NOT) { - OPENVDB_THROW(LLVMUnaryOperationError, "Unrecognised unary operator \"" + - ast::tokens::operatorNameFromToken(token) + "\""); + if (!mLog.error("unrecognised unary operator \"" + + ast::tokens::operatorNameFromToken(token) + "\"", node)) return false; + return true; } llvm::Value* value = mValues.top(); mValues.pop(); @@ -500,8 +549,8 @@ bool ComputeGenerator::visit(const ast::UnaryOperator* node) if (token == ast::tokens::MINUS) result = mBuilder.CreateFNeg(value); else if (token == ast::tokens::NOT) result = mBuilder.CreateFCmpOEQ(value, llvm::ConstantFP::get(type, 0)); else if (token == ast::tokens::BITNOT) { - OPENVDB_THROW(LLVMUnaryOperationError, "Unable to perform operation \"" - + ast::tokens::operatorNameFromToken(token) + "\" on floating points values"); + if (!mLog.error("unable to perform operation \"" + + ast::tokens::operatorNameFromToken(token) + "\" on floating points values", node)) return false; } } else if (type->isArrayTy()) { @@ -536,25 +585,21 @@ bool ComputeGenerator::visit(const ast::UnaryOperator* node) } else { //@todo support NOT? - OPENVDB_THROW(LLVMUnaryOperationError, "Unable to perform operation \"" - + ast::tokens::operatorNameFromToken(token) + - "\" on floating point vector/matrices"); + if (!mLog.error("unable to perform operation \"" + + ast::tokens::operatorNameFromToken(token) + "\" on arrays/vectors", node)) return false; } } else { - OPENVDB_THROW(LLVMUnaryOperationError, - "Unrecognised array element type"); + if (!mLog.error("unrecognised array element type", node)) return false; } result = arrayPack(elements, mBuilder); } else { - OPENVDB_THROW(LLVMUnaryOperationError, - "Value is not a scalar, vector or matrix."); + if (!mLog.error("value is not a scalar or vector", node)) return false; } - assert(result); - mValues.push(result); + if (result) mValues.push(result); return true; } @@ -566,10 +611,13 @@ bool ComputeGenerator::visit(const ast::AssignExpression* assign) llvm::Type* rhsType = rhs->getType(); if (assign->isCompound()) { - rhs = this->binaryExpression(lhs, rhs, assign->operation()); - rhsType = rhs->getType(); + llvm::Value* rhsValue = nullptr; + if (!this->binaryExpression(rhsValue, lhs, rhs, assign->operation(), assign)) return false; + if (rhsValue) { + rhs = rhsValue; + rhsType = rhs->getType(); + } } - // rhs must be loaded for assignExpression() if it's a scalar if (rhsType->isPointerTy()) { rhsType = rhsType->getPointerElementType(); @@ -578,38 +626,41 @@ bool ComputeGenerator::visit(const ast::AssignExpression* assign) } } - this->assignExpression(lhs, rhs); + if (!this->assignExpression(lhs, rhs, assign)) return false; + return true; } bool ComputeGenerator::visit(const ast::Crement* node) { - llvm::Value* value = mValues.top(); mValues.pop(); - + llvm::Value* value = mValues.top(); if (!value->getType()->isPointerTy()) { - OPENVDB_THROW(LLVMBinaryOperationError, "Unable to assign to an rvalue"); + if (!mLog.error("unable to assign to an rvalue", node)) return false; + return true; } - + mValues.pop(); llvm::Value* rvalue = mBuilder.CreateLoad(value); llvm::Type* type = rvalue->getType(); if (type->isIntegerTy(1) || (!type->isIntegerTy() && !type->isFloatingPointTy())) { - OPENVDB_THROW(LLVMTypeError, "Variable is an unsupported type for " - "crement. Must be a non-boolean scalar."); + if (!mLog.error("variable is an unsupported type for " + "crement. Must be a non-boolean scalar.", node)) return false; + return true; } + else { + llvm::Value* crement = nullptr; + assert((node->increment() || node->decrement()) && "unrecognised crement operation"); + if (node->increment()) crement = LLVMType::get(mContext, 1); + else if (node->decrement()) crement = LLVMType::get(mContext, -1); - llvm::Value* crement = nullptr; - if (node->increment()) crement = LLVMType::get(mContext, 1); - else if (node->decrement()) crement = LLVMType::get(mContext, -1); - else OPENVDB_THROW(LLVMTokenError, "Unrecognised crement operation token"); - - crement = arithmeticConversion(crement, type, mBuilder); - if (type->isIntegerTy()) crement = mBuilder.CreateAdd(rvalue, crement); - if (type->isFloatingPointTy()) crement = mBuilder.CreateFAdd(rvalue, crement); + crement = arithmeticConversion(crement, type, mBuilder); + if (type->isIntegerTy()) crement = mBuilder.CreateAdd(rvalue, crement); + if (type->isFloatingPointTy()) crement = mBuilder.CreateFAdd(rvalue, crement); - mBuilder.CreateStore(crement, value); + mBuilder.CreateStore(crement, value); - // decide what to put on the expression stack + // decide what to put on the expression stack + } if (node->post()) mValues.push(rvalue); else mValues.push(value); @@ -618,43 +669,90 @@ bool ComputeGenerator::visit(const ast::Crement* node) bool ComputeGenerator::visit(const ast::FunctionCall* node) { + const FunctionGroup::Ptr function = this->getFunction(node->name()); - const size_t args = node->children(); - assert(mValues.size() >= args); - - // initialize arguments. scalars are always passed by value, arrays - // and strings always by pointer - llvm::Type* strType = LLVMType::get(mContext); - - std::vector arguments; - arguments.resize(args); - - for (auto r = arguments.rbegin(); r != arguments.rend(); ++r) { - llvm::Value* arg = mValues.top(); mValues.pop(); - llvm::Type* type = arg->getType(); - if (type->isPointerTy()) { - type = type->getPointerElementType(); - if (type->isIntegerTy() || type->isFloatingPointTy()) { - // pass by value - arg = mBuilder.CreateLoad(arg); + if (!function) { + if (!mLog.error("unable to locate function \"" + node->name() + "\".", node)) return false; + } + else { + const size_t args = node->children(); + assert(mValues.size() >= args); + + // initialize arguments. scalars are always passed by value, arrays + // and strings always by pointer + + std::vector arguments; + arguments.resize(args); + + for (auto r = arguments.rbegin(); r != arguments.rend(); ++r) { + llvm::Value* arg = mValues.top(); mValues.pop(); + llvm::Type* type = arg->getType(); + if (type->isPointerTy()) { + type = type->getPointerElementType(); + if (type->isIntegerTy() || type->isFloatingPointTy()) { + // pass by value + arg = mBuilder.CreateLoad(arg); + } } - else if (type->isArrayTy()) {/*pass by pointer*/} - else if (type == strType) {/*pass by pointer*/} + else { + // arrays should never be loaded + assert(!type->isArrayTy() && type != LLVMType::get(mContext)); + if (type->isIntegerTy() || type->isFloatingPointTy()) { + /*pass by value*/ + } + } + *r = arg; + } + + std::vector inputTypes; + valuesToTypes(arguments, inputTypes); + + Function::SignatureMatch match; + const Function::Ptr target = function->match(inputTypes, mContext, &match); + + if (!target) { + assert(!function->list().empty() + && "FunctionGroup has no function declarations"); + + std::ostringstream os; + if (match == Function::SignatureMatch::None) { + os << "wrong number of arguments. \"" << node->name() << "\"" + << " was called with: ("; + llvm::raw_os_ostream stream(os); + printTypes(stream, inputTypes); + stream << ")"; + } + else { + // match == Function::SignatureMatch::Size + os << "no matching function for "; + printSignature(os, inputTypes, + LLVMType::get(mContext), + node->name().c_str(), {}, true); + os << "."; + } + + os << " \ncandidates are: "; + for (const auto& sig : function->list()) { + os << std::endl; + sig->print(mContext, os, node->name().c_str()); + } + if (!mLog.error(os.str(), node)) return false; } else { - // arrays should never be loaded - assert(!type->isArrayTy()); - assert(type != strType); - if (type->isIntegerTy() || type->isFloatingPointTy()) { - /*pass by value*/ + llvm::Value* result = nullptr; + if (match == Function::SignatureMatch::Implicit) { + if (!mLog.warning("implicit conversion in function call", node)) return false; + result = target->call(arguments, mBuilder, /*cast=*/true); + } + else { + // match == Function::SignatureMatch::Explicit + result = target->call(arguments, mBuilder, /*cast=*/false); } + + assert(result && "Function has been invoked with no valid llvm Value return"); + mValues.push(result); } - *r = arg; } - - llvm::Value* result = function->execute(arguments, mBuilder); - assert(result); - mValues.push(result); return true; } @@ -667,28 +765,30 @@ bool ComputeGenerator::visit(const ast::Cast* node) value->getType(); if (!type->isIntegerTy() && !type->isFloatingPointTy()) { - OPENVDB_THROW(LLVMTypeError, "Unable to cast non scalar values"); + if (!mLog.error("unable to cast non scalar values", node)) return false; } + else { + // If the value to cast is already the correct type, return + llvm::Type* targetType = llvmTypeFromToken(node->type(), mContext); + if (type == targetType) return true; - // If the value to cast is already the correct type, return - llvm::Type* targetType = llvmTypeFromToken(node->type(), mContext); - if (type == targetType) return true; + mValues.pop(); - mValues.pop(); + if (value->getType()->isPointerTy()) { + value = mBuilder.CreateLoad(value); + } - if (value->getType()->isPointerTy()) { - value = mBuilder.CreateLoad(value); - } - if (targetType->isIntegerTy(1)) { - // if target is bool, perform standard boolean conversion (*not* truncation). - value = boolComparison(value, mBuilder); - assert(value->getType()->isIntegerTy(1)); - } - else { - value = arithmeticConversion(value, targetType, mBuilder); + if (targetType->isIntegerTy(1)) { + // if target is bool, perform standard boolean conversion (*not* truncation). + value = boolComparison(value, mBuilder); + assert(value->getType()->isIntegerTy(1)); + } + else { + value = arithmeticConversion(value, targetType, mBuilder); + } + mValues.push(value); } - mValues.push(value); return true; } @@ -711,19 +811,19 @@ bool ComputeGenerator::visit(const ast::DeclareLocal* node) } SymbolTable* current = mSymbolTables.getOrInsert(mScopeIndex); + const std::string& name = node->local()->name(); if (!current->insert(name, value)) { - OPENVDB_THROW(LLVMDeclarationError, "Local variable \"" + name + - "\" has already been declared!"); + if (!mLog.error("local variable \"" + name + + "\" has already been declared!", node)) return false; } - if (mWarnings) { - if (mSymbolTables.find(name, mScopeIndex - 1)) { - mWarnings->emplace_back("Declaration of variable \"" + name - + "\" shadows a previous declaration."); - } + if (mSymbolTables.find(name, mScopeIndex - 1)) { + if (!mLog.warning("declaration of variable \"" + name + + "\" shadows a previous declaration.", node)) return false; } + // do this to ensure all AST nodes are visited if (!this->traverse(node->local())) return false; value = mValues.top(); mValues.pop(); @@ -741,10 +841,11 @@ bool ComputeGenerator::visit(const ast::DeclareLocal* node) } } - this->assignExpression(value, init); + if (!this->assignExpression(value, init, node)) return false; + // note that loop conditions allow uses of initialized declarations // and so require the value - mValues.push(value); + if (value) mValues.push(value); } return true; @@ -773,8 +874,10 @@ bool ComputeGenerator::visit(const ast::Local* node) mValues.push(value); } else { - OPENVDB_THROW(LLVMDeclarationError, "Variable \"" + node->name() + - "\" hasn't been declared!"); + if (!mLog.error("variable \"" + node->name() + "\" hasn't been declared!", node)) return false; + // if not a fatal error, add a dummy to use in subsequent codegen + llvm::Value* dummy = insertStaticAlloca(mBuilder, LLVMType::get(mContext)); + mValues.push(dummy); } return true; } @@ -799,17 +902,19 @@ bool ComputeGenerator::visit(const ast::ArrayUnpack* node) llvm::Type* type = value->getType(); if (!type->isPointerTy() || !type->getPointerElementType()->isArrayTy()) { - OPENVDB_THROW(LLVMArrayError, - "Variable is not a valid type for component access."); + if (!mLog.error("variable is not a valid type for component access.", node)) return false; + return true; } // type now guaranteed to be an array type type = type->getPointerElementType(); const size_t size = type->getArrayNumElements(); if (component1 && size <= 4) { - OPENVDB_THROW(LLVMArrayError, - "Attribute or Local variable is not a compatible matrix type " - "for [,] indexing."); + { + if (!mLog.error("attribute or Local variable is not a compatible matrix type " + "for [,] indexing.", node)) return false; + return true; + } } if (component0->getType()->isPointerTy()) { @@ -829,9 +934,11 @@ bool ComputeGenerator::visit(const ast::ArrayUnpack* node) component1->getType()->print(stream); } stream.flush(); - OPENVDB_THROW(LLVMArrayError, - "Unable to index into array with a non integer value. Types are [" - + os.str() + "]"); + { + if (!mLog.error("unable to index into array with a non integer value. Types are [" + + os.str() + "]", node)) return false; + return true; + } } llvm::Value* zero = LLVMType::get(mContext, 0); @@ -937,10 +1044,6 @@ FunctionGroup::Ptr ComputeGenerator::getFunction(const std::string &identifier, const bool allowInternal) { FunctionGroup::Ptr function = mFunctionRegistry.getOrInsert(identifier, mOptions, allowInternal); - if (!function) { - OPENVDB_THROW(LLVMFunctionError, - "Unable to locate function \"" + identifier + "\"."); - } return function; } @@ -952,16 +1055,14 @@ ComputeGenerator::visit(const ast::Value* node) const ValueType literal = static_cast(node->value()); - if (mWarnings) { - static const ContainerT max = static_cast(std::numeric_limits::max()); - if (node->text()) { - mWarnings->emplace_back("Integer constant is too large to be represented: " + *(node->text()) - + " will be converted to \"" + std::to_string(literal) + "\""); - } - else if (node->asContainerType() > max) { - mWarnings->emplace_back("Signed integer overflow: " + std::to_string(node->asContainerType()) - + " cannot be represented in type '" + openvdb::typeNameAsString() + "'"); - } + static const ContainerT max = static_cast(std::numeric_limits::max()); + if (node->text()) { + if (!mLog.warning("integer constant is too large to be represented: " + *(node->text()) + + " will be converted to \"" + std::to_string(literal) + "\"", node)) return false; + } + else if (node->asContainerType() > max) { + if (!mLog.warning("signed integer overflow " + std::to_string(node->asContainerType()) + + " cannot be represented in type '" + openvdb::typeNameAsString() + "'", node)) return false; } llvm::Constant* value = LLVMType::get(mContext, literal); @@ -978,17 +1079,15 @@ ComputeGenerator::visit(const ast::Value* node) assert(std::isinf(node->value()) || node->value() >= static_cast(0.0)); const ValueType literal = static_cast(node->value()); - if (mWarnings) { - static const ContainerT max = static_cast(std::numeric_limits::max()); - if (node->text()) { - mWarnings->emplace_back("Floating constant exceeds range of double: " + *(node->text()) - + ". Converted to inf."); - } - else if (node->asContainerType() > max) { - mWarnings->emplace_back("Floating point overflow: " + std::to_string(node->asContainerType()) - + " cannot be represented in type '" + openvdb::typeNameAsString() - + "'. Converted to \"" + std::to_string(literal) + "\""); - } + static const ContainerT max = static_cast(std::numeric_limits::max()); + if (node->text()) { + if (!mLog.warning("floating constant exceeds range of double: " + *(node->text()) + + " will be converted to inf.", node)) return false; + } + else if (node->asContainerType() > max) { + if (!mLog.warning("floating point overflow " + std::to_string(node->asContainerType()) + + " cannot be represented in type '" + openvdb::typeNameAsString() + + "' and will be converted to \"" + std::to_string(literal) + "\"", node)) return false; } llvm::Constant* value = LLVMType::get(mContext, literal); @@ -1028,14 +1127,13 @@ bool ComputeGenerator::visit(const ast::Tree*) bool ComputeGenerator::visit(const ast::Attribute*) { - OPENVDB_THROW(LLVMContextError, - "Base ComputeGenerator attempted to generate code for an Attribute. " + assert(false && "Base ComputeGenerator attempted to generate code for an Attribute. " "PointComputeGenerator or VolumeComputeGenerator should be used for " "attribute accesses."); return false; } -void ComputeGenerator::assignExpression(llvm::Value* lhs, llvm::Value*& rhs) +bool ComputeGenerator::assignExpression(llvm::Value* lhs, llvm::Value*& rhs, const ast::Node* node) { llvm::Type* strtype = LLVMType::get(mContext); @@ -1043,7 +1141,8 @@ void ComputeGenerator::assignExpression(llvm::Value* lhs, llvm::Value*& rhs) llvm::Type* rtype = rhs->getType(); if (!ltype->isPointerTy()) { - OPENVDB_THROW(LLVMBinaryOperationError, "Unable to assign to an rvalue"); + if (!mLog.error("unable to assign to an rvalue", node)) return false; + return true; } ltype = ltype->getPointerElementType(); @@ -1070,14 +1169,16 @@ void ComputeGenerator::assignExpression(llvm::Value* lhs, llvm::Value*& rhs) if (lsize != rsize) { if (lsize > 1 && rsize > 1) { - OPENVDB_THROW(LLVMArrayError, "Unable to assign vector/array " - "attributes with mismatching sizes"); + if (!mLog.error("unable to assign vector/array " + "attributes with mismatching sizes", node)) return false; + return true; } else if (lsize == 1) { assert(rsize > 1); - OPENVDB_THROW(LLVMArrayError, "Cannot assign a scalar value " + if (!mLog.error("cannot assign a scalar value " "from a vector or matrix. Consider using the [] operator to " - "get a particular element."); + "get a particular element.", node)) return false; + return true; } } @@ -1101,10 +1202,11 @@ void ComputeGenerator::assignExpression(llvm::Value* lhs, llvm::Value*& rhs) // i.e. if rhs is anything but zero, lhs is true // @todo zeroval should be at rhstype if (opprec->isIntegerTy(1)) { - rhs = this->binaryExpression(rhs, - LLVMType::get(mContext, 0), - ast::tokens::NOTEQUALS); - assert(rhs->getType()->isIntegerTy(1)); + llvm::Value* newRhs = nullptr; + if (!this->binaryExpression(newRhs, LLVMType::get(mContext, 0), rhs, ast::tokens::NOTEQUALS, node)) return false; + if (!newRhs) return true; + rhs = newRhs; + assert(newRhs->getType()->isIntegerTy(1)); } for (size_t i = 0; i < resultsize; ++i) { @@ -1160,12 +1262,13 @@ void ComputeGenerator::assignExpression(llvm::Value* lhs, llvm::Value*& rhs) mBuilder.CreateStore(size, lsize); } else { - OPENVDB_THROW(LLVMCastError, "Unsupported implicit cast in assignment."); + if (!mLog.error("unsupported implicit cast in assignment.", node)) return false; } + return true; } -llvm::Value* ComputeGenerator::binaryExpression(llvm::Value* lhs, llvm::Value* rhs, - const ast::tokens::OperatorToken op) +bool ComputeGenerator::binaryExpression(llvm::Value*& result, llvm::Value* lhs, llvm::Value* rhs, + const ast::tokens::OperatorToken op, const ast::Node* node) { llvm::Type* strtype = LLVMType::get(mContext); @@ -1208,8 +1311,7 @@ llvm::Value* ComputeGenerator::binaryExpression(llvm::Value* lhs, llvm::Value* r // const ast::tokens::OperatorType opType = ast::tokens::operatorType(op); - llvm::Value* result = nullptr; - + result = nullptr; // Handle custom matrix operators if (lsize >= 9 || rsize >= 9) @@ -1233,8 +1335,9 @@ llvm::Value* ComputeGenerator::binaryExpression(llvm::Value* lhs, llvm::Value* r result = this->getFunction("transform")->execute({lhs, rhs}, mBuilder); } else { - OPENVDB_THROW(LLVMBinaryOperationError, "Unsupported * operator on " - "vector/matrix sizes"); + if (!mLog.error("unsupported * operator on " + "vector/matrix sizes", node)) return false; + return true; } } else if (op == ast::tokens::MORETHAN || @@ -1245,9 +1348,10 @@ llvm::Value* ComputeGenerator::binaryExpression(llvm::Value* lhs, llvm::Value* r op == ast::tokens::MODULO || // no % support for mats opType == ast::tokens::LOGICAL || opType == ast::tokens::BITWISE) { - OPENVDB_THROW(LLVMBinaryOperationError, "Call to unsupported operator \"" + if (!mLog.error("call to unsupported operator \"" + ast::tokens::operatorNameFromToken(op) + - "\" with a vector/matrix argument"); + "\" with a vector/matrix argument", node)) return false; + return true; } } @@ -1255,8 +1359,9 @@ llvm::Value* ComputeGenerator::binaryExpression(llvm::Value* lhs, llvm::Value* r // Handle matrix/vector ops of mismatching sizes if (lsize > 1 || rsize > 1) { if (lsize != rsize && (lsize > 1 && rsize > 1)) { - OPENVDB_THROW(LLVMArrayError, "Unsupported binary operator on vector/matrix " - "arguments of mismatching sizes."); + if (!mLog.error("unsupported binary operator on vector/matrix " + "arguments of mismatching sizes.", node)) return false; + return true; } if (op == ast::tokens::MORETHAN || op == ast::tokens::LESSTHAN || @@ -1264,18 +1369,20 @@ llvm::Value* ComputeGenerator::binaryExpression(llvm::Value* lhs, llvm::Value* r op == ast::tokens::LESSTHANOREQUAL || opType == ast::tokens::LOGICAL || opType == ast::tokens::BITWISE) { - OPENVDB_THROW(LLVMBinaryOperationError, "Call to unsupported operator \"" + if (!mLog.error("call to unsupported operator \"" + ast::tokens::operatorNameFromToken(op) + - "\" with a vector/matrix argument"); + "\" with a vector/matrix argument", node)) return false; + return true; } } // Handle invalid floating point ops if (rtype->isFloatingPointTy() || ltype->isFloatingPointTy()) { if (opType == ast::tokens::BITWISE) { - OPENVDB_THROW(LLVMBinaryOperationError, "Call to unsupported operator \"" + if (!mLog.error("call to unsupported operator \"" + ast::tokens::operatorNameFromToken(op) + - "\" with a floating point argument"); + "\" with a floating point argument", node)) return false; + return true; } } } @@ -1341,7 +1448,6 @@ llvm::Value* ComputeGenerator::binaryExpression(llvm::Value* lhs, llvm::Value* r } // handle vec/mat results - if (resultsize > 1) { if (op == ast::tokens::EQUALSEQUALS || op == ast::tokens::NOTEQUALS) { const ast::tokens::OperatorToken reductionOp = @@ -1372,8 +1478,9 @@ llvm::Value* ComputeGenerator::binaryExpression(llvm::Value* lhs, llvm::Value* r if (string) { if (op != ast::tokens::PLUS) { - OPENVDB_THROW(LLVMBinaryOperationError, "Unsupported string operation \"" - + ast::tokens::operatorNameFromToken(op) + "\""); + if (!mLog.error("unsupported string operation \"" + + ast::tokens::operatorNameFromToken(op) + "\"", node)) return false; + return true; } auto& B = mBuilder; @@ -1446,16 +1553,15 @@ llvm::Value* ComputeGenerator::binaryExpression(llvm::Value* lhs, llvm::Value* r } if (!result) { - OPENVDB_THROW(LLVMBinaryOperationError, - "Unsupported implicit cast in binary op."); + if (!mLog.error("unsupported implicit cast in binary op.", node)) return false; } - return result; + return true; } -} -} -} -} +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb_ax/openvdb_ax/codegen/ComputeGenerator.h b/openvdb_ax/openvdb_ax/codegen/ComputeGenerator.h index 6cb7c534fb..dad09ecf26 100644 --- a/openvdb_ax/openvdb_ax/codegen/ComputeGenerator.h +++ b/openvdb_ax/openvdb_ax/codegen/ComputeGenerator.h @@ -18,6 +18,9 @@ #include "../ast/AST.h" #include "../ast/Visitor.h" #include "../compiler/CompilerOptions.h" +#include "../compiler/Logger.h" + +#include #include #include @@ -67,7 +70,7 @@ struct ComputeGenerator : public ast::Visitor ComputeGenerator(llvm::Module& module, const FunctionOptions& options, FunctionRegistry& functionRegistry, - std::vector* const warnings = nullptr); + Logger& logger); virtual ~ComputeGenerator() = default; @@ -176,9 +179,9 @@ struct ComputeGenerator : public ast::Visitor FunctionGroup::Ptr getFunction(const std::string& identifier, const bool allowInternal = false); - llvm::Value* binaryExpression(llvm::Value* lhs, llvm::Value* rhs, - const ast::tokens::OperatorToken op); - void assignExpression(llvm::Value* lhs, llvm::Value*& rhs); + bool binaryExpression(llvm::Value*& result, llvm::Value* lhs, llvm::Value* rhs, + const ast::tokens::OperatorToken op, const ast::Node* node); + bool assignExpression(llvm::Value* lhs, llvm::Value*& rhs, const ast::Node* node); llvm::Module& mModule; llvm::LLVMContext& mContext; @@ -196,22 +199,21 @@ struct ComputeGenerator : public ast::Visitor // The map of scope number to local variable names to values SymbolTableBlocks mSymbolTables; - // Warnings that are generated during code generation - std::vector* const mWarnings; - // The function used as the base code block llvm::Function* mFunction; const FunctionOptions mOptions; + Logger& mLog; + private: FunctionRegistry& mFunctionRegistry; }; -} -} -} -} +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_COMPUTE_GENERATOR_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/codegen/ConstantFolding.h b/openvdb_ax/openvdb_ax/codegen/ConstantFolding.h index c0914895e8..c3a61709ff 100644 --- a/openvdb_ax/openvdb_ax/codegen/ConstantFolding.h +++ b/openvdb_ax/openvdb_ax/codegen/ConstantFolding.h @@ -11,7 +11,9 @@ #ifndef OPENVDB_AX_CODEGEN_CONSTANT_FOLDING_HAS_BEEN_INCLUDED #define OPENVDB_AX_CODEGEN_CONSTANT_FOLDING_HAS_BEEN_INCLUDED -#include "../codegen/Types.h" +#include "Types.h" + +#include #include @@ -157,10 +159,10 @@ struct ConstantFolder } }; -} -} -} -} +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_CODEGEN_CONSTANT_FOLDING_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/codegen/FunctionRegistry.cc b/openvdb_ax/openvdb_ax/codegen/FunctionRegistry.cc index eb24b28462..72949b6185 100644 --- a/openvdb_ax/openvdb_ax/codegen/FunctionRegistry.cc +++ b/openvdb_ax/openvdb_ax/codegen/FunctionRegistry.cc @@ -22,7 +22,7 @@ void FunctionRegistry::insert(const std::string& identifier, if (!mMap.emplace(std::piecewise_construct, std::forward_as_tuple(identifier), std::forward_as_tuple(creator, internal)).second) { - OPENVDB_THROW(LLVMFunctionError, "A function already exists" + OPENVDB_THROW(AXCompilerError, "A function already exists" " with the provided identifier: \"" + identifier + "\""); } } @@ -36,7 +36,7 @@ void FunctionRegistry::insertAndCreate(const std::string& identifier, std::forward_as_tuple(identifier), std::forward_as_tuple(creator, internal)); if (!inserted.second) { - OPENVDB_THROW(LLVMFunctionError, "A function already exists" + OPENVDB_THROW(AXCompilerError, "A function already exists" " with the provided token: \"" + identifier + "\""); } inserted.first->second.create(op); @@ -113,8 +113,8 @@ void FunctionRegistry::createAll(const FunctionOptions& op, const bool verify) } } -} -} -} -} +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb_ax/openvdb_ax/codegen/FunctionRegistry.h b/openvdb_ax/openvdb_ax/codegen/FunctionRegistry.h index 4afe9577aa..4eb0572fbe 100644 --- a/openvdb_ax/openvdb_ax/codegen/FunctionRegistry.h +++ b/openvdb_ax/openvdb_ax/codegen/FunctionRegistry.h @@ -15,9 +15,10 @@ #include "FunctionTypes.h" #include "Types.h" -#include "../version.h" #include "../compiler/CompilerOptions.h" +#include + #include namespace openvdb { @@ -143,10 +144,10 @@ class FunctionRegistry RegistryMap mMap; }; -} -} -} -} +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_CODEGEN_FUNCTION_REGISTRY_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/codegen/FunctionTypes.cc b/openvdb_ax/openvdb_ax/codegen/FunctionTypes.cc index 9489d581f6..b9b2afd94a 100644 --- a/openvdb_ax/openvdb_ax/codegen/FunctionTypes.cc +++ b/openvdb_ax/openvdb_ax/codegen/FunctionTypes.cc @@ -4,7 +4,6 @@ /// @file codegen/FunctionTypes.cc #include "FunctionTypes.h" - #include "Types.h" #include "Utils.h" @@ -23,6 +22,7 @@ namespace OPENVDB_VERSION_NAME { namespace ax { namespace codegen { +namespace { inline void printType(const llvm::Type* type, llvm::raw_os_ostream& stream, const bool axTypes) @@ -64,6 +64,8 @@ printTypes(llvm::raw_os_ostream& stream, } } +} + void printSignature(std::ostream& os, const std::vector& signature, @@ -398,37 +400,9 @@ FunctionGroup::execute(const std::vector& args, Function::SignatureMatch match; const Function::Ptr target = this->match(inputTypes, C, &match); - if (!target) { - if (this->list().empty()) { - OPENVDB_THROW(LLVMFunctionError, "FunctionGroup \"" << mName << "\" " - "has no function declarations."); - } - - std::ostringstream os; - if (match == Function::SignatureMatch::None) { - os << "Wrong number of arguments. \"" << mName << "\"" - << " was called with: ("; - llvm::raw_os_ostream stream(os); - printTypes(stream, inputTypes); - stream << ")"; - } - else { - // match == Function::SignatureMatch::Size - os << "No matching function for "; - printSignature(os, inputTypes, LLVMType::get(C), mName, {}, true); - os << "."; - } - - os << " Candidates are: "; - for (const auto& function : mFunctionList) { - os << std::endl; - function->print(C, os, mName); - os << ", "; - } - OPENVDB_THROW(LLVMFunctionError, os.str()); - } - llvm::Value* result = nullptr; + if (!target) return result; + if (match == Function::SignatureMatch::Implicit) { result = target->call(args, B, /*cast=*/true); } @@ -436,17 +410,12 @@ FunctionGroup::execute(const std::vector& args, // match == Function::SignatureMatch::Explicit result = target->call(args, B, /*cast=*/false); } - - if (!result) { - OPENVDB_THROW(LLVMFunctionError, "Function \"" << mName << - "\" has been invoked with no valid llvm Value return."); - } - + assert(result); return result; } -} -} -} -} +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb_ax/openvdb_ax/codegen/FunctionTypes.h b/openvdb_ax/openvdb_ax/codegen/FunctionTypes.h index deadf506f3..1dfb612275 100644 --- a/openvdb_ax/openvdb_ax/codegen/FunctionTypes.h +++ b/openvdb_ax/openvdb_ax/codegen/FunctionTypes.h @@ -66,9 +66,11 @@ #ifndef OPENVDB_AX_CODEGEN_FUNCTION_TYPES_HAS_BEEN_INCLUDED #define OPENVDB_AX_CODEGEN_FUNCTION_TYPES_HAS_BEEN_INCLUDED -#include "../codegen/Types.h" -#include "../codegen/Utils.h" // isValidCast -#include "../codegen/ConstantFolding.h" +#include "Types.h" +#include "Utils.h" // isValidCast +#include "ConstantFolding.h" + +#include #include #include @@ -715,7 +717,7 @@ struct IRFunctionBase : public Function std::string source, target; if (result) llvmTypeToString(result, source); llvmTypeToString(expected, target); - OPENVDB_THROW(LLVMFunctionError, "Function \"" + std::string(this->symbol()) + + OPENVDB_THROW(AXCodeGenError, "Function \"" + std::string(this->symbol()) + "\" has been invoked with a mismatching return type. Expected: \"" + target + "\", got \"" + source + "\"."); } @@ -1110,10 +1112,10 @@ struct FunctionBuilder Settings::Ptr mCurrentSettings = nullptr; }; -} -} -} -} +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_CODEGEN_FUNCTION_TYPES_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/codegen/Functions.h b/openvdb_ax/openvdb_ax/codegen/Functions.h index a1fafe27f5..37c9c299c4 100644 --- a/openvdb_ax/openvdb_ax/codegen/Functions.h +++ b/openvdb_ax/openvdb_ax/codegen/Functions.h @@ -13,8 +13,11 @@ #ifndef OPENVDB_AX_CODEGEN_GENERIC_FUNCTIONS_HAS_BEEN_INCLUDED #define OPENVDB_AX_CODEGEN_GENERIC_FUNCTIONS_HAS_BEEN_INCLUDED +#include "FunctionRegistry.h" + #include "../compiler/CompilerOptions.h" -#include "../codegen/FunctionRegistry.h" + +#include namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE @@ -65,10 +68,10 @@ inline FunctionRegistry::UniquePtr createDefaultRegistry(const FunctionOptions* return registry; } -} -} -} -} +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_CODEGEN_GENERIC_FUNCTIONS_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/codegen/PointComputeGenerator.cc b/openvdb_ax/openvdb_ax/codegen/PointComputeGenerator.cc index 0893b80494..d4ef8bdea5 100644 --- a/openvdb_ax/openvdb_ax/codegen/PointComputeGenerator.cc +++ b/openvdb_ax/openvdb_ax/codegen/PointComputeGenerator.cc @@ -62,8 +62,8 @@ std::string PointRangeKernel::getDefaultName() { return "ax.compute.pointrange"; PointComputeGenerator::PointComputeGenerator(llvm::Module& module, const FunctionOptions& options, FunctionRegistry& functionRegistry, - std::vector* const warnings) - : ComputeGenerator(module, options, functionRegistry, warnings) {} + Logger& logger) + : ComputeGenerator(module, options, functionRegistry, logger) {} AttributeRegistry::Ptr PointComputeGenerator::generate(const ast::Tree& tree) { @@ -183,8 +183,9 @@ AttributeRegistry::Ptr PointComputeGenerator::generate(const ast::Tree& tree) } // full code generation + // errors can stop traversal, but dont always, so check the log - this->traverse(&tree); + if (!this->traverse(&tree) || mLog.hasError()) return nullptr; // insert set code @@ -192,6 +193,8 @@ AttributeRegistry::Ptr PointComputeGenerator::generate(const ast::Tree& tree) for (const AttributeRegistry::AccessData& access : registry->data()) { if (access.writes()) write.emplace_back(&access); } + // if it doesn't write to any externally accessible data (i.e attributes) + // then early exit if (write.empty()) return registry; for (auto block = mFunction->begin(); block != mFunction->end(); ++block) { @@ -253,7 +256,6 @@ AttributeRegistry::Ptr PointComputeGenerator::generate(const ast::Tree& tree) function->execute(args, mBuilder); } } - return registry; } @@ -344,12 +346,9 @@ llvm::Value* PointComputeGenerator::attributeHandleFromToken(const std::string& return mBuilder.CreateLoad(handlePtr); } -/////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////// - -} -} -} -} +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb_ax/openvdb_ax/codegen/PointComputeGenerator.h b/openvdb_ax/openvdb_ax/codegen/PointComputeGenerator.h index 346fc3e46b..ff3b115a6b 100644 --- a/openvdb_ax/openvdb_ax/codegen/PointComputeGenerator.h +++ b/openvdb_ax/openvdb_ax/codegen/PointComputeGenerator.h @@ -19,6 +19,8 @@ #include "../compiler/AttributeRegistry.h" +#include + namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { @@ -85,12 +87,11 @@ struct PointComputeGenerator : public ComputeGenerator /// @param options Options for the function registry behaviour /// @param functionRegistry Function registry object which will be used when generating IR /// for function calls - /// @param warnings Vector which will hold compiler warnings. If null, no warnings will - /// be stored. + /// @param logger Logger for collecting logical errors and warnings PointComputeGenerator(llvm::Module& module, const FunctionOptions& options, FunctionRegistry& functionRegistry, - std::vector* const warnings = nullptr); + Logger& logger); ~PointComputeGenerator() override = default; @@ -105,10 +106,10 @@ struct PointComputeGenerator : public ComputeGenerator void getAttributeValue(const std::string& globalName, llvm::Value* location); }; -} -} -} -} +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_POINT_COMPUTE_GENERATOR_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/codegen/PointFunctions.cc b/openvdb_ax/openvdb_ax/codegen/PointFunctions.cc index a50f34da3e..be59b44c13 100644 --- a/openvdb_ax/openvdb_ax/codegen/PointFunctions.cc +++ b/openvdb_ax/openvdb_ax/codegen/PointFunctions.cc @@ -21,7 +21,6 @@ #include "../compiler/CompilerOptions.h" #include "../compiler/LeafLocalData.h" #include "../Exceptions.h" -#include "../version.h" #include #include @@ -56,7 +55,7 @@ inline HandleT* groupHandle(const std::string& name, void** groupHandles, const void* const data) { const openvdb::points::AttributeSet* const attributeSet = - static_cast(data); + static_cast(data); const size_t groupIdx = attributeSet->groupOffset(name); if (groupIdx == openvdb::points::AttributeSet::INVALID_POS) return nullptr; @@ -89,7 +88,7 @@ inline FunctionGroup::Ptr ax_ingroup(const FunctionOptions& op) // If the handle doesn't exist, check to see if any new groups have // been added const openvdb::ax::compiler::LeafLocalData* const leafData = - static_cast(leafDataPtr); + static_cast(leafDataPtr); handle = leafData->get(nameStr); return handle ? handle->get(static_cast(index)) : false; }; @@ -180,7 +179,7 @@ inline FunctionGroup::Ptr axeditgroup(const FunctionOptions& op) if (!handle) { openvdb::ax::compiler::LeafLocalData* const leafData = - static_cast(leafDataPtr); + static_cast(leafDataPtr); // If we are setting membership and the handle doesnt exist, create in in // the set of new data thats being added @@ -357,7 +356,7 @@ inline FunctionGroup::Ptr axsetattribute(const FunctionOptions& op) AttributeHandleType* const handle = static_cast(attributeHandle); openvdb::ax::compiler::LeafLocalData* const leafData = - static_cast(leafDataPtr); + static_cast(leafDataPtr); // Check to see if the string exists in the metadata cache. If so, set the string and // remove any new data associated with it, otherwise set the new data @@ -474,7 +473,7 @@ inline FunctionGroup::Ptr axgetattribute(const FunctionOptions& op) AttributeHandleType* const handle = static_cast(attributeHandle); const openvdb::ax::compiler::LeafLocalData* const leafData = - static_cast(leafDataPtr); + static_cast(leafDataPtr); std::string data; if (!leafData->getNewStringData(&(handle->array()), index, data)) { @@ -555,7 +554,7 @@ inline FunctionGroup::Ptr axstrattribsize(const FunctionOptions& op) const AttributeHandleType* const handle = static_cast(attributeHandle); const openvdb::ax::compiler::LeafLocalData* const leafData = - static_cast(leafDataPtr); + static_cast(leafDataPtr); std::string data; if (!leafData->getNewStringData(&(handle->array()), index, data)) { diff --git a/openvdb_ax/openvdb_ax/codegen/StandardFunctions.cc b/openvdb_ax/openvdb_ax/codegen/StandardFunctions.cc index 8f65845ab5..486ca640a6 100644 --- a/openvdb_ax/openvdb_ax/codegen/StandardFunctions.cc +++ b/openvdb_ax/openvdb_ax/codegen/StandardFunctions.cc @@ -11,15 +11,14 @@ /// intrinsics. #include "Functions.h" +#include "FunctionTypes.h" +#include "Types.h" +#include "Utils.h" -#include "../version.h" #include "../Exceptions.h" #include "../math/OpenSimplexNoise.h" #include "../compiler/CompilerOptions.h" #include "../compiler/CustomData.h" -#include "../codegen/FunctionTypes.h" -#include "../codegen/Types.h" -#include "../codegen/Utils.h" #include #include @@ -541,6 +540,7 @@ inline FunctionGroup::Ptr axmin(const FunctionOptions& op) return FunctionBuilder("min") .addSignature(generate, (double(*)(double,double))(min)) .addSignature(generate, (float(*)(float,float))(min)) + .addSignature(generate, (int64_t(*)(int64_t,int64_t))(min)) .addSignature(generate, (int32_t(*)(int32_t,int32_t))(min)) .setArgumentNames({"a", "b"}) .addFunctionAttribute(llvm::Attribute::ReadOnly) @@ -571,6 +571,7 @@ inline FunctionGroup::Ptr axmax(const FunctionOptions& op) return FunctionBuilder("max") .addSignature(generate, (double(*)(double,double))(max)) .addSignature(generate, (float(*)(float,float))(max)) + .addSignature(generate, (int64_t(*)(int64_t,int64_t))(max)) .addSignature(generate, (int32_t(*)(int32_t,int32_t))(max)) .setArgumentNames({"a", "b"}) .addFunctionAttribute(llvm::Attribute::ReadOnly) @@ -597,10 +598,12 @@ inline FunctionGroup::Ptr axclamp(const FunctionOptions& op) using ClampD = double(double, double, double); using ClampF = float(float, float, float); using ClampI = int32_t(int32_t, int32_t, int32_t); + using ClampL = int64_t(int64_t, int64_t, int64_t); return FunctionBuilder("clamp") .addSignature(generate, &openvdb::math::Clamp) .addSignature(generate, &openvdb::math::Clamp) + .addSignature(generate, &openvdb::math::Clamp) .addSignature(generate, &openvdb::math::Clamp) .addDependency("min") .addDependency("max") @@ -682,11 +685,13 @@ inline FunctionGroup::Ptr axfit(const FunctionOptions& op) using FitD = double(double, double, double, double, double); using FitF = float(float, float, float, float, float); + using FitL = double(int64_t, int64_t, int64_t, int64_t, int64_t); using FitI = double(int32_t, int32_t, int32_t, int32_t, int32_t); return FunctionBuilder("fit") .addSignature(generate) .addSignature(generate) + .addSignature(generate) .addSignature(generate) .addDependency("clamp") .setArgumentNames({"value", "omin", "omax", "nmin", "nmax"}) @@ -788,6 +793,52 @@ inline FunctionGroup::Ptr axrand(const FunctionOptions& op) .get(); } +inline FunctionGroup::Ptr axsign(const FunctionOptions& op) +{ + static auto generate = + [](const std::vector& args, + llvm::IRBuilder<>& B) -> llvm::Value* + { + // int r = (T(0) < val) - (val < T(0)); + assert(args.size() == 1); + llvm::Value* arg = args.front(); + llvm::Type* type = arg->getType(); + llvm::Value* zero; + if (type->isIntegerTy()) { + zero = llvm::ConstantInt::get(type, static_cast(0), /*signed*/true); + } + else { + assert(type->isFloatingPointTy()); + zero = llvm::ConstantFP::get(type, static_cast(0.0)); + } + + llvm::Value* c1 = binaryOperator(zero, arg, ast::tokens::LESSTHAN, B); + c1 = arithmeticConversion(c1, LLVMType::get(B.getContext()), B); + llvm::Value* c2 = binaryOperator(arg, zero, ast::tokens::LESSTHAN, B); + c2 = arithmeticConversion(c2, LLVMType::get(B.getContext()), B); + llvm::Value* r = binaryOperator(c1, c2, ast::tokens::MINUS, B); + return arithmeticConversion(r, LLVMType::get(B.getContext()), B); + }; + + return FunctionBuilder("sign") + .addSignature(generate) + .addSignature(generate) + .addSignature(generate) + .addSignature(generate) + .setArgumentNames({"n"}) + .addFunctionAttribute(llvm::Attribute::ReadOnly) + .addFunctionAttribute(llvm::Attribute::NoRecurse) + .addFunctionAttribute(llvm::Attribute::NoUnwind) + .addFunctionAttribute(llvm::Attribute::AlwaysInline) + .setConstantFold(op.mConstantFoldCBindings) + .setPreferredImpl(op.mPrioritiseIR ? FunctionBuilder::IR : FunctionBuilder::C) + .setDocumentation("Implements signum, determining if the input is negative, zero " + "or positive. Returns -1 for a negative number, 0 for the number zero, and +1 " + "for a positive number. Note that this function does not check the sign of " + "floating point +/-0.0 values. See signbit().") + .get(); +} + inline FunctionGroup::Ptr axsignbit(const FunctionOptions& op) { return FunctionBuilder("signbit") @@ -800,7 +851,9 @@ inline FunctionGroup::Ptr axsignbit(const FunctionOptions& op) .addFunctionAttribute(llvm::Attribute::AlwaysInline) .setConstantFold(op.mConstantFoldCBindings) .setPreferredImpl(op.mPrioritiseIR ? FunctionBuilder::IR : FunctionBuilder::C) - .setDocumentation("Determines if the given floating point number input is negative.") + .setDocumentation("Determines if the given floating point number input is negative. " + "Returns true if arg is negative, false otherwise. Will return true for -0.0, " + "false for +0.0") .get(); } @@ -1162,8 +1215,9 @@ inline FunctionGroup::Ptr axpolardecompose(const FunctionOptions& op) .addFunctionAttribute(llvm::Attribute::NoUnwind) .setConstantFold(op.mConstantFoldCBindings) .setPreferredImpl(op.mPrioritiseIR ? FunctionBuilder::IR : FunctionBuilder::C) - .setDocumentation("Decompose an invertible 3x3 matrix into its orthogonal matrix " - "and symmetric matrix components.") + .setDocumentation("Decompose an invertible 3x3 matrix into its orthogonal (unitary) " + "matrix and symmetric matrix components. If the determinant of the unitary matrix " + "is 1 it is a rotation, otherwise if it is -1 there is some part reflection.") .get(); } @@ -1730,6 +1784,7 @@ inline FunctionGroup::Ptr axprint(const FunctionOptions& op) return FunctionBuilder("print") .addSignature((void(*)(double))(print)) .addSignature((void(*)(float))(print)) + .addSignature((void(*)(int64_t))(print)) .addSignature((void(*)(int32_t))(print)) .setArgumentNames({"n"}) .addFunctionAttribute(llvm::Attribute::ReadOnly) @@ -1773,7 +1828,7 @@ inline FunctionGroup::Ptr ax_external(const FunctionOptions& op) { using ValueType = typename std::remove_pointer::type; const ax::CustomData* const customData = - static_cast(data); + static_cast(data); const std::string nameStr(name->ptr, name->size); const TypedMetadata* const metaData = customData->getData>(nameStr); @@ -1921,6 +1976,7 @@ void insertStandardFunctions(FunctionRegistry& registry, add("min", axmin); add("normalize", axnormalize); add("rand", axrand); + add("sign", axsign); add("signbit", axsignbit); // matrix math @@ -1975,8 +2031,8 @@ void insertStandardFunctions(FunctionRegistry& registry, } -} -} -} -} +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb_ax/openvdb_ax/codegen/SymbolTable.h b/openvdb_ax/openvdb_ax/codegen/SymbolTable.h index 9fc729a4d5..4ff9eb46e6 100644 --- a/openvdb_ax/openvdb_ax/codegen/SymbolTable.h +++ b/openvdb_ax/openvdb_ax/codegen/SymbolTable.h @@ -12,7 +12,7 @@ #ifndef OPENVDB_AX_CODEGEN_SYMBOL_TABLE_HAS_BEEN_INCLUDED #define OPENVDB_AX_CODEGEN_SYMBOL_TABLE_HAS_BEEN_INCLUDED -#include "../version.h" +#include #include @@ -222,10 +222,10 @@ struct SymbolTableBlocks MapType mTables; }; -} -} -} -} +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_CODEGEN_SYMBOL_TABLE_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/codegen/Types.h b/openvdb_ax/openvdb_ax/codegen/Types.h index 87d8cce401..153d249349 100644 --- a/openvdb_ax/openvdb_ax/codegen/Types.h +++ b/openvdb_ax/openvdb_ax/codegen/Types.h @@ -15,10 +15,11 @@ #include "../ast/Literals.h" #include "../Exceptions.h" +#include +#include #include #include #include -#include #include #include @@ -77,7 +78,7 @@ struct LLVMType case 64: return llvm::Type::getDoubleTy(C); } } - OPENVDB_THROW(LLVMTypeError, "LLVMType called with an unsupported type \"" + + OPENVDB_THROW(AXCodeGenError, "LLVMType called with an unsupported type \"" + std::string(typeNameAsString()) + "\"."); #endif } @@ -328,7 +329,8 @@ llvmFloatType(const uint32_t size, llvm::LLVMContext& C) switch (size) { case 32 : return LLVMType::get(C); case 64 : return LLVMType::get(C); - default : OPENVDB_THROW(LLVMTypeError, "Invalid float size"); + default : OPENVDB_THROW(AXCodeGenError, + "Invalid float size requested from LLVM Context"); } } @@ -366,7 +368,8 @@ llvmTypeFromToken(const ast::tokens::CoreType& type, case ast::tokens::STRING : return LLVMType::get(C); case ast::tokens::UNKNOWN : default : - OPENVDB_THROW(LLVMTypeError, "Attribute Type not recognised"); + OPENVDB_THROW(AXCodeGenError, + "Token type not recognised in request for LLVM type"); } } @@ -416,10 +419,10 @@ tokenFromLLVMType(const llvm::Type* type) return ast::tokens::UNKNOWN; } -} -} -} -} +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_CODEGEN_TYPES_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/codegen/Utils.h b/openvdb_ax/openvdb_ax/codegen/Utils.h index 0a0c988188..28c269768b 100644 --- a/openvdb_ax/openvdb_ax/codegen/Utils.h +++ b/openvdb_ax/openvdb_ax/codegen/Utils.h @@ -17,6 +17,8 @@ #include "../ast/Tokens.h" #include "../Exceptions.h" +#include + #include #include @@ -197,7 +199,7 @@ typePrecedence(llvm::Type* const typeA, typeB->print(llvm::errs()); std::cerr << std::endl; - OPENVDB_THROW(LLVMTypeError, "Invalid type precedence"); + OPENVDB_THROW(AXCodeGenError, "Invalid LLVM type precedence"); } /// @brief Returns a CastFunction which represents the corresponding instruction @@ -287,7 +289,7 @@ llvmArithmeticConversion(const llvm::Type* const sourceType, targetType->print(llvm::errs()); std::cerr << std::endl; - OPENVDB_THROW(LLVMTypeError, "Invalid type conversion"); + OPENVDB_THROW(AXCodeGenError, "Invalid LLVM type conversion"); } /// @brief Returns a BinaryFunction representing the corresponding instruction to @@ -320,7 +322,7 @@ llvmBinaryConversion(const llvm::Type* const type, if (type->isFloatingPointTy()) { const ast::tokens::OperatorType opType = ast::tokens::operatorType(token); if (opType == ast::tokens::LOGICAL || opType == ast::tokens::BITWISE) { - OPENVDB_THROW(LLVMBinaryOperationError, "Unable to perform operation \"" + OPENVDB_THROW(AXCodeGenError, "Unable to perform operation \"" + ast::tokens::operatorNameFromToken(token) + "\" on floating points values"); } @@ -335,7 +337,7 @@ llvmBinaryConversion(const llvm::Type* const type, else if (token == ast::tokens::LESSTHAN) return BIND_BINARY_OP(CreateFCmpOLT); else if (token == ast::tokens::MORETHANOREQUAL) return BIND_BINARY_OP(CreateFCmpOGE); else if (token == ast::tokens::LESSTHANOREQUAL) return BIND_BINARY_OP(CreateFCmpOLE); - OPENVDB_THROW(LLVMTokenError, "Unrecognised binary operator \"" + + OPENVDB_THROW(AXCodeGenError, "Unrecognised binary operator \"" + ast::tokens::operatorNameFromToken(token) + "\""); } else if (type->isIntegerTy()) { @@ -357,7 +359,7 @@ llvmBinaryConversion(const llvm::Type* const type, else if (token == ast::tokens::BITAND) return BIND_BINARY_OP(CreateAnd); else if (token == ast::tokens::BITOR) return BIND_BINARY_OP(CreateOr); else if (token == ast::tokens::BITXOR) return BIND_BINARY_OP(CreateXor); - OPENVDB_THROW(LLVMTokenError, "Unrecognised binary operator \"" + + OPENVDB_THROW(AXCodeGenError, "Unrecognised binary operator \"" + ast::tokens::operatorNameFromToken(token) + "\""); } @@ -369,7 +371,7 @@ llvmBinaryConversion(const llvm::Type* const type, type->print(llvm::errs()); - OPENVDB_THROW(LLVMTypeError, "Invalid type for binary operation"); + OPENVDB_THROW(AXCodeGenError, "Invalid LLVM type for binary operation"); } /// @brief Returns true if the llvm Type 'from' can be safely cast to the llvm @@ -558,7 +560,7 @@ boolComparison(llvm::Value* value, type->print(llvm::errs()); std::cerr << std::endl; - OPENVDB_THROW(LLVMTypeError, "Invalid type for bool conversion"); + OPENVDB_THROW(AXCodeGenError, "Invalid type for bool conversion"); } /// @ brief Performs a binary operation on two loaded llvm scalar values. The type of @@ -584,7 +586,7 @@ binaryOperator(llvm::Value* lhs, llvm::Value* rhs, llvm::raw_string_ostream os(error); os << "LHS Type: "; lhsType->print(os); os << ", "; os << "RHS Type: "; rhs->getType()->print(os); os << " "; - OPENVDB_THROW(LLVMTypeError, "Mismatching argument types for binary operation \"" + + OPENVDB_THROW(AXCodeGenError, "Mismatching argument types for binary operation \"" + ast::tokens::operatorNameFromToken(token) + "\". " + os.str()); } @@ -817,10 +819,10 @@ scalarToMatrix(llvm::Value* scalar, return array; } -} -} -} -} +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_CODEGEN_UTILS_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/codegen/VolumeComputeGenerator.cc b/openvdb_ax/openvdb_ax/codegen/VolumeComputeGenerator.cc index 8d492359dc..d9b302fa63 100644 --- a/openvdb_ax/openvdb_ax/codegen/VolumeComputeGenerator.cc +++ b/openvdb_ax/openvdb_ax/codegen/VolumeComputeGenerator.cc @@ -4,7 +4,6 @@ /// @file codegen/VolumeComputeGenerator.cc #include "VolumeComputeGenerator.h" - #include "FunctionRegistry.h" #include "FunctionTypes.h" #include "Types.h" @@ -46,8 +45,8 @@ std::string VolumeKernel::getDefaultName() { return "ax.compute.voxel"; } VolumeComputeGenerator::VolumeComputeGenerator(llvm::Module& module, const FunctionOptions& options, FunctionRegistry& functionRegistry, - std::vector* const warnings) - : ComputeGenerator(module, options, functionRegistry, warnings) {} + Logger& logger) + : ComputeGenerator(module, options, functionRegistry, logger) {} AttributeRegistry::Ptr VolumeComputeGenerator::generate(const ast::Tree& tree) { @@ -101,8 +100,9 @@ AttributeRegistry::Ptr VolumeComputeGenerator::generate(const ast::Tree& tree) } // full code generation + // errors can stop traversal, but dont always, so check the log - this->traverse(&tree); + if (!this->traverse(&tree) || mLog.hasError()) return nullptr; // insert set code @@ -264,8 +264,8 @@ llvm::Value* VolumeComputeGenerator::accessorHandleFromToken(const std::string& /////////////////////////////////////////////////////////////////////////// -} -} -} -} +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb_ax/openvdb_ax/codegen/VolumeComputeGenerator.h b/openvdb_ax/openvdb_ax/codegen/VolumeComputeGenerator.h index 370b2d826d..0b2884fa4f 100644 --- a/openvdb_ax/openvdb_ax/codegen/VolumeComputeGenerator.h +++ b/openvdb_ax/openvdb_ax/codegen/VolumeComputeGenerator.h @@ -17,6 +17,8 @@ #include "../compiler/AttributeRegistry.h" +#include + namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { @@ -72,12 +74,11 @@ struct VolumeComputeGenerator : public ComputeGenerator /// @param options Options for the function registry behaviour /// @param functionRegistry Function registry object which will be used when generating IR /// for function calls - /// @param warnings Vector which will hold compiler warnings. If null, no warnings will - /// be stored. + /// @param logger Logger for collecting logical errors and warnings VolumeComputeGenerator(llvm::Module& module, const FunctionOptions& options, FunctionRegistry& functionRegistry, - std::vector* const warnings = nullptr); + Logger& logger); ~VolumeComputeGenerator() override = default; @@ -88,15 +89,14 @@ struct VolumeComputeGenerator : public ComputeGenerator bool visit(const ast::Attribute*) override; private: - - void getAccessorValue(const std::string&, llvm::Value*); llvm::Value* accessorHandleFromToken(const std::string&); + void getAccessorValue(const std::string&, llvm::Value*); }; -} -} -} -} +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_VOLUME_COMPUTE_GENERATOR_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/codegen/VolumeFunctions.cc b/openvdb_ax/openvdb_ax/codegen/VolumeFunctions.cc index 9acca8d954..16cc3372fe 100644 --- a/openvdb_ax/openvdb_ax/codegen/VolumeFunctions.cc +++ b/openvdb_ax/openvdb_ax/codegen/VolumeFunctions.cc @@ -18,7 +18,8 @@ #include "../compiler/CompilerOptions.h" #include "../Exceptions.h" -#include "../version.h" + +#include #include @@ -114,7 +115,7 @@ inline FunctionGroup::Ptr axsetvoxel(const FunctionOptions& op) // set value only to avoid changing topology const openvdb::Coord* ijk = reinterpret_cast(coord); - AccessorType* const accessorPtr = static_cast(accessor); + AccessorType* const accessorPtr = static_cast(accessor); // Check the depth to avoid creating voxel topology for higher levels // @todo As this option is not configurable outside of the executable, we @@ -244,9 +245,9 @@ inline FunctionGroup::Ptr axgetvoxel(const FunctionOptions& op) assert(wspos); assert(transform); - const AccessorType* const accessorPtr = static_cast(accessor); + const AccessorType* const accessorPtr = static_cast(accessor); const openvdb::math::Transform* const transformPtr = - static_cast(transform); + static_cast(transform); const openvdb::Coord coordIS = transformPtr->worldToIndexCellCentered(*wspos); (*value) = accessorPtr->getValue(coordIS); }; @@ -267,9 +268,9 @@ inline FunctionGroup::Ptr axgetvoxel(const FunctionOptions& op) assert(wspos); assert(transform); - const AccessorType* const accessorPtr = static_cast(accessor); + const AccessorType* const accessorPtr = static_cast(accessor); const openvdb::math::Transform* const transformPtr = - static_cast(transform); + static_cast(transform); openvdb::Coord coordIS = transformPtr->worldToIndexCellCentered(*wspos); const std::string& str = accessorPtr->getValue(coordIS); value->ptr = str.c_str(); @@ -358,8 +359,8 @@ void insertVDBVolumeFunctions(FunctionRegistry& registry, add("setvoxel", axsetvoxel, true); } -} -} -} -} +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb_ax/openvdb_ax/compiler/AttributeRegistry.h b/openvdb_ax/openvdb_ax/compiler/AttributeRegistry.h index 697f853b04..127fedd0f3 100644 --- a/openvdb_ax/openvdb_ax/compiler/AttributeRegistry.h +++ b/openvdb_ax/openvdb_ax/compiler/AttributeRegistry.h @@ -20,6 +20,8 @@ #include "../ast/Tokens.h" #include "../ast/Scanners.h" +#include + #include namespace openvdb { @@ -272,9 +274,9 @@ inline void AttributeRegistry::print(std::ostream& os) const } } -} -} -} +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_COMPILER_TARGET_REGISTRY_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/compiler/Compiler.cc b/openvdb_ax/openvdb_ax/compiler/Compiler.cc index c53ef7837e..61feb5d097 100644 --- a/openvdb_ax/openvdb_ax/compiler/Compiler.cc +++ b/openvdb_ax/openvdb_ax/compiler/Compiler.cc @@ -22,8 +22,6 @@ #include #include #include -#include -#include #include #include #include @@ -34,12 +32,10 @@ #include #include #include -#include // llvm_shutdown #include #include #include // SMDiagnostic #include -#include #include #include @@ -57,8 +53,6 @@ #include #include -#include - #include namespace openvdb { @@ -67,107 +61,6 @@ namespace OPENVDB_VERSION_NAME { namespace ax { - -namespace { -// Declare this at file scope to ensure thread-safe initialization. -tbb::mutex sInitMutex; -bool sIsInitialized = false; -bool sShutdown = false; -} - - -bool isInitialized() -{ - tbb::mutex::scoped_lock lock(sInitMutex); - return sIsInitialized; -} - -void initialize() -{ - tbb::mutex::scoped_lock lock(sInitMutex); - if (sIsInitialized) return; - - if (sShutdown) { - OPENVDB_THROW(LLVMInitialisationError, "Unable to re-initialize after uninitialize has been called."); - } - - // Init JIT - - if (llvm::InitializeNativeTarget() || - llvm::InitializeNativeTargetAsmPrinter() || - llvm::InitializeNativeTargetAsmParser()) { - OPENVDB_THROW(LLVMInitialisationError, "Failed to initialize for JIT"); - } - - LLVMLinkInMCJIT(); - - // Initialize passes - llvm::PassRegistry& registry = *llvm::PassRegistry::getPassRegistry(); - llvm::initializeCore(registry); - llvm::initializeScalarOpts(registry); - llvm::initializeObjCARCOpts(registry); - llvm::initializeVectorization(registry); - llvm::initializeIPO(registry); - llvm::initializeAnalysis(registry); - llvm::initializeTransformUtils(registry); - llvm::initializeInstCombine(registry); -#if LLVM_VERSION_MAJOR > 6 - llvm::initializeAggressiveInstCombine(registry); -#endif - llvm::initializeInstrumentation(registry); - llvm::initializeTarget(registry); - // For codegen passes, only passes that do IR to IR transformation are - // supported. -#if LLVM_VERSION_MAJOR > 5 - llvm::initializeExpandMemCmpPassPass(registry); -#endif - llvm::initializeScalarizeMaskedMemIntrinPass(registry); - llvm::initializeCodeGenPreparePass(registry); - llvm::initializeAtomicExpandPass(registry); - llvm::initializeRewriteSymbolsLegacyPassPass(registry); - llvm::initializeWinEHPreparePass(registry); - llvm::initializeDwarfEHPreparePass(registry); - llvm::initializeSafeStackLegacyPassPass(registry); - llvm::initializeSjLjEHPreparePass(registry); - llvm::initializePreISelIntrinsicLoweringLegacyPassPass(registry); - llvm::initializeGlobalMergePass(registry); -#if LLVM_VERSION_MAJOR > 6 - llvm::initializeIndirectBrExpandPassPass(registry); -#endif -#if LLVM_VERSION_MAJOR > 7 - llvm::initializeInterleavedLoadCombinePass(registry); -#endif - llvm::initializeInterleavedAccessPass(registry); -#if LLVM_VERSION_MAJOR > 5 - llvm::initializeEntryExitInstrumenterPass(registry); - llvm::initializePostInlineEntryExitInstrumenterPass(registry); -#else - llvm::initializeCountingFunctionInserterPass(registry); -#endif - llvm::initializeUnreachableBlockElimLegacyPassPass(registry); - llvm::initializeExpandReductionsPass(registry); -#if LLVM_VERSION_MAJOR > 6 - llvm::initializeWasmEHPreparePass(registry); -#endif -#if LLVM_VERSION_MAJOR > 5 - llvm::initializeWriteBitcodePassPass(registry); -#endif - - sIsInitialized = true; -} - -void uninitialize() -{ - tbb::mutex::scoped_lock lock(sInitMutex); - if (!sIsInitialized) return; - - // @todo consider replacing with storage to Support/InitLLVM - llvm::llvm_shutdown(); - - sIsInitialized = false; - sShutdown = true; -} - namespace { @@ -398,7 +291,7 @@ void optimiseAndVerify(llvm::Module* module, if (verify) { llvm::raw_os_ostream out(std::cout); if (llvm::verifyModule(*module, &out)) { - OPENVDB_THROW(LLVMIRError, "LLVM IR is not valid."); + OPENVDB_THROW(AXCompilerError, "Generated LLVM IR is not valid."); } } @@ -494,7 +387,7 @@ void initializeGlobalFunctions(const codegen::FunctionRegistry& registry, const uint64_t address = binding->address(); if (address == 0) { - OPENVDB_THROW(LLVMFunctionError, "No available mapping for C Binding " + OPENVDB_THROW(AXCompilerError, "No available mapping for C Binding " "with symbol \"" << decl->symbol() << "\""); } const std::string mangled = @@ -504,15 +397,35 @@ void initializeGlobalFunctions(const codegen::FunctionRegistry& registry, // we've overwritten something const uint64_t oldAddress = engine.updateGlobalMapping(mangled, address); if (oldAddress != 0 && oldAddress != address) { - OPENVDB_THROW(LLVMFunctionError, "Function registry mapping error - " + OPENVDB_THROW(AXCompilerError, "Function registry mapping error - " "multiple functions are using the same symbol \"" << decl->symbol() << "\"."); } } } + +#ifndef NDEBUG + // Loop through all functions and check to see if they have valid engine mappings. + // This can occur if lazy functions don't initialize their dependencies properly. + // @todo Really we should just loop through the module functions to begin with + // to init engine mappings - it would probably be faster but we'd have to do + // some string manip and it would assume function names have been set up + // correctly + const auto& list = module.getFunctionList(); + for (const auto& F : list) { + if (F.size() > 0) continue; + // Some LLVM functions may also not be defined at this stage which is expected + if (!F.getName().startswith("ax")) continue; + const std::string mangled = + getMangledName(llvm::cast(&F), engine); + const uint64_t address = + engine.getAddressToGlobalIfAvailable(mangled); + assert(address != 0 && "Unbound function!"); + } +#endif } -void verifyTypedAccesses(const ast::Tree& tree) +void verifyTypedAccesses(const ast::Tree& tree, openvdb::ax::Logger& logger) { // verify the attributes and external variables requested in the syntax tree // only have a single type. Note that the executer will also throw a runtime @@ -523,14 +436,14 @@ void verifyTypedAccesses(const ast::Tree& tree) std::unordered_map nameType; auto attributeOp = - [&nameType](const ast::Attribute& node) -> bool { + [&nameType, &logger](const ast::Attribute& node) -> bool { auto iter = nameType.find(node.name()); if (iter == nameType.end()) { nameType[node.name()] = node.typestr(); } else if (iter->second != node.typestr()) { - OPENVDB_THROW(AXCompilerError, "Failed to compile ambiguous @ parameters. " - "\"" + node.name() + "\" has been accessed with different types."); + logger.error("failed to compile ambiguous @ parameters. " + "\"" + node.name() + "\" has been accessed with different type elsewhere.", &node); } return true; }; @@ -540,14 +453,14 @@ void verifyTypedAccesses(const ast::Tree& tree) nameType.clear(); auto externalOp = - [&nameType](const ast::ExternalVariable& node) -> bool { + [&nameType, &logger](const ast::ExternalVariable& node) -> bool { auto iter = nameType.find(node.name()); if (iter == nameType.end()) { nameType[node.name()] = node.typestr(); } else if (iter->second != node.typestr()) { - OPENVDB_THROW(AXCompilerError, "Failed to compile ambiguous $ parameters. " - "\"" + node.name() + "\" has been accessed with different types."); + logger.error("failed to compile ambiguous $ parameters. " + "\"" + node.name() + "\" has been accessed with different type elsewhere.", &node); } return true; }; @@ -631,7 +544,7 @@ registerExternalGlobals(const codegen::SymbolTable& globals, CustomData::Ptr& da case ast::tokens::UNKNOWN : default : { // grammar guarantees this is unreachable as long as all types are supported - OPENVDB_THROW(LLVMTypeError, "Attribute Type unsupported or not recognised"); + OPENVDB_THROW(AXCompilerError, "Attribute type unsupported or not recognised"); } } }; @@ -674,6 +587,7 @@ struct PointDefaultModifier : using openvdb::ax::ast::Visitor::visit; PointDefaultModifier() = default; + virtual ~PointDefaultModifier() = default; const std::set autoVecAttribs {"P", "v", "N", "Cd"}; @@ -685,7 +599,7 @@ struct PointDefaultModifier : openvdb::ax::ast::Attribute::UniquePtr replacement(new openvdb::ax::ast::Attribute(attrib->name(), ast::tokens::VEC3F, true)); if (!attrib->replace(replacement.get())) { - OPENVDB_THROW(AXExecutionError, + OPENVDB_THROW(AXCompilerError, "Auto conversion of inferred attributes failed."); } replacement.release(); @@ -718,21 +632,18 @@ void Compiler::setFunctionRegistry(std::unique_ptr&& mFunctionRegistry = std::move(functionRegistry); } - template<> PointExecutable::Ptr Compiler::compile(const ast::Tree& syntaxTree, - const CustomData::Ptr customData, - std::vector* warnings) + Logger& logger, + const CustomData::Ptr customData) { openvdb::SharedPtr tree(syntaxTree.copy()); PointDefaultModifier modifier; modifier.traverse(tree.get()); - verifyTypedAccesses(*tree); - + verifyTypedAccesses(*tree, logger); // initialize the module and generate LLVM IR - std::unique_ptr module(new llvm::Module("module", *mContext)); std::unique_ptr TM = initializeTargetMachine(); if (TM) { @@ -742,13 +653,17 @@ Compiler::compile(const ast::Tree& syntaxTree, codegen::PointComputeGenerator codeGenerator(*module, mCompilerOptions.mFunctionOptions, - *mFunctionRegistry, warnings); + *mFunctionRegistry, logger); + AttributeRegistry::Ptr attributes = codeGenerator.generate(*tree); - AttributeRegistry::Ptr registry = codeGenerator.generate(*tree); + // if there has been a compilation error through user error, exit + if (!attributes) { + assert(logger.hasError()); + return nullptr; + } // map accesses (always do this prior to optimising as globals may be removed) - - registerAccesses(codeGenerator.globals(), *registry); + registerAccesses(codeGenerator.globals(), *attributes); CustomData::Ptr validCustomData(customData); registerExternalGlobals(codeGenerator.globals(), validCustomData, *mContext); @@ -776,7 +691,7 @@ Compiler::compile(const ast::Tree& syntaxTree, .create()); if (!executionEngine) { - OPENVDB_THROW(AXExecutionError, "Failed to create ExecutionEngine: " + error); + OPENVDB_THROW(AXCompilerError, "Failed to create LLVMExecutionEngine: " + error); } // map functions @@ -808,19 +723,20 @@ Compiler::compile(const ast::Tree& syntaxTree, PointExecutable::Ptr executable(new PointExecutable(mContext, executionEngine, - registry, + attributes, validCustomData, functionMap)); + return executable; } template<> VolumeExecutable::Ptr Compiler::compile(const ast::Tree& syntaxTree, - const CustomData::Ptr customData, - std::vector* warnings) + Logger& logger, + const CustomData::Ptr customData) { - verifyTypedAccesses(syntaxTree); + verifyTypedAccesses(syntaxTree, logger); // initialize the module and generate LLVM IR @@ -833,13 +749,17 @@ Compiler::compile(const ast::Tree& syntaxTree, codegen::VolumeComputeGenerator codeGenerator(*module, mCompilerOptions.mFunctionOptions, - *mFunctionRegistry, warnings); + *mFunctionRegistry, logger); + AttributeRegistry::Ptr attributes = codeGenerator.generate(syntaxTree); - AttributeRegistry::Ptr registry = codeGenerator.generate(syntaxTree); + // if there has been a compilation error through user error, exit + if (!attributes) { + assert(logger.hasError()); + return nullptr; + } // map accesses (always do this prior to optimising as globals may be removed) - - registerAccesses(codeGenerator.globals(), *registry); + registerAccesses(codeGenerator.globals(), *attributes); CustomData::Ptr validCustomData(customData); registerExternalGlobals(codeGenerator.globals(), validCustomData, *mContext); @@ -854,10 +774,10 @@ Compiler::compile(const ast::Tree& syntaxTree, .setErrorStr(&error) .create()); + if (!executionEngine) { - OPENVDB_THROW(AXExecutionError, "Failed to create ExecutionEngine: " + error); + OPENVDB_THROW(AXCompilerError, "Failed to create LLVMExecutionEngine: " + error); } - // map functions initializeGlobalFunctions(*mFunctionRegistry, *executionEngine, @@ -880,14 +800,14 @@ Compiler::compile(const ast::Tree& syntaxTree, VolumeExecutable::Ptr executable(new VolumeExecutable(mContext, executionEngine, - registry, + attributes, validCustomData, functionMap)); return executable; } -} -} -} +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb_ax/openvdb_ax/compiler/Compiler.h b/openvdb_ax/openvdb_ax/compiler/Compiler.h index 611f00ace9..c839b27f8e 100644 --- a/openvdb_ax/openvdb_ax/compiler/Compiler.h +++ b/openvdb_ax/openvdb_ax/compiler/Compiler.h @@ -15,11 +15,17 @@ #ifndef OPENVDB_AX_COMPILER_HAS_BEEN_INCLUDED #define OPENVDB_AX_COMPILER_HAS_BEEN_INCLUDED -#include "../ast/AST.h" -#include "../compiler/CompilerOptions.h" -#include "../compiler/CustomData.h" +#include "CompilerOptions.h" +#include "CustomData.h" +#include "Logger.h" + +#include "../ax.h" // backward compat support for initialize() +#include "../ast/Parse.h" + +#include #include +#include // forward namespace llvm { @@ -37,18 +43,9 @@ namespace codegen { class FunctionRegistry; } -/// @brief Initializes llvm. Must be called before any AX compilation or execution is performed. -void initialize(); - -/// @brief Check to see if llvm has been initialized. -bool isInitialized(); - -/// @brief Shuts down llvm. Must be called on application termination -void uninitialize(); - -/// @brief The compiler class. This holds an llvm context and set of compiler options, and constructs -/// executable objects (e.g. PointExecutable or VolumeExecutable) from a syntax tree or -/// snippet of code. +/// @brief The compiler class. This holds an llvm context and set of compiler +/// options, and constructs executable objects (e.g. PointExecutable or +/// VolumeExecutable) from a syntax tree or snippet of code. class Compiler { public: @@ -65,52 +62,172 @@ class Compiler /// @brief Static method for creating Compiler objects static UniquePtr create(const CompilerOptions& options = CompilerOptions()); - /// @brief Compile/build a given AST into an executable object of the given type. + /// @brief Compile a given AST into an executable object of the given type. /// @param syntaxTree An abstract syntax tree to compile - /// @param data External/custom data which is to be referenced by the executable object. It - /// allows one to reference data held elsewhere, such as inside of a DCC, inside of the - /// executable - /// @param compilerErrors A vector of strings where errors are inserted into + /// @param logger Logger for errors and warnings during compilation, this + /// should be linked to an ast::Tree and populated with AST node + line + /// number mappings for this Tree, e.g. during ast::parse(). This Tree can + /// be different from the syntaxTree argument. + /// @param data Optional external/custom data which is to be referenced by + /// the executable object. It allows one to reference data held elsewhere, + /// such as inside of a DCC, from inside the AX code + /// @note If the logger has not been populated with AST node and line + /// mappings, all messages will appear without valid line and column + /// numbers. template typename ExecutableT::Ptr compile(const ast::Tree& syntaxTree, - const CustomData::Ptr data = CustomData::Ptr(), - std::vector* compilerErrors = nullptr); + Logger& logger, + const CustomData::Ptr data = CustomData::Ptr()); + + /// @brief Compile a given snippet of AX code into an executable object of + /// the given type. + /// @param code A string of AX code + /// @param logger Logger for errors and warnings during compilation, will be + /// cleared of existing data + /// @param data Optional external/custom data which is to be referenced by + /// the executable object. It allows one to reference data held elsewhere, + /// such as inside of a DCC, from inside the AX code + /// @note If compilation is unsuccessful, will return nullptr. Logger can + /// then be queried for errors. + template + typename ExecutableT::Ptr + compile(const std::string& code, + Logger& logger, + const CustomData::Ptr data = CustomData::Ptr()) + { + logger.clear(); + const ast::Tree::ConstPtr syntaxTree = ast::parse(code.c_str(), logger); + if (syntaxTree) return compile(*syntaxTree, logger, data); + else return nullptr; + } - /// @brief Compile/build a given snippet of AX code into an executable object of the given type. + /// @brief Compile a given snippet of AX code into an executable object of + /// the given type. /// @param code A string of AX code - /// @param data External/custom data which is to be referenced by the executable object. It - /// allows one to reference data held elsewhere, such as inside of a DCC, from inside - /// the AX code - /// @param compilerErrors A vector of strings where errors are inserted into + /// @param data Optional external/custom data which is to be referenced by + /// the executable object. It allows one to reference data held elsewhere, + /// such as inside of a DCC, from inside the AX code + /// @note Parser errors are handled separately from compiler errors. + /// Each are collected and produce runtime errors. template typename ExecutableT::Ptr compile(const std::string& code, - const CustomData::Ptr data = CustomData::Ptr(), - std::vector* compilerErrors = nullptr) + const CustomData::Ptr data = CustomData::Ptr()) { - ast::Tree::Ptr syntaxTree = ast::parse(code.c_str()); - return compile(*syntaxTree, data, compilerErrors); + std::vector errors; + openvdb::ax::Logger logger( + [&errors] (const std::string& error) { + errors.emplace_back(error + "\n"); + }, + // ignore warnings + [] (const std::string&) {} + ); + const ast::Tree::ConstPtr syntaxTree = ast::parse(code.c_str(), logger); + typename ExecutableT::Ptr exe; + if (syntaxTree) { + exe = this->compile(*syntaxTree, logger, data); + } + if (!errors.empty()) { + std::ostringstream os; + for (const auto& e : errors) os << e << "\n"; + OPENVDB_THROW(AXCompilerError, os.str()); + } + assert(exe); + return exe; + } + + /// @brief Compile a given AST into an executable object of the given type. + /// @param syntaxTree An abstract syntax tree to compile + /// @param data Optional external/custom data which is to be referenced by + /// the executable object. It allows one to reference data held elsewhere, + /// such as inside of a DCC, from inside the AX code + /// @note Any errors encountered are collected into a single runtime error + template + typename ExecutableT::Ptr + compile(const ast::Tree& syntaxTree, + const CustomData::Ptr data = CustomData::Ptr()) + { + std::vector errors; + openvdb::ax::Logger logger( + [&errors] (const std::string& error) { + errors.emplace_back(error + "\n"); + }, + // ignore warnings + [] (const std::string&) {} + ); + auto exe = compile(syntaxTree, logger, data); + if (!errors.empty()) { + std::ostringstream os; + for (const auto& e : errors) os << e << "\n"; + OPENVDB_THROW(AXCompilerError, os.str()); + } + assert(exe); + return exe; } /// @brief Sets the compiler's function registry object. - /// @param functionRegistry A unique pointer to a FunctionRegistry object. The compiler will - /// take ownership of the registry that was passed in. - /// @todo Perhaps allow one to register individual functions into this class rather than the entire - /// registry at once, and/or allow one to extract a pointer to the registry and update it - /// manually. + /// @param functionRegistry A unique pointer to a FunctionRegistry object. + /// The compiler will take ownership of the registry that was passed in. + /// @todo Perhaps allow one to register individual functions into this + /// class rather than the entire registry at once, and/or allow one to + /// extract a pointer to the registry and update it manually. void setFunctionRegistry(std::unique_ptr&& functionRegistry); + /////////////////////////////////////////////////////////////////////////// + + /// @brief deprecated methods + template + OPENVDB_DEPRECATED + typename ExecutableT::Ptr + compile(const ast::Tree& syntaxTree, + const CustomData::Ptr data, + std::vector* warnings) { + openvdb::ax::Logger logger( + // throw immediately on first error + [] (const std::string& error) { + OPENVDB_THROW(AXSyntaxError, error); + }, + // collect warnings in vector + [&warnings] (const std::string& warn) { + if (warnings) warnings->emplace_back(warn); + } + ); + return compile(syntaxTree, logger, data); + } + + template + OPENVDB_DEPRECATED + typename ExecutableT::Ptr + compile(const std::string& code, + const CustomData::Ptr data, + std::vector* warnings) { + openvdb::ax::Logger logger( + // throw immediately on first error + [] (const std::string& error) { + OPENVDB_THROW(AXSyntaxError, error); + }, + // collect warnings in vector + [&warnings] (const std::string& warn) { + if (warnings) warnings->emplace_back(warn); + } + ); + return compile(code, logger, data); + } + + /////////////////////////////////////////////////////////////////////////// + private: + std::shared_ptr mContext; const CompilerOptions mCompilerOptions; std::shared_ptr mFunctionRegistry; }; -} -} -} +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_COMPILER_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/compiler/CompilerOptions.h b/openvdb_ax/openvdb_ax/compiler/CompilerOptions.h index a0bcccc5d0..c0d06a6209 100644 --- a/openvdb_ax/openvdb_ax/compiler/CompilerOptions.h +++ b/openvdb_ax/openvdb_ax/compiler/CompilerOptions.h @@ -12,6 +12,7 @@ #define OPENVDB_AX_COMPILER_COMPILER_OPTIONS_HAS_BEEN_INCLUDED #include +#include namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE @@ -66,9 +67,9 @@ struct CompilerOptions FunctionOptions mFunctionOptions = FunctionOptions(); }; -} -} -} +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_COMPILER_FUNCTION_REGISTRY_OPTIONS_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/compiler/CustomData.h b/openvdb_ax/openvdb_ax/compiler/CustomData.h index 84d1cd8746..b39a1771cf 100644 --- a/openvdb_ax/openvdb_ax/compiler/CustomData.h +++ b/openvdb_ax/openvdb_ax/compiler/CustomData.h @@ -14,6 +14,7 @@ #include "../ast/Literals.h" +#include #include #include @@ -226,9 +227,9 @@ struct AXStringMetadata : public StringMetadata }; -} -} -} +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_COMPILER_CUSTOM_DATA_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/compiler/LeafLocalData.h b/openvdb_ax/openvdb_ax/compiler/LeafLocalData.h index d832397e92..0cd63cee6d 100644 --- a/openvdb_ax/openvdb_ax/compiler/LeafLocalData.h +++ b/openvdb_ax/openvdb_ax/compiler/LeafLocalData.h @@ -12,6 +12,7 @@ #define OPENVDB_AX_COMPILER_LEAF_LOCAL_DATA_HAS_BEEN_INCLUDED #include +#include #include #include #include @@ -220,10 +221,10 @@ struct LeafLocalData StringArrayMap mStringMap; }; -} -} -} -} +} // namespace compiler +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_COMPILER_LEAF_LOCAL_DATA_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/compiler/Logger.cc b/openvdb_ax/openvdb_ax/compiler/Logger.cc new file mode 100644 index 0000000000..e05fb267dc --- /dev/null +++ b/openvdb_ax/openvdb_ax/compiler/Logger.cc @@ -0,0 +1,304 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file compiler/Logger.cc + +#include "Logger.h" + +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +namespace ax { + +struct Logger::Settings { + size_t mMaxErrors = 0; + bool mWarningsAsErrors = false; + // message formatting settings + bool mNumbered = true; + const char* mErrorPrefix = "error: "; + const char* mWarningPrefix = "warning: "; + bool mPrintLines = false; +}; + + +/// @brief Wrapper around a code snippet to print individual lines from a multi line string +/// @note Assumes a null terminated c-style string input +struct Logger::SourceCode +{ + SourceCode(const char* string = nullptr) + : mString(string) + , mOffsets() + , mLines() { + reset(string); + } + + /// @brief Print a line of the multi-line string to the stream + /// @note If no string hs been provided, will do nothing + /// @param line Line number to print + /// @param os Output stream + void getLine(const size_t num, std::ostream* os) + { + if (num < 1) return; + if (mOffsets.empty()) getLineOffsets(); + if (num > mLines) return; + const size_t start = mOffsets[num - 1]; + const size_t end = mOffsets[num]; + for (size_t i = start; i < end - 1; ++i) *os << mString[i]; + } + void reset(const char* string) { + mString = string; + mOffsets.clear(); + mLines = 0; + } + + bool hasString() const { + return static_cast(mString); + } + +private: + + void getLineOffsets() + { + if (!mString) return; + mOffsets.emplace_back(0); + size_t offset = 1; + const char* iter = mString; + while (*iter != '\0') { + if (*iter == '\n') mOffsets.emplace_back(offset); + ++iter; ++offset; + } + mOffsets.emplace_back(offset); + mLines = mOffsets.size(); + } + + const char* mString; + std::vector mOffsets; + size_t mLines; +}; + +namespace { + +/// @brief Return a stack denoting the position in the tree of this node +/// Where each node is represented by its childidx of its parent +/// This gives a branching path to follow to reach this node from the +/// tree root +/// @parm node Node pointer to create position stack for +inline std::stack pathStackFromNode(const ast::Node* node) +{ + std::stack path; + const ast::Node* child = node; + const ast::Node* parent = node->parent(); + while (parent) { + path.emplace(child->childidx()); + child = parent; + parent = child->parent(); + } + return path; +} + +/// @brief Iterate through a tree, following the branch numbers from the path stack, +/// returning a Node* to the node at this position +/// @parm path Stack of child branches to follow +/// @parm tree Tree containing node to return +inline const ast::Node* nodeFromPathStack(std::stack& path, + const ast::Tree& tree) +{ + const ast::Node* node = &tree; + while (node) { + if (path.empty()) return node; + node = node->child(path.top()); + path.pop(); + } + return nullptr; +} + +/// @brief Given any node and a tree and node to location map, return the line and column +/// number for the nodes equivalent (in terms of position in the tree) from the supplied tree +/// @note Requires the map to have been populated for all nodes in the supplied tree, otherwise +/// will return 0:0 +inline const Logger::CodeLocation nodeToCodeLocation(const ast::Node* node, + const ast::Tree::ConstPtr tree, + const std::unordered_map< + const ax::ast::Node*, + Logger::CodeLocation>& map) +{ + if (!tree) return Logger::CodeLocation(0,0); + assert(node); + std::stack pathStack = pathStackFromNode(node); + const ast::Node* nodeInMap = nodeFromPathStack(pathStack, *tree); + const auto locationIter = map.find(nodeInMap); + if (locationIter == map.end()) return Logger::CodeLocation(0,0); + return locationIter->second; +} + +std::string format(const std::string& message, + const Logger::CodeLocation& loc, + const size_t numMessage, + const bool numbered, + const bool printLines, + Logger::SourceCode* sourceCode) +{ + std::stringstream ss; + if (numbered) ss << "[" << numMessage + 1 << "] "; + ss << message; + if (loc.first > 0) { + ss << " " << loc.first << ":" << loc.second; + if (printLines && sourceCode) { + ss << "\n"; + sourceCode->getLine(loc.first, &ss); + ss << "\n"; + for (size_t i = 0; i < loc.second - 1; ++i) ss << "-"; + ss << "^"; + } + } + return ss.str(); +} + +} + +Logger::Logger(const Logger::OutputFunction& errors, + const Logger::OutputFunction& warnings) + : mErrorOutput(errors) + , mWarningOutput(warnings) + , mNumErrors(0) + , mNumWarnings(0) + , mSettings(new Logger::Settings()) + , mCode() {} + +Logger::~Logger() {} + +void Logger::setSourceCode(const char* code) +{ + mCode.reset(new SourceCode(code)); +} + +bool Logger::error(const std::string& message, + const Logger::CodeLocation& lineCol) +{ + mErrorOutput(format(this->getErrorPrefix() + message, + lineCol, + this->errors(), + this->getNumberedOutput(), + this->getPrintLines(), + this->mCode.get())); + ++mNumErrors; + if (this->getMaxErrors() > 0 && this->errors() >= this->getMaxErrors()) return false; + else return true; +} + +bool Logger::error(const std::string& message, + const ax::ast::Node* node) +{ + return this->error(message, nodeToCodeLocation(node, mTreePtr, mNodeToLineColMap)); +} + +bool Logger::warning(const std::string& message, + const Logger::CodeLocation& lineCol) +{ + if (this->getWarningsAsErrors()) { + return this->error(message + " [warning-as-error]", lineCol); + } + else { + mWarningOutput(format(this->getWarningPrefix() + message, + lineCol, + this->warnings(), + this->getNumberedOutput(), + this->getPrintLines(), + this->mCode.get())); + ++mNumWarnings; + return true; + } +} + +bool Logger::warning(const std::string& message, + const ax::ast::Node* node) +{ + return this->warning(message, nodeToCodeLocation(node, mTreePtr, mNodeToLineColMap)); +} + +void Logger::setWarningsAsErrors(const bool warnAsError) +{ + mSettings->mWarningsAsErrors = warnAsError; +} + +bool Logger::getWarningsAsErrors() const +{ + return mSettings->mWarningsAsErrors; +} + +void Logger::setMaxErrors(const size_t maxErrors) +{ + mSettings->mMaxErrors = maxErrors; +} + +size_t Logger::getMaxErrors() const +{ + return mSettings->mMaxErrors; +} + +void Logger::setNumberedOutput(const bool numbered) +{ + mSettings->mNumbered = numbered; +} + +void Logger::setErrorPrefix(const char* prefix) +{ + mSettings->mErrorPrefix = prefix; +} + +void Logger::setWarningPrefix(const char* prefix) +{ + mSettings->mWarningPrefix = prefix; +} + +void Logger::setPrintLines(const bool print) +{ + mSettings->mPrintLines = print; +} + +bool Logger::getNumberedOutput() const +{ + return mSettings->mNumbered; +} + +const char* Logger::getErrorPrefix() const +{ + return mSettings->mErrorPrefix; +} + +const char* Logger::getWarningPrefix() const +{ + return mSettings->mWarningPrefix; +} + +bool Logger::getPrintLines() const +{ + return mSettings->mPrintLines; +} + +void Logger::clear() +{ + mCode.reset(); + mNumErrors = 0; + mNumWarnings = 0; + mNodeToLineColMap.clear(); + mTreePtr = nullptr; +} + +void Logger::setSourceTree(openvdb::ax::ast::Tree::ConstPtr tree) +{ + mTreePtr = tree; +} + +void Logger::addNodeLocation(const ax::ast::Node* node, const Logger::CodeLocation& location) +{ + mNodeToLineColMap.emplace(node, location); +} + +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + diff --git a/openvdb_ax/openvdb_ax/compiler/Logger.h b/openvdb_ax/openvdb_ax/compiler/Logger.h new file mode 100644 index 0000000000..f4bcf868c1 --- /dev/null +++ b/openvdb_ax/openvdb_ax/compiler/Logger.h @@ -0,0 +1,190 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file codegen/Logger.h +/// +/// @authors Richard Jones +/// +/// @brief Logging system to collect errors and warnings throughout the different stages +/// of parsing and compilation. +/// + +#ifndef OPENVDB_AX_COMPILER_LOGGER_HAS_BEEN_INCLUDED +#define OPENVDB_AX_COMPILER_LOGGER_HAS_BEEN_INCLUDED + +#include "../ast/AST.h" + +#include + +#include +#include +#include + +class TestLogger; + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +namespace ax { + +/// @brief Logger for collecting errors and warnings that occur during AX compilation. +/// Error and warning output can be customised using the function pointer arguments. These +/// require a function that takes the formatted error/warning string and handles the output, +/// returning void. +/// e.g. +/// void streamCerr(const std::string& message) { std::cerr << message << std::endl; } +/// +/// The Logger handles formatting of messages, tracking of number of errors/warnings and +/// retrieval of errored lines of code to be printed if needed. +/// Use of the Logger to track new errors or warnings can be done either with the line/column +/// numbers directly (e.g during lexing and parsing where the code is being iterated through) +/// or referring to the AST node using its position in the Tree (e.g. during codegen +/// where only the AST node is known directly, not the corresponding line/column numbers). +/// To find the line/column numbers for events logged using AST nodes, the Logger stores +/// a map of Node* to line and column numbers. This must be populated e.g. during parsing, +/// to allow resolution of code locations when they are not explicitly available. +/// The Logger also stores a pointer to the AST Tree that these nodes belong to and the code +/// used to create it. +/// +class Logger +{ +public: + using Ptr = std::shared_ptr; + + using CodeLocation = std::pair; + using OutputFunction = std::function; + + /// @brief Construct a Logger with optional error and warning output functions, + /// defaults stream errors to std::cerr and suppress warnings + /// @param errors Optional error output function + /// @param warnings Optional warning output function + Logger(const OutputFunction& errors = [](const std::string& msg){ std::cerr << msg << std::endl; }, + const OutputFunction& warnings = [](const std::string&){}); + + ~Logger(); + + /// @brief Log a compiler error and its offending code location. Returns true if non-fatal + /// and can continue to capture future messages. + /// @param message The error message + /// @param lineCol The line and column number where the offending code can be found + /// @todo: add logging of non position specific errors + bool error(const std::string& message, const CodeLocation& lineCol); + /// @brief Log a compiler error using the offending AST node, for use in AST traversal. + /// Returns true if non-fatal and can continue to capture future messages. + /// @param message The error message + /// @param node The offending AST node causing the error + bool error(const std::string& message, const ax::ast::Node* node); + + /// @brief Log a compiler warning and its offending code location. + /// Returns true if non-fatal and can continue to capture future messages. + /// @param message The warning message + /// @param lineCol The line and column number where the offending code can be found + bool warning(const std::string& message, const CodeLocation& lineCol); + /// @brief Log a compiler warning using the offending AST node, for use in AST traversal. + /// Returns true if non-fatal and can continue to capture future messages. + /// @param message The warning message + /// @param node The offending AST node causing the warning + bool warning(const std::string& message, const ax::ast::Node* node); + + /// @brief Returns the number of errors that have been encountered + inline size_t errors() const { return mNumErrors; } + /// @brief Returns the number of warnings that have been encountered + inline size_t warnings() const { return mNumWarnings; } + + /// @brief Returns true if an error has been found, false otherwise + inline bool hasError() const { return this->errors() > 0; } + /// @brief Returns true if a warning has been found, false otherwise + inline bool hasWarning() const { return this->warnings() > 0; } + + /// @brief Clear the tree-code mapping and reset the number of errors/warnings + /// @note The tree-code mapping must be repopulated to retrieve line and column numbers + /// during AST traversal i.e. code generation. The ax::ast::parse() function + /// does this for a given input code string. + void clear(); + + /// @brief Set any warnings that are encountered to be promoted to errors + /// @param warnAsError If true, warnings will be treated as errors + void setWarningsAsErrors(const bool warnAsError = false); + /// @brief Returns if warning are promoted to errors + bool getWarningsAsErrors() const; + + /// @brief Sets the maximum number of errors that are allowed before compilation should + /// exit + /// @param maxErrors The number of allowed errors + void setMaxErrors(const size_t maxErrors = 0); + /// @brief Returns the number of allowed errors + size_t getMaxErrors() const; + + /// Error/warning formatting options + + /// @brief Set whether the output should number the errors/warnings + /// @param numbered If true, messages will be numbered + void setNumberedOutput(const bool numbered = true); + /// @brief Set a prefix for each error message + void setErrorPrefix(const char* prefix = "error: "); + /// @brief Set a prefix for each warning message + void setWarningPrefix(const char* prefix = "warning: "); + /// @brief Set whether the output should include the offending line of code + /// @param print If true, offending lines of code will be appended to the output message + void setPrintLines(const bool print = true); + + /// @brief Returns whether the messages will be numbered + bool getNumberedOutput() const; + /// @brief Returns the prefix for each error message + const char* getErrorPrefix() const; + /// @brief Returns the prefix for each warning message + const char* getWarningPrefix() const; + /// @brief Returns whether the messages will include the line of offending code + bool getPrintLines() const; + + /// @brief Set the source code that lines can be printed from if an error or warning + /// is raised + /// @param code The AX code as a c-style string + void setSourceCode(const char* code); + + /// These functions are only to be used during parsing to allow line and column + /// number retrieval during later stages of compilation when working solely with an AST + + /// @brief Set the AST source tree which will be used as reference for the locations of + /// nodes when resolving line and column numbers during AST traversal + /// @note To be used just by ax::parse before any AST modifications to ensure traversal + /// of original source tree is possible, when adding messages using Node* + /// which may correspond to modified trees + /// @param tree Pointer to const AST + void setSourceTree(openvdb::ax::ast::Tree::ConstPtr tree); + + /// @brief Add a node to the code location map + /// @param node Pointer to AST node + /// @param location Line and column number in code + void addNodeLocation(const ax::ast::Node* node, const CodeLocation& location); + + // forward declaration + struct Settings; + struct SourceCode; + +private: + + friend class ::TestLogger; + + std::function mErrorOutput; + std::function mWarningOutput; + + size_t mNumErrors; + size_t mNumWarnings; + + std::unique_ptr mSettings; + + // components needed for verbose error info i.e. line/column numbers + // and lines from source code + std::unique_ptr mCode; + ax::ast::Tree::ConstPtr mTreePtr; + std::unordered_map mNodeToLineColMap; +}; + +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_AX_COMPILER_LOGGER_HAS_BEEN_INCLUDED + diff --git a/openvdb_ax/openvdb_ax/compiler/PointExecutable.cc b/openvdb_ax/openvdb_ax/compiler/PointExecutable.cc index 935857484e..cadcb445b3 100644 --- a/openvdb_ax/openvdb_ax/compiler/PointExecutable.cc +++ b/openvdb_ax/openvdb_ax/compiler/PointExecutable.cc @@ -4,15 +4,14 @@ /// @file compiler/PointExecutable.cc #include "PointExecutable.h" - -#include +#include "LeafLocalData.h" #include "../Exceptions.h" - // @TODO refactor so we don't have to include PointComputeGenerator.h, // but still have the functions defined in one place #include "../codegen/PointComputeGenerator.h" -#include "../compiler/LeafLocalData.h" + +#include #include #include @@ -487,7 +486,7 @@ void appendMissingAttributes(points::PointDataGrid& grid, if (typetoken != iter.type() && !(type.second == "str" && iter.type() == ast::tokens::STRING)) { - OPENVDB_THROW(TypeError, "Mismatching attributes types. \"" + name + + OPENVDB_THROW(AXExecutionError, "Mismatching attributes types. \"" + name + "\" exists of type \"" + type.first + "\" but has been " "accessed with type \"" + ast::tokens::typeStringFromToken(iter.type()) + "\""); } @@ -512,7 +511,7 @@ void checkAttributesExist(const points::PointDataGrid& grid, const std::string& name = iter.name(); const size_t pos = desc.find(name); if (pos == points::AttributeSet::INVALID_POS) { - OPENVDB_THROW(openvdb::LookupError, "Attribute \"" + name + + OPENVDB_THROW(AXExecutionError, "Attribute \"" + name + "\" does not exist on grid \"" + grid.getName() + "\""); } } @@ -721,7 +720,7 @@ const std::string& PointExecutable::getGroupExecution() const return mSettings->mGroup; } -} -} -} +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb_ax/openvdb_ax/compiler/PointExecutable.h b/openvdb_ax/openvdb_ax/compiler/PointExecutable.h index edf3216f23..3f7eefb27a 100644 --- a/openvdb_ax/openvdb_ax/compiler/PointExecutable.h +++ b/openvdb_ax/openvdb_ax/compiler/PointExecutable.h @@ -12,10 +12,11 @@ #ifndef OPENVDB_AX_COMPILER_POINT_EXECUTABLE_HAS_BEEN_INCLUDED #define OPENVDB_AX_COMPILER_POINT_EXECUTABLE_HAS_BEEN_INCLUDED -#include "../compiler/CustomData.h" -#include "../compiler/AttributeRegistry.h" +#include "CustomData.h" +#include "AttributeRegistry.h" #include +#include #include #include @@ -140,9 +141,9 @@ class PointExecutable std::unique_ptr mSettings; }; -} -} -} +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_COMPILER_POINT_EXECUTABLE_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/compiler/VolumeExecutable.cc b/openvdb_ax/openvdb_ax/compiler/VolumeExecutable.cc index b065d7e311..08b80abbd6 100644 --- a/openvdb_ax/openvdb_ax/compiler/VolumeExecutable.cc +++ b/openvdb_ax/openvdb_ax/compiler/VolumeExecutable.cc @@ -6,7 +6,6 @@ #include "VolumeExecutable.h" #include "../Exceptions.h" - // @TODO refactor so we don't have to include VolumeComputeGenerator.h, // but still have the functions defined in one place #include "../codegen/VolumeComputeGenerator.h" @@ -31,7 +30,7 @@ namespace OPENVDB_VERSION_NAME { /// If no matrix support exists in the core library, enable these /// inline operators for this translation unit to allow AX to build /// the volume executable with matrix support -#ifdef OPENVDB_AX_NO_MATRIX +#ifndef OPENVDB_HAS_MATRIX_SUPPORT namespace math { #define MATRIX_OPS(TYPE) \ inline TYPE operator+(const TYPE&, const float&) { throw std::runtime_error("Invalid Matrix op+ called."); } \ @@ -45,7 +44,7 @@ MATRIX_OPS(Mat4) MATRIX_OPS(Mat4) #undef MATRIX_OPS } -#endif // OPENVDB_AX_NO_MATRIX +#endif // OPENVDB_HAS_MATRIX_SUPPORT namespace ax { @@ -389,11 +388,11 @@ void registerVolumes(GridPtrVec& grids, } } if (!matchedName && !matchedGrid) { - OPENVDB_THROW(LookupError, "Missing grid \"" + + OPENVDB_THROW(AXExecutionError, "Missing grid \"" + ast::tokens::typeStringFromToken(iter.type()) + "@" + iter.name() + "\"."); } if (matchedName && !matchedGrid) { - OPENVDB_THROW(TypeError, "Mismatching grid access type. \"@" + iter.name() + + OPENVDB_THROW(AXExecutionError, "Mismatching grid access type. \"@" + iter.name() + "\" exists but has been accessed with type \"" + ast::tokens::typeStringFromToken(iter.type()) + "\"."); } @@ -401,7 +400,7 @@ void registerVolumes(GridPtrVec& grids, assert(matchedGrid); if (!supported(type)) { - OPENVDB_THROW(TypeError, "Could not register volume '" + OPENVDB_THROW(AXExecutionError, "Could not register volume '" + matchedGrid->getName() + "' as it has an unknown or unsupported value type '" + matchedGrid->valueType() + "'"); } @@ -488,7 +487,7 @@ inline void run(const openvdb::GridPtrVec& writeableGrids, run(*grid, readptrs.data(), kernel, registry, custom, S); }); if (!success) { - OPENVDB_THROW(TypeError, "Could not retrieve volume '" + grid->getName() + OPENVDB_THROW(AXExecutionError, "Could not retrieve volume '" + grid->getName() + "' as it has an unknown or unsupported value type '" + grid->valueType() + "'"); } @@ -536,7 +535,7 @@ void VolumeExecutable::execute(openvdb::GridPtrVec& grids) const } if (kernel == nullptr) { OPENVDB_THROW(AXCompilerError, - "No code has been successfully compiled for execution."); + "No AX kernel found for execution."); } if (mSettings->mValueIterator == IterType::ON) @@ -655,7 +654,7 @@ size_t VolumeExecutable::getGrainSize() const } -} -} -} +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb diff --git a/openvdb_ax/openvdb_ax/compiler/VolumeExecutable.h b/openvdb_ax/openvdb_ax/compiler/VolumeExecutable.h index 973fe83c0e..317cb152f4 100644 --- a/openvdb_ax/openvdb_ax/compiler/VolumeExecutable.h +++ b/openvdb_ax/openvdb_ax/compiler/VolumeExecutable.h @@ -12,9 +12,10 @@ #ifndef OPENVDB_AX_COMPILER_VOLUME_EXECUTABLE_HAS_BEEN_INCLUDED #define OPENVDB_AX_COMPILER_VOLUME_EXECUTABLE_HAS_BEEN_INCLUDED -#include "../compiler/CustomData.h" -#include "../compiler/AttributeRegistry.h" +#include "CustomData.h" +#include "AttributeRegistry.h" +#include #include #include @@ -152,9 +153,9 @@ class VolumeExecutable std::unique_ptr mSettings; }; -} -} -} +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb #endif // OPENVDB_AX_COMPILER_VOLUME_EXECUTABLE_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/grammar/axlexer.l b/openvdb_ax/openvdb_ax/grammar/axlexer.l index b9e2263e4b..62cd5e592d 100644 --- a/openvdb_ax/openvdb_ax/grammar/axlexer.l +++ b/openvdb_ax/openvdb_ax/grammar/axlexer.l @@ -10,19 +10,22 @@ SPDX-License-Identifier: MPL-2.0 */ %{ - #include - #include #include "openvdb_ax/Exceptions.h" - #include "openvdb_ax/ast/AST.h" + #include "openvdb_ax/ast/Parse.h" #include "axparser.h" /*generated by bison*/ + #include + #include /// @note Bypasses conversion warnings in YY_CURRENT_BUFFER macro. /// This is a bit over zealous as we only need to suppress /// -Wnull-conversion OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + extern openvdb::ax::Logger* axlog; + /// @note Location tracking macro for axlloc token locations. /// YY_USER_ACTION is called before any and each lexer action + /// is performed. Instead of manually tracking newlines, we /// can simply scan for them in the current text held by axtext #define YY_USER_ACTION \ @@ -31,7 +34,7 @@ SPDX-License-Identifier: MPL-2.0 for (int i = 0; axtext[i] != '\0'; i++) { \ if (axtext[i] == '\n') { \ axlloc.last_line++; \ - axlloc.last_column = 0; \ + axlloc.last_column = 1; \ } \ else { \ axlloc.last_column++; \ @@ -196,12 +199,11 @@ COMMENT "//".* "struct"|"switch"|"template"|"this"|"typedef" | \ "uniform"|"union"|"unsigned"|"until"|"virtual" | \ "void" { - // @todo add a proper error manager to the parser + /* @todo: move this into parser */ std::ostringstream os; - os << axlloc.first_line << ":" << axlloc.first_column - << " Reserved keyword not currently implemented: '" - << axtext << "'"; - OPENVDB_THROW(openvdb::LLVMSyntaxError, os.str()); + os <<"\""<< axtext << "\" is a reserved keyword."; + assert(axlog); + axlog->error(os.str(), {axlloc.first_line, axlloc.first_column}); } {WHITESPACE} { } /* ignore whitespace */ @@ -249,15 +251,11 @@ COMMENT "//".* . { /* error on everything else */ - // note that this doesn't call through to yyerrror. This - // allows us to catch errors easily but is limited to the first - // invalid token. - // @todo add a proper error manager to the parser - std::ostringstream os; - os << axlloc.first_line << ":" << axlloc.first_column - << " Stray or invalid character '" - << axtext << "'"; - OPENVDB_THROW(openvdb::LLVMSyntaxError, os.str()); + /* @todo: move this into parser */ + assert(axlog); + axlog->error("stray or invalid character.", + {axlloc.first_line, axlloc.first_column}); + } %% diff --git a/openvdb_ax/openvdb_ax/grammar/axparser.y b/openvdb_ax/openvdb_ax/grammar/axparser.y index 8f0f02b2ac..de448dfb67 100644 --- a/openvdb_ax/openvdb_ax/grammar/axparser.y +++ b/openvdb_ax/openvdb_ax/grammar/axparser.y @@ -8,25 +8,31 @@ /// @brief OpenVDB AX Grammar Rules /// -%{ +%code top { #include #include #include // for OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + #include "openvdb_ax/ast/AST.h" + #include "openvdb_ax/ast/Parse.h" #include "openvdb_ax/ast/Tokens.h" + #include "openvdb_ax/compiler/Logger.h" + /// @note Bypasses bison conversion warnings in yyparse OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN extern int axlex(); + extern openvdb::ax::Logger* axlog; using namespace openvdb::ax::ast; + using namespace openvdb::ax; - void yyerror(Tree** tree, const char* s); + void axerror(Tree** tree, const char* s); using ExpList = std::vector; -%} +} /* Option 'parse.error verbose' tells bison to output verbose parsing errors * as a char* array to yyerror (axerror). Note that this is in lieu of doing @@ -75,8 +81,21 @@ ExpList* explist; } + +%code { + + template + T* newNode(YYLTYPE* loc, const Args&... args) { + T* ptr = new T(args...); + assert(axlog); + axlog->addNodeLocation(ptr, {loc->first_line, loc->first_column}); + return ptr; + } +} + /* AX token type names/terminal symbols */ + %token TRUE FALSE %token SEMICOLON AT DOLLAR %token IF ELSE @@ -178,6 +197,7 @@ /* The start token from AX for bison, represents a fully constructed AST. */ %parse-param {openvdb::ax::ast::Tree** tree} + %start tree /* Begin grammar @@ -185,15 +205,23 @@ %% tree: - /*empty*/ { *tree = new Tree(); $$ = *tree; } - | body { *tree = new Tree($1); $$ = *tree; } + /*empty*/ { *tree = newNode(&@$); + $$ = *tree; + } + | body { *tree = newNode(&@1, $1); + $$ = *tree; + } ; body: body statement { $1->addStatement($2); $$ = $1; } | body block { $1->addStatement($2); $$ = $1; } - | statement { $$ = new Block(); $$->addStatement($1); } - | block { $$ = new Block(); $$->addStatement($1); } + | statement { $$ = newNode(&@$); + $$->addStatement($1); + } + | block { $$ = newNode(&@$); + $$->addStatement($1); + } ; block: @@ -208,11 +236,10 @@ statement: | declarations SEMICOLON { $$ = $1; } | conditional_statement { $$ = $1; } | loop { $$ = $1; } - | RETURN SEMICOLON { $$ = new Keyword(tokens::RETURN); } - | BREAK SEMICOLON { $$ = new Keyword(tokens::BREAK); } - | CONTINUE SEMICOLON { $$ = new Keyword(tokens::CONTINUE); } + | RETURN SEMICOLON { $$ = newNode(&@$, tokens::RETURN); } + | BREAK SEMICOLON { $$ = newNode(&@$, tokens::BREAK); } + | CONTINUE SEMICOLON { $$ = newNode(&@$, tokens::CONTINUE); } | SEMICOLON { $$ = nullptr; } -; expressions: expression { $$ = $1; } @@ -243,37 +270,34 @@ expression: /// @brief Syntax for the declaration of supported local variable types declaration: - type IDENTIFIER { $$ = new DeclareLocal(static_cast($1), new Local($2)); free(const_cast($2)); } - | type IDENTIFIER EQUALS expression { $$ = new DeclareLocal(static_cast($1), - new Local($2), - $4); free(const_cast($2)); } + type IDENTIFIER { $$ = newNode(&@1, static_cast($1), newNode(&@2, $2)); + free(const_cast($2)); } + | type IDENTIFIER EQUALS expression { $$ = newNode(&@1, static_cast($1), newNode(&@2, $2), $4); + free(const_cast($2)); } ; /// @brief A declaration list of at least size 2 declaration_list: - declaration COMMA IDENTIFIER EQUALS expression { $$ = new StatementList($1); + declaration COMMA IDENTIFIER EQUALS expression { $$ = newNode(&@$, $1); const tokens::CoreType type = static_cast($1)->type(); - $$->addStatement( - new DeclareLocal(type, new Local($3), $5)); + $$->addStatement(newNode(&@1, type, newNode(&@3, $3), $5)); free(const_cast($3)); } - | declaration COMMA IDENTIFIER { $$ = new StatementList($1); + | declaration COMMA IDENTIFIER { $$ = newNode(&@$, $1); const tokens::CoreType type = static_cast($1)->type(); - $$->addStatement(new DeclareLocal(type, new Local($3))); + $$->addStatement(newNode(&@1, type, newNode(&@3, $3))); free(const_cast($3)); } | declaration_list COMMA IDENTIFIER EQUALS expression { const auto firstNode = $1->child(0); assert(firstNode); const tokens::CoreType type = static_cast(firstNode)->type(); - $$->addStatement( - new DeclareLocal(type, new Local($3), $5)); - free(const_cast($3)); + $$->addStatement(newNode(&@1, type, newNode(&@3, $3), $5)); $$ = $1; } | declaration_list COMMA IDENTIFIER { const auto firstNode = $1->child(0); assert(firstNode); const tokens::CoreType type = static_cast(firstNode)->type(); - $$->addStatement(new DeclareLocal(type, new Local($3))); + $$->addStatement(newNode(&@1, type, newNode(&@3, $3))); free(const_cast($3)); $$ = $1; } @@ -288,14 +312,14 @@ declarations: /// @brief A single line scope or a scoped block block_or_statement: block { $$ = $1; } - | statement { $$ = new Block(); $$->addStatement($1); } + | statement { $$ = newNode(&@$); $$->addStatement($1); } ; /// @brief Syntax for a conditional statement, capable of supporting a single if /// and an optional single else. Multiple else ifs are handled by this. conditional_statement: - IF LPARENS expressions RPARENS block_or_statement %prec LOWER_THAN_ELSE { $$ = new ConditionalStatement($3, $5); } - | IF LPARENS expressions RPARENS block_or_statement ELSE block_or_statement { $$ = new ConditionalStatement($3, $5, $7); } + IF LPARENS expressions RPARENS block_or_statement %prec LOWER_THAN_ELSE { $$ = newNode(&@$, $3, $5); } + | IF LPARENS expressions RPARENS block_or_statement ELSE block_or_statement { $$ = newNode(&@$, $3, $5, $7); } ; /// @brief A loop condition statement, either an initialized declaration or a list of expressions @@ -325,94 +349,94 @@ loop_iter: /// @brief For loops, while loops and do-while loops. loop: FOR LPARENS loop_init SEMICOLON loop_condition_optional SEMICOLON loop_iter RPARENS block_or_statement - { $$ = new Loop(tokens::FOR, ($5 ? $5 : new Value(true)), $9, $3, $7); } - | DO block_or_statement WHILE LPARENS loop_condition RPARENS { $$ = new Loop(tokens::DO, $5, $2); } - | WHILE LPARENS loop_condition RPARENS block_or_statement { $$ = new Loop(tokens::WHILE, $3, $5); } + { $$ = newNode(&@$, tokens::FOR, ($5 ? $5 : newNode>(&@$, true)), $9, $3, $7); } + | DO block_or_statement WHILE LPARENS loop_condition RPARENS { $$ = newNode(&@$, tokens::DO, $5, $2); } + | WHILE LPARENS loop_condition RPARENS block_or_statement { $$ = newNode(&@$, tokens::WHILE, $3, $5); } ; /// @brief Beginning/builder syntax for function calls with arguments function_start_expression: - IDENTIFIER LPARENS expression { $$ = new FunctionCall($1); $$->append($3); free(const_cast($1)); } + IDENTIFIER LPARENS expression { $$ = newNode(&@1, $1); $$->append($3); free(const_cast($1)); } | function_start_expression COMMA expression { $1->append($3); $$ = $1; } ; /// @brief A function call, taking zero or a comma separated list of arguments function_call_expression: - IDENTIFIER LPARENS RPARENS { $$ = new FunctionCall($1); free(const_cast($1)); } + IDENTIFIER LPARENS RPARENS { $$ = newNode(&@1, $1); free(const_cast($1)); } | function_start_expression RPARENS { $$ = $1; } - | scalar_type LPARENS expression RPARENS { $$ = new Cast($3, static_cast($1)); } + | scalar_type LPARENS expression RPARENS { $$ = newNode(&@1, $3, static_cast($1)); } ; /// @brief Assign expressions for attributes and local variables assign_expression: - variable_reference EQUALS expression { $$ = new AssignExpression($1, $3); } - | variable_reference PLUSEQUALS expression { $$ = new AssignExpression($1, $3, tokens::PLUS); } - | variable_reference MINUSEQUALS expression { $$ = new AssignExpression($1, $3, tokens::MINUS); } - | variable_reference MULTIPLYEQUALS expression { $$ = new AssignExpression($1, $3, tokens::MULTIPLY); } - | variable_reference DIVIDEEQUALS expression { $$ = new AssignExpression($1, $3, tokens::DIVIDE); } - | variable_reference MODULOEQUALS expression { $$ = new AssignExpression($1, $3, tokens::MODULO); } - | variable_reference BITANDEQUALS expression { $$ = new AssignExpression($1, $3, tokens::BITAND); } - | variable_reference BITXOREQUALS expression { $$ = new AssignExpression($1, $3, tokens::BITXOR); } - | variable_reference BITOREQUALS expression { $$ = new AssignExpression($1, $3, tokens::BITOR); } - | variable_reference SHIFTLEFTEQUALS expression { $$ = new AssignExpression($1, $3, tokens::SHIFTLEFT); } - | variable_reference SHIFTRIGHTEQUALS expression { $$ = new AssignExpression($1, $3, tokens::SHIFTRIGHT); } + variable_reference EQUALS expression { $$ = newNode(&@1, $1, $3); } + | variable_reference PLUSEQUALS expression { $$ = newNode(&@1, $1, $3, tokens::PLUS); } + | variable_reference MINUSEQUALS expression { $$ = newNode(&@1, $1, $3, tokens::MINUS); } + | variable_reference MULTIPLYEQUALS expression { $$ = newNode(&@1, $1, $3, tokens::MULTIPLY); } + | variable_reference DIVIDEEQUALS expression { $$ = newNode(&@1, $1, $3, tokens::DIVIDE); } + | variable_reference MODULOEQUALS expression { $$ = newNode(&@1, $1, $3, tokens::MODULO); } + | variable_reference BITANDEQUALS expression { $$ = newNode(&@1, $1, $3, tokens::BITAND); } + | variable_reference BITXOREQUALS expression { $$ = newNode(&@1, $1, $3, tokens::BITXOR); } + | variable_reference BITOREQUALS expression { $$ = newNode(&@1, $1, $3, tokens::BITOR); } + | variable_reference SHIFTLEFTEQUALS expression { $$ = newNode(&@1, $1, $3, tokens::SHIFTLEFT); } + | variable_reference SHIFTRIGHTEQUALS expression { $$ = newNode(&@1, $1, $3, tokens::SHIFTRIGHT); } ; /// @brief A binary expression which takes a left and right hand side expression /// and returns an expression binary_expression: - expression PLUS expression { $$ = new BinaryOperator($1, $3, tokens::PLUS); } - | expression MINUS expression { $$ = new BinaryOperator($1, $3, tokens::MINUS); } - | expression MULTIPLY expression { $$ = new BinaryOperator($1, $3, tokens::MULTIPLY); } - | expression DIVIDE expression { $$ = new BinaryOperator($1, $3, tokens::DIVIDE); } - | expression MODULO expression { $$ = new BinaryOperator($1, $3, tokens::MODULO); } - | expression SHIFTLEFT expression { $$ = new BinaryOperator($1, $3, tokens::SHIFTLEFT); } - | expression SHIFTRIGHT expression { $$ = new BinaryOperator($1, $3, tokens::SHIFTRIGHT); } - | expression BITAND expression { $$ = new BinaryOperator($1, $3, tokens::BITAND); } - | expression BITOR expression { $$ = new BinaryOperator($1, $3, tokens::BITOR); } - | expression BITXOR expression { $$ = new BinaryOperator($1, $3, tokens::BITXOR); } - | expression AND expression { $$ = new BinaryOperator($1, $3, tokens::AND); } - | expression OR expression { $$ = new BinaryOperator($1, $3, tokens::OR); } - | expression EQUALSEQUALS expression { $$ = new BinaryOperator($1, $3, tokens::EQUALSEQUALS); } - | expression NOTEQUALS expression { $$ = new BinaryOperator($1, $3, tokens::NOTEQUALS); } - | expression MORETHAN expression { $$ = new BinaryOperator($1, $3, tokens::MORETHAN); } - | expression LESSTHAN expression { $$ = new BinaryOperator($1, $3, tokens::LESSTHAN); } - | expression MORETHANOREQUAL expression { $$ = new BinaryOperator($1, $3, tokens::MORETHANOREQUAL); } - | expression LESSTHANOREQUAL expression { $$ = new BinaryOperator($1, $3, tokens::LESSTHANOREQUAL); } + expression PLUS expression { $$ = newNode(&@1, $1, $3, tokens::PLUS); } + | expression MINUS expression { $$ = newNode(&@1, $1, $3, tokens::MINUS); } + | expression MULTIPLY expression { $$ = newNode(&@1, $1, $3, tokens::MULTIPLY); } + | expression DIVIDE expression { $$ = newNode(&@1, $1, $3, tokens::DIVIDE); } + | expression MODULO expression { $$ = newNode(&@1, $1, $3, tokens::MODULO); } + | expression SHIFTLEFT expression { $$ = newNode(&@1, $1, $3, tokens::SHIFTLEFT); } + | expression SHIFTRIGHT expression { $$ = newNode(&@1, $1, $3, tokens::SHIFTRIGHT); } + | expression BITAND expression { $$ = newNode(&@1, $1, $3, tokens::BITAND); } + | expression BITOR expression { $$ = newNode(&@1, $1, $3, tokens::BITOR); } + | expression BITXOR expression { $$ = newNode(&@1, $1, $3, tokens::BITXOR); } + | expression AND expression { $$ = newNode(&@1, $1, $3, tokens::AND); } + | expression OR expression { $$ = newNode(&@1, $1, $3, tokens::OR); } + | expression EQUALSEQUALS expression { $$ = newNode(&@1, $1, $3, tokens::EQUALSEQUALS); } + | expression NOTEQUALS expression { $$ = newNode(&@1, $1, $3, tokens::NOTEQUALS); } + | expression MORETHAN expression { $$ = newNode(&@1, $1, $3, tokens::MORETHAN); } + | expression LESSTHAN expression { $$ = newNode(&@1, $1, $3, tokens::LESSTHAN); } + | expression MORETHANOREQUAL expression { $$ = newNode(&@1, $1, $3, tokens::MORETHANOREQUAL); } + | expression LESSTHANOREQUAL expression { $$ = newNode(&@1, $1, $3, tokens::LESSTHANOREQUAL); } ; ternary_expression: - expression QUESTION expression COLON expression { $$ = new TernaryOperator($1, $3, $5); } - | expression QUESTION COLON expression { $$ = new TernaryOperator($1, nullptr, $4); } + expression QUESTION expression COLON expression { $$ = newNode(&@1, $1, $3, $5); } + | expression QUESTION COLON expression { $$ = newNode(&@1, $1, nullptr, $4); } ; /// @brief A unary expression which takes an expression and returns an expression unary_expression: - PLUS expression { $$ = new UnaryOperator($2, tokens::PLUS); } - | MINUS expression { $$ = new UnaryOperator($2, tokens::MINUS); } - | BITNOT expression { $$ = new UnaryOperator($2, tokens::BITNOT); } - | NOT expression { $$ = new UnaryOperator($2, tokens::NOT); } + PLUS expression { $$ = newNode(&@1, $2, tokens::PLUS); } + | MINUS expression { $$ = newNode(&@1, $2, tokens::MINUS); } + | BITNOT expression { $$ = newNode(&@1, $2, tokens::BITNOT); } + | NOT expression { $$ = newNode(&@1, $2, tokens::NOT); } ; pre_crement: - PLUSPLUS variable_reference { $$ = new Crement($2, Crement::Increment, /*post*/false); } - | MINUSMINUS variable_reference { $$ = new Crement($2, Crement::Decrement, /*post*/false); } + PLUSPLUS variable_reference { $$ = newNode(&@1, $2, Crement::Increment, /*post*/false); } + | MINUSMINUS variable_reference { $$ = newNode(&@1, $2, Crement::Decrement, /*post*/false); } ; post_crement: - variable_reference PLUSPLUS { $$ = new Crement($1, Crement::Increment, /*post*/true); } - | variable_reference MINUSMINUS { $$ = new Crement($1, Crement::Decrement, /*post*/true); } + variable_reference PLUSPLUS { $$ = newNode(&@1, $1, Crement::Increment, /*post*/true); } + | variable_reference MINUSMINUS { $$ = newNode(&@1, $1, Crement::Decrement, /*post*/true); } ; /// @brief Syntax which can return a valid variable lvalue variable_reference: variable { $$ = $1; } | pre_crement { $$ = $1; } - | variable DOT_X { $$ = new ArrayUnpack($1, new Value(0)); } - | variable DOT_Y { $$ = new ArrayUnpack($1, new Value(1)); } - | variable DOT_Z { $$ = new ArrayUnpack($1, new Value(2)); } - | variable LSQUARE expression RSQUARE { $$ = new ArrayUnpack($1, $3); } - | variable LSQUARE expression COMMA expression RSQUARE { $$ = new ArrayUnpack($1, $3, $5); } + | variable DOT_X { $$ = newNode(&@1, $1, newNode>(&@2, 0)); } + | variable DOT_Y { $$ = newNode(&@1, $1, newNode>(&@2, 1)); } + | variable DOT_Z { $$ = newNode(&@1, $1, newNode>(&@2, 2)); } + | variable LSQUARE expression RSQUARE { $$ = newNode(&@1, $1, $3); } + | variable LSQUARE expression COMMA expression RSQUARE { $$ = newNode(&@1, $1, $3, $5); } ; /// @brief Terminating syntax for containers @@ -424,7 +448,7 @@ variable_reference: /// This requires it to take in a comma_operator which is temporarily /// represented as an vector of non-owned expressions. array: - LCURLY comma_operator RCURLY { $$ = new ArrayPack(*$2); } + LCURLY comma_operator RCURLY { $$ = newNode(&@1, *$2); } ; /// @brief Objects which are assignable are considered variables. Importantly, @@ -436,45 +460,45 @@ variable: /// @brief Syntax for supported attribute access attribute: - type AT IDENTIFIER { $$ = new Attribute($3, static_cast($1)); free(const_cast($3)); } - | I_AT IDENTIFIER { $$ = new Attribute($2, tokens::INT); free(const_cast($2)); } - | F_AT IDENTIFIER { $$ = new Attribute($2, tokens::FLOAT); free(const_cast($2)); } - | V_AT IDENTIFIER { $$ = new Attribute($2, tokens::VEC3F); free(const_cast($2)); } - | S_AT IDENTIFIER { $$ = new Attribute($2, tokens::STRING); free(const_cast($2)); } - | M3F_AT IDENTIFIER { $$ = new Attribute($2, tokens::MAT3F); free(const_cast($2)); } - | M4F_AT IDENTIFIER { $$ = new Attribute($2, tokens::MAT4F); free(const_cast($2)); } - | AT IDENTIFIER { $$ = new Attribute($2, tokens::FLOAT, true); free(const_cast($2)); } + type AT IDENTIFIER { $$ = newNode(&@$, $3, static_cast($1)); free(const_cast($3)); } + | I_AT IDENTIFIER { $$ = newNode(&@$, $2, tokens::INT); free(const_cast($2)); } + | F_AT IDENTIFIER { $$ = newNode(&@$, $2, tokens::FLOAT); free(const_cast($2)); } + | V_AT IDENTIFIER { $$ = newNode(&@$, $2, tokens::VEC3F); free(const_cast($2)); } + | S_AT IDENTIFIER { $$ = newNode(&@$, $2, tokens::STRING); free(const_cast($2)); } + | M3F_AT IDENTIFIER { $$ = newNode(&@$, $2, tokens::MAT3F); free(const_cast($2)); } + | M4F_AT IDENTIFIER { $$ = newNode(&@$, $2, tokens::MAT4F); free(const_cast($2)); } + | AT IDENTIFIER { $$ = newNode(&@$, $2, tokens::FLOAT, true); free(const_cast($2)); } ; /// @brief Syntax for supported external variable access external: - type DOLLAR IDENTIFIER { $$ = new ExternalVariable($3, static_cast($1)); free(const_cast($3)); } - | I_DOLLAR IDENTIFIER { $$ = new ExternalVariable($2, tokens::INT); free(const_cast($2)); } - | F_DOLLAR IDENTIFIER { $$ = new ExternalVariable($2, tokens::FLOAT); free(const_cast($2)); } - | V_DOLLAR IDENTIFIER { $$ = new ExternalVariable($2, tokens::VEC3F); free(const_cast($2)); } - | S_DOLLAR IDENTIFIER { $$ = new ExternalVariable($2, tokens::STRING); free(const_cast($2)); } - | DOLLAR IDENTIFIER { $$ = new ExternalVariable($2, tokens::FLOAT); free(const_cast($2)); } + type DOLLAR IDENTIFIER { $$ = newNode(&@$, $3, static_cast($1)); free(const_cast($3)); } + | I_DOLLAR IDENTIFIER { $$ = newNode(&@$, $2, tokens::INT); free(const_cast($2)); } + | F_DOLLAR IDENTIFIER { $$ = newNode(&@$, $2, tokens::FLOAT); free(const_cast($2)); } + | V_DOLLAR IDENTIFIER { $$ = newNode(&@$, $2, tokens::VEC3F); free(const_cast($2)); } + | S_DOLLAR IDENTIFIER { $$ = newNode(&@$, $2, tokens::STRING); free(const_cast($2)); } + | DOLLAR IDENTIFIER { $$ = newNode(&@$, $2, tokens::FLOAT); free(const_cast($2)); } ; /// @brief Syntax for text identifiers which resolves to a local. Types have /// have their own tokens which do not evaluate to a local variable /// @note Anything which uses an IDENTIFIER must free the returned char array local: - IDENTIFIER { $$ = new Local($1); free(const_cast($1)); } + IDENTIFIER { $$ = newNode(&@$, $1); free(const_cast($1)); } ; /// @brief Syntax numerical and boolean literal values /// @note Anything which uses one of the below tokens must free the returned char /// array (aside from TRUE and FALSE tokens) literal: - L_SHORT { $$ = new Value($1); free(const_cast($1)); } - | L_INT { $$ = new Value($1); free(const_cast($1)); } - | L_LONG { $$ = new Value($1); free(const_cast($1)); } - | L_FLOAT { $$ = new Value($1); free(const_cast($1)); } - | L_DOUBLE { $$ = new Value($1); free(const_cast($1)); } - | L_STRING { $$ = new Value($1); free(const_cast($1)); } - | TRUE { $$ = new Value(true); } - | FALSE { $$ = new Value(false); } + L_SHORT { $$ = newNode>(&@1, $1); free(const_cast($1)); } + | L_INT { $$ = newNode>(&@1, $1); free(const_cast($1)); } + | L_LONG { $$ = newNode>(&@1, $1); free(const_cast($1)); } + | L_FLOAT { $$ = newNode>(&@1, $1); free(const_cast($1)); } + | L_DOUBLE { $$ = newNode>(&@1, $1); free(const_cast($1)); } + | L_STRING { $$ = newNode>(&@1, $1); free(const_cast($1)); } + | TRUE { $$ = newNode>(&@1, true); } + | FALSE { $$ = newNode>(&@1, false); } ; type: diff --git a/openvdb_ax/openvdb_ax/grammar/generated/README b/openvdb_ax/openvdb_ax/grammar/generated/README index 3d442f49e1..5885c865f5 100644 --- a/openvdb_ax/openvdb_ax/grammar/generated/README +++ b/openvdb_ax/openvdb_ax/grammar/generated/README @@ -1,5 +1,5 @@ This folder contains pre-generated grammar source and header files for -OpenVDB AX. The OpenVDB AX CMake build system copies these to the -intermediate build directory from where they are referenced. This -allows you to optionally re-generate the grammar yourself by -specifying OPENVDB_BUILD_AX_GRAMMAR during the first run of CMake. +OpenVDB AX. The grammar can be re-generated by using the OpenVDB AX +CMake variable OPENVDB_BUILD_AX_GRAMMAR during the first run of CMake, +in which case these files are ignored in favour of the new files from +CMakes temporary build folder. diff --git a/openvdb_ax/openvdb_ax/grammar/generated/axlexer.cc b/openvdb_ax/openvdb_ax/grammar/generated/axlexer.cc index 1b5991dac5..5fb1c711b7 100644 --- a/openvdb_ax/openvdb_ax/grammar/generated/axlexer.cc +++ b/openvdb_ax/openvdb_ax/grammar/generated/axlexer.cc @@ -911,19 +911,22 @@ SPDX-License-Identifier: MPL-2.0 @brief OpenVDB AX Lexer */ - #include - #include #include "openvdb_ax/Exceptions.h" - #include "openvdb_ax/ast/AST.h" + #include "openvdb_ax/ast/Parse.h" #include "axparser.h" /*generated by bison*/ + #include + #include /// @note Bypasses conversion warnings in YY_CURRENT_BUFFER macro. /// This is a bit over zealous as we only need to suppress /// -Wnull-conversion OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + extern openvdb::ax::Logger* axlog; + /// @note Location tracking macro for axlloc token locations. /// YY_USER_ACTION is called before any and each lexer action + /// is performed. Instead of manually tracking newlines, we /// can simply scan for them in the current text held by axtext #define YY_USER_ACTION \ @@ -932,7 +935,7 @@ SPDX-License-Identifier: MPL-2.0 for (int i = 0; axtext[i] != '\0'; i++) { \ if (axtext[i] == '\n') { \ axlloc.last_line++; \ - axlloc.last_column = 0; \ + axlloc.last_column = 1; \ } \ else { \ axlloc.last_column++; \ @@ -1624,12 +1627,11 @@ case 102: case 103: YY_RULE_SETUP { - // @todo add a proper error manager to the parser + /* @todo: move this into parser */ std::ostringstream os; - os << axlloc.first_line << ":" << axlloc.first_column - << " Reserved keyword not currently implemented: '" - << axtext << "'"; - OPENVDB_THROW(openvdb::LLVMSyntaxError, os.str()); + os <<"\""<< axtext << "\" is a reserved keyword."; + assert(axlog); + axlog->error(os.str(), {axlloc.first_line, axlloc.first_column}); } YY_BREAK case 104: @@ -1702,15 +1704,11 @@ case 118: YY_RULE_SETUP { /* error on everything else */ - // note that this doesn't call through to yyerrror. This - // allows us to catch errors easily but is limited to the first - // invalid token. - // @todo add a proper error manager to the parser - std::ostringstream os; - os << axlloc.first_line << ":" << axlloc.first_column - << " Stray or invalid character '" - << axtext << "'"; - OPENVDB_THROW(openvdb::LLVMSyntaxError, os.str()); + /* @todo: move this into parser */ + assert(axlog); + axlog->error("stray or invalid character.", + {axlloc.first_line, axlloc.first_column}); + } YY_BREAK case 119: diff --git a/openvdb_ax/openvdb_ax/grammar/generated/axparser.cc b/openvdb_ax/openvdb_ax/grammar/generated/axparser.cc index 0a5f58ac25..4cdc35d9c1 100644 --- a/openvdb_ax/openvdb_ax/grammar/generated/axparser.cc +++ b/openvdb_ax/openvdb_ax/grammar/generated/axparser.cc @@ -58,42 +58,51 @@ /* Pull parsers. */ #define YYPULL 1 -/* Substitute the type names. */ -#define YYSTYPE AXSTYPE -#define YYLTYPE AXLTYPE -/* Substitute the variable and function names. */ -#define yyparse axparse -#define yylex axlex -#define yyerror axerror -#define yydebug axdebug -#define yynerrs axnerrs - -#define yylval axlval -#define yychar axchar -#define yylloc axlloc - -/* Copy the first part of user declarations. */ +/* "%code top" blocks. */ #include #include #include // for OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + #include "openvdb_ax/ast/AST.h" + #include "openvdb_ax/ast/Parse.h" #include "openvdb_ax/ast/Tokens.h" + #include "openvdb_ax/compiler/Logger.h" + /// @note Bypasses bison conversion warnings in yyparse OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN extern int axlex(); + extern openvdb::ax::Logger* axlog; using namespace openvdb::ax::ast; + using namespace openvdb::ax; - void yyerror(Tree** tree, const char* s); + void axerror(Tree** tree, const char* s); using ExpList = std::vector; +/* Substitute the type names. */ +#define YYSTYPE AXSTYPE +#define YYLTYPE AXLTYPE +/* Substitute the variable and function names. */ +#define yyparse axparse +#define yylex axlex +#define yyerror axerror +#define yydebug axdebug +#define yynerrs axnerrs + +#define yylval axlval +#define yychar axchar +#define yylloc axlloc + +/* Copy the first part of user declarations. */ + + # ifndef YY_NULLPTR # if defined __cplusplus && 201103L <= __cplusplus @@ -297,6 +306,19 @@ int axparse (openvdb::ax::ast::Tree** tree); /* Copy the second part of user declarations. */ +/* Unqualified %code blocks. */ + + + + template + T* newNode(YYLTYPE* loc, const Args&... args) { + T* ptr = new T(args...); + assert(axlog); + axlog->addNodeLocation(ptr, {loc->first_line, loc->first_column}); + return ptr; + } + + #ifdef short # undef short @@ -605,22 +627,22 @@ static const yytype_uint8 yytranslate[] = /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 215, 215, 216, 220, 221, 222, 223, 227, 228, - 234, 235, 236, 237, 238, 239, 240, 241, 245, 246, - 251, 252, 258, 259, 260, 261, 262, 263, 264, 265, - 266, 267, 268, 273, 274, 281, 287, 292, 300, 311, - 312, 317, 318, 324, 325, 330, 331, 335, 336, 341, - 342, 343, 348, 349, 354, 356, 357, 362, 363, 368, - 369, 370, 375, 376, 377, 378, 379, 380, 381, 382, - 383, 384, 385, 391, 392, 393, 394, 395, 396, 397, - 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, - 408, 412, 413, 418, 419, 420, 421, 425, 426, 430, - 431, 436, 437, 438, 439, 440, 441, 442, 454, 460, - 461, 466, 467, 468, 469, 470, 471, 472, 473, 478, - 479, 480, 481, 482, 483, 490, 497, 498, 499, 500, - 501, 502, 503, 504, 508, 509, 510, 511, 516, 517, - 518, 519, 524, 525, 526, 527, 528, 529, 534, 535, - 536, 537, 538, 539, 540, 541, 542 + 0, 235, 235, 238, 244, 245, 246, 249, 255, 256, + 262, 263, 264, 265, 266, 267, 268, 269, 272, 273, + 278, 279, 285, 286, 287, 288, 289, 290, 291, 292, + 293, 294, 295, 300, 302, 308, 313, 318, 324, 335, + 336, 341, 342, 348, 349, 354, 355, 359, 360, 365, + 366, 367, 372, 373, 378, 380, 381, 386, 387, 392, + 393, 394, 399, 400, 401, 402, 403, 404, 405, 406, + 407, 408, 409, 415, 416, 417, 418, 419, 420, 421, + 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, + 432, 436, 437, 442, 443, 444, 445, 449, 450, 454, + 455, 460, 461, 462, 463, 464, 465, 466, 478, 484, + 485, 490, 491, 492, 493, 494, 495, 496, 497, 502, + 503, 504, 505, 506, 507, 514, 521, 522, 523, 524, + 525, 526, 527, 528, 532, 533, 534, 535, 540, 541, + 542, 543, 548, 549, 550, 551, 552, 553, 558, 559, + 560, 561, 562, 563, 564, 565, 566 }; #endif @@ -2071,13 +2093,17 @@ yyparse (openvdb::ax::ast::Tree** tree) { case 2: - { *tree = new Tree(); (yyval.tree) = *tree; } + { *tree = newNode(&(yyloc)); + (yyval.tree) = *tree; + } break; case 3: - { *tree = new Tree((yyvsp[0].block)); (yyval.tree) = *tree; } + { *tree = newNode(&(yylsp[0]), (yyvsp[0].block)); + (yyval.tree) = *tree; + } break; @@ -2095,13 +2121,17 @@ yyparse (openvdb::ax::ast::Tree** tree) case 6: - { (yyval.block) = new Block(); (yyval.block)->addStatement((yyvsp[0].statement)); } + { (yyval.block) = newNode(&(yyloc)); + (yyval.block)->addStatement((yyvsp[0].statement)); + } break; case 7: - { (yyval.block) = new Block(); (yyval.block)->addStatement((yyvsp[0].block)); } + { (yyval.block) = newNode(&(yyloc)); + (yyval.block)->addStatement((yyvsp[0].block)); + } break; @@ -2143,19 +2173,19 @@ yyparse (openvdb::ax::ast::Tree** tree) case 14: - { (yyval.statement) = new Keyword(tokens::RETURN); } + { (yyval.statement) = newNode(&(yyloc), tokens::RETURN); } break; case 15: - { (yyval.statement) = new Keyword(tokens::BREAK); } + { (yyval.statement) = newNode(&(yyloc), tokens::BREAK); } break; case 16: - { (yyval.statement) = new Keyword(tokens::CONTINUE); } + { (yyval.statement) = newNode(&(yyloc), tokens::CONTINUE); } break; @@ -2257,24 +2287,23 @@ yyparse (openvdb::ax::ast::Tree** tree) case 33: - { (yyval.declare_local) = new DeclareLocal(static_cast((yyvsp[-1].index)), new Local((yyvsp[0].string))); free(const_cast((yyvsp[0].string))); } + { (yyval.declare_local) = newNode(&(yylsp[-1]), static_cast((yyvsp[-1].index)), newNode(&(yylsp[0]), (yyvsp[0].string))); + free(const_cast((yyvsp[0].string))); } break; case 34: - { (yyval.declare_local) = new DeclareLocal(static_cast((yyvsp[-3].index)), - new Local((yyvsp[-2].string)), - (yyvsp[0].expression)); free(const_cast((yyvsp[-2].string))); } + { (yyval.declare_local) = newNode(&(yylsp[-3]), static_cast((yyvsp[-3].index)), newNode(&(yylsp[-2]), (yyvsp[-2].string)), (yyvsp[0].expression)); + free(const_cast((yyvsp[-2].string))); } break; case 35: - { (yyval.statementlist) = new StatementList((yyvsp[-4].declare_local)); + { (yyval.statementlist) = newNode(&(yyloc), (yyvsp[-4].declare_local)); const tokens::CoreType type = static_cast((yyvsp[-4].declare_local))->type(); - (yyval.statementlist)->addStatement( - new DeclareLocal(type, new Local((yyvsp[-2].string)), (yyvsp[0].expression))); + (yyval.statementlist)->addStatement(newNode(&(yylsp[-4]), type, newNode(&(yylsp[-2]), (yyvsp[-2].string)), (yyvsp[0].expression))); free(const_cast((yyvsp[-2].string))); } @@ -2282,9 +2311,9 @@ yyparse (openvdb::ax::ast::Tree** tree) case 36: - { (yyval.statementlist) = new StatementList((yyvsp[-2].declare_local)); + { (yyval.statementlist) = newNode(&(yyloc), (yyvsp[-2].declare_local)); const tokens::CoreType type = static_cast((yyvsp[-2].declare_local))->type(); - (yyval.statementlist)->addStatement(new DeclareLocal(type, new Local((yyvsp[0].string)))); + (yyval.statementlist)->addStatement(newNode(&(yylsp[-2]), type, newNode(&(yylsp[0]), (yyvsp[0].string)))); free(const_cast((yyvsp[0].string))); } @@ -2295,9 +2324,7 @@ yyparse (openvdb::ax::ast::Tree** tree) { const auto firstNode = (yyvsp[-4].statementlist)->child(0); assert(firstNode); const tokens::CoreType type = static_cast(firstNode)->type(); - (yyval.statementlist)->addStatement( - new DeclareLocal(type, new Local((yyvsp[-2].string)), (yyvsp[0].expression))); - free(const_cast((yyvsp[-2].string))); + (yyval.statementlist)->addStatement(newNode(&(yylsp[-4]), type, newNode(&(yylsp[-2]), (yyvsp[-2].string)), (yyvsp[0].expression))); (yyval.statementlist) = (yyvsp[-4].statementlist); } @@ -2308,7 +2335,7 @@ yyparse (openvdb::ax::ast::Tree** tree) { const auto firstNode = (yyvsp[-2].statementlist)->child(0); assert(firstNode); const tokens::CoreType type = static_cast(firstNode)->type(); - (yyval.statementlist)->addStatement(new DeclareLocal(type, new Local((yyvsp[0].string)))); + (yyval.statementlist)->addStatement(newNode(&(yylsp[-2]), type, newNode(&(yylsp[0]), (yyvsp[0].string)))); free(const_cast((yyvsp[0].string))); (yyval.statementlist) = (yyvsp[-2].statementlist); } @@ -2335,19 +2362,19 @@ yyparse (openvdb::ax::ast::Tree** tree) case 42: - { (yyval.block) = new Block(); (yyval.block)->addStatement((yyvsp[0].statement)); } + { (yyval.block) = newNode(&(yyloc)); (yyval.block)->addStatement((yyvsp[0].statement)); } break; case 43: - { (yyval.statement) = new ConditionalStatement((yyvsp[-2].expression), (yyvsp[0].block)); } + { (yyval.statement) = newNode(&(yyloc), (yyvsp[-2].expression), (yyvsp[0].block)); } break; case 44: - { (yyval.statement) = new ConditionalStatement((yyvsp[-4].expression), (yyvsp[-2].block), (yyvsp[0].block)); } + { (yyval.statement) = newNode(&(yyloc), (yyvsp[-4].expression), (yyvsp[-2].block), (yyvsp[0].block)); } break; @@ -2407,25 +2434,25 @@ yyparse (openvdb::ax::ast::Tree** tree) case 54: - { (yyval.statement) = new Loop(tokens::FOR, ((yyvsp[-4].statement) ? (yyvsp[-4].statement) : new Value(true)), (yyvsp[0].block), (yyvsp[-6].statement), (yyvsp[-2].expression)); } + { (yyval.statement) = newNode(&(yyloc), tokens::FOR, ((yyvsp[-4].statement) ? (yyvsp[-4].statement) : newNode>(&(yyloc), true)), (yyvsp[0].block), (yyvsp[-6].statement), (yyvsp[-2].expression)); } break; case 55: - { (yyval.statement) = new Loop(tokens::DO, (yyvsp[-1].statement), (yyvsp[-4].block)); } + { (yyval.statement) = newNode(&(yyloc), tokens::DO, (yyvsp[-1].statement), (yyvsp[-4].block)); } break; case 56: - { (yyval.statement) = new Loop(tokens::WHILE, (yyvsp[-2].statement), (yyvsp[0].block)); } + { (yyval.statement) = newNode(&(yyloc), tokens::WHILE, (yyvsp[-2].statement), (yyvsp[0].block)); } break; case 57: - { (yyval.function) = new FunctionCall((yyvsp[-2].string)); (yyval.function)->append((yyvsp[0].expression)); free(const_cast((yyvsp[-2].string))); } + { (yyval.function) = newNode(&(yylsp[-2]), (yyvsp[-2].string)); (yyval.function)->append((yyvsp[0].expression)); free(const_cast((yyvsp[-2].string))); } break; @@ -2437,7 +2464,7 @@ yyparse (openvdb::ax::ast::Tree** tree) case 59: - { (yyval.expression) = new FunctionCall((yyvsp[-2].string)); free(const_cast((yyvsp[-2].string))); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].string)); free(const_cast((yyvsp[-2].string))); } break; @@ -2449,241 +2476,241 @@ yyparse (openvdb::ax::ast::Tree** tree) case 61: - { (yyval.expression) = new Cast((yyvsp[-1].expression), static_cast((yyvsp[-3].index))); } + { (yyval.expression) = newNode(&(yylsp[-3]), (yyvsp[-1].expression), static_cast((yyvsp[-3].index))); } break; case 62: - { (yyval.expression) = new AssignExpression((yyvsp[-2].expression), (yyvsp[0].expression)); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression)); } break; case 63: - { (yyval.expression) = new AssignExpression((yyvsp[-2].expression), (yyvsp[0].expression), tokens::PLUS); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::PLUS); } break; case 64: - { (yyval.expression) = new AssignExpression((yyvsp[-2].expression), (yyvsp[0].expression), tokens::MINUS); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::MINUS); } break; case 65: - { (yyval.expression) = new AssignExpression((yyvsp[-2].expression), (yyvsp[0].expression), tokens::MULTIPLY); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::MULTIPLY); } break; case 66: - { (yyval.expression) = new AssignExpression((yyvsp[-2].expression), (yyvsp[0].expression), tokens::DIVIDE); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::DIVIDE); } break; case 67: - { (yyval.expression) = new AssignExpression((yyvsp[-2].expression), (yyvsp[0].expression), tokens::MODULO); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::MODULO); } break; case 68: - { (yyval.expression) = new AssignExpression((yyvsp[-2].expression), (yyvsp[0].expression), tokens::BITAND); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::BITAND); } break; case 69: - { (yyval.expression) = new AssignExpression((yyvsp[-2].expression), (yyvsp[0].expression), tokens::BITXOR); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::BITXOR); } break; case 70: - { (yyval.expression) = new AssignExpression((yyvsp[-2].expression), (yyvsp[0].expression), tokens::BITOR); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::BITOR); } break; case 71: - { (yyval.expression) = new AssignExpression((yyvsp[-2].expression), (yyvsp[0].expression), tokens::SHIFTLEFT); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::SHIFTLEFT); } break; case 72: - { (yyval.expression) = new AssignExpression((yyvsp[-2].expression), (yyvsp[0].expression), tokens::SHIFTRIGHT); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::SHIFTRIGHT); } break; case 73: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::PLUS); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::PLUS); } break; case 74: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::MINUS); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::MINUS); } break; case 75: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::MULTIPLY); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::MULTIPLY); } break; case 76: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::DIVIDE); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::DIVIDE); } break; case 77: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::MODULO); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::MODULO); } break; case 78: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::SHIFTLEFT); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::SHIFTLEFT); } break; case 79: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::SHIFTRIGHT); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::SHIFTRIGHT); } break; case 80: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::BITAND); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::BITAND); } break; case 81: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::BITOR); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::BITOR); } break; case 82: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::BITXOR); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::BITXOR); } break; case 83: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::AND); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::AND); } break; case 84: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::OR); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::OR); } break; case 85: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::EQUALSEQUALS); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::EQUALSEQUALS); } break; case 86: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::NOTEQUALS); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::NOTEQUALS); } break; case 87: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::MORETHAN); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::MORETHAN); } break; case 88: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::LESSTHAN); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::LESSTHAN); } break; case 89: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::MORETHANOREQUAL); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::MORETHANOREQUAL); } break; case 90: - { (yyval.expression) = new BinaryOperator((yyvsp[-2].expression), (yyvsp[0].expression), tokens::LESSTHANOREQUAL); } + { (yyval.expression) = newNode(&(yylsp[-2]), (yyvsp[-2].expression), (yyvsp[0].expression), tokens::LESSTHANOREQUAL); } break; case 91: - { (yyval.expression) = new TernaryOperator((yyvsp[-4].expression), (yyvsp[-2].expression), (yyvsp[0].expression)); } + { (yyval.expression) = newNode(&(yylsp[-4]), (yyvsp[-4].expression), (yyvsp[-2].expression), (yyvsp[0].expression)); } break; case 92: - { (yyval.expression) = new TernaryOperator((yyvsp[-3].expression), nullptr, (yyvsp[0].expression)); } + { (yyval.expression) = newNode(&(yylsp[-3]), (yyvsp[-3].expression), nullptr, (yyvsp[0].expression)); } break; case 93: - { (yyval.expression) = new UnaryOperator((yyvsp[0].expression), tokens::PLUS); } + { (yyval.expression) = newNode(&(yylsp[-1]), (yyvsp[0].expression), tokens::PLUS); } break; case 94: - { (yyval.expression) = new UnaryOperator((yyvsp[0].expression), tokens::MINUS); } + { (yyval.expression) = newNode(&(yylsp[-1]), (yyvsp[0].expression), tokens::MINUS); } break; case 95: - { (yyval.expression) = new UnaryOperator((yyvsp[0].expression), tokens::BITNOT); } + { (yyval.expression) = newNode(&(yylsp[-1]), (yyvsp[0].expression), tokens::BITNOT); } break; case 96: - { (yyval.expression) = new UnaryOperator((yyvsp[0].expression), tokens::NOT); } + { (yyval.expression) = newNode(&(yylsp[-1]), (yyvsp[0].expression), tokens::NOT); } break; case 97: - { (yyval.expression) = new Crement((yyvsp[0].expression), Crement::Increment, /*post*/false); } + { (yyval.expression) = newNode(&(yylsp[-1]), (yyvsp[0].expression), Crement::Increment, /*post*/false); } break; case 98: - { (yyval.expression) = new Crement((yyvsp[0].expression), Crement::Decrement, /*post*/false); } + { (yyval.expression) = newNode(&(yylsp[-1]), (yyvsp[0].expression), Crement::Decrement, /*post*/false); } break; case 99: - { (yyval.expression) = new Crement((yyvsp[-1].expression), Crement::Increment, /*post*/true); } + { (yyval.expression) = newNode(&(yylsp[-1]), (yyvsp[-1].expression), Crement::Increment, /*post*/true); } break; case 100: - { (yyval.expression) = new Crement((yyvsp[-1].expression), Crement::Decrement, /*post*/true); } + { (yyval.expression) = newNode(&(yylsp[-1]), (yyvsp[-1].expression), Crement::Decrement, /*post*/true); } break; @@ -2701,37 +2728,37 @@ yyparse (openvdb::ax::ast::Tree** tree) case 103: - { (yyval.expression) = new ArrayUnpack((yyvsp[-1].variable), new Value(0)); } + { (yyval.expression) = newNode(&(yylsp[-1]), (yyvsp[-1].variable), newNode>(&(yylsp[0]), 0)); } break; case 104: - { (yyval.expression) = new ArrayUnpack((yyvsp[-1].variable), new Value(1)); } + { (yyval.expression) = newNode(&(yylsp[-1]), (yyvsp[-1].variable), newNode>(&(yylsp[0]), 1)); } break; case 105: - { (yyval.expression) = new ArrayUnpack((yyvsp[-1].variable), new Value(2)); } + { (yyval.expression) = newNode(&(yylsp[-1]), (yyvsp[-1].variable), newNode>(&(yylsp[0]), 2)); } break; case 106: - { (yyval.expression) = new ArrayUnpack((yyvsp[-3].variable), (yyvsp[-1].expression)); } + { (yyval.expression) = newNode(&(yylsp[-3]), (yyvsp[-3].variable), (yyvsp[-1].expression)); } break; case 107: - { (yyval.expression) = new ArrayUnpack((yyvsp[-5].variable), (yyvsp[-3].expression), (yyvsp[-1].expression)); } + { (yyval.expression) = newNode(&(yylsp[-5]), (yyvsp[-5].variable), (yyvsp[-3].expression), (yyvsp[-1].expression)); } break; case 108: - { (yyval.expression) = new ArrayPack(*(yyvsp[-1].explist)); } + { (yyval.expression) = newNode(&(yylsp[-2]), *(yyvsp[-1].explist)); } break; @@ -2749,139 +2776,139 @@ yyparse (openvdb::ax::ast::Tree** tree) case 111: - { (yyval.attribute) = new Attribute((yyvsp[0].string), static_cast((yyvsp[-2].index))); free(const_cast((yyvsp[0].string))); } + { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), static_cast((yyvsp[-2].index))); free(const_cast((yyvsp[0].string))); } break; case 112: - { (yyval.attribute) = new Attribute((yyvsp[0].string), tokens::INT); free(const_cast((yyvsp[0].string))); } + { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::INT); free(const_cast((yyvsp[0].string))); } break; case 113: - { (yyval.attribute) = new Attribute((yyvsp[0].string), tokens::FLOAT); free(const_cast((yyvsp[0].string))); } + { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::FLOAT); free(const_cast((yyvsp[0].string))); } break; case 114: - { (yyval.attribute) = new Attribute((yyvsp[0].string), tokens::VEC3F); free(const_cast((yyvsp[0].string))); } + { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::VEC3F); free(const_cast((yyvsp[0].string))); } break; case 115: - { (yyval.attribute) = new Attribute((yyvsp[0].string), tokens::STRING); free(const_cast((yyvsp[0].string))); } + { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::STRING); free(const_cast((yyvsp[0].string))); } break; case 116: - { (yyval.attribute) = new Attribute((yyvsp[0].string), tokens::MAT3F); free(const_cast((yyvsp[0].string))); } + { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::MAT3F); free(const_cast((yyvsp[0].string))); } break; case 117: - { (yyval.attribute) = new Attribute((yyvsp[0].string), tokens::MAT4F); free(const_cast((yyvsp[0].string))); } + { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::MAT4F); free(const_cast((yyvsp[0].string))); } break; case 118: - { (yyval.attribute) = new Attribute((yyvsp[0].string), tokens::FLOAT, true); free(const_cast((yyvsp[0].string))); } + { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::FLOAT, true); free(const_cast((yyvsp[0].string))); } break; case 119: - { (yyval.external) = new ExternalVariable((yyvsp[0].string), static_cast((yyvsp[-2].index))); free(const_cast((yyvsp[0].string))); } + { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), static_cast((yyvsp[-2].index))); free(const_cast((yyvsp[0].string))); } break; case 120: - { (yyval.external) = new ExternalVariable((yyvsp[0].string), tokens::INT); free(const_cast((yyvsp[0].string))); } + { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), tokens::INT); free(const_cast((yyvsp[0].string))); } break; case 121: - { (yyval.external) = new ExternalVariable((yyvsp[0].string), tokens::FLOAT); free(const_cast((yyvsp[0].string))); } + { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), tokens::FLOAT); free(const_cast((yyvsp[0].string))); } break; case 122: - { (yyval.external) = new ExternalVariable((yyvsp[0].string), tokens::VEC3F); free(const_cast((yyvsp[0].string))); } + { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), tokens::VEC3F); free(const_cast((yyvsp[0].string))); } break; case 123: - { (yyval.external) = new ExternalVariable((yyvsp[0].string), tokens::STRING); free(const_cast((yyvsp[0].string))); } + { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), tokens::STRING); free(const_cast((yyvsp[0].string))); } break; case 124: - { (yyval.external) = new ExternalVariable((yyvsp[0].string), tokens::FLOAT); free(const_cast((yyvsp[0].string))); } + { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), tokens::FLOAT); free(const_cast((yyvsp[0].string))); } break; case 125: - { (yyval.local) = new Local((yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } + { (yyval.local) = newNode(&(yyloc), (yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } break; case 126: - { (yyval.value) = new Value((yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } + { (yyval.value) = newNode>(&(yylsp[0]), (yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } break; case 127: - { (yyval.value) = new Value((yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } + { (yyval.value) = newNode>(&(yylsp[0]), (yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } break; case 128: - { (yyval.value) = new Value((yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } + { (yyval.value) = newNode>(&(yylsp[0]), (yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } break; case 129: - { (yyval.value) = new Value((yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } + { (yyval.value) = newNode>(&(yylsp[0]), (yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } break; case 130: - { (yyval.value) = new Value((yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } + { (yyval.value) = newNode>(&(yylsp[0]), (yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } break; case 131: - { (yyval.value) = new Value((yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } + { (yyval.value) = newNode>(&(yylsp[0]), (yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } break; case 132: - { (yyval.value) = new Value(true); } + { (yyval.value) = newNode>(&(yylsp[0]), true); } break; case 133: - { (yyval.value) = new Value(false); } + { (yyval.value) = newNode>(&(yylsp[0]), false); } break; diff --git a/openvdb_ax/openvdb_ax/math/OpenSimplexNoise.h b/openvdb_ax/openvdb_ax/math/OpenSimplexNoise.h index 2995937ff2..4cf2da3824 100644 --- a/openvdb_ax/openvdb_ax/math/OpenSimplexNoise.h +++ b/openvdb_ax/openvdb_ax/math/OpenSimplexNoise.h @@ -20,7 +20,7 @@ #ifndef OPENVDB_AX_MATH_OPEN_SIMPLEX_NOISE_HAS_BEEN_INCLUDED #define OPENVDB_AX_MATH_OPEN_SIMPLEX_NOISE_HAS_BEEN_INCLUDED -#include "../version.h" +#include #include namespace openvdb { diff --git a/openvdb_ax/openvdb_ax/test/CMakeLists.txt b/openvdb_ax/openvdb_ax/test/CMakeLists.txt index c8d49195ce..ed6002cfc9 100644 --- a/openvdb_ax/openvdb_ax/test/CMakeLists.txt +++ b/openvdb_ax/openvdb_ax/test/CMakeLists.txt @@ -35,6 +35,7 @@ set(TEST_SOURCE_FILES backend/TestFunctionGroup.cc backend/TestFunctionRegistry.cc backend/TestFunctionTypes.cc + backend/TestLogger.cc backend/TestSymbolTable.cc backend/TestTypes.cc compiler/TestPointExecutable.cc @@ -96,8 +97,5 @@ target_include_directories(vdb_ax_test if(OPENVDB_AX_TEST_PROFILE) target_compile_definitions(vdb_ax_test PRIVATE "-DPROFILE") endif() -if(NOT MATRIX_SUPPORT) - target_compile_definitions(vdb_ax_test PRIVATE -DOPENVDB_AX_NO_MATRIX) -endif() add_test(NAME vdb_ax_unit_test COMMAND vdb_ax_test -v WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../) diff --git a/openvdb_ax/openvdb_ax/test/ast/TestPrinters.cc b/openvdb_ax/openvdb_ax/test/ast/TestPrinters.cc index bd1a90f556..94d8eb6198 100644 --- a/openvdb_ax/openvdb_ax/test/ast/TestPrinters.cc +++ b/openvdb_ax/openvdb_ax/test/ast/TestPrinters.cc @@ -1,8 +1,9 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/PrintTree.h" +#include +#include +#include #include @@ -54,7 +55,7 @@ void TestPrinters::testReprint() // Test binary ops std::string in = "a + b * c / d % e << f >> g = h & i | j ^ k && l || m;"; std::string expected = "(((a + (((b * c) / d) % e)) << f) >> g = ((((h & i) | (j ^ k)) && l) || m));\n"; - Tree::Ptr tree = parse(in.c_str()); + Tree::ConstPtr tree = parse(in.c_str()); CPPUNIT_ASSERT(tree.get()); reprint(*tree, os, ""); check(os.str(), ("{\n" + expected + "}\n")); diff --git a/openvdb_ax/openvdb_ax/test/ast/TestScanners.cc b/openvdb_ax/openvdb_ax/test/ast/TestScanners.cc index 77695d3be5..074f2c19fe 100644 --- a/openvdb_ax/openvdb_ax/test/ast/TestScanners.cc +++ b/openvdb_ax/openvdb_ax/test/ast/TestScanners.cc @@ -1,9 +1,9 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../test/util.h" +#include +#include +#include #include @@ -231,7 +231,7 @@ void TestScanners::testFirstLastLocation() for (const auto& samples : snippets) { for (const std::string& code : *samples) { - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT(tree); const Variable* first = firstUse(*tree, "@a"); const Variable* last = lastUse(*tree, "@a"); @@ -339,7 +339,7 @@ void TestScanners::testFirstLastLocation() void TestScanners::testAttributeDependencyTokens() { for (const std::string& code : none) { - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT(tree); std::vector dependencies; attributeDependencyTokens(*tree, "a", tokens::CoreType::FLOAT, dependencies); @@ -349,7 +349,7 @@ void TestScanners::testAttributeDependencyTokens() } for (const std::string& code : self) { - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT(tree); std::vector dependencies; attributeDependencyTokens(*tree, "a", tokens::CoreType::FLOAT, dependencies); @@ -359,7 +359,7 @@ void TestScanners::testAttributeDependencyTokens() } for (const std::string& code : direct) { - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT(tree); std::vector dependencies; attributeDependencyTokens(*tree, "a", tokens::CoreType::FLOAT, dependencies); @@ -369,7 +369,7 @@ void TestScanners::testAttributeDependencyTokens() } for (const std::string& code : directvec) { - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT(tree); std::vector dependencies; attributeDependencyTokens(*tree, "a", tokens::CoreType::FLOAT, dependencies); @@ -379,7 +379,7 @@ void TestScanners::testAttributeDependencyTokens() } for (const std::string& code : indirect) { - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT(tree); std::vector dependencies; attributeDependencyTokens(*tree, "a", tokens::CoreType::FLOAT, dependencies); @@ -411,7 +411,7 @@ void TestScanners::testAttributeDependencyTokens() " }" "}"; - const Tree::Ptr tree = parse(complex.c_str()); + const Tree::ConstPtr tree = parse(complex.c_str()); CPPUNIT_ASSERT(tree); std::vector dependencies; attributeDependencyTokens(*tree, "b", tokens::CoreType::FLOAT, dependencies); @@ -462,7 +462,7 @@ void TestScanners::testAttributeDependencyTokens() void TestScanners::testVariableDependencies() { for (const std::string& code : none) { - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT(tree); const Variable* last = lastUse(*tree, "@a"); CPPUNIT_ASSERT(last); @@ -474,7 +474,7 @@ void TestScanners::testVariableDependencies() } for (const std::string& code : self) { - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT(tree); const Variable* last = lastUse(*tree, "@a"); CPPUNIT_ASSERT(last); @@ -490,7 +490,7 @@ void TestScanners::testVariableDependencies() } for (const std::string& code : direct) { - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT(tree); const Variable* last = lastUse(*tree, "@a"); CPPUNIT_ASSERT(last); @@ -506,7 +506,7 @@ void TestScanners::testVariableDependencies() } for (const std::string& code : indirect) { - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT(tree); const Variable* last = lastUse(*tree, "@a"); CPPUNIT_ASSERT(last); @@ -552,7 +552,7 @@ void TestScanners::testVariableDependencies() " }" "}"; - const Tree::Ptr tree = parse(complex.c_str()); + const Tree::ConstPtr tree = parse(complex.c_str()); CPPUNIT_ASSERT(tree); const Variable* lasta = lastUse(*tree, "@a"); const Variable* lastb = lastUse(*tree, "@b"); diff --git a/openvdb_ax/openvdb_ax/test/backend/TestComputeGeneratorFailures.cc b/openvdb_ax/openvdb_ax/test/backend/TestComputeGeneratorFailures.cc index 681e76f867..5eb22402c1 100644 --- a/openvdb_ax/openvdb_ax/test/backend/TestComputeGeneratorFailures.cc +++ b/openvdb_ax/openvdb_ax/test/backend/TestComputeGeneratorFailures.cc @@ -6,11 +6,13 @@ #include "util.h" #include "../util.h" -#include "../compiler/CompilerOptions.h" -#include "../codegen/Functions.h" -#include "../codegen/FunctionRegistry.h" -#include "../codegen/ComputeGenerator.h" -#include "../ast/AST.h" +#include +#include +#include +#include +#include +#include + #include static const std::vector tests { @@ -197,9 +199,6 @@ static const std::vector tests { "true ? 1.0f : \"foo\";", "string a; true ? a : 1;" "string a; true ? 1.0f : a;" - /// attrib access - this test only tests the base - // generator which has no attribute access support - "@a;" }; class TestComputeGeneratorFailures : public CppUnit::TestCase @@ -215,22 +214,28 @@ class TestComputeGeneratorFailures : public CppUnit::TestCase CPPUNIT_TEST_SUITE_REGISTRATION(TestComputeGeneratorFailures); + void TestComputeGeneratorFailures::testFailures() { openvdb::ax::FunctionOptions opts; - // empty reg openvdb::ax::codegen::FunctionRegistry reg; + // create logger that suppresses all messages, but still logs number of errors/warnings + openvdb::ax::Logger logger([](const std::string&) {}); + logger.setMaxErrors(1); + for (const auto& code : tests) { - const openvdb::ax::ast::Tree::Ptr ast = - openvdb::ax::ast::parse(code.c_str()); + const openvdb::ax::ast::Tree::ConstPtr ast = + openvdb::ax::ast::parse(code.c_str(), logger); CPPUNIT_ASSERT(ast.get()); unittest_util::LLVMState state; - openvdb::ax::codegen::ComputeGenerator gen(state.module(), opts, reg); - CPPUNIT_ASSERT_THROW_MESSAGE(ERROR_MSG("Expected Compiler Error", code), - gen.generate(*ast), openvdb::Exception); + openvdb::ax::codegen::ComputeGenerator gen(state.module(), opts, reg, logger); + gen.generate(*ast); + + CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("Expected Compiler Error", code), logger.hasError()); + logger.clear(); } } diff --git a/openvdb_ax/openvdb_ax/test/backend/TestFunctionGroup.cc b/openvdb_ax/openvdb_ax/test/backend/TestFunctionGroup.cc index 0e025151e2..6d966d346a 100644 --- a/openvdb_ax/openvdb_ax/test/backend/TestFunctionGroup.cc +++ b/openvdb_ax/openvdb_ax/test/backend/TestFunctionGroup.cc @@ -3,7 +3,7 @@ #include "util.h" -#include "../codegen/FunctionTypes.h" +#include #include @@ -412,19 +412,16 @@ TestFunctionGroup::testExecute() // test invalid arguments throws FunctionGroup::Ptr group(new FunctionGroup("empty", "", {})); - CPPUNIT_ASSERT_THROW(group->execute(/*args*/{}, B), - openvdb::LLVMFunctionError); + CPPUNIT_ASSERT(!group->execute(/*args*/{}, B)); group = axtestscalar(C); const std::vector* list = &group->list(); - CPPUNIT_ASSERT_THROW(group->execute({}, B), - openvdb::LLVMFunctionError); - CPPUNIT_ASSERT_THROW(group->execute({ + CPPUNIT_ASSERT(!group->execute({}, B)); + CPPUNIT_ASSERT(!group->execute({ B.getTrue(), B.getTrue() - }, B), - openvdb::LLVMFunctionError); + }, B)); args.resize(1); diff --git a/openvdb_ax/openvdb_ax/test/backend/TestFunctionRegistry.cc b/openvdb_ax/openvdb_ax/test/backend/TestFunctionRegistry.cc index 2e4d95f1ff..544cf92616 100644 --- a/openvdb_ax/openvdb_ax/test/backend/TestFunctionRegistry.cc +++ b/openvdb_ax/openvdb_ax/test/backend/TestFunctionRegistry.cc @@ -5,9 +5,9 @@ #include "util.h" -#include "../compiler/CompilerOptions.h" -#include "../codegen/Functions.h" -#include "../codegen/FunctionRegistry.h" +#include +#include +#include #include class TestFunctionRegistry : public CppUnit::TestCase diff --git a/openvdb_ax/openvdb_ax/test/backend/TestFunctionTypes.cc b/openvdb_ax/openvdb_ax/test/backend/TestFunctionTypes.cc index bebcffc58c..c65b69bfc8 100644 --- a/openvdb_ax/openvdb_ax/test/backend/TestFunctionTypes.cc +++ b/openvdb_ax/openvdb_ax/test/backend/TestFunctionTypes.cc @@ -3,7 +3,7 @@ #include "util.h" -#include "../codegen/FunctionTypes.h" +#include #include @@ -1863,7 +1863,7 @@ TestFunctionTypes::testIRFunctions() CPPUNIT_ASSERT(!M.getFunction("ax.ir.retnull.test")); // will throw as the function expects a float ret, not void or null // NOTE: The function will still be created, but be in an invaid state - CPPUNIT_ASSERT_THROW(test->create(M), openvdb::LLVMFunctionError); + CPPUNIT_ASSERT_THROW(test->create(M), openvdb::AXCodeGenError); function = M.getFunction("ax.ir.retnull.test"); CPPUNIT_ASSERT(function); diff --git a/openvdb_ax/openvdb_ax/test/backend/TestLogger.cc b/openvdb_ax/openvdb_ax/test/backend/TestLogger.cc new file mode 100644 index 0000000000..8032d949d2 --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/backend/TestLogger.cc @@ -0,0 +1,253 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include "util.h" + +#include +#include + +#include + +class TestLogger : public CppUnit::TestCase +{ +public: + + CPPUNIT_TEST_SUITE(TestLogger); + //CPPUNIT_TEST(testParseNewNode); + CPPUNIT_TEST(testParseSetsTree); + CPPUNIT_TEST(testAddError); + CPPUNIT_TEST(testAddWarning); + CPPUNIT_TEST(testWarningsAsErrors); + CPPUNIT_TEST(testMaxErrors); + + CPPUNIT_TEST_SUITE_END(); + + //void testParseNewNode(); + void testParseSetsTree(); + void testAddError(); + void testAddWarning(); + void testWarningsAsErrors(); + void testMaxErrors(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestLogger); + +/* +extern openvdb::ax::Logger* axlog; +/// @note We don't deploy the grammar c files as part of the AX install. +/// Because the unit tests are structured to be able to build against +/// an existing version of AX we can't include the parser.cc here for +/// access to newNode. It's tested through other methods, but we +/// should restructure how this code is shared by perhaps moving it to +/// a shared header (including the definition of AXLTYPE) +void +TestLogger::testParseNewNode() +{ + openvdb::ax::Logger logger; + axlog = &logger;// setting global Logger* used in parser + AXLTYPE location; + location.first_line = 100; + location.first_column = 65; + const auto& nodeToLineColMap = logger.mNodeToLineColMap; + CPPUNIT_ASSERT(nodeToLineColMap.empty()); + + const openvdb::ax::ast::Local* testLocal = + newNode(&location, "test"); + + CPPUNIT_ASSERT_EQUAL(nodeToLineColMap.size(),static_cast(1)); + openvdb::ax::Logger::CodeLocation lineCol = nodeToLineColMap.at(testLocal); + CPPUNIT_ASSERT_EQUAL(lineCol.first, static_cast(100)); + CPPUNIT_ASSERT_EQUAL(lineCol.second, static_cast(65)); +} +*/ + +void +TestLogger::testParseSetsTree() +{ + openvdb::ax::Logger logger; + CPPUNIT_ASSERT(!logger.mTreePtr); + std::string code(""); + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse(code.c_str(), logger); + CPPUNIT_ASSERT(tree); + CPPUNIT_ASSERT_EQUAL(tree, logger.mTreePtr); +} + +void +TestLogger::testAddError() +{ + std::vector messages; + openvdb::ax::Logger logger([&messages](const std::string& message) { + messages.emplace_back(message); + }); + CPPUNIT_ASSERT(!logger.hasError()); + CPPUNIT_ASSERT_EQUAL(logger.errors(), messages.size()); + + openvdb::ax::Logger::CodeLocation codeLocation(1,1); + std::string message("test"); + + logger.error(message, codeLocation); + CPPUNIT_ASSERT(logger.hasError()); + CPPUNIT_ASSERT_EQUAL(messages.size(), static_cast(1)); + CPPUNIT_ASSERT_EQUAL(logger.errors(), static_cast(1)); + CPPUNIT_ASSERT_EQUAL(strcmp(messages.back().c_str(), "[1] error: test 1:1"), 0); + + logger.error(message, codeLocation); + CPPUNIT_ASSERT_EQUAL(messages.size(), static_cast(2)); + CPPUNIT_ASSERT_EQUAL(logger.errors(), static_cast(2)); + + logger.clear(); + CPPUNIT_ASSERT(!logger.hasError()); + CPPUNIT_ASSERT_EQUAL(logger.errors(), static_cast(0)); + + openvdb::ax::ast::Local testLocal("name"); + logger.error(message, &testLocal); + CPPUNIT_ASSERT(logger.hasError()); + CPPUNIT_ASSERT_EQUAL(logger.errors(), static_cast(1)); + CPPUNIT_ASSERT_EQUAL(messages.size(), static_cast(3)); + + CPPUNIT_ASSERT(!logger.mTreePtr); + CPPUNIT_ASSERT_EQUAL(strcmp(messages.back().c_str(), "[1] error: test"), 0); + + logger.clear(); + CPPUNIT_ASSERT(!logger.hasError()); + + // test that add error finds code location + { + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse(" a;", logger); + const openvdb::ax::ast::Node* local = tree->child(0)->child(0); + CPPUNIT_ASSERT(local); + + logger.error(message, local); + CPPUNIT_ASSERT(logger.hasError()); + CPPUNIT_ASSERT_EQUAL(logger.errors(), static_cast(1)); + CPPUNIT_ASSERT(logger.mTreePtr); + CPPUNIT_ASSERT_EQUAL(strcmp(messages.back().c_str(), "[1] error: test 1:2"), 0); + } + + logger.clear(); + CPPUNIT_ASSERT(!logger.hasError()); + // test add error finds code location even when node is deep copy + { + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("a;", logger); + openvdb::ax::ast::Tree::ConstPtr treeCopy(tree->copy()); + const openvdb::ax::ast::Node* local = tree->child(0)->child(0); + CPPUNIT_ASSERT(local); + const openvdb::ax::ast::Node* localCopy = treeCopy->child(0)->child(0); + CPPUNIT_ASSERT(localCopy); + // add referring to copy + logger.error(message, localCopy); + CPPUNIT_ASSERT(logger.hasError()); + CPPUNIT_ASSERT_EQUAL(logger.errors(), static_cast(1)); + CPPUNIT_ASSERT_EQUAL(messages.size(), static_cast(5)); + + CPPUNIT_ASSERT(logger.mTreePtr); + CPPUNIT_ASSERT_EQUAL(strcmp(messages.back().c_str(), "[1] error: test 1:1"), 0); + } +} + +void +TestLogger::testAddWarning() +{ + std::vector messages; + openvdb::ax::Logger logger([](const std::string&) {}, + [&messages](const std::string& message) { + messages.emplace_back(message); + }); + CPPUNIT_ASSERT(!logger.hasWarning()); + CPPUNIT_ASSERT_EQUAL(logger.warnings(), messages.size()); + + openvdb::ax::Logger::CodeLocation codeLocation(1,1); + std::string message("test"); + + logger.warning(message, codeLocation); + CPPUNIT_ASSERT(logger.hasWarning()); + CPPUNIT_ASSERT_EQUAL(messages.size(), static_cast(1)); + CPPUNIT_ASSERT_EQUAL(logger.warnings(), static_cast(1)); + CPPUNIT_ASSERT_EQUAL(strcmp(messages.back().c_str(), "[1] warning: test 1:1"), 0); + + logger.warning(message, codeLocation); + CPPUNIT_ASSERT_EQUAL(messages.size(), static_cast(2)); + CPPUNIT_ASSERT_EQUAL(logger.warnings(), static_cast(2)); + + logger.clear(); + CPPUNIT_ASSERT(!logger.hasWarning()); + CPPUNIT_ASSERT_EQUAL(logger.warnings(), static_cast(0)); + + openvdb::ax::ast::Local testLocal("name"); + logger.warning(message, &testLocal); + CPPUNIT_ASSERT(logger.hasWarning()); + CPPUNIT_ASSERT_EQUAL(logger.warnings(), static_cast(1)); + CPPUNIT_ASSERT_EQUAL(messages.size(), static_cast(3)); + + CPPUNIT_ASSERT(!logger.mTreePtr); + CPPUNIT_ASSERT_EQUAL(strcmp(messages.back().c_str(), "[1] warning: test"), 0); + + logger.clear(); + CPPUNIT_ASSERT(!logger.hasWarning()); + + // test that add warning finds code location + { + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse(" a;", logger); + const openvdb::ax::ast::Node* local = tree->child(0)->child(0); + CPPUNIT_ASSERT(local); + + logger.warning(message, local); + CPPUNIT_ASSERT(logger.hasWarning()); + CPPUNIT_ASSERT_EQUAL(logger.warnings(), static_cast(1)); + CPPUNIT_ASSERT(logger.mTreePtr); + CPPUNIT_ASSERT_EQUAL(strcmp(messages.back().c_str(), "[1] warning: test 1:2"), 0); + } + + logger.clear(); + CPPUNIT_ASSERT(!logger.hasWarning()); + // test add warning finds code location even when node is deep copy + { + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("a;", logger); + openvdb::ax::ast::Tree::ConstPtr treeCopy(tree->copy()); + const openvdb::ax::ast::Node* local = tree->child(0)->child(0); + CPPUNIT_ASSERT(local); + const openvdb::ax::ast::Node* localCopy = treeCopy->child(0)->child(0); + CPPUNIT_ASSERT(localCopy); + // add referring to copy + logger.warning(message, localCopy); + CPPUNIT_ASSERT(logger.hasWarning()); + CPPUNIT_ASSERT_EQUAL(logger.warnings(), static_cast(1)); + CPPUNIT_ASSERT_EQUAL(messages.size(), static_cast(5)); + + CPPUNIT_ASSERT(logger.mTreePtr); + CPPUNIT_ASSERT_EQUAL(strcmp(messages.back().c_str(), "[1] warning: test 1:1"), 0); + } +} + +void +TestLogger::testWarningsAsErrors() +{ + openvdb::ax::Logger logger([](const std::string&) {}); + const std::string message("test"); + const openvdb::ax::Logger::CodeLocation location(10,20); + logger.setWarningsAsErrors(true); + CPPUNIT_ASSERT(!logger.hasError()); + CPPUNIT_ASSERT(!logger.hasWarning()); + + logger.warning(message, location); + CPPUNIT_ASSERT(logger.hasError()); + CPPUNIT_ASSERT(!logger.hasWarning()); +} + +void +TestLogger::testMaxErrors() +{ + openvdb::ax::Logger logger([](const std::string&) {}); + const std::string message("test"); + const openvdb::ax::Logger::CodeLocation location(10,20); + + CPPUNIT_ASSERT(logger.error(message, location)); + CPPUNIT_ASSERT(logger.error(message, location)); + CPPUNIT_ASSERT(logger.error(message, location)); + logger.clear(); + logger.setMaxErrors(2); + CPPUNIT_ASSERT(logger.error(message, location)); + CPPUNIT_ASSERT(!logger.error(message, location)); + CPPUNIT_ASSERT(!logger.error(message, location)); +} + diff --git a/openvdb_ax/openvdb_ax/test/backend/TestSymbolTable.cc b/openvdb_ax/openvdb_ax/test/backend/TestSymbolTable.cc index 84ea423ca6..529ab5fd0f 100644 --- a/openvdb_ax/openvdb_ax/test/backend/TestSymbolTable.cc +++ b/openvdb_ax/openvdb_ax/test/backend/TestSymbolTable.cc @@ -3,7 +3,7 @@ #include "util.h" -#include "../codegen/SymbolTable.h" +#include #include diff --git a/openvdb_ax/openvdb_ax/test/backend/TestTypes.cc b/openvdb_ax/openvdb_ax/test/backend/TestTypes.cc index 87c724dba0..15e9421f18 100644 --- a/openvdb_ax/openvdb_ax/test/backend/TestTypes.cc +++ b/openvdb_ax/openvdb_ax/test/backend/TestTypes.cc @@ -3,7 +3,8 @@ #include "util.h" -#include "../codegen/Types.h" +#include + #include #include #include diff --git a/openvdb_ax/openvdb_ax/test/backend/util.h b/openvdb_ax/openvdb_ax/test/backend/util.h index 784f6d4ff1..a72978b2c4 100644 --- a/openvdb_ax/openvdb_ax/test/backend/util.h +++ b/openvdb_ax/openvdb_ax/test/backend/util.h @@ -4,7 +4,7 @@ #ifndef OPENVDB_AX_UNITTEST_BACKEND_UTIL_HAS_BEEN_INCLUDED #define OPENVDB_AX_UNITTEST_BACKEND_UTIL_HAS_BEEN_INCLUDED -#include "../codegen/Types.h" +#include #include #include diff --git a/openvdb_ax/openvdb_ax/test/compiler/TestPointExecutable.cc b/openvdb_ax/openvdb_ax/test/compiler/TestPointExecutable.cc index 601b6abd2c..b2d5e436d2 100644 --- a/openvdb_ax/openvdb_ax/test/compiler/TestPointExecutable.cc +++ b/openvdb_ax/openvdb_ax/test/compiler/TestPointExecutable.cc @@ -1,8 +1,8 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../compiler/Compiler.h" -#include "../compiler/PointExecutable.h" +#include +#include #include #include @@ -21,11 +21,13 @@ class TestPointExecutable : public CppUnit::TestCase CPPUNIT_TEST(testConstructionDestruction); CPPUNIT_TEST(testCreateMissingAttributes); CPPUNIT_TEST(testGroupExecution); + CPPUNIT_TEST(testCompilerCases); CPPUNIT_TEST_SUITE_END(); void testConstructionDestruction(); void testCreateMissingAttributes(); void testGroupExecution(); + void testCompilerCases(); }; CPPUNIT_TEST_SUITE_REGISTRATION(TestPointExecutable); @@ -93,9 +95,10 @@ TestPointExecutable::testCreateMissingAttributes() openvdb::ax::Compiler::UniquePtr compiler = openvdb::ax::Compiler::create(); openvdb::ax::PointExecutable::Ptr executable = compiler->compile("@a=v@b.x;"); + CPPUNIT_ASSERT(executable); executable->setCreateMissing(false); - CPPUNIT_ASSERT_THROW(executable->execute(*grid), openvdb::LookupError); + CPPUNIT_ASSERT_THROW(executable->execute(*grid), openvdb::AXExecutionError); executable->setCreateMissing(true); executable->execute(*grid); @@ -160,6 +163,7 @@ TestPointExecutable::testGroupExecution() openvdb::ax::Compiler::UniquePtr compiler = openvdb::ax::Compiler::create(); openvdb::ax::PointExecutable::Ptr executable = compiler->compile("i@a=1;"); + CPPUNIT_ASSERT(executable); const std::string group = "test"; @@ -181,3 +185,113 @@ TestPointExecutable::testGroupExecution() checkValues(1); } +void +TestPointExecutable::testCompilerCases() +{ + openvdb::ax::Compiler::UniquePtr compiler = openvdb::ax::Compiler::create(); + CPPUNIT_ASSERT(compiler); + { + // with string only + CPPUNIT_ASSERT(static_cast(compiler->compile("int i;"))); + CPPUNIT_ASSERT_THROW(compiler->compile("i;"), openvdb::AXCompilerError); + CPPUNIT_ASSERT_THROW(compiler->compile("i"), openvdb::AXCompilerError); + // with AST only + auto ast = openvdb::ax::ast::parse("i;"); + CPPUNIT_ASSERT_THROW(compiler->compile(*ast), openvdb::AXCompilerError); + } + + openvdb::ax::Logger logger([](const std::string&) {}); + + // using string and logger + { + openvdb::ax::PointExecutable::Ptr executable = + compiler->compile("", logger); // empty + CPPUNIT_ASSERT(executable); + } + logger.clear(); + { + openvdb::ax::PointExecutable::Ptr executable = + compiler->compile("i;", logger); // undeclared variable error + CPPUNIT_ASSERT(!executable); + CPPUNIT_ASSERT(logger.hasError()); + logger.clear(); + openvdb::ax::PointExecutable::Ptr executable2 = + compiler->compile("i", logger); // expected ; error (parser) + CPPUNIT_ASSERT(!executable2); + CPPUNIT_ASSERT(logger.hasError()); + } + logger.clear(); + { + openvdb::ax::PointExecutable::Ptr executable = + compiler->compile("int i = 1e200000000;", logger); // warning + CPPUNIT_ASSERT(executable); + CPPUNIT_ASSERT(logger.hasWarning()); + } + + // using syntax tree and logger + logger.clear(); + { + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("", logger); + CPPUNIT_ASSERT(tree); + openvdb::ax::PointExecutable::Ptr executable = + compiler->compile(*tree, logger); // empty + CPPUNIT_ASSERT(executable); + logger.clear(); // no tree for line col numbers + openvdb::ax::PointExecutable::Ptr executable2 = + compiler->compile(*tree, logger); // empty + CPPUNIT_ASSERT(executable2); + } + logger.clear(); + { + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("i;", logger); + CPPUNIT_ASSERT(tree); + openvdb::ax::PointExecutable::Ptr executable = + compiler->compile(*tree, logger); // undeclared variable error + CPPUNIT_ASSERT(!executable); + CPPUNIT_ASSERT(logger.hasError()); + logger.clear(); // no tree for line col numbers + openvdb::ax::PointExecutable::Ptr executable2 = + compiler->compile(*tree, logger); // undeclared variable error + CPPUNIT_ASSERT(!executable2); + CPPUNIT_ASSERT(logger.hasError()); + } + logger.clear(); + { + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("int i = 1e200000000;", logger); + CPPUNIT_ASSERT(tree); + openvdb::ax::PointExecutable::Ptr executable = + compiler->compile(*tree, logger); // warning + CPPUNIT_ASSERT(executable); + CPPUNIT_ASSERT(logger.hasWarning()); + logger.clear(); // no tree for line col numbers + openvdb::ax::PointExecutable::Ptr executable2 = + compiler->compile(*tree, logger); // warning + CPPUNIT_ASSERT(executable2); + CPPUNIT_ASSERT(logger.hasWarning()); + } + logger.clear(); + + // with copied tree + { + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("", logger); + openvdb::ax::PointExecutable::Ptr executable = + compiler->compile(*(tree->copy()), logger); // empty + CPPUNIT_ASSERT(executable); + } + logger.clear(); + { + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("i;", logger); + openvdb::ax::PointExecutable::Ptr executable = + compiler->compile(*(tree->copy()), logger); // undeclared variable error + CPPUNIT_ASSERT(!executable); + } + logger.clear(); + { + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("int i = 1e200000000;", logger); + openvdb::ax::PointExecutable::Ptr executable = + compiler->compile(*(tree->copy()), logger); // warning + CPPUNIT_ASSERT(executable); + } + logger.clear(); +} + diff --git a/openvdb_ax/openvdb_ax/test/compiler/TestVolumeExecutable.cc b/openvdb_ax/openvdb_ax/test/compiler/TestVolumeExecutable.cc index d382837721..11c3d82074 100644 --- a/openvdb_ax/openvdb_ax/test/compiler/TestVolumeExecutable.cc +++ b/openvdb_ax/openvdb_ax/test/compiler/TestVolumeExecutable.cc @@ -1,8 +1,8 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../compiler/Compiler.h" -#include "../compiler/VolumeExecutable.h" +#include +#include #include @@ -16,11 +16,13 @@ class TestVolumeExecutable : public CppUnit::TestCase CPPUNIT_TEST(testConstructionDestruction); CPPUNIT_TEST(testCreateMissingGrids); CPPUNIT_TEST(testTreeExecutionLevel); + CPPUNIT_TEST(testCompilerCases); CPPUNIT_TEST_SUITE_END(); void testConstructionDestruction(); void testCreateMissingGrids(); void testTreeExecutionLevel(); + void testCompilerCases(); }; CPPUNIT_TEST_SUITE_REGISTRATION(TestVolumeExecutable); @@ -80,12 +82,13 @@ TestVolumeExecutable::testCreateMissingGrids() openvdb::ax::Compiler::UniquePtr compiler = openvdb::ax::Compiler::create(); openvdb::ax::VolumeExecutable::Ptr executable = compiler->compile("@a=v@b.x;"); + CPPUNIT_ASSERT(executable); executable->setCreateMissing(false); executable->setValueIterator(openvdb::ax::VolumeExecutable::IterType::ON); openvdb::GridPtrVec grids; - CPPUNIT_ASSERT_THROW(executable->execute(grids), openvdb::LookupError); + CPPUNIT_ASSERT_THROW(executable->execute(grids), openvdb::AXExecutionError); CPPUNIT_ASSERT(grids.empty()); executable->setCreateMissing(true); @@ -113,6 +116,7 @@ TestVolumeExecutable::testTreeExecutionLevel() openvdb::ax::Compiler::UniquePtr compiler = openvdb::ax::Compiler::create(); openvdb::ax::VolumeExecutable::Ptr executable = compiler->compile("f@test = 1.0f;"); + CPPUNIT_ASSERT(executable); using NodeT0 = openvdb::FloatGrid::Accessor::NodeT0; using NodeT1 = openvdb::FloatGrid::Accessor::NodeT1; @@ -178,3 +182,116 @@ TestVolumeExecutable::testTreeExecutionLevel() CPPUNIT_ASSERT_THROW(executable->setTreeExecutionLevel(4), openvdb::RuntimeError); } + +void +TestVolumeExecutable::testCompilerCases() +{ + openvdb::ax::Compiler::UniquePtr compiler = openvdb::ax::Compiler::create(); + CPPUNIT_ASSERT(compiler); + { + // with string only + CPPUNIT_ASSERT(static_cast(compiler->compile("int i;"))); + CPPUNIT_ASSERT_THROW(compiler->compile("i;"), openvdb::AXCompilerError); + CPPUNIT_ASSERT_THROW(compiler->compile("i"), openvdb::AXCompilerError); + // with AST only + auto ast = openvdb::ax::ast::parse("i;"); + CPPUNIT_ASSERT_THROW(compiler->compile(*ast), openvdb::AXCompilerError); + } + + openvdb::ax::Logger logger([](const std::string&) {}); + + // using string and logger + { + openvdb::ax::VolumeExecutable::Ptr executable = + compiler->compile("", logger); // empty + CPPUNIT_ASSERT(executable); + } + logger.clear(); + { + openvdb::ax::VolumeExecutable::Ptr executable = + compiler->compile("i;", logger); // undeclared variable error + CPPUNIT_ASSERT(!executable); + CPPUNIT_ASSERT(logger.hasError()); + logger.clear(); + openvdb::ax::VolumeExecutable::Ptr executable2 = + compiler->compile("i", logger); // expected ; error (parser) + CPPUNIT_ASSERT(!executable2); + CPPUNIT_ASSERT(logger.hasError()); + } + logger.clear(); + { + openvdb::ax::VolumeExecutable::Ptr executable = + compiler->compile("int i = 1e200000000;", logger); // warning + CPPUNIT_ASSERT(executable); + CPPUNIT_ASSERT(logger.hasWarning()); + } + + // using syntax tree and logger + logger.clear(); + { + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("", logger); + CPPUNIT_ASSERT(tree); + openvdb::ax::VolumeExecutable::Ptr executable = + compiler->compile(*tree, logger); // empty + CPPUNIT_ASSERT(executable); + logger.clear(); // no tree for line col numbers + openvdb::ax::VolumeExecutable::Ptr executable2 = + compiler->compile(*tree, logger); // empty + CPPUNIT_ASSERT(executable2); + } + logger.clear(); + { + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("i;", logger); + CPPUNIT_ASSERT(tree); + openvdb::ax::VolumeExecutable::Ptr executable = + compiler->compile(*tree, logger); // undeclared variable error + CPPUNIT_ASSERT(!executable); + CPPUNIT_ASSERT(logger.hasError()); + logger.clear(); // no tree for line col numbers + openvdb::ax::VolumeExecutable::Ptr executable2 = + compiler->compile(*tree, logger); // undeclared variable error + CPPUNIT_ASSERT(!executable2); + CPPUNIT_ASSERT(logger.hasError()); + } + logger.clear(); + { + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("int i = 1e200000000;", logger); + CPPUNIT_ASSERT(tree); + openvdb::ax::VolumeExecutable::Ptr executable = + compiler->compile(*tree, logger); // warning + CPPUNIT_ASSERT(executable); + CPPUNIT_ASSERT(logger.hasWarning()); + logger.clear(); // no tree for line col numbers + openvdb::ax::VolumeExecutable::Ptr executable2 = + compiler->compile(*tree, logger); // warning + CPPUNIT_ASSERT(executable2); + CPPUNIT_ASSERT(logger.hasWarning()); + } + logger.clear(); + + // with copied tree + { + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("", logger); + openvdb::ax::VolumeExecutable::Ptr executable = + compiler->compile(*(tree->copy()), logger); // empty + CPPUNIT_ASSERT(executable); + } + logger.clear(); + { + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("i;", logger); + openvdb::ax::VolumeExecutable::Ptr executable = + compiler->compile(*(tree->copy()), logger); // undeclared variable error + CPPUNIT_ASSERT(!executable); + CPPUNIT_ASSERT(logger.hasError()); + } + logger.clear(); + { + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("int i = 1e200000000;", logger); + openvdb::ax::VolumeExecutable::Ptr executable = + compiler->compile(*(tree->copy()), logger); // warning + CPPUNIT_ASSERT(executable); + CPPUNIT_ASSERT(logger.hasWarning()); + } + logger.clear(); +} + diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestArrayPack.cc b/openvdb_ax/openvdb_ax/test/frontend/TestArrayPack.cc index 38d25506ac..cf6b98c77c 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestArrayPack.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestArrayPack.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -135,7 +135,7 @@ void TestArrayPack::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestArrayUnpackNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestArrayUnpackNode.cc index e330863259..59496bcb62 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestArrayUnpackNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestArrayUnpackNode.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -127,7 +127,7 @@ void TestArrayUnpackNode::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestAssignExpressionNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestAssignExpressionNode.cc index 6f41409a55..1c1f5ec295 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestAssignExpressionNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestAssignExpressionNode.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -245,7 +245,7 @@ void TestAssignExpressionNode::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestAttributeNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestAttributeNode.cc index 07b0b4793e..880c595407 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestAttributeNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestAttributeNode.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -67,7 +67,7 @@ void TestAttributeNode::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestBinaryOperatorNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestBinaryOperatorNode.cc index 46b16dc5de..2f975c1678 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestBinaryOperatorNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestBinaryOperatorNode.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -314,7 +314,7 @@ void TestBinaryOperatorNode::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestCastNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestCastNode.cc index dd2d372144..504e6349f6 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestCastNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestCastNode.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -70,7 +70,7 @@ void TestCastNode::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestCommaOperator.cc b/openvdb_ax/openvdb_ax/test/frontend/TestCommaOperator.cc index e24df7eb5c..c113340ae4 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestCommaOperator.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestCommaOperator.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -132,7 +132,7 @@ void TestCommaOperator::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestConditionalStatementNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestConditionalStatementNode.cc index e0195e9530..ac415149d4 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestConditionalStatementNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestConditionalStatementNode.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -305,7 +305,7 @@ void TestConditionalStatementNode::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestCrementNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestCrementNode.cc index 759e783fd4..c805a5ba8a 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestCrementNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestCrementNode.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -50,7 +50,7 @@ void TestCrementNode::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestDeclareLocalNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestDeclareLocalNode.cc index 309859490d..cfeee53592 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestDeclareLocalNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestDeclareLocalNode.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -89,7 +89,7 @@ void TestDeclareLocalNode::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestExternalVariableNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestExternalVariableNode.cc index 55a84ae660..808376397a 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestExternalVariableNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestExternalVariableNode.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -57,7 +57,7 @@ void TestExternalVariableNode::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestFunctionCallNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestFunctionCallNode.cc index 531114018d..9de01c912d 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestFunctionCallNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestFunctionCallNode.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -130,7 +130,7 @@ void TestFunctionCallNode::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestKeywordNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestKeywordNode.cc index f20948b596..5d376c8a44 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestKeywordNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestKeywordNode.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -45,7 +45,7 @@ void TestKeywordNode::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestLocalNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestLocalNode.cc index 06167ed4c0..db0f1eb496 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestLocalNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestLocalNode.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -53,7 +53,7 @@ void TestLocalNode::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestLoopNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestLoopNode.cc index 08317f3472..c3e3a8dcde 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestLoopNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestLoopNode.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -327,7 +327,7 @@ void TestLoopNode::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestStatementListNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestStatementListNode.cc index 8c499ec57f..2e36393083 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestStatementListNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestStatementListNode.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -85,7 +85,7 @@ void TestStatementList::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestSyntaxFailures.cc b/openvdb_ax/openvdb_ax/test/frontend/TestSyntaxFailures.cc index 2e2459623c..f332d62416 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestSyntaxFailures.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestSyntaxFailures.cc @@ -1,17 +1,17 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include +#include "test/util.h" -#include "../compiler/Compiler.h" -#include "../Exceptions.h" +#include +#include #include #include #include #include -#include "test/util.h" +#include namespace { diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestTernaryOperatorNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestTernaryOperatorNode.cc index 8221d67db9..5e1f1c98ef 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestTernaryOperatorNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestTernaryOperatorNode.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -101,7 +101,7 @@ void TestTernaryOperatorNode::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement @@ -124,3 +124,4 @@ void TestTernaryOperatorNode::testASTNode() } } } + diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestUnaryOperatorNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestUnaryOperatorNode.cc index 48297fe9e3..8a115001df 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestUnaryOperatorNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestUnaryOperatorNode.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -107,7 +107,7 @@ void TestUnaryOperatorNode::testASTNode() for (const auto& test : tests) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestValueNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestValueNode.cc index 873e50a6b3..4bb115103c 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestValueNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestValueNode.cc @@ -1,11 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include "../ast/AST.h" -#include "../ast/Scanners.h" -#include "../ast/PrintTree.h" -#include "../Exceptions.h" -#include "../test/util.h" +#include +#include +#include +#include +#include #include @@ -192,7 +192,7 @@ void TestValueNode::testASTNode() for (const auto& test : tests.second) { const std::string& code = test.first; const Node* expected = test.second.get(); - const Tree::Ptr tree = parse(code.c_str()); + const Tree::ConstPtr tree = parse(code.c_str()); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("No AST returned", code), static_cast(tree)); // get the first statement diff --git a/openvdb_ax/openvdb_ax/test/integration/CompareGrids.cc b/openvdb_ax/openvdb_ax/test/integration/CompareGrids.cc index e129e645b2..4986e855d1 100644 --- a/openvdb_ax/openvdb_ax/test/integration/CompareGrids.cc +++ b/openvdb_ax/openvdb_ax/test/integration/CompareGrids.cc @@ -7,7 +7,7 @@ #include -#ifdef OPENVDB_AX_NO_MATRIX +#ifndef OPENVDB_HAS_MATRIX_SUPPORT namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { @@ -34,12 +34,54 @@ template <> bool isApproxEqual(const Mat4d& a, const Mat4d& b, const Mat4 } } } -#endif // OPENVDB_AX_NO_MATRIX +#endif // OPENVDB_HAS_MATRIX_SUPPORT namespace unittest_util { +#if (OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER == 7 && \ + OPENVDB_LIBRARY_MINOR_VERSION_NUMBER == 1) +// Issue with TypeList where Unqiue defines recursively in 7.1 +template struct TListFix; + +template struct TSAppendImpl; +template +struct TSAppendImpl, OtherTs...> { using type = TListFix; }; +template +struct TSAppendImpl, TListFix> { using type = TListFix; }; + +template struct TSEraseImpl; +template struct TSEraseImpl, T> { using type = TListFix<>; }; +template +struct TSEraseImpl, T> { using type = typename TSEraseImpl, T>::type; }; +template +struct TSEraseImpl, T> { + using type = typename TSAppendImpl, + typename TSEraseImpl, T>::type>::type; +}; + +template struct TSRemoveImpl; +template struct TSRemoveImpl { using type = ListT; }; +template +struct TSRemoveImpl { using type = typename TSRemoveImpl::type, Ts...>::type; }; +template +struct TSRemoveImpl> { using type = typename TSRemoveImpl::type; }; + +template +struct TListFix +{ + using Self = TListFix; + template + using Remove = typename TSRemoveImpl::type; + template + using Append = typename TSAppendImpl::type; + template + static void foreach(OpT op) { openvdb::internal::TSForEachImpl(op); } +}; +using TypeList = TListFix< +#else using TypeList = openvdb::TypeList< +#endif double, float, int64_t, diff --git a/openvdb_ax/openvdb_ax/test/integration/CompareGrids.h b/openvdb_ax/openvdb_ax/test/integration/CompareGrids.h index 04252a9291..77775fc3f9 100644 --- a/openvdb_ax/openvdb_ax/test/integration/CompareGrids.h +++ b/openvdb_ax/openvdb_ax/test/integration/CompareGrids.h @@ -17,7 +17,7 @@ #include #include -#ifdef OPENVDB_AX_NO_MATRIX +#ifndef OPENVDB_HAS_MATRIX_SUPPORT namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { @@ -36,7 +36,7 @@ MATRIX_OPS(Mat4) } } } -#endif // OPENVDB_AX_NO_MATRIX +#endif // OPENVDB_HAS_MATRIX_SUPPORT namespace unittest_util { diff --git a/openvdb_ax/openvdb_ax/test/integration/TestArrayUnpack.cc b/openvdb_ax/openvdb_ax/test/integration/TestArrayUnpack.cc index f8723a5d4a..ddc5f9c45c 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestArrayUnpack.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestArrayUnpack.cc @@ -6,8 +6,8 @@ #include "../test/util.h" -#include "../compiler/CustomData.h" -#include "../Exceptions.h" +#include +#include #include diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign.cc b/openvdb_ax/openvdb_ax/test/integration/TestAssign.cc index 99f001e525..1c201f43ff 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestAssign.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestAssign.cc @@ -6,8 +6,8 @@ #include "../test/util.h" -#include "../compiler/CustomData.h" -#include "../Exceptions.h" +#include +#include #include @@ -1533,9 +1533,8 @@ float@test1 = var; const auto names = unittest_util::nameSequence("test", 7); mHarness.addAttributes(names, {30.0f, 1.0f, -10.0f, -15.0f, 50.0f, 50.0f, 1.0f}); + this->execute("assign_scoped.float.ax"); - std::vector warnings; - this->execute("assign_scoped.float.ax", nullptr, &warnings, false); - CPPUNIT_ASSERT(!warnings.empty()); + CPPUNIT_ASSERT(mHarness.mLogger.hasWarning()); } diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCrement.cc b/openvdb_ax/openvdb_ax/test/integration/TestCrement.cc index 15fca9f54a..705246976d 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestCrement.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestCrement.cc @@ -6,8 +6,8 @@ #include "../test/util.h" -#include "../compiler/CustomData.h" -#include "../Exceptions.h" +#include +#include #include diff --git a/openvdb_ax/openvdb_ax/test/integration/TestDeclare.cc b/openvdb_ax/openvdb_ax/test/integration/TestDeclare.cc index 552015067c..098cad22c6 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestDeclare.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestDeclare.cc @@ -116,7 +116,7 @@ TestDeclare::testNewAttributes() mHarness.addInputVolumes({"long_test"}, {int64_t(3)}); mHarness.addInputVolumes({"double_test"}, {0.3}); - mHarness.executeCode("test/snippets/declare/declareAttributes", nullptr, nullptr, true); + mHarness.executeCode("test/snippets/declare/declareAttributes", nullptr, true); AXTESTS_STANDARD_ASSERT(); } @@ -137,7 +137,7 @@ TestDeclare::testNewVectorAttributes() {openvdb::Vec3i::zero(), openvdb::Vec3i(5, 6, 7)}); mHarness.addInputVolumes({"vec_double_test"}, {openvdb::Vec3d(0.3, 0.4, 0.5)}); - mHarness.executeCode("test/snippets/declare/declareNewVectorAttributes", nullptr, nullptr, true); + mHarness.executeCode("test/snippets/declare/declareNewVectorAttributes", nullptr, true); AXTESTS_STANDARD_ASSERT(); } @@ -168,28 +168,28 @@ TestDeclare::testVectorAttributeImplicit() void TestDeclare::testAmbiguousScalarAttributes() { - CPPUNIT_ASSERT_THROW(mHarness.executeCode("test/snippets/declare/declareAmbiguousScalarAttributes"), - openvdb::AXCompilerError); + const bool success = mHarness.executeCode("test/snippets/declare/declareAmbiguousScalarAttributes"); + CPPUNIT_ASSERT(!success); } void TestDeclare::testAmbiguousVectorAttributes() { - CPPUNIT_ASSERT_THROW(mHarness.executeCode("test/snippets/declare/declareAmbiguousScalarAttributes"), - openvdb::AXCompilerError); + const bool success = mHarness.executeCode("test/snippets/declare/declareAmbiguousVectorAttributes"); + CPPUNIT_ASSERT(!success); } void TestDeclare::testAmbiguousScalarExternals() { - CPPUNIT_ASSERT_THROW(mHarness.executeCode("test/snippets/declare/declareAmbiguousScalarExternals"), - openvdb::AXCompilerError); + const bool success = mHarness.executeCode("test/snippets/declare/declareAmbiguousScalarExternals"); + CPPUNIT_ASSERT(!success); } void TestDeclare::testAmbiguousVectorExternals() { - CPPUNIT_ASSERT_THROW(mHarness.executeCode("test/snippets/declare/declareAmbiguousScalarExternals"), - openvdb::AXCompilerError); + const bool success = mHarness.executeCode("test/snippets/declare/declareAmbiguousVectorExternals"); + CPPUNIT_ASSERT(!success); } diff --git a/openvdb_ax/openvdb_ax/test/integration/TestEmpty.cc b/openvdb_ax/openvdb_ax/test/integration/TestEmpty.cc index b7100851b0..45db3bfb0c 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestEmpty.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestEmpty.cc @@ -3,7 +3,7 @@ #include "TestHarness.h" -#include "../Exceptions.h" +#include #include diff --git a/openvdb_ax/openvdb_ax/test/integration/TestExternals.cc b/openvdb_ax/openvdb_ax/test/integration/TestExternals.cc index cf38876a99..b19360227b 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestExternals.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestExternals.cc @@ -6,8 +6,8 @@ #include "../test/util.h" -#include "../compiler/CustomData.h" -#include "../Exceptions.h" +#include +#include #include diff --git a/openvdb_ax/openvdb_ax/test/integration/TestHarness.cc b/openvdb_ax/openvdb_ax/test/integration/TestHarness.cc index d4caa7ffd5..d3073e947a 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestHarness.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestHarness.cc @@ -4,9 +4,10 @@ #include "TestHarness.h" #include "util.h" +#include +#include + #include -#include "../compiler/PointExecutable.h" -#include "../compiler/VolumeExecutable.h" namespace unittest_util { @@ -24,10 +25,10 @@ std::string loadText(const std::string& codeFileName) return sstream.str(); } -void wrapExecution(openvdb::points::PointDataGrid& grid, +bool wrapExecution(openvdb::points::PointDataGrid& grid, const std::string& codeFileName, const std::string * const group, - std::vector* warnings, + openvdb::ax::Logger& logger, const openvdb::ax::CustomData::Ptr& data, const openvdb::ax::CompilerOptions& opts, const bool createMissing) @@ -36,16 +37,18 @@ void wrapExecution(openvdb::points::PointDataGrid& grid, Compiler compiler(opts); const std::string code = loadText(codeFileName); - ast::Tree::Ptr syntaxTree = ast::parse(code.c_str()); - PointExecutable::Ptr executable = compiler.compile(*syntaxTree, data, warnings); + ast::Tree::ConstPtr syntaxTree = ast::parse(code.c_str(), logger); + PointExecutable::Ptr executable = compiler.compile(*syntaxTree, logger, data); + if (!executable) return false; executable->setCreateMissing(createMissing); if (group) executable->setGroupExecution(*group); executable->execute(grid); + return true; } -void wrapExecution(openvdb::GridPtrVec& grids, +bool wrapExecution(openvdb::GridPtrVec& grids, const std::string& codeFileName, - std::vector* warnings, + openvdb::ax::Logger& logger, const openvdb::ax::CustomData::Ptr& data, const openvdb::ax::CompilerOptions& opts, const bool createMissing) @@ -54,11 +57,14 @@ void wrapExecution(openvdb::GridPtrVec& grids, Compiler compiler(opts); const std::string code = loadText(codeFileName); - ast::Tree::Ptr syntaxTree = ast::parse(code.c_str()); - VolumeExecutable::Ptr executable = compiler.compile(*syntaxTree, data, warnings); + + ast::Tree::ConstPtr syntaxTree = ast::parse(code.c_str(), logger); + VolumeExecutable::Ptr executable = compiler.compile(*syntaxTree, logger, data); + if (!executable) return false; executable->setCreateMissing(createMissing); executable->setValueIterator(VolumeExecutable::IterType::ON); executable->execute(grids); + return true; } void AXTestHarness::addInputGroups(const std::vector &names, @@ -83,20 +89,24 @@ void AXTestHarness::addExpectedGroups(const std::vector &names, } } -void AXTestHarness::executeCode(const std::string& codeFile, - const std::string * const group, - std::vector* warnings, +bool AXTestHarness::executeCode(const std::string& codeFile, + const std::string* const group, const bool createMissing) { + bool success = false; if (mUsePoints) { for (auto& grid : mInputPointGrids) { - wrapExecution(*grid, codeFile, group, warnings, mCustomData, mOpts, createMissing); + mLogger.clear(); + success = wrapExecution(*grid, codeFile, group, mLogger, mCustomData, mOpts, createMissing); + if (!success) break; } } if (mUseVolumes) { - wrapExecution(mInputVolumeGrids, codeFile, warnings, mCustomData, mOpts, createMissing); + mLogger.clear(); + success = wrapExecution(mInputVolumeGrids, codeFile, mLogger, mCustomData, mOpts, createMissing); } + return success; } template @@ -218,6 +228,8 @@ void AXTestHarness::reset(const openvdb::Index64 ppv, const openvdb::CoordBBox& mOutputPointGrids.back()->setName("custom_expected"); mVolumeBounds = bounds; + + mLogger.clear(); } void AXTestHarness::reset() @@ -261,6 +273,8 @@ void AXTestHarness::reset() mOutputPointGrids.back()->setName("4_points_expected"); mVolumeBounds = openvdb::CoordBBox({0,0,0}, {0,0,0}); + + mLogger.clear(); } template diff --git a/openvdb_ax/openvdb_ax/test/integration/TestHarness.h b/openvdb_ax/openvdb_ax/test/integration/TestHarness.h index a37e8287bb..3d9affe093 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestHarness.h +++ b/openvdb_ax/openvdb_ax/test/integration/TestHarness.h @@ -12,9 +12,9 @@ #include "CompareGrids.h" -#include "../ast/Tokens.h" -#include "../compiler/Compiler.h" -#include "../compiler/CustomData.h" +#include +#include +#include #include #include @@ -30,24 +30,20 @@ namespace unittest_util std::string loadText(const std::string& codeFileName); -void wrapExecution(openvdb::points::PointDataGrid& grid, +bool wrapExecution(openvdb::points::PointDataGrid& grid, const std::string& codeFileName, - const std::string * const group = nullptr, - std::vector* warnings = nullptr, - const openvdb::ax::CustomData::Ptr& data = - openvdb::ax::CustomData::create(), - const openvdb::ax::CompilerOptions& opts = - openvdb::ax::CompilerOptions(), - const bool createMissing = true); - -void wrapExecution(openvdb::GridPtrVec& grids, + const std::string * const group, + openvdb::ax::Logger& logger, + const openvdb::ax::CustomData::Ptr& data, + const openvdb::ax::CompilerOptions& opts, + const bool createMissing); + +bool wrapExecution(openvdb::GridPtrVec& grids, const std::string& codeFileName, - std::vector* warnings = nullptr, - const openvdb::ax::CustomData::Ptr& data = - openvdb::ax::CustomData::create(), - const openvdb::ax::CompilerOptions& opts = - openvdb::ax::CompilerOptions(), - const bool createMissing = false); + openvdb::ax::Logger& logger, + const openvdb::ax::CustomData::Ptr& data, + const openvdb::ax::CompilerOptions& opts, + const bool createMissing); /// @brief Structure for wrapping up most of the existing integration /// tests with a simple interface @@ -63,6 +59,7 @@ struct AXTestHarness , mVolumeBounds({0,0,0},{0,0,0}) , mOpts(openvdb::ax::CompilerOptions()) , mCustomData(openvdb::ax::CustomData::create()) + , mLogger([](const std::string&) {}) { reset(); } @@ -137,9 +134,8 @@ struct AXTestHarness } /// @brief excecutes a snippet of code contained in a file to the input data sets - void executeCode(const std::string& codeFile, - const std::string * const group = nullptr, - std::vector* warnings = nullptr, + bool executeCode(const std::string& codeFile, + const std::string* const group = nullptr, const bool createMissing = false); /// @brief rebuilds the input and output data sets to their default harness states. This @@ -189,7 +185,7 @@ struct AXTestHarness openvdb::ax::CompilerOptions mOpts; openvdb::ax::CustomData::Ptr mCustomData; - + openvdb::ax::Logger mLogger; }; class AXTestCase : public CppUnit::TestCase @@ -244,7 +240,9 @@ class AXTestCase : public CppUnit::TestCase mTestFiles[filename] = true; // has been used // execute - mHarness.executeCode(this->dir() + "/" + filename, args...); + const bool success = mHarness.executeCode(this->dir() + "/" + filename, args...); + CPPUNIT_ASSERT_MESSAGE("error thrown during test: " + filename, success); + //@todo: print error message here // check std::stringstream out; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestStandardFunctions.cc b/openvdb_ax/openvdb_ax/test/integration/TestStandardFunctions.cc index b1b276ea6e..8b62cda230 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestStandardFunctions.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestStandardFunctions.cc @@ -1,17 +1,14 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include -#include -#include - #include "TestHarness.h" #include "../test/util.h" -#include "../compiler/CustomData.h" -#include "../math/OpenSimplexNoise.h" -#include "../compiler/PointExecutable.h" -#include "../compiler/VolumeExecutable.h" + +#include +#include +#include +#include #include #include @@ -22,6 +19,10 @@ #include #include +#include +#include +#include + using namespace openvdb::points; using namespace openvdb::ax; @@ -70,6 +71,7 @@ class TestStandardFunctions : public unittest_util::AXTestCase CPPUNIT_TEST(pretransform); CPPUNIT_TEST(print); CPPUNIT_TEST(rand); + CPPUNIT_TEST(sign); CPPUNIT_TEST(signbit); CPPUNIT_TEST(simplexnoise); CPPUNIT_TEST(sinh); @@ -114,6 +116,7 @@ class TestStandardFunctions : public unittest_util::AXTestCase void pretransform(); void print(); void rand(); + void sign(); void signbit(); void simplexnoise(); void sinh(); @@ -835,6 +838,14 @@ TestStandardFunctions::rand() testFunctionOptions(mHarness, "rand"); } +void +TestStandardFunctions::sign() +{ + mHarness.addAttributes(unittest_util::nameSequence("test", 13), + { 0,0,0,0,0,0,0, -1,-1,-1, 1,1,1 }); + testFunctionOptions(mHarness, "sign"); +} + void TestStandardFunctions::signbit() { diff --git a/openvdb_ax/openvdb_ax/test/integration/TestString.cc b/openvdb_ax/openvdb_ax/test/integration/TestString.cc index dbcb357191..0bb80996ae 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestString.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestString.cc @@ -54,7 +54,7 @@ TestString::testAssignFromAttributes() mHarness.addInputVolumes(unittest_util::nameSequence("string_test", 6), {"test", "test", "new value", "new value", "", ""}); - mHarness.executeCode("test/snippets/string/assignFromAttributes", nullptr, nullptr, true); + mHarness.executeCode("test/snippets/string/assignFromAttributes", nullptr, true); AXTESTS_STANDARD_ASSERT(); } @@ -76,7 +76,7 @@ TestString::testAssignNewOverwrite() mHarness.addInputVolumes({"string_test1", "string_test2"}, {"next_value", "new_value"}); - mHarness.executeCode("test/snippets/string/assignNewOverwrite", nullptr, nullptr, true); + mHarness.executeCode("test/snippets/string/assignNewOverwrite", nullptr, true); AXTESTS_STANDARD_ASSERT(); } @@ -89,7 +89,7 @@ TestString::testBinaryConcat() mHarness.addInputVolumes(unittest_util::nameSequence("string_test", 6), {"test new value", "test new value", "test new value", "test new value", "", "test new value"}); - mHarness.executeCode("test/snippets/string/binaryConcat", nullptr, nullptr, true); + mHarness.executeCode("test/snippets/string/binaryConcat", nullptr, true); AXTESTS_STANDARD_ASSERT(); } diff --git a/openvdb_ax/openvdb_ax/test/integration/TestTernary.cc b/openvdb_ax/openvdb_ax/test/integration/TestTernary.cc index 3170976329..1d98e2b61a 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestTernary.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestTernary.cc @@ -86,3 +86,4 @@ TestTernary::testTernaryVoid() AXTESTS_STANDARD_ASSERT(); } + diff --git a/openvdb_ax/openvdb_ax/test/integration/TestVDBFunctions.cc b/openvdb_ax/openvdb_ax/test/integration/TestVDBFunctions.cc index a648fa1cd0..49f947693d 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestVDBFunctions.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestVDBFunctions.cc @@ -4,13 +4,13 @@ #include "TestHarness.h" #include "util.h" -#include "../ax.h" -#include "../codegen/Types.h" -#include "../codegen/Functions.h" -#include "../codegen/FunctionRegistry.h" -#include "../codegen/FunctionTypes.h" -#include "../compiler/PointExecutable.h" -#include "../compiler/VolumeExecutable.h" +#include +#include +#include +#include +#include +#include +#include #include #include @@ -255,7 +255,7 @@ TestVDBFunctions::ingroupOrder() mHarness.addInputGroups({"b", "a"}, {false, true}); mHarness.addExpectedGroups({"b", "a"}, {false, true}); - mHarness.executeCode("test/snippets/vdb_functions/ingroup", nullptr, nullptr, true); + mHarness.executeCode("test/snippets/vdb_functions/ingroup", nullptr, true); AXTESTS_STANDARD_ASSERT(); } @@ -270,10 +270,9 @@ TestVDBFunctions::ingroup() // compile and execute openvdb::ax::Compiler compiler; - openvdb::ax::CustomData::Ptr customData = openvdb::ax::CustomData::create(); std::string code = unittest_util::loadText("test/snippets/vdb_functions/ingroup"); openvdb::ax::PointExecutable::Ptr executable = - compiler.compile(code, customData); + compiler.compile(code); CPPUNIT_ASSERT_NO_THROW(executable->execute(*pointDataGrid1)); @@ -299,7 +298,7 @@ TestVDBFunctions::ingroup() openvdb::points::appendGroup(pointTree, "testGroup"); setGroup(pointTree, "testGroup", false); - executable = compiler.compile(code, customData); + executable = compiler.compile(code); CPPUNIT_ASSERT_NO_THROW(executable->execute(*pointDataGrid1)); for (auto leafIter = pointTree.cbeginLeaf(); leafIter; ++leafIter) { @@ -343,8 +342,7 @@ TestVDBFunctions::ingroup() std::vector membershipTestGroup2{0, 0, 1, 0}; openvdb::points::setGroup(*pointDataTree2, pointIndexGrid->tree(), membershipTestGroup2, "testGroup2"); - customData->reset(); - executable = compiler.compile(code, customData); + executable = compiler.compile(code); CPPUNIT_ASSERT_NO_THROW(executable->execute(*pointDataGrid2)); auto leafIter2 = pointDataTree2->cbeginLeaf(); diff --git a/openvdb_ax/openvdb_ax/test/integration/TestWorldSpaceAccessors.cc b/openvdb_ax/openvdb_ax/test/integration/TestWorldSpaceAccessors.cc index 6458f5a7bc..8432d27738 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestWorldSpaceAccessors.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestWorldSpaceAccessors.cc @@ -3,7 +3,7 @@ #include "TestHarness.h" -#include "../ax.h" +#include #include #include diff --git a/openvdb_ax/openvdb_ax/test/main.cc b/openvdb_ax/openvdb_ax/test/main.cc index 46a7f643f5..cec902d295 100644 --- a/openvdb_ax/openvdb_ax/test/main.cc +++ b/openvdb_ax/openvdb_ax/test/main.cc @@ -1,12 +1,13 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include -#include "../compiler/Compiler.h" +#include #include +#include #include #include + #include #include #include @@ -15,6 +16,7 @@ #include #include #include + #include // for std::shuffle() #include // for std::round() #include // for EXIT_SUCCESS diff --git a/openvdb_ax/openvdb_ax/test/snippets/function/sign b/openvdb_ax/openvdb_ax/test/snippets/function/sign new file mode 100644 index 0000000000..9b4cf2b0ad --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/snippets/function/sign @@ -0,0 +1,13 @@ +i@test1 = sign(-0.0); +i@test2 = sign(+0.0); +i@test3 = sign(-0.0f); +i@test4 = sign(+0.0f); +i@test5 = sign(0.0); +i@test6 = sign(0.0f); +i@test7 = sign(0); +i@test8 = sign(-0.1); +i@test9 = sign(-0.1f); +i@test10 = sign(-1); +i@test11 = sign(0.1); +i@test12 = sign(0.1f); +i@test13 = sign(1); diff --git a/openvdb_ax/openvdb_ax/test/util.h b/openvdb_ax/openvdb_ax/test/util.h index 6e96353be5..b8799dc098 100644 --- a/openvdb_ax/openvdb_ax/test/util.h +++ b/openvdb_ax/openvdb_ax/test/util.h @@ -10,8 +10,11 @@ #ifndef OPENVDB_AX_UNITTEST_UTIL_HAS_BEEN_INCLUDED #define OPENVDB_AX_UNITTEST_UTIL_HAS_BEEN_INCLUDED -#include "../ast/AST.h" -#include "../ast/Tokens.h" +#include +#include +#include +#include + #include #include @@ -24,25 +27,29 @@ #define TEST_SYNTAX_PASSES(Tests) \ { \ + openvdb::ax::Logger logger;\ for (const auto& test : Tests) { \ + logger.clear();\ const std::string& code = test.first; \ - CPPUNIT_ASSERT_NO_THROW_MESSAGE(ERROR_MSG("Unexpected parsing error", code), \ - openvdb::ax::ast::parse(code.c_str())); \ + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse(code.c_str(), logger);\ + std::stringstream str; \ + CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("Unexpected parsing error(s)\n", str.str()), tree); \ } \ } \ #define TEST_SYNTAX_FAILS(Tests) \ { \ + openvdb::ax::Logger logger([](const std::string&) {});\ for (const auto& test : Tests) { \ + logger.clear();\ const std::string& code = test.first; \ - CPPUNIT_ASSERT_THROW_MESSAGE(ERROR_MSG("Expected LLVMSyntaxError", code), \ - openvdb::ax::ast::parse(code.c_str()), openvdb::LLVMSyntaxError); \ + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse(code.c_str(), logger);\ + CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("Expected parsing error", code), logger.hasError()); \ } \ } \ namespace unittest_util { - // Use shared pointers rather than unique pointers so initializer lists can easily // be used. Could easily introduce some move semantics to work around this if // necessary. @@ -219,7 +226,6 @@ nameSequence(const std::string& base, const size_t number) return names; } - } #endif // OPENVDB_AX_UNITTEST_UTIL_HAS_BEEN_INCLUDED diff --git a/openvdb_ax/openvdb_ax/version.h b/openvdb_ax/openvdb_ax/version.h deleted file mode 100644 index c2ccc85f3c..0000000000 --- a/openvdb_ax/openvdb_ax/version.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright Contributors to the OpenVDB Project -// SPDX-License-Identifier: MPL-2.0 - -/// @file openvdb_ax/version.h -/// -/// @brief Library and file format version numbers -/// -/// @details Based off of the versioning system within openvdb. There is -/// currently no AX namespace versioning which instead leverages openvdb's -/// version namespace. -/// -/// The library minor version number gets incremented whenever a change is made -/// to any aspect of the public API (not just the grid API) that necessitates -/// changes to client code. Changes to APIs in private or internal namespaces -/// do not trigger a minor version number increment; such APIs should not be -/// used in client code. -/// -/// A patch version number increment indicates a change, usually a new feature -/// or a bug fix, that does not necessitate changes to client code but rather -/// only recompilation of that code (because the library namespace -/// incorporates the version number). -/// - -#ifndef OPENVDB_AX_VERSION_HAS_BEEN_INCLUDED -#define OPENVDB_AX_VERSION_HAS_BEEN_INCLUDED - -#include - -// Library major, minor and patch version numbers -#define OPENVDB_AX_LIBRARY_MAJOR_VERSION_NUMBER 0 -#define OPENVDB_AX_LIBRARY_MINOR_VERSION_NUMBER 3 -#define OPENVDB_AX_LIBRARY_PATCH_VERSION_NUMBER 0 - -#define OPENVDB_AX_VERSION_NAME \ - OPENVDB_PREPROC_CONCAT(v, \ - OPENVDB_PREPROC_CONCAT(OPENVDB_AX_LIBRARY_MAJOR_VERSION_NUMBER, \ - OPENVDB_PREPROC_CONCAT(_, OPENVDB_AX_LIBRARY_MINOR_VERSION_NUMBER))) - -/// @brief Library version number string of the form ".." -/// @details This is a macro rather than a static constant because we typically -/// want the compile-time version number, not the runtime version number -/// (although the two are usually the same). -/// @hideinitializer -#define OPENVDB_AX_LIBRARY_VERSION_STRING \ - OPENVDB_PREPROC_STRINGIFY(OPENVDB_AX_LIBRARY_MAJOR_VERSION_NUMBER) "." \ - OPENVDB_PREPROC_STRINGIFY(OPENVDB_AX_LIBRARY_MINOR_VERSION_NUMBER) "." \ - OPENVDB_PREPROC_STRINGIFY(OPENVDB_AX_LIBRARY_PATCH_VERSION_NUMBER) - -/// Library version number as a packed integer ("%02x%02x%04x", major, minor, patch) -#define OPENVDB_AX_LIBRARY_VERSION_NUMBER \ - ((OPENVDB_AX_LIBRARY_MAJOR_VERSION_NUMBER << 24) | \ - ((OPENVDB_AX_LIBRARY_MINOR_VERSION_NUMBER & 0xFF) << 16) | \ - (OPENVDB_AX_LIBRARY_PATCH_VERSION_NUMBER & 0xFFFF)) - -namespace openvdb { -OPENVDB_USE_VERSION_NAMESPACE -namespace OPENVDB_VERSION_NAME { -namespace ax { - -// Library major, minor and patch version numbers -const uint32_t - OPENVDB_AX_LIBRARY_MAJOR_VERSION = OPENVDB_AX_LIBRARY_MAJOR_VERSION_NUMBER, - OPENVDB_AX_LIBRARY_MINOR_VERSION = OPENVDB_AX_LIBRARY_MINOR_VERSION_NUMBER, - OPENVDB_AX_LIBRARY_PATCH_VERSION = OPENVDB_AX_LIBRARY_PATCH_VERSION_NUMBER; -/// Library version number as a packed integer ("%02x%02x%04x", major, minor, patch) -const uint32_t OPENVDB_AX_LIBRARY_VERSION = OPENVDB_AX_LIBRARY_VERSION_NUMBER; - -/// Return a library version number string of the form "..". -inline constexpr const char* getLibraryVersionString() { return OPENVDB_AX_LIBRARY_VERSION_STRING; } - -} // namespace ax -} // namspace OPENVDB_VERSION_NAME -} // namespace openvdb - -#endif // OPENVDB_AX_VERSION_HAS_BEEN_INCLUDED - diff --git a/openvdb_houdini/openvdb_houdini/SOP_OpenVDB_Convert.cc b/openvdb_houdini/openvdb_houdini/SOP_OpenVDB_Convert.cc index 73e534c4cc..5a1264bed7 100644 --- a/openvdb_houdini/openvdb_houdini/SOP_OpenVDB_Convert.cc +++ b/openvdb_houdini/openvdb_houdini/SOP_OpenVDB_Convert.cc @@ -1017,9 +1017,13 @@ SOP_OpenVDB_Convert::Cache::convertVDBType( const UT_String& outPrecStr, hvdb::Interrupter& boss) { + GA_RWHandleS name_h(gdp, GA_ATTRIB_PRIMITIVE, "name"); for (hvdb::VdbPrimIterator it(&dst, group); it; ++it) { if (boss.wasInterrupted()) return; + if (name_h.isValid()) + it->getGrid().setName(static_cast (name_h.get(it->getMapOffset()))); + const UT_VDBType inType = it->getStorageType(); const UT_String inTypeName = getVDBTypeName(inType); const int inBits = getVDBPrecision(inType); diff --git a/tsc/meetings/2020-09-22.md b/tsc/meetings/2020-09-22.md new file mode 100644 index 0000000000..16f98f5d1a --- /dev/null +++ b/tsc/meetings/2020-09-22.md @@ -0,0 +1,89 @@ +Minutes from 64th OpenVDB TSC meeting, Sep 22nd, 2020, (EDT) + +Attendees: *Nick* A., *Jeff* L., *Ken* M., *Dan* B. + +Additional Attendees: Johannes Meng (Intel), JT Nelson (Blender), +Andre Pradhana (DW), Bruce Cherniak (Intel) + +Regrets: *Peter* C. + +Agenda: + +1) Confirm quorum +2) Secretary +3) Forum +4) NanoVDB +5) PRs Outstanding +6) Checksums +7) Next Meeting + + +1) Quorum was confirmed. + +2) Secretary was Jeff Lait + +3) Forum + +Ken still to reply to the out of core question. The answer to the question is straightforward, but what would we like in the future? + +4) NanoVDB + +NanoVDB also has interest in delayed loading. + +Jeff does not want NanoVDB to be more complicated than it has to be. + +There are some additional changes still coming to support DirectX. + +An improved C-port also exists. + +5) PRs Outstanding + +What is the process for external PR. Does the second Reviewer merge or the first? Or someone else does? We will continue to work ad-hoc. + +Feature branches, however, must be merged by the owner of the feature branch. + +Likewise, if a PR is for a particular person's domain, that person will be left with the merge. + +a) PR 829, sync names. To be merged + +b) PR 823, make delayed loading/iostreams optional. + +This disables delayed loading. This does change the File::Impl, but this should be name-spaced and internal, so not cause actual ABI changes. + +Should we have ENABLE or DISABLE? First we should have config flags in the source file only where possible. Flag names depending on cmake defaults is scary. Our Find Module is not yet powerful enough yet. So we want to keep Find Module as simple as possible. + +If we tried to move the delayed loading to the C file we'd lose the ability to remove the mutex. + +Configuration of the build system is USE_FOO. These Cmake variables then drive -D variables. These vary currently. Should half and delayed loading be off by default? Apparently our models on the web are all half... + +Ideally the default is no dependency. + +The define will be DISABLE for delayed loading. + +C) PR 819. Remove half. + +Should we just bring in a Half.C/Half.h rather than removing Half.h? + +In future version of OpenEXR it will be its own header we can include. + +What should we do with missing dependencies? A runtime check is required because we don't know the source of the grid, it can come from another library that has support. We will go for runtime errors, not compile time or linker error. + +D) PR 818, all of AX + +This has AX based CI. It has to install multiple versons of LLVM, which eventually evicts the Houdini caches. But as LLVM isn't EULA based, we should be able to pre-populate the containers with them. + +We should read over this prior to the next TSC, especially any questions inside. + +6) Checksums + +Ken is researching CRC-variants, some are parallel, so fast. Three modes have been added. No checksum. Checksum for the metadata only. Full checksum. Separate CRC are for grid, tree, root, and else. + +One use is debugging, quickly verifying things are identical. + +Validation tool for NanoVDB to verify no pointers go out of scope. The security member approves. + +7) Next Meeting + +Focus on AX. + +Tuesday September 29th 2020, 1-2pm EDT (GMT-4) diff --git a/tsc/meetings/2020-09-29.md b/tsc/meetings/2020-09-29.md new file mode 100644 index 0000000000..ae337b1728 --- /dev/null +++ b/tsc/meetings/2020-09-29.md @@ -0,0 +1,179 @@ +Minutes from 64th OpenVDB TSC meeting, Sep 29th, 2020, (EDT) + +Attendees: *Nick* A., *Jeff* L., *Ken* M., *Dan* B. + +Additional Attendees: Johannes Meng (Intel), JT Nelson (Blender), +Andre Pradhana (DW), Robin Rowe (Cinepaint) + +Regrets: *Peter* C. + +Agenda: + +1) Confirm quorum +2) Secretary +3) Forum +4) PR 818 AX Review +5) Simd +6) Next Meeting + +1) Quorum was confirmed. + +2) Secretary was Nick Avramoussis + +3) Forum + +Still need to reply to some forum posts, Dan to take a look. A few issues w.r.t +VDB 7.x dependent software not building as users are not realizing C++14 is now +required. Dan to put in a compile time check in Platform.h to explicitly fail +if C++14 is not enabled. + +4) PR 818 AX Review + +Most of this meeting was spent reviewing the comments made on PR 818. Replies +are copied here for convenience. + +AX CI +Currently the AX CI builds LLVM from source and caches it on the available +github actions cache. Dan has initiated conversations with the LF to see if they +can provide docker containers for different LLVM versions. + +Precision of integers and strict type binding +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496308313 + +AX currently support short, int and long signed integer values which are aliased +to 16, 32 and 64 bit respectively. This is primarily required due to the way AX +strictly binds to grids/attributes of the same type (i.e short@myattrib). Agreed +that, realistically, 32 or 64 bits should be exposed. There are some performance +considerations making everything 64 bit on some architectures. Best solution was +to first allow @ to bind to the same type category, remove short, rename int and +long to int32 and int64 and alias int to either int32 or int64 at compile time. +Same comments were made w.r.t float and double, however GPU support will +definitely need float and the lack of float for CPU code will make it impossible +to produce deterministic results. + +$ syntax in Houdini +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496311518 + +The AX Houdini SOP supports some vex functions like ch/chramp etc. It evaluates +the raw string to avoid $ expanding at the cost of no backtick support (though +this can be added). Ultimately the presence of ch means that this isn't an issue. + +Attrib dependencies +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496312075 + +No evaluation is performed on code values, so dead/unreachable code paths still +contribute to attribute/grid creation and dependencies. + +C vs python style modulo +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496312423 + +Currently uses LLVM's default IR builder modulo instruction which will be C +style remainder op. Agreed to instead switch to python style "true" modulo op. + +Line directives, pre-processing and runtime errors +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496315058 + +Currently no # directive support. Agreed adding it would be good. Catching +exceptions is not an ideal way to control top level behavior. Agreed that +custom logging would be better. This actually already exists but needs to be +upstreamed. This was going to be done as a subsequent PR but may as well be +bundled into this now. + +lerp() +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496317012 +fit() +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496318424 + +Agreed to investigate better formulation of lerp() and fit() + +signbit() +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496319359 + +Agreed to change to sign() + +Function signature type support +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496317548 + +Depending on the int precision work, this work will be simplified. Signatures +should really exist for all types unless it really doesn't make sense for some +specifically. Agreed to add a maxComponent style method for vector elements. + +Determinant checking of polardecomp +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496319863 + +Need to check the behavior of openvdbs polardecomposition function if the +calculated det is negative and document this. + +AX namespace, API/ABI +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496322339 +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496449902 + +Agree on the two options stated - either AX should use its own versioned namespace +or be locked to VDBs. This includes lib versioning too. Preference is to lock to +VDBs namespace. Will peruse this and see if any issues arise. + +VDB changes +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496433146 + +Although minor, agreed to split out VDB specific changes to a different PR. + +Deprecated methods and odd implementations +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496434030 +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496441704 + +For backwards compatibility and internal reasons, some design decisions were +made which now look odd in the open source version of AX. These still need to +exist in the standalone repo but can be removed and re-worked for the merge into +VDB. Agreed to action these. + +Executable terminology +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496439529 + +Agreeded to rename to Binary and see what it looks like. + +Executable member interface and usage +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496444627 +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496443272 + +In the current design, the executables store all settings on a sub struct unique +ptr. This produces a clean API with separation of the trivial execute calls +from the member settings. However it means that they must be copied to make +modifications if the exe is const (though this copying is cheap) and requires +explicit copy constructors/assignment operators. Another option would be to have +users provide these settings on each call to execute. This better ties in some +settings to the grid data being executed, produces a trivially copyable object +and means the executable can be const. Nick, will play around with these ideas. + +Combination of Compiler to Executable +https://github.com/AcademySoftwareFoundation/openvdb/pull/818#discussion_r496446789 + +The templated compile methods exist for internal reasons and can be removed. It +may be better to introduce static creators on the executables which take a compiler +vs the friend implementation. No clear advantage currently, but agreeded that +the workflow between the code generators, compiler and exes could use some +attention. + +ax::run() with multiple grids/attribute bindings + +Agreed that this particular signature could use some more explicit behavior. +Agreed to error if points and volumes are provided. Custom way to bind attributes +to grids should exist that don't rely on the grid name (i.e. what if the grid is +const). + +Below are the main action points which block the initial merge: + - Integer precision changes + - Modulo implementation + - Deprecated code removal + - lerp(), fit(), signbit() changes + - AX Versioning changes + +5) Simd + +Ken, working on some more intrinsic functionality for VDB. Nick, has code he'd +like to upstream which uses SIMD wrappers. TSC is open to including external +SIMD intrinsic wrapper software in VDB. + +6) Next Meeting + +Skipping next week. +Next meeting is October 13th, 2020. 1pm-2pm EDT (GMT-4). diff --git a/tsc/meetings/2020-10-13.md b/tsc/meetings/2020-10-13.md new file mode 100644 index 0000000000..51f5b1059a --- /dev/null +++ b/tsc/meetings/2020-10-13.md @@ -0,0 +1,78 @@ +Minutes from 65th OpenVDB TSC meeting, Oct 13th, 2020, (EDT) + +Attendees: *Nick* A., *Jeff* L., *Ken* M., *Dan* B. + +Additional Attendees: Johannes Meng (Intel), JT Nelson (Blender), +Andre Pradhana (DW), Bruce Cherniak (Intel) + +Regrets: *Peter* C. + +Agenda: + +1) Confirm quorum +2) Secretary +3) Forum +4) AX Follow-up +5) Filter Tile Support +6) Paged Array Segfault +7) USD Support +8) Next Meeting + +1) Quorum was confirmed. + +2) Secretary was Dan Bailey + +3) Forum + +One question on the forum about using Visual Studio and CMake. No-one in the TSC +has much experience in this area. Still might be useful to post that we don't +know the answer and encourage others in the community to comment. D&I group are +looking at how to improve onboarding. Many people who use OpenVDB through +Windows use vcpkg rather than compiling from source. + +4) AX Follow-up + +Nick to review all feedback. Will take a while to get through everything, +priority is the core library at the moment. SOP feedback will follow. + +Int conversion is proving tricky. No JIT compilation based on the type in VEX. +In AX, there is a separate accessor per-type, so hard to resolve integer issues. +Most likely route forward is to introduce explicit integer types for bindings +and then in future turn these into an alias to make them all int. That would +avoid backwards compatibility issues. + +Looking for alternatives to running over inactive values, however Nick relies on +this mechanism heavily. Jeff prefers the ability to provide an optional stencil +mask as an optimization. + +Don't use VEX language editor, not only will it highlight syntax incorrectly, +but it will reference the VEX documentation. Currently no hooks to introduce a +custom language editor in Houdini. Easiest route is likely to incorporate this +into VDB then let SideFX introduce a new language editor when natively +integrating AX into Houdini. + +5) Filter Tile Support + +Nick has submitted a PR to add tile support to the filter methods. It eliminates +tile artefacts in smoothing. + +6) Paged Array Segfault + +Nick has reported a number of sporadic segfaults related to the PagedArray data +structure. Last instance of a segfault in PagedArray was fixed by switching from +vector to deque. May be related to a standard library implementation offering a +deque implementation that is not thread-safe. However, there does not appear to +be any pattern in compiler, standard library or platform. + +Nick to provide Ken with a bit more data to help try and track down the root +problem. + +7) USD Support + +All agreed that it could use some work as the current VDB integration is fairly +basic. + +8) Next Meeting + +Next meeting is October 20th, 2020. 1pm-2pm EDT (GMT-4). Dan to present PR785 +for discussion and review.