diff --git a/.github/workflows/build_latest.yml b/.github/workflows/build_latest.yml index d3351f31f..b586f4bf1 100644 --- a/.github/workflows/build_latest.yml +++ b/.github/workflows/build_latest.yml @@ -20,7 +20,7 @@ jobs: fail-fast: false matrix: os: ['ubuntu', 'windows'] - qt-version: [ '5.12.*', '5.15.*', '6.7.*' ] + qt-version: [ '5.12.*', '5.15.*', '6.7.*', '6.8.*' ] python-version: [ '3.12' ] runs-on: ${{ matrix.os }}-latest steps: @@ -34,10 +34,9 @@ jobs: - name: Install Qt ${{matrix.qt-version}} uses: jurplel/install-qt-action@v4 with: - # 6.7.* currently does not work with ubuntu with this action - version: ${{ matrix.os == 'ubuntu' && matrix.qt-version == '6.7.*' && '6.6.*' || matrix.qt-version }} + version: ${{ matrix.qt-version }} modules: ${{startsWith(matrix.qt-version, '6') && 'qt5compat qtscxml qtpositioning qtwebchannel qtmultimedia qtwebengine' || '' }} - arch: ${{ matrix.os == 'ubuntu' && 'gcc_64' || (startsWith(matrix.qt-version, '5.12') && 'win64_msvc2017_64' || 'win64_msvc2019_64') }} + arch: ${{ (matrix.os == 'ubuntu' && (startsWith(matrix.qt-version, '5') && 'gcc_64' || 'linux_gcc_64')) || startsWith(matrix.qt-version, '5.12') && 'win64_msvc2017_64' || startsWith(matrix.qt-version, '6.8') && 'win64_msvc2022_64' || 'win64_msvc2019_64' }} - name: Setup Python ${{ matrix.python-version }} uses: actions/setup-python@v5 diff --git a/README.md b/README.md index 1ee9a2b4c..39f4e3449 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,11 @@ See https://code.qt.io/cgit/qt-labs/qtscriptgenerator.git for the original proje The PythonQt wrappers generated by the generator are distributed under the `LGPL` as well. +The integrated preprocessor has been replaced with code from the simplecpp project, +see https://github.com/danmar/simplecpp. It is licensed under the `0BSDL` license, +see the `LICENSE` file in the generator/simplecpp directory. +Copyright (C) 2016-2023 simplecpp team + The generated wrappers are pre-generated and checked-in, so you only need to build and run the generator when you want to build additional wrappers or you want to upgrade/downgrade to another Qt version, but this requires diff --git a/extensions/PythonQt_QtAll/PythonQt_QtAll.pro b/extensions/PythonQt_QtAll/PythonQt_QtAll.pro index 8881d00c1..39293951d 100644 --- a/extensions/PythonQt_QtAll/PythonQt_QtAll.pro +++ b/extensions/PythonQt_QtAll/PythonQt_QtAll.pro @@ -69,6 +69,8 @@ unix { unix: target.path = $${INSTALL_PREFIX}/lib win32: target.path = / +msvc:QMAKE_CXXFLAGS += /bigobj + headers.files = $${HEADERS} headers.path = $${INSTALL_PREFIX}/include diff --git a/generator/abstractmetabuilder.cpp b/generator/abstractmetabuilder.cpp index d73b0792b..d8df13e42 100644 --- a/generator/abstractmetabuilder.cpp +++ b/generator/abstractmetabuilder.cpp @@ -1169,7 +1169,7 @@ void AbstractMetaBuilder::traverseFunctions(ScopeModelItem scope_item, AbstractM ReportHandler::warning(warn); } - meta_class->addFunction(meta_function); + meta_class->addFunction(meta_function, /*check_duplicates=*/true); } else if (meta_function->isDestructor()) { meta_class->setDestructorException(meta_function->exception()); if (!meta_function->isPublic()) { diff --git a/generator/generator.pri b/generator/generator.pri index 9bcfaac44..5c272cce9 100644 --- a/generator/generator.pri +++ b/generator/generator.pri @@ -16,7 +16,7 @@ RESOURCES += generator.qrc include($$GENERATORPATH/parser/rxx.pri) -include($$GENERATORPATH/parser/rpp/rpp.pri) +include($$GENERATORPATH/simplecpp/simplecpp.pri) CONFIG += strict_c++ win32-msvc*{ diff --git a/generator/generator.qrc b/generator/generator.qrc index 0c591c714..e7203d779 100644 --- a/generator/generator.qrc +++ b/generator/generator.qrc @@ -15,6 +15,5 @@ typesystem_multimedia.xml typesystem_qml.xml typesystem_quick.xml -parser/rpp/pp-qt-configuration diff --git a/generator/main.cpp b/generator/main.cpp index a6494363b..621542f52 100644 --- a/generator/main.cpp +++ b/generator/main.cpp @@ -48,7 +48,7 @@ #include "generatorset.h" #include "fileout.h" #include "control.h" -#include "pp.h" +#include "simplecpp.h" #include #include @@ -56,6 +56,8 @@ #include #include +#include + void displayHelp(GeneratorSet *generatorSet); namespace @@ -125,53 +127,86 @@ namespace } bool - preprocess(const QString &sourceFile, const QString &targetFile, const QString &commandLineIncludes = QString()) + preprocess(const QString& sourceFile, const QString& targetFile, const QString& commandLineIncludes = QString()) { - rpp::pp_environment env; - rpp::pp preprocess(env); - - rpp::pp_null_output_iterator null_out; + simplecpp::DUI dui; // settings - const char *ppconfig = ":/trolltech/generator/parser/rpp/pp-qt-configuration"; + for(QString include : getIncludeDirectories(commandLineIncludes)) { + dui.includePaths.push_back(QDir::toNativeSeparators(include).toStdString()); + } + dui.defines.push_back("__cplusplus=1"); + dui.defines.push_back("__STDC__"); + dui.std = "c++20"; + dui.removeComments = true; - QFile file(ppconfig); + QFile file(sourceFile); if (!file.open(QFile::ReadOnly)) { - fprintf(stderr, "Preprocessor configuration file not found '%s'\n", ppconfig); + std::cerr << "Main file not found:" << sourceFile.toUtf8().constData() << std::endl; return false; } QByteArray ba = file.readAll(); file.close(); - preprocess.operator()(ba.constData(), ba.constData() + ba.size(), null_out); - foreach(QString - include, getIncludeDirectories(commandLineIncludes)) { - preprocess.push_include_path(QDir::toNativeSeparators(include).toStdString()); + // Perform preprocessing (code originally taken from simplecpp/main.cpp and modified) + simplecpp::OutputList outputList; + std::vector files; + std::unique_ptr rawtokens(new simplecpp::TokenList(ba.constData(), ba.size(), files, {}, &outputList)); + rawtokens->removeComments(); + simplecpp::TokenList outputTokens(files); + std::map filedata; + simplecpp::preprocess(outputTokens, *rawtokens, files, filedata, dui, &outputList); + simplecpp::cleanup(filedata); + rawtokens.reset(); + + for (const simplecpp::Output& output : outputList) { + if (output.type == simplecpp::Output::MISSING_HEADER) { + // do not print these messages for now, we are not interested + continue; + } + std::cerr << output.location.file() << ':' << output.location.line << ": "; + switch (output.type) { + case simplecpp::Output::ERROR: + std::cerr << "#error: "; + break; + case simplecpp::Output::WARNING: + std::cerr << "#warning: "; + break; + case simplecpp::Output::MISSING_HEADER: + std::cerr << "missing header: "; + break; + case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: + std::cerr << "include nested too deeply: "; + break; + case simplecpp::Output::SYNTAX_ERROR: + std::cerr << "syntax error: "; + break; + case simplecpp::Output::PORTABILITY_BACKSLASH: + std::cerr << "portability: "; + break; + case simplecpp::Output::UNHANDLED_CHAR_ERROR: + std::cerr << "unhandled char error: "; + break; + case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND: + std::cerr << "explicit include not found: "; + break; + case simplecpp::Output::FILE_NOT_FOUND: + std::cerr << "file not found: "; + break; + case simplecpp::Output::DUI_ERROR: + std::cerr << "dui error: "; + break; + } + std::cerr << output.msg << std::endl; } - QString currentDir = QDir::current().absolutePath(); - QFileInfo sourceInfo(sourceFile); - QDir::setCurrent(sourceInfo.absolutePath()); - - std::string result; - result.reserve(20 * 1024); // 20K - - result += "# 1 \"builtins\"\n"; - result += "# 1 \""; - result += sourceFile.toStdString(); - result += "\"\n"; - - preprocess.file(sourceInfo.fileName().toStdString(), - rpp::pp_output_iterator(result)); - - QDir::setCurrent(currentDir); - QFile f(targetFile); if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) { fprintf(stderr, "Failed to write preprocessed file: %s\n", qPrintable(targetFile)); } + std::string result = outputTokens.stringify(); f.write(result.c_str(), result.length()); return true; diff --git a/generator/parser/lexer.cpp b/generator/parser/lexer.cpp index be6c4ccf1..2ea0bcf59 100644 --- a/generator/parser/lexer.cpp +++ b/generator/parser/lexer.cpp @@ -69,6 +69,9 @@ void LocationManager::extract_line(int offset, int *line, QString *filename) con const unsigned char *cursor = begin_buffer + offset; ++cursor; // skip '#' + if (cursor[0] == 'l' && cursor[1] == 'i' && cursor[2] == 'n' && cursor[3] == 'e') { + cursor += 4; + } if (std::isspace(*cursor) && std::isdigit(*(cursor + 1))) { ++cursor; diff --git a/generator/parser/rpp/builtin-macros.cpp b/generator/parser/rpp/builtin-macros.cpp deleted file mode 100644 index bae1ab77d..000000000 --- a/generator/parser/rpp/builtin-macros.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt Script Generator project on Qt Labs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - diff --git a/generator/parser/rpp/pp-cctype.h b/generator/parser/rpp/pp-cctype.h deleted file mode 100644 index 9bfef9097..000000000 --- a/generator/parser/rpp/pp-cctype.h +++ /dev/null @@ -1,65 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt Script Generator project on Qt Labs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PP_CCTYPE_H -#define PP_CCTYPE_H - -#include - -namespace rpp { - -inline bool pp_isalpha (int __ch) -{ return std::isalpha ((unsigned char) __ch) != 0; } - -inline bool pp_isalnum (int __ch) -{ return std::isalnum ((unsigned char) __ch) != 0; } - -inline bool pp_isdigit (int __ch) -{ return std::isdigit ((unsigned char) __ch) != 0; } - -inline bool pp_isspace (int __ch) -{ return std::isspace ((unsigned char) __ch) != 0; } - -} // namespace rpp - -#endif // PP_CCTYPE_H - -// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/generator/parser/rpp/pp-configuration b/generator/parser/rpp/pp-configuration deleted file mode 100644 index 15586dd88..000000000 --- a/generator/parser/rpp/pp-configuration +++ /dev/null @@ -1,86 +0,0 @@ -#define __DBL_MIN_EXP__ (-1021) -#define __FLT_MIN__ 1.17549435e-38F -#define __CHAR_BIT__ 8 -#define __WCHAR_MAX__ 2147483647 -#define __DBL_DENORM_MIN__ 4.9406564584124654e-324 -#define __FLT_EVAL_METHOD__ 2 -#define __DBL_MIN_10_EXP__ (-307) -#define __FINITE_MATH_ONLY__ 0 -#define __GNUC_PATCHLEVEL__ 2 -#define __SHRT_MAX__ 32767 -#define __LDBL_MAX__ 1.18973149535723176502e+4932L -#define __UINTMAX_TYPE__ long long unsigned int -#define __linux 1 -#define __unix 1 -#define __LDBL_MAX_EXP__ 16384 -#define __linux__ 1 -#define __SCHAR_MAX__ 127 -#define __USER_LABEL_PREFIX__ -#define __STDC_HOSTED__ 1 -#define __LDBL_HAS_INFINITY__ 1 -#define __DBL_DIG__ 15 -#define __FLT_EPSILON__ 1.19209290e-7F -#define __GXX_WEAK__ 1 -#define __LDBL_MIN__ 3.36210314311209350626e-4932L -#define __unix__ 1 -#define __DECIMAL_DIG__ 21 -#define __gnu_linux__ 1 -#define __LDBL_HAS_QUIET_NAN__ 1 -#define __GNUC__ 4 -#define __DBL_MAX__ 1.7976931348623157e+308 -#define __DBL_HAS_INFINITY__ 1 -#define __cplusplus 1 -#define __DEPRECATED 1 -#define __DBL_MAX_EXP__ 1024 -#define __GNUG__ 4 -#define __LONG_LONG_MAX__ 9223372036854775807LL -#define __GXX_ABI_VERSION 1002 -#define __FLT_MIN_EXP__ (-125) -#define __DBL_MIN__ 2.2250738585072014e-308 -#define __FLT_MIN_10_EXP__ (-37) -#define __DBL_HAS_QUIET_NAN__ 1 -#define __REGISTER_PREFIX__ -#define __NO_INLINE__ 1 -#define __i386 1 -#define __FLT_MANT_DIG__ 24 -#define __VERSION__ "4.0.2 20050808 (prerelease) (Ubuntu 4.0.1-4ubuntu9)" -#define i386 1 -#define __i486__ 1 -#define unix 1 -#define __i386__ 1 -#define __SIZE_TYPE__ unsigned int -#define __ELF__ 1 -#define __FLT_RADIX__ 2 -#define __LDBL_EPSILON__ 1.08420217248550443401e-19L -#define __FLT_HAS_QUIET_NAN__ 1 -#define __FLT_MAX_10_EXP__ 38 -#define __LONG_MAX__ 2147483647L -#define __FLT_HAS_INFINITY__ 1 -#define linux 1 -#define __EXCEPTIONS 1 -#define __LDBL_MANT_DIG__ 64 -#define __WCHAR_TYPE__ int -#define __FLT_DIG__ 6 -#define __INT_MAX__ 2147483647 -#define __i486 1 -#define __FLT_MAX_EXP__ 128 -#define __DBL_MANT_DIG__ 53 -#define __WINT_TYPE__ unsigned int -#define __LDBL_MIN_EXP__ (-16381) -#define __LDBL_MAX_10_EXP__ 4932 -#define __DBL_EPSILON__ 2.2204460492503131e-16 -#define __tune_i486__ 1 -#define __INTMAX_MAX__ 9223372036854775807LL -#define __FLT_DENORM_MIN__ 1.40129846e-45F -#define __FLT_MAX__ 3.40282347e+38F -#define __INTMAX_TYPE__ long long int -#define __GNUC_MINOR__ 0 -#define __DBL_MAX_10_EXP__ 308 -#define __LDBL_DENORM_MIN__ 3.64519953188247460253e-4951L -#define __PTRDIFF_TYPE__ int -#define __LDBL_MIN_10_EXP__ (-4931) -#define __LDBL_DIG__ 18 -#define _GNU_SOURCE 1 - - -#define __STDC__ diff --git a/generator/parser/rpp/pp-engine-bits.h b/generator/parser/rpp/pp-engine-bits.h deleted file mode 100644 index cbae8e072..000000000 --- a/generator/parser/rpp/pp-engine-bits.h +++ /dev/null @@ -1,1392 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt Script Generator project on Qt Labs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PP_ENGINE_BITS_H -#define PP_ENGINE_BITS_H - -#include - -namespace rpp { - -inline std::string pp::fix_file_path(std::string const &filename) const -{ -#if defined (PP_OS_WIN) - std::string s = filename; - for (std::string::iterator it = s.begin(); it != s.end(); ++it) - { - if (*it == '/') - *it = '\\'; - } - return s; -#else - return filename; -#endif -} - -inline bool pp::is_absolute(std::string const &filename) const -{ -#if defined(PP_OS_WIN) - return filename.length() >= 3 - && filename.at(1) == ':' - && (filename.at(2) == '\\' || filename.at(2) == '/'); -#else - return filename.length() >= 1 - && filename.at(0) == '/'; -#endif -} - -template -void pp::file (std::string const &filename, _OutputIterator _result) -{ - FILE *fp = fopen (filename.c_str(), "rb"); - if (fp != 0) - { - std::string was = env.current_file; - env.current_file = filename; - file (fp, _result); - env.current_file = was; - } - //else - //std::cerr << "** WARNING file ``" << filename << " not found!" << std::endl; -} - -template -void pp::file (FILE *fp, _OutputIterator _result) -{ - assert (fp != 0); - -#if defined (HAVE_MMAP) - struct stat st; - fstat(FILENO (fp), &st); - std::size_t size = st.st_size; - char *buffer = 0; - buffer = (char *) ::mmap(0, size, PROT_READ, MAP_SHARED, FILENO (fp), 0); - fclose (fp); - if (!buffer || buffer == (char*) -1) - return; - this->operator () (buffer, buffer + size, _result); - ::munmap(buffer, size); -#else - std::string buffer; - while (!feof(fp)) { - char tmp[1024]; - int read = (int) fread (tmp, sizeof(char), 1023, fp); - tmp[read] = '\0'; - buffer += tmp; - } - fclose (fp); - this->operator () (buffer.c_str(), buffer.c_str() + buffer.size(), _result); -#endif -} - -template -bool pp::find_header_protection (InputIterator _first, InputIterator _last, std::string *_prot) -{ - int was = env.current_line; - - while (_first != _last) - { - if (pp_isspace (*_first)) - { - if (*_first == '\n') - ++env.current_line; - - ++_first; - } - else if (_PP_internal::comment_p (_first, _last)) - { - _first = skip_comment_or_divop (_first, _last); - env.current_line += skip_comment_or_divop.lines; - } - else if (*_first == '#') - { - _first = skip_blanks (++_first, _last); - env.current_line += skip_blanks.lines; - - if (_first != _last && *_first == 'i') - { - InputIterator begin = _first; - _first = skip_identifier (begin, _last); - env.current_line += skip_identifier.lines; - - std::string directive (begin, _first); - - if (directive == "ifndef") - { - _first = skip_blanks (_first, _last); - env.current_line += skip_blanks.lines; - - begin = _first; - _first = skip_identifier (_first, _last); - env.current_line += skip_identifier.lines; - - if (begin != _first && _first != _last) - { - _prot->assign (begin, _first); - return true; - } - } - } - break; - } - else - break; - } - - env.current_line = was; - return false; -} - -inline pp::PP_DIRECTIVE_TYPE pp::find_directive (char const *_directive, std::size_t _size) const -{ - switch (_size) - { - case 2: - if (_directive[0] == 'i' - && _directive[1] == 'f') - return PP_IF; - break; - - case 4: - if (_directive[0] == 'e' && !strcmp (_directive, "elif")) - return PP_ELIF; - else if (_directive[0] == 'e' && !strcmp (_directive, "else")) - return PP_ELSE; - break; - - case 5: - if (_directive[0] == 'i' && !strcmp (_directive, "ifdef")) - return PP_IFDEF; - else if (_directive[0] == 'u' && !strcmp (_directive, "undef")) - return PP_UNDEF; - else if (_directive[0] == 'e') { - if (!strcmp (_directive, "endif")) - return PP_ENDIF; - else if (!strcmp (_directive, "error")) - return PP_ERROR; - } - break; - - case 6: - if (_directive[0] == 'i' && !strcmp (_directive, "ifndef")) - return PP_IFNDEF; - else if (_directive[0] == 'd' && !strcmp (_directive, "define")) - return PP_DEFINE; - else if (_directive[0] == 'p' && !strcmp (_directive, "pragma")) - return PP_PRAGMA; - break; - - case 7: - if (_directive[0] == 'i' && !strcmp (_directive, "include")) - return PP_INCLUDE; - else if (_directive[0] == 'w' && !strcmp(_directive, "warning")) - return PP_WARNING; - break; - - case 12: - if (_directive[0] == 'i' && !strcmp (_directive, "include_next")) - return PP_INCLUDE_NEXT; - break; - - default: - break; - } - if (strlen(_directive)) { - std::cerr << "** WARNING unknown directive '#" << _directive << "' at " << env.current_file << ":" << env.current_line << std::endl; - } - return PP_UNKNOWN_DIRECTIVE; -} - -inline bool pp::file_isdir (std::string const &_filename) const -{ - struct stat st; -#if defined(PP_OS_WIN) - if (stat(_filename.c_str (), &st) == 0) - return (st.st_mode & _S_IFDIR) == _S_IFDIR; - else - return false; -#else - if (lstat(_filename.c_str (), &st) == 0) - return (st.st_mode & S_IFDIR) == S_IFDIR; - else - return false; -#endif -} - -inline bool pp::file_exists (std::string const &_filename) const -{ - struct stat st; -#if defined(PP_OS_WIN) - return stat(_filename.c_str (), &st) == 0; -#else - return lstat (_filename.c_str (), &st) == 0; -#endif -} - -inline FILE *pp::find_include_file(std::string const &_input_filename, std::string *_filepath, - INCLUDE_POLICY _include_policy, bool _skip_current_path) const -{ - assert (_filepath != 0); - assert (! _input_filename.empty()); - - _filepath->assign (_input_filename); - - if (is_absolute (*_filepath)) - return fopen (_filepath->c_str(), "r"); - - if (! env.current_file.empty ()) - _PP_internal::extract_file_path (env.current_file, _filepath); - - if (_include_policy == INCLUDE_LOCAL && ! _skip_current_path) - { - std::string tmp (*_filepath); - tmp += _input_filename; - - if (file_exists (tmp) && !file_isdir(tmp)) - { - _filepath->append (_input_filename); - return fopen (_filepath->c_str (), "r"); - } - } - - std::vector::const_iterator it = include_paths.begin (); - - if (_skip_current_path) - { - it = std::find (include_paths.begin (), include_paths.end (), *_filepath); - - if (it != include_paths.end ()) - ++it; - - else - it = include_paths.begin (); - } - - for (; it != include_paths.end (); ++it) - { - if (_skip_current_path && it == include_paths.begin()) - continue; - - _filepath->assign (*it); - _filepath->append (_input_filename); - - if (file_exists (*_filepath) && !file_isdir(*_filepath)) - return fopen (_filepath->c_str(), "r"); - -#ifdef Q_OS_MAC - // try in Framework path on Mac, if there is a path in front - // ### what about escaped slashes? - size_t slashPos = _input_filename.find('/'); - if (slashPos != std::string::npos) { - _filepath->assign (*it); - _filepath->append (_input_filename.substr(0, slashPos)); - _filepath->append (".framework/Headers/"); - _filepath->append (_input_filename.substr(slashPos+1, std::string::npos)); - std::cerr << *_filepath << "\n"; - - if (file_exists (*_filepath) && !file_isdir(*_filepath)) - return fopen (_filepath->c_str(), "r"); - } -#endif // Q_OS_MAC - } - - return 0; -} - -template -InputIterator pp::handle_directive(char const *_directive, std::size_t _size, - InputIterator _first, InputIterator _last, OutputIterator _result) -{ - _first = skip_blanks (_first, _last); - - PP_DIRECTIVE_TYPE d = find_directive (_directive, _size); - switch (d) - { - case PP_DEFINE: - if (! skipping ()) - return handle_define (_first, _last); - break; - - case PP_INCLUDE: - case PP_INCLUDE_NEXT: - if (! skipping ()) - return handle_include (d == PP_INCLUDE_NEXT, _first, _last, _result); - break; - - case PP_UNDEF: - if (! skipping ()) - return handle_undef(_first, _last); - break; - - case PP_ELIF: - return handle_elif (_first, _last); - - case PP_ELSE: - return handle_else (_first, _last); - - case PP_ENDIF: - return handle_endif (_first, _last); - - case PP_IF: - return handle_if (_first, _last); - - case PP_IFDEF: - return handle_ifdef (false, _first, _last); - - case PP_IFNDEF: - return handle_ifdef (true, _first, _last); - - default: - break; - } - - return _first; -} - -template -InputIterator pp::handle_include (bool _skip_current_path, InputIterator _first, InputIterator _last, - OutputIterator _result) -{ - // uncomment to print included files - // std::cout << env.current_file << std::endl; - if (pp_isalpha (*_first) || *_first == '_') - { - pp_macro_expander expand_include (env); - std::string name; - name.reserve (255); - expand_include (_first, _last, std::back_inserter (name)); - std::string::iterator it = skip_blanks (name.begin (), name.end ()); - printf("%s", name.c_str()); - assert((it != name.end () && (*it == '<' || *it == '"'))); - handle_include (_skip_current_path, it, name.end (), _result); - return _first; - } - - assert (*_first == '<' || *_first == '"'); - int quote = (*_first == '"') ? '"' : '>'; - ++_first; - - InputIterator end_name = _first; - for (; end_name != _last; ++end_name) - { - assert (*end_name != '\n'); - - if (*end_name == quote) - break; - } - - std::string filename (_first, end_name); - -#ifdef PP_OS_WIN - std::replace(filename.begin(), filename.end(), '/', '\\'); -#endif - - std::string filepath; - FILE *fp = find_include_file (filename, &filepath, quote == '>' ? INCLUDE_GLOBAL : INCLUDE_LOCAL, _skip_current_path); - -#if defined (PP_HOOK_ON_FILE_INCLUDED) - PP_HOOK_ON_FILE_INCLUDED (env.current_file, fp ? filepath : filename, fp); -#endif - - if (fp != 0) - { - std::string old_file = env.current_file; - env.current_file = filepath; - int saved_lines = env.current_line; - - env.current_line = 1; - //output_line (env.current_file, 1, _result); - - file (fp, _result); - - // restore the file name and the line position - env.current_file = old_file; - env.current_line = saved_lines; - - // sync the buffer - _PP_internal::output_line (env.current_file, env.current_line, _result); - } -#ifndef RPP_JAMBI -// else -// std::cerr << "*** WARNING " << filename << ": No such file or directory" << std::endl; -#endif - - return _first; -} - -template -void pp::operator () (InputIterator _first, InputIterator _last, OutputIterator _result) -{ -#ifndef PP_NO_SMART_HEADER_PROTECTION - std::string prot; - prot.reserve (255); - pp_fast_string tmp (prot.c_str (), prot.size ()); - - if (find_header_protection (_first, _last, &prot) - && env.resolve (&tmp) != 0) - { - // std::cerr << "** DEBUG found header protection:" << prot << std::endl; - return; - } -#endif - - env.current_line = 1; - char buffer[512]; - - while (true) - { - _first = skip_blanks (_first, _last); - env.current_line += skip_blanks.lines; - - if (_first == _last) - break; - else if (*_first == '#') - { - assert (*_first == '#'); - _first = skip_blanks (++_first, _last); - env.current_line += skip_blanks.lines; - - InputIterator end_id = skip_identifier (_first, _last); - env.current_line += skip_identifier.lines; - std::size_t size = end_id - _first; - - assert (size < 512); - char *cp = buffer; - std::copy (_first, end_id, cp); - cp[size] = '\0'; - - end_id = skip_blanks (end_id, _last); - _first = skip (end_id, _last); - - int was = env.current_line; - (void) handle_directive (buffer, size, end_id, _first, _result); - - if (env.current_line != was) - { - env.current_line = was; - _PP_internal::output_line (env.current_file, env.current_line, _result); - } - } - else if (*_first == '\n') - { - // ### compress the line - *_result++ = *_first++; - ++env.current_line; - } - else if (skipping ()) - _first = skip (_first, _last); - else - { - _PP_internal::output_line (env.current_file, env.current_line, _result); - _first = expand (_first, _last, _result); - env.current_line += expand.lines; - - if (expand.generated_lines) - _PP_internal::output_line (env.current_file, env.current_line, _result); - } - } -} - -inline pp::pp (pp_environment &_env): - env (_env), expand (env), skip_comment(false) -{ - iflevel = 0; - _M_skipping[iflevel] = 0; - _M_true_test[iflevel] = 0; -} - -inline std::back_insert_iterator > pp::include_paths_inserter () -{ return std::back_inserter (include_paths); } - -inline std::vector::iterator pp::include_paths_begin () -{ return include_paths.begin (); } - -inline std::vector::iterator pp::include_paths_end () -{ return include_paths.end (); } - -inline std::vector::const_iterator pp::include_paths_begin () const -{ return include_paths.begin (); } - -inline std::vector::const_iterator pp::include_paths_end () const -{ return include_paths.end (); } - -inline void pp::push_include_path (std::string const &_path) -{ - if (_path.empty () || _path [_path.size () - 1] != PATH_SEPARATOR) - { - std::string tmp (_path); - tmp += PATH_SEPARATOR; - include_paths.push_back (tmp); - } - - else - include_paths.push_back (_path); -} - -template -InputIterator pp::handle_define (InputIterator _first, InputIterator _last) -{ - pp_macro macro; -#if defined (PP_WITH_MACRO_POSITION) - macro.file = pp_symbol::get (env.current_file); -#endif - std::string definition; - - _first = skip_blanks (_first, _last); - InputIterator end_macro_name = skip_identifier (_first, _last); - pp_fast_string const *macro_name = pp_symbol::get (_first, end_macro_name); - _first = end_macro_name; - - if (_first != _last && *_first == '(') - { - macro.is.function_like = true; - macro.formals.reserve (5); - - _first = skip_blanks (++_first, _last); // skip '(' - InputIterator arg_end = skip_identifier (_first, _last); - if (_first != arg_end) - macro.formals.push_back (pp_symbol::get (_first, arg_end)); - - _first = skip_blanks (arg_end, _last); - - if (*_first == '.') - { - macro.is.variadics = true; - while (*_first == '.') - ++_first; - } - - while (_first != _last && *_first == ',') - { - _first = skip_blanks (++_first, _last); - - arg_end = skip_identifier (_first, _last); - if (_first != arg_end) - macro.formals.push_back (pp_symbol::get (_first, arg_end)); - - _first = skip_blanks (arg_end, _last); - - if (*_first == '.') - { - macro.is.variadics = true; - while (*_first == '.') - ++_first; - } - } - - assert (*_first == ')'); - ++_first; - } - - _first = skip_blanks (_first, _last); - - while (_first != _last && *_first != '\n') - { - if (*_first == '/') { - const InputIterator _before = _first; - _first = skip_comment(_first, _last); - env.current_line += skip_comment.lines; - if (_first != _before) { - // do not indiscriminately add the character after comment - continue; - } - } - - if (*_first == '\\') - { - InputIterator begin = _first; - begin = skip_blanks (++begin, _last); - - if (begin != _last && *begin == '\n') - { - ++macro.lines; - _first = skip_blanks (++begin, _last); - definition += ' '; - continue; - } - } - - definition += *_first++; - } - - macro.definition = pp_symbol::get (definition); - env.bind (macro_name, macro); - - return _first; -} - -template -InputIterator pp::skip (InputIterator _first, InputIterator _last) -{ - pp_skip_string_literal skip_string_literal; - pp_skip_char_literal skip_char_literal; - - while (_first != _last && *_first != '\n') - { - if (*_first == '/') - { - _first = skip_comment_or_divop (_first, _last); - env.current_line += skip_comment_or_divop.lines; - } - else if (*_first == '"') - { - _first = skip_string_literal (_first, _last); - env.current_line += skip_string_literal.lines; - } - else if (*_first == '\'') - { - _first = skip_char_literal (_first, _last); - env.current_line += skip_char_literal.lines; - } - else if (*_first == '\\') - { - _first = skip_blanks (++_first, _last); - env.current_line += skip_blanks.lines; - - if (_first != _last && *_first == '\n') - { - ++_first; - ++env.current_line; - } - } - else - ++_first; - } - - return _first; -} - -inline bool pp::test_if_level() -{ - bool result = !_M_skipping[iflevel++]; - _M_skipping[iflevel] = _M_skipping[iflevel - 1]; - _M_true_test[iflevel] = false; - return result; -} - -inline int pp::skipping() const -{ return _M_skipping[iflevel]; } - -template -InputIterator pp::eval_primary(InputIterator _first, InputIterator _last, Value *result) -{ - bool expect_paren = false; - int token; - _first = next_token (_first, _last, &token); - - switch (token) - { - case TOKEN_NUMBER: - result->set_long (token_value); - break; - - case TOKEN_UNUMBER: - result->set_ulong (token_uvalue); - break; - - case TOKEN_DEFINED: - _first = next_token (_first, _last, &token); - - if (token == '(') - { - expect_paren = true; - _first = next_token (_first, _last, &token); - } - - if (token != TOKEN_IDENTIFIER) - { - std::cerr << "** WARNING expected ``identifier'' found:" << char(token) << " at " << env.current_file << ":" << env.current_line << std::endl; - result->set_long (0); - break; - } - - result->set_long (env.resolve (token_text->c_str (), token_text->size ()) != 0); - - next_token (_first, _last, &token); // skip '(' - - if (expect_paren) - { - InputIterator next = next_token (_first, _last, &token); - if (token != ')') - std::cerr << "** WARNING expected ``)'' at " << env.current_file << ":" << env.current_line << std::endl; - else - _first = next; - } - break; - - case TOKEN_IDENTIFIER: - result->set_long (0); - break; - - case '-': - _first = eval_primary (_first, _last, result); - result->set_long (- result->l); - return _first; - - case '+': - _first = eval_primary (_first, _last, result); - return _first; - - case '!': - _first = eval_primary (_first, _last, result); - result->set_long (result->is_zero ()); - return _first; - - case '(': - _first = eval_constant_expression(_first, _last, result); - next_token (_first, _last, &token); - - if (token != ')') - std::cerr << "** WARNING expected ``)'' = " << token << " at " << env.current_file << ":" << env.current_line << std::endl; - else - _first = next_token(_first, _last, &token); - break; - - default: - result->set_long (0); - } - - return _first; -} - -template -InputIterator pp::eval_multiplicative(InputIterator _first, InputIterator _last, Value *result) -{ - _first = eval_primary(_first, _last, result); - - int token; - InputIterator next = next_token (_first, _last, &token); - - while (token == '*' || token == '/' || token == '%') - { - Value value; - _first = eval_primary(next, _last, &value); - - if (token == '*') - result->op_mult (value); - else if (token == '/') - { - if (value.is_zero ()) - { - std::cerr << "** WARNING division by zero at " << env.current_file << ":" << env.current_line << std::endl; - result->set_long (0); - } - else - result->op_div (value); - } - else - { - if (value.is_zero ()) - { - std::cerr << "** WARNING division by zero at " << env.current_file << ":" << env.current_line << std::endl; - result->set_long (0); - } - else - result->op_mod (value); - } - next = next_token (_first, _last, &token); - } - - return _first; -} - -template -InputIterator pp::eval_additive(InputIterator _first, InputIterator _last, Value *result) -{ - _first = eval_multiplicative(_first, _last, result); - - int token; - InputIterator next = next_token (_first, _last, &token); - - while (token == '+' || token == '-') - { - Value value; - _first = eval_multiplicative(next, _last, &value); - - if (token == '+') - result->op_add (value); - else - result->op_sub (value); - next = next_token (_first, _last, &token); - } - - return _first; -} - -template -InputIterator pp::eval_shift(InputIterator _first, InputIterator _last, Value *result) -{ - _first = eval_additive(_first, _last, result); - - int token; - InputIterator next = next_token (_first, _last, &token); - - while (token == TOKEN_LT_LT || token == TOKEN_GT_GT) - { - Value value; - _first = eval_additive (next, _last, &value); - - if (token == TOKEN_LT_LT) - result->op_lhs (value); - else - result->op_rhs (value); - next = next_token (_first, _last, &token); - } - - return _first; -} - -template -InputIterator pp::eval_relational(InputIterator _first, InputIterator _last, Value *result) -{ - _first = eval_shift(_first, _last, result); - - int token; - InputIterator next = next_token (_first, _last, &token); - - while (token == '<' - || token == '>' - || token == TOKEN_LT_EQ - || token == TOKEN_GT_EQ) - { - Value value; - _first = eval_shift(next, _last, &value); - - switch (token) - { - default: - assert (0); - break; - - case '<': - result->op_lt (value); - break; - - case '>': - result->op_gt (value); - break; - - case TOKEN_LT_EQ: - result->op_le (value); - break; - - case TOKEN_GT_EQ: - result->op_ge (value); - break; - } - next = next_token (_first, _last, &token); - } - - return _first; -} - -template -InputIterator pp::eval_equality(InputIterator _first, InputIterator _last, Value *result) -{ - _first = eval_relational(_first, _last, result); - - int token; - InputIterator next = next_token (_first, _last, &token); - - while (token == TOKEN_EQ_EQ || token == TOKEN_NOT_EQ) - { - Value value; - _first = eval_relational(next, _last, &value); - - if (token == TOKEN_EQ_EQ) - result->op_eq (value); - else - result->op_ne (value); - next = next_token (_first, _last, &token); - } - - return _first; -} - -template -InputIterator pp::eval_and(InputIterator _first, InputIterator _last, Value *result) -{ - _first = eval_equality(_first, _last, result); - - int token; - InputIterator next = next_token (_first, _last, &token); - - while (token == '&') - { - Value value; - _first = eval_equality(next, _last, &value); - result->op_bit_and (value); - next = next_token (_first, _last, &token); - } - - return _first; -} - -template -InputIterator pp::eval_xor(InputIterator _first, InputIterator _last, Value *result) -{ - _first = eval_and(_first, _last, result); - - int token; - InputIterator next = next_token (_first, _last, &token); - - while (token == '^') - { - Value value; - _first = eval_and(next, _last, &value); - result->op_bit_xor (value); - next = next_token (_first, _last, &token); - } - - return _first; -} - -template -InputIterator pp::eval_or(InputIterator _first, InputIterator _last, Value *result) -{ - _first = eval_xor(_first, _last, result); - - int token; - InputIterator next = next_token (_first, _last, &token); - - while (token == '|') - { - Value value; - _first = eval_xor(next, _last, &value); - result->op_bit_or (value); - next = next_token (_first, _last, &token); - } - - return _first; -} - -template -InputIterator pp::eval_logical_and(InputIterator _first, InputIterator _last, Value *result) -{ - _first = eval_or(_first, _last, result); - - int token; - InputIterator next = next_token (_first, _last, &token); - - while (token == TOKEN_AND_AND) - { - Value value; - _first = eval_or(next, _last, &value); - result->op_and (value); - next = next_token (_first, _last, &token); - } - - return _first; -} - -template -InputIterator pp::eval_logical_or(InputIterator _first, InputIterator _last, Value *result) -{ - _first = eval_logical_and (_first, _last, result); - - int token; - InputIterator next = next_token (_first, _last, &token); - - while (token == TOKEN_OR_OR) - { - Value value; - _first = eval_logical_and(next, _last, &value); - result->op_or (value); - next = next_token (_first, _last, &token); - } - - return _first; -} - -template -InputIterator pp::eval_constant_expression(InputIterator _first, InputIterator _last, Value *result) -{ - _first = eval_logical_or(_first, _last, result); - - int token; - InputIterator next = next_token (_first, _last, &token); - - if (token == '?') - { - Value left_value; - _first = eval_constant_expression(next, _last, &left_value); - _first = skip_blanks (_first, _last); - - _first = next_token(_first, _last, &token); - if (token == ':') - { - Value right_value; - _first = eval_constant_expression(_first, _last, &right_value); - - *result = !result->is_zero () ? left_value : right_value; - } - else - { - std::cerr << "** WARNING expected ``:'' = " << int (token) << " at " << env.current_file << ":" << env.current_line << std::endl; - *result = left_value; - } - } - - return _first; -} - -template -InputIterator pp::eval_expression (InputIterator _first, InputIterator _last, Value *result) -{ - return _first = eval_constant_expression (skip_blanks (_first, _last), _last, result); -} - -template -std::string pp::expand_condition(InputIterator _first, InputIterator _last) -{ - pp_macro_expander expand_condition(env); - std::string condition; - condition.reserve(255); - expand_condition(skip_blanks(_first, _last), _last, std::back_inserter(condition)); - std::string condition2ndpass; - condition2ndpass.reserve(255); - const char* first = condition.c_str(); - const char* last = first + condition.size(); - expand_condition(skip_blanks(first, last), last, std::back_inserter(condition2ndpass)); - return condition2ndpass; -} - -template -InputIterator pp::handle_if (InputIterator _first, InputIterator _last) -{ - if (test_if_level()) - { - std::string condition = expand_condition(_first, _last); - - Value result; - result.set_long (0); - eval_expression(condition.c_str (), condition.c_str () + condition.size (), &result); - - _M_true_test[iflevel] = !result.is_zero (); - _M_skipping[iflevel] = result.is_zero (); - } - - return _first; -} - -template -InputIterator pp::handle_else (InputIterator __first, InputIterator /*__last*/) -{ - if (iflevel == 0 && !skipping ()) - { - std::cerr << "** WARNING #else without #if" << " at " << env.current_file << ":" << env.current_line << std::endl; - } - else if (iflevel > 0 && _M_skipping[iflevel - 1]) - { - _M_skipping[iflevel] = true; - } - else - { - _M_skipping[iflevel] = _M_true_test[iflevel]; - } - - return __first; -} - -template -InputIterator pp::handle_elif (InputIterator _first, InputIterator _last) -{ - assert(iflevel > 0); - - if (iflevel == 0 && !skipping()) - { - std::cerr << "** WARNING #else without #if" << " at " << env.current_file << ":" << env.current_line << std::endl; - } - else if (!_M_true_test[iflevel] && !_M_skipping[iflevel - 1]) - { - std::string condition = expand_condition(_first, _last); - - Value result; - result.set_long(0); - eval_expression(condition.c_str(), condition.c_str() + condition.size(), &result); - _M_true_test[iflevel] = !result.is_zero (); - _M_skipping[iflevel] = result.is_zero (); - } - else - { - _M_skipping[iflevel] = true; - } - - return _first; -} - -template -InputIterator pp::handle_endif (InputIterator _first, InputIterator /*__last*/) -{ - if (iflevel == 0 && !skipping()) - { - std::cerr << "** WARNING #endif without #if" << " at " << env.current_file << ":" << env.current_line << std::endl; - } - else - { - _M_skipping[iflevel] = 0; - _M_true_test[iflevel] = 0; - - --iflevel; - } - - return _first; -} - -template -InputIterator pp::handle_ifdef (bool check_undefined, InputIterator _first, InputIterator _last) -{ - if (test_if_level()) - { - InputIterator end_macro_name = skip_identifier (_first, _last); - - std::size_t __size; -#if defined(__SUNPRO_CC) - std::distance (__first, end_macro_name, __size); -#else - __size = std::distance (_first, end_macro_name); -#endif - assert (__size < 256); - - char __buffer [256]; - std::copy (_first, end_macro_name, __buffer); - - bool value = env.resolve (__buffer, __size) != 0; - - _first = end_macro_name; - - if (check_undefined) - value = !value; - - _M_true_test[iflevel] = value; - _M_skipping[iflevel] = !value; - } - - return _first; -} - -template -InputIterator pp::handle_undef(InputIterator _first, InputIterator _last) -{ - _first = skip_blanks (_first, _last); - InputIterator end_macro_name = skip_identifier (_first, _last); - assert (end_macro_name != _first); - - std::size_t __size; -#if defined(__SUNPRO_CC) - std::distance (__first, end_macro_name, __size); -#else - __size = std::distance (_first, end_macro_name); -#endif - - assert (__size < 256); - - char __buffer [256]; - std::copy (_first, end_macro_name, __buffer); - - pp_fast_string const __tmp (__buffer, __size); - env.unbind (&__tmp); - - _first = end_macro_name; - - return _first; -} - -template -char pp::peek_char (InputIterator _first, InputIterator _last) -{ - if (_first == _last) - return 0; - - return *++_first; -} - -template -InputIterator pp::next_token (InputIterator _first, InputIterator _last, int *kind) -{ - _first = skip_blanks (_first, _last); - - if (_first == _last) - { - *kind = 0; - return _first; - } - - char ch = *_first; - char ch2 = peek_char (_first, _last); - - switch (ch) - { - case '/': - if (ch2 == '/' || ch2 == '*') - { - _first = skip_comment_or_divop (_first, _last); - return next_token (_first, _last, kind); - } - ++_first; - *kind = '/'; - break; - - case '<': - ++_first; - if (ch2 == '<') - { - ++_first; - *kind = TOKEN_LT_LT; - } - else if (ch2 == '=') - { - ++_first; - *kind = TOKEN_LT_EQ; - } - else - *kind = '<'; - - return _first; - - case '>': - ++_first; - if (ch2 == '>') - { - ++_first; - *kind = TOKEN_GT_GT; - } - else if (ch2 == '=') - { - ++_first; - *kind = TOKEN_GT_EQ; - } - else - *kind = '>'; - - return _first; - - case '!': - ++_first; - if (ch2 == '=') - { - ++_first; - *kind = TOKEN_NOT_EQ; - } - else - *kind = '!'; - - return _first; - - case '=': - ++_first; - if (ch2 == '=') - { - ++_first; - *kind = TOKEN_EQ_EQ; - } - else - *kind = '='; - - return _first; - - case '|': - ++_first; - if (ch2 == '|') - { - ++_first; - *kind = TOKEN_OR_OR; - } - else - *kind = '|'; - - return _first; - - case '&': - ++_first; - if (ch2 == '&') - { - ++_first; - *kind = TOKEN_AND_AND; - } - else - *kind = '&'; - - return _first; - - default: - if (pp_isalpha (ch) || ch == '_') - { - InputIterator end = skip_identifier (_first, _last); - _M_current_text.assign (_first, end); - - token_text = &_M_current_text; - _first = end; - - if (*token_text == "defined") - *kind = TOKEN_DEFINED; - else - *kind = TOKEN_IDENTIFIER; - } - else if (pp_isdigit (ch)) - { - InputIterator end = skip_number (_first, _last); - std::string __str (_first, _last); - char ch = __str [__str.size () - 1]; - if (ch == 'u' || ch == 'U') - { - token_uvalue = strtoul (__str.c_str (), 0, 0); - *kind = TOKEN_UNUMBER; - } - else - { - token_value = strtol (__str.c_str (), 0, 0); - *kind = TOKEN_NUMBER; - } - _first = end; - } - else - *kind = *_first++; - } - - return _first; -} - -} // namespace rpp - -#endif // PP_ENGINE_BITS_H - -// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/generator/parser/rpp/pp-engine.h b/generator/parser/rpp/pp-engine.h deleted file mode 100644 index 2704fa274..000000000 --- a/generator/parser/rpp/pp-engine.h +++ /dev/null @@ -1,297 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt Script Generator project on Qt Labs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PP_ENGINE_H -#define PP_ENGINE_H - -namespace rpp { - -struct Value -{ - enum Kind { - Kind_Long, - Kind_ULong, - }; - - Kind kind; - - union { - long l; - unsigned long ul; - }; - - inline bool is_ulong () const { return kind == Kind_ULong; } - - inline void set_ulong (unsigned long v) - { - ul = v; - kind = Kind_ULong; - } - - inline void set_long (long v) - { - l = v; - kind = Kind_Long; - } - - inline bool is_zero () const { return l == 0; } - -#define PP_DEFINE_BIN_OP(name, op) \ - inline Value &name (const Value &other) \ - { \ - if (is_ulong () || other.is_ulong ()) \ - set_ulong (ul op other.ul); \ - else \ - set_long (l op other.l); \ - return *this; \ - } - - PP_DEFINE_BIN_OP(op_add, +) - PP_DEFINE_BIN_OP(op_sub, -) - PP_DEFINE_BIN_OP(op_mult, *) - PP_DEFINE_BIN_OP(op_div, /) - PP_DEFINE_BIN_OP(op_mod, %) - PP_DEFINE_BIN_OP(op_lhs, <<) - PP_DEFINE_BIN_OP(op_rhs, >>) - PP_DEFINE_BIN_OP(op_lt, <) - PP_DEFINE_BIN_OP(op_gt, >) - PP_DEFINE_BIN_OP(op_le, <=) - PP_DEFINE_BIN_OP(op_ge, >=) - PP_DEFINE_BIN_OP(op_eq, ==) - PP_DEFINE_BIN_OP(op_ne, !=) - PP_DEFINE_BIN_OP(op_bit_and, &) - PP_DEFINE_BIN_OP(op_bit_or, |) - PP_DEFINE_BIN_OP(op_bit_xor, ^) - PP_DEFINE_BIN_OP(op_and, &&) - PP_DEFINE_BIN_OP(op_or, ||) - -#undef PP_DEFINE_BIN_OP -}; - -class pp -{ - pp_environment &env; - pp_macro_expander expand; - pp_skip_identifier skip_identifier; - pp_skip_comment_or_divop skip_comment_or_divop; - pp_skip_comment_or_divop skip_comment; - pp_skip_blanks skip_blanks; - pp_skip_number skip_number; - std::vector include_paths; - std::string _M_current_text; - - enum { MAX_LEVEL = 512 }; - int _M_skipping[MAX_LEVEL]; - int _M_true_test[MAX_LEVEL]; - int iflevel; - - union - { - long token_value; - unsigned long token_uvalue; - std::string *token_text; - }; - - enum INCLUDE_POLICY - { - INCLUDE_GLOBAL, - INCLUDE_LOCAL - }; - - enum TOKEN_TYPE - { - TOKEN_NUMBER = 1000, - TOKEN_UNUMBER, - TOKEN_IDENTIFIER, - TOKEN_DEFINED, - TOKEN_LT_LT, - TOKEN_LT_EQ, - TOKEN_GT_GT, - TOKEN_GT_EQ, - TOKEN_EQ_EQ, - TOKEN_NOT_EQ, - TOKEN_OR_OR, - TOKEN_AND_AND, - }; - - enum PP_DIRECTIVE_TYPE - { - PP_UNKNOWN_DIRECTIVE, - PP_DEFINE, - PP_INCLUDE, - PP_INCLUDE_NEXT, - PP_ELIF, - PP_ELSE, - PP_ENDIF, - PP_IF, - PP_IFDEF, - PP_IFNDEF, - PP_UNDEF, - PP_PRAGMA, - PP_ERROR, - PP_WARNING - }; - -public: - pp (pp_environment &env); - - inline std::back_insert_iterator > include_paths_inserter (); - - inline void push_include_path (std::string const &path); - - inline std::vector::iterator include_paths_begin (); - inline std::vector::iterator include_paths_end (); - - inline std::vector::const_iterator include_paths_begin () const; - inline std::vector::const_iterator include_paths_end () const; - - template - inline InputIterator eval_expression (InputIterator _first, InputIterator _last, Value *result); - - template - std::string expand_condition(InputIterator _first, InputIterator _last); - - template - void file (std::string const &filename, OutputIterator _result); - - template - void file (FILE *fp, OutputIterator _result); - - template - void operator () (InputIterator _first, InputIterator _last, OutputIterator _result); - -private: - inline bool file_isdir (std::string const &_filename) const; - inline bool file_exists (std::string const &_filename) const; - FILE *find_include_file (std::string const &_filename, std::string *_filepath, - INCLUDE_POLICY include_policy, bool _skip_current_path = false) const; - - inline int skipping() const; - bool test_if_level(); - - inline std::string fix_file_path (std::string const &filename) const; - inline bool is_absolute (std::string const &filename) const; - - PP_DIRECTIVE_TYPE find_directive (char const *_directive, std::size_t _size) const; - - template - bool find_header_protection (InputIterator _first, InputIterator _last, std::string *_prot); - - template - InputIterator skip (InputIterator _first, InputIterator _last); - - template - InputIterator eval_primary(InputIterator _first, InputIterator _last, Value *result); - - template - InputIterator eval_multiplicative(InputIterator _first, InputIterator _last, Value *result); - - template - InputIterator eval_additive(InputIterator _first, InputIterator _last, Value *result); - - template - InputIterator eval_shift(InputIterator _first, InputIterator _last, Value *result); - - template - InputIterator eval_relational(InputIterator _first, InputIterator _last, Value *result); - - template - InputIterator eval_equality(InputIterator _first, InputIterator _last, Value *result); - - template - InputIterator eval_and(InputIterator _first, InputIterator _last, Value *result); - - template - InputIterator eval_xor(InputIterator _first, InputIterator _last, Value *result); - - template - InputIterator eval_or(InputIterator _first, InputIterator _last, Value *result); - - template - InputIterator eval_logical_and(InputIterator _first, InputIterator _last, Value *result); - - template - InputIterator eval_logical_or(InputIterator _first, InputIterator _last, Value *result); - - template - InputIterator eval_constant_expression(InputIterator _first, InputIterator _last, Value *result); - - template - InputIterator handle_directive(char const *_directive, std::size_t _size, - InputIterator _first, InputIterator _last, OutputIterator _result); - - template - InputIterator handle_include(bool skip_current_path, InputIterator _first, InputIterator _last, - OutputIterator _result); - - template - InputIterator handle_define (InputIterator _first, InputIterator _last); - - template - InputIterator handle_if (InputIterator _first, InputIterator _last); - - template - InputIterator handle_else (InputIterator _first, InputIterator _last); - - template - InputIterator handle_elif (InputIterator _first, InputIterator _last); - - template - InputIterator handle_endif (InputIterator _first, InputIterator _last); - - template - InputIterator handle_ifdef (bool check_undefined, InputIterator _first, InputIterator _last); - - template - InputIterator handle_undef(InputIterator _first, InputIterator _last); - - template - char peek_char (InputIterator _first, InputIterator _last); - - template - InputIterator next_token (InputIterator _first, InputIterator _last, int *kind); -}; - -} // namespace rpp - -#endif // PP_ENGINE_H - -// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/generator/parser/rpp/pp-environment.h b/generator/parser/rpp/pp-environment.h deleted file mode 100644 index 049ff5899..000000000 --- a/generator/parser/rpp/pp-environment.h +++ /dev/null @@ -1,159 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt Script Generator project on Qt Labs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PP_ENVIRONMENT_H -#define PP_ENVIRONMENT_H - -#include -#include - -namespace rpp { - -class pp_environment -{ -public: - typedef std::vector::const_iterator const_iterator; - -public: - pp_environment (): - current_line (0), - _M_hash_size (4093) - { - _M_base = (pp_macro **) memset (new pp_macro* [_M_hash_size], 0, _M_hash_size * sizeof (pp_macro*)); - } - - ~pp_environment () - { - for (std::size_t i = 0; i < _M_macros.size (); ++i) - delete _M_macros [i]; - - delete [] _M_base; - } - - const_iterator first_macro () const { return _M_macros.begin (); } - const_iterator last_macro () const { return _M_macros.end (); } - - inline void bind (pp_fast_string const *_name, pp_macro const &_macro) - { - std::size_t h = hash_code (*_name) % _M_hash_size; - pp_macro *m = new pp_macro (_macro); - m->name = _name; - m->next = _M_base [h]; - m->hash_code = h; - _M_base [h] = m; - - _M_macros.push_back (m); - - if (_M_macros.size() == _M_hash_size) - rehash(); - } - - inline void unbind (pp_fast_string const *_name) - { - if (pp_macro *m = resolve (_name)) - m->is.hidden = true; - } - - inline void unbind (char const *_s, std::size_t _size) - { - pp_fast_string tmp (_s, _size); - unbind (&tmp); - } - - inline pp_macro *resolve (pp_fast_string const *_name) const - { - std::size_t h = hash_code (*_name) % _M_hash_size; - pp_macro *it = _M_base [h]; - - while (it && it->name && it->hash_code == h && (*it->name != *_name || it->is.hidden)) - it = it->next; - - return it; - } - - inline pp_macro *resolve (char const *_data, std::size_t _size) const - { - pp_fast_string const tmp (_data, _size); - return resolve (&tmp); - } - - std::string current_file; - int current_line; - -private: - inline std::size_t hash_code (pp_fast_string const &s) const - { - std::size_t hash_value = 0; - - for (std::size_t i = 0; i < s.size (); ++i) - hash_value = (hash_value << 5) - hash_value + s.at (i); - - return hash_value; - } - - void rehash() - { - delete[] _M_base; - - _M_hash_size <<= 1; - - _M_base = (pp_macro **) memset (new pp_macro* [_M_hash_size], 0, _M_hash_size * sizeof(pp_macro*)); - for (std::size_t index = 0; index < _M_macros.size (); ++index) - { - pp_macro *elt = _M_macros [index]; - std::size_t h = hash_code (*elt->name) % _M_hash_size; - elt->next = _M_base [h]; - elt->hash_code = h; - _M_base [h] = elt; - } - } - -private: - std::vector _M_macros; - pp_macro **_M_base; - std::size_t _M_hash_size; -}; - -} // namespace rpp - -#endif // PP_ENVIRONMENT_H - -// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/generator/parser/rpp/pp-fwd.h b/generator/parser/rpp/pp-fwd.h deleted file mode 100644 index eacac5dac..000000000 --- a/generator/parser/rpp/pp-fwd.h +++ /dev/null @@ -1,55 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt Script Generator project on Qt Labs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PP_FWD_H -#define PP_FWD_H - -namespace rpp { - -template class pp_string; - -typedef pp_string pp_fast_string; - -#endif // PP_FWD_H - -} // namespace rpp - -// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/generator/parser/rpp/pp-internal.h b/generator/parser/rpp/pp-internal.h deleted file mode 100644 index 4821ccb26..000000000 --- a/generator/parser/rpp/pp-internal.h +++ /dev/null @@ -1,134 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt Script Generator project on Qt Labs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PP_INTERNAL_H -#define PP_INTERNAL_H - -#include -#include - -namespace rpp { - -namespace _PP_internal -{ - -inline void extract_file_path (const std::string &_filename, std::string *_filepath) -{ - std::size_t index = _filename.rfind (PATH_SEPARATOR); - - if (index == std::string::npos) - *_filepath = "/"; - - else - _filepath->assign (_filename, 0, index + 1); -} - -template -void output_line(const std::string &_filename, int _line, OutputIterator _result) -{ - std::string msg; - - msg += "# "; - - char line_descr[16]; - pp_snprintf (line_descr, 16, "%d", _line); - msg += line_descr; - - msg += " \""; - - if (_filename.empty ()) - msg += ""; - else - msg += _filename; - - msg += "\"\n"; - std::copy (msg.begin (), msg.end (), _result); -} - -template -inline bool comment_p (InputIterator _first, InputIterator _last) /*const*/ -{ - if (_first == _last) - return false; - - if (*_first != '/') - return false; - - if (++_first == _last) - return false; - - return (*_first == '/' || *_first == '*'); -} - -struct _Compare_string -{ - inline bool operator () (pp_fast_string const *__lhs, pp_fast_string const *__rhs) const - { return *__lhs < *__rhs; } -}; - -struct _Equal_to_string -{ - inline bool operator () (pp_fast_string const *__lhs, pp_fast_string const *__rhs) const - { return *__lhs == *__rhs; } -}; - -struct _Hash_string -{ - inline std::size_t operator () (pp_fast_string const *__s) const - { - char const *__ptr = __s->begin (); - std::size_t __size = __s->size (); - std::size_t __h = 0; - - for (std::size_t i = 0; i < __size; ++i) - __h = (__h << 5) - __h + __ptr [i]; - - return __h; - } -}; - -} // _PP_internal - -} // namespace rpp - -#endif // PP_INTERNAL_H - -// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/generator/parser/rpp/pp-iterator.h b/generator/parser/rpp/pp-iterator.h deleted file mode 100644 index 230e3c898..000000000 --- a/generator/parser/rpp/pp-iterator.h +++ /dev/null @@ -1,103 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt Script Generator project on Qt Labs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PP_ITERATOR_H -#define PP_ITERATOR_H - -#include -#include // Q_CC_MSVC - -namespace rpp { - -class pp_null_output_iterator - : public std::iterator -{ -public: - pp_null_output_iterator() {} - - template - pp_null_output_iterator &operator=(_Tp const &) - { return *this; } - - inline pp_null_output_iterator &operator * () { return *this; } - inline pp_null_output_iterator &operator ++ () { return *this; } - inline pp_null_output_iterator operator ++ (int) { return *this; } -}; - -template -class pp_output_iterator - : public std::iterator -{ - std::string &_M_result; - -public: - explicit pp_output_iterator(std::string &__result): - _M_result (__result) {} - -#ifdef Q_CC_MSVC - // this copy constructor was needed for Visual Studio 2012 release builds: - inline pp_output_iterator &operator=(const pp_output_iterator& other) - { - *(std::iterator*)(this) = other; - _M_result = other._M_result; - return *this; - } -#endif - - inline pp_output_iterator &operator=(typename _Container::const_reference __v) - { - if (_M_result.capacity () == _M_result.size ()) - _M_result.reserve (_M_result.capacity () << 2); - - _M_result.push_back(__v); - return *this; - } - - inline pp_output_iterator &operator * () { return *this; } - inline pp_output_iterator &operator ++ () { return *this; } - inline pp_output_iterator operator ++ (int) { return *this; } -}; - -} // namespace rpp - -#endif // PP_ITERATOR_H - -// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/generator/parser/rpp/pp-macro-expander.h b/generator/parser/rpp/pp-macro-expander.h deleted file mode 100644 index 85de8a111..000000000 --- a/generator/parser/rpp/pp-macro-expander.h +++ /dev/null @@ -1,421 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt Script Generator project on Qt Labs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PP_MACRO_EXPANDER_H -#define PP_MACRO_EXPANDER_H - -#include - -namespace rpp { - -struct pp_frame -{ - pp_macro *expanding_macro; - std::vector *actuals; - - pp_frame (pp_macro *_expanding_macro, std::vector *_actuals): - expanding_macro (_expanding_macro), actuals (_actuals) {} -}; - -class pp_macro_expander -{ - pp_environment &env; - pp_frame *frame; - - pp_skip_number skip_number; - pp_skip_identifier skip_identifier; - pp_skip_string_literal skip_string_literal; - pp_skip_char_literal skip_char_literal; - pp_skip_argument skip_argument; - pp_skip_comment_or_divop skip_comment_or_divop; - pp_skip_blanks skip_blanks; - pp_skip_whitespaces skip_whitespaces; - - std::string const *resolve_formal (pp_fast_string const *_name) - { - static const pp_fast_string va_args_name("__VA_ARGS__", 11); - static std::string empty(""); - - assert (_name != 0); - - if (! frame) - return 0; - - assert (frame->expanding_macro != 0); - - std::vector formals = frame->expanding_macro->formals; - if (frame->expanding_macro->is.variadics) - formals.push_back(&va_args_name); - - for (std::size_t index = 0; index < formals.size(); ++index) - { - pp_fast_string const *formal = formals[index]; - - if (*formal != *_name) - continue; - - else if (frame->actuals && index < frame->actuals->size()) - return &(*frame->actuals)[index]; - - else if (frame->expanding_macro->is.variadics && index == formals.size()-1) - // variadic argument may also be missing, replace with empty then - return ∅ - - else - assert (0); // internal error? - } - - return 0; - } - -public: // attributes - int lines; - int generated_lines; - -public: - pp_macro_expander (pp_environment &_env, pp_frame *_frame = 0): - env (_env), frame (_frame), lines (0), generated_lines (0) {} - - template - InputIterator operator () (InputIterator _first, InputIterator _last, OutputIterator _result) - { - generated_lines = 0; - _first = skip_blanks (_first, _last); - lines = skip_blanks.lines; - - while (_first != _last) - { - if (*_first == '\n') - { - *_result++ = *_first; - ++lines; - - _first = skip_blanks (++_first, _last); - lines += skip_blanks.lines; - - if (_first != _last && *_first == '#') - break; - } - else if (*_first == '#') - { - _first = skip_blanks (++_first, _last); - lines += skip_blanks.lines; - - InputIterator end_id = skip_identifier (_first, _last); - - // ### rewrite: not safe - char name_buffer[512], *cp = name_buffer; - std::copy (_first, end_id, cp); - std::size_t name_size = end_id - _first; - name_buffer[name_size] = '\0'; - - pp_fast_string fast_name (name_buffer, name_size); - - if (std::string const *actual = resolve_formal (&fast_name)) - { - *_result++ = '\"'; - - for (std::string::const_iterator it = skip_whitespaces (actual->begin (), actual->end ()); - it != actual->end (); ++it) - { - if (*it == '"') - { - *_result++ = '\\'; - *_result++ = *it; - } - - else if (*it == '\n') - { - *_result++ = '"'; - *_result++ = '\n'; - *_result++ = '"'; - } - - else - *_result++ = *it; - } - - *_result++ = '\"'; - _first = end_id; - } - else - *_result++ = '#'; // ### warning message? - } - else if (*_first == '\"') - { - InputIterator next_pos = skip_string_literal (_first, _last); - lines += skip_string_literal.lines; - std::copy (_first, next_pos, _result); - _first = next_pos; - } - else if (*_first == '\'') - { - InputIterator next_pos = skip_char_literal (_first, _last); - lines += skip_char_literal.lines; - std::copy (_first, next_pos, _result); - _first = next_pos; - } - else if (_PP_internal::comment_p (_first, _last)) - { - _first = skip_comment_or_divop (_first, _last); - int n = skip_comment_or_divop.lines; - lines += n; - - while (n-- > 0) - *_result++ = '\n'; - } - else if (pp_isspace (*_first)) - { - for (; _first != _last; ++_first) - { - if (*_first == '\n' || !pp_isspace (*_first)) - break; - } - - *_result = ' '; - } - else if (pp_isdigit (*_first)) - { - InputIterator next_pos = skip_number (_first, _last); - lines += skip_number.lines; - std::copy (_first, next_pos, _result); - _first = next_pos; - } - else if (pp_isalpha (*_first) || *_first == '_') - { - InputIterator name_begin = _first; - InputIterator name_end = skip_identifier (_first, _last); - _first = name_end; // advance - - // search for the paste token - InputIterator next = skip_blanks (_first, _last); - if (next != _last && *next == '#') - { - ++next; - if (next != _last && *next == '#') - _first = skip_blanks(++next, _last); - } - - // ### rewrite: not safe - - std::ptrdiff_t name_size; -#if defined(__SUNPRO_CC) - std::distance (name_begin, name_end, name_size); -#else - name_size = std::distance (name_begin, name_end); -#endif - assert (name_size >= 0 && name_size < 512); - - char name_buffer[512], *cp = name_buffer; - std::size_t _size = name_end - name_begin; - std::copy (name_begin, name_end, cp); - name_buffer[_size] = '\0'; - - pp_fast_string fast_name (name_buffer, name_size); - - if (std::string const *actual = resolve_formal (&fast_name)) - { - std::copy (actual->begin (), actual->end (), _result); - continue; - } - - static bool hide_next = false; // ### remove me - - pp_macro *macro = env.resolve (name_buffer, name_size); - if (! macro || macro->is.hidden || hide_next) - { - hide_next = ! strcmp (name_buffer, "defined"); - - if (_size == 8 && name_buffer [0] == '_' && name_buffer [1] == '_') - { - if (! strcmp (name_buffer, "__LINE__")) - { - char buf [16]; - char *end = buf + pp_snprintf (buf, 16, "%d", env.current_line + lines); - - std::copy (&buf [0], end, _result); - continue; - } - - else if (! strcmp (name_buffer, "__FILE__")) - { - _result++ = '"'; - std::copy (env.current_file.begin (), env.current_file.end (), _result); // ### quote - _result++ = '"'; - continue; - } - } - - std::copy (name_begin, name_end, _result); - continue; - } - - if (! macro->is.function_like) - { - pp_macro *m = 0; - - if (macro->definition) - { - macro->is.hidden = true; - - std::string tmp; - tmp.reserve (256); - - pp_macro_expander expand_macro (env); - expand_macro (macro->definition->begin (), macro->definition->end (), std::back_inserter (tmp)); - generated_lines += expand_macro.lines; - - if (! tmp.empty ()) - { - std::string::iterator begin_id = skip_whitespaces (tmp.begin (), tmp.end ()); - std::string::iterator end_id = skip_identifier (begin_id, tmp.end ()); - - if (end_id == tmp.end ()) - { - std::string id; - id.assign (begin_id, end_id); - - std::size_t x; -#if defined(__SUNPRO_CC) - std::distance (__begin_id, __end_id, x); -#else - x = std::distance (begin_id, end_id); -#endif - m = env.resolve (id.c_str (), x); - } - - if (! m) - std::copy (tmp.begin (), tmp.end (), _result); - } - - macro->is.hidden = false; - } - - if (! m) - continue; - - macro = m; - } - - // function like macro - InputIterator arg_it = skip_whitespaces (_first, _last); - - if (arg_it == _last || *arg_it != '(') - { - std::copy (name_begin, name_end, _result); - lines += skip_whitespaces.lines; - _first = arg_it; - continue; - } - - std::vector actuals; - actuals.reserve (5); - ++arg_it; // skip '(' - - pp_macro_expander expand_actual (env, frame); - - InputIterator arg_end = skip_argument_variadics (actuals, macro, arg_it, _last); - if (arg_it != arg_end) - { - std::string actual (arg_it, arg_end); - actuals.resize (actuals.size() + 1); - actuals.back ().reserve (255); - expand_actual (actual.begin (), actual.end(), std::back_inserter (actuals.back())); - arg_it = arg_end; - } - - while (arg_it != _last && *arg_end == ',') - { - ++arg_it; // skip ',' - - arg_end = skip_argument_variadics (actuals, macro, arg_it, _last); - std::string actual (arg_it, arg_end); - actuals.resize (actuals.size() + 1); - actuals.back ().reserve (255); - expand_actual (actual.begin (), actual.end(), std::back_inserter (actuals.back())); - arg_it = arg_end; - } - - assert (arg_it != _last && *arg_it == ')'); - - ++arg_it; // skip ')' - _first = arg_it; - -#if 0 // ### enable me - assert ((macro->is.variadics && macro->formals.size () >= actuals.size ()) - || macro->formals.size() == actuals.size()); -#endif - - pp_frame frame (macro, &actuals); - pp_macro_expander expand_macro (env, &frame); - macro->is.hidden = true; - expand_macro (macro->definition->begin (), macro->definition->end (), _result); - macro->is.hidden = false; - generated_lines += expand_macro.lines; - } - else - *_result++ = *_first++; - } - - return _first; - } - - template - InputIterator skip_argument_variadics (std::vector const &_actuals, pp_macro *_macro, - InputIterator _first, InputIterator _last) - { - InputIterator arg_end = skip_argument (_first, _last); - - while (_macro->is.variadics && arg_end != _last && *arg_end == ',' - && _actuals.size () == _macro->formals.size ()) - { - arg_end = skip_argument (++arg_end, _last); - } - - return arg_end; - } -}; - -} // namespace rpp - -#endif // PP_MACRO_EXPANDER_H - -// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/generator/parser/rpp/pp-macro.h b/generator/parser/rpp/pp-macro.h deleted file mode 100644 index 6342549d4..000000000 --- a/generator/parser/rpp/pp-macro.h +++ /dev/null @@ -1,89 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt Script Generator project on Qt Labs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PP_MACRO_H -#define PP_MACRO_H - -namespace rpp { - -struct pp_macro -{ -#if defined (PP_WITH_MACRO_POSITION) - pp_fast_string const *file; -#endif - pp_fast_string const *name; - pp_fast_string const *definition; - std::vector formals; - - union - { - int unsigned state; - - struct - { - int unsigned hidden: 1; - int unsigned function_like: 1; - int unsigned variadics: 1; - } is; - }; - - int lines; - pp_macro *next; - std::size_t hash_code; - - inline pp_macro(): -#if defined (PP_WITH_MACRO_POSITION) - file (0), -#endif - name (0), - definition (0), - state (0), - lines (0), - next (0), - hash_code (0) - {} -}; - -} // namespace rpp - -#endif // PP_MACRO_H - -// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/generator/parser/rpp/pp-main.cpp b/generator/parser/rpp/pp-main.cpp deleted file mode 100644 index aaf09e141..000000000 --- a/generator/parser/rpp/pp-main.cpp +++ /dev/null @@ -1,340 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt Script Generator project on Qt Labs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include "pp.h" - -using namespace rpp; - -#ifndef GCC_MACHINE -# define GCC_MACHINE "i386-redhat-linux" -#endif - -#ifndef GCC_VERSION -# define GCC_VERSION "4.1.1" -#endif - -void usage () -{ - std::cerr << "usage: rpp file.cpp" << std::endl; - ::exit (EXIT_FAILURE); -} - -void dump_macros (pp_environment &env, pp &, std::ostream &__out) -{ - for (pp_environment::const_iterator it = env.first_macro (); it != env.last_macro (); ++it) - { - pp_macro const *m = *it; - - if (m->hidden) - continue; - - std::string id (m->name->begin (), m->name->end ()); - __out << "#define " << id; - - if (m->function_like) - { - __out << "("; - - for (std::size_t i = 0; i < m->formals.size (); ++i) - { - if (i != 0) - __out << ", "; - - pp_fast_string const *f = m->formals [i]; - std::string name (f->begin (), f->end ()); - __out << name; - } - - if (m->variadics) - __out << "..."; - - __out << ")"; - } - - __out << "\t"; - if (m->definition) - { - std::string def (m->definition->begin (), m->definition->end ()); - __out << def; - } - - __out << std::endl; - } -} - -int main (int, char *argv []) -{ - char const *input_file = 0; - char const *output_file = 0; - char const *include_pch_file = 0; - bool opt_help = false; - bool opt_dump_macros = false; - bool opt_pch = false; - - pp_environment env; - pp preprocess(env); - - std::string result; - result.reserve (20 * 1024); // 20K - - pp_output_iterator out (result); - pp_null_output_iterator null_out; - - preprocess.push_include_path ("/usr/include"); - preprocess.push_include_path ("/usr/lib/gcc/" GCC_MACHINE "/" GCC_VERSION "/include"); - - preprocess.push_include_path ("/usr/include/c++/" GCC_VERSION); - preprocess.push_include_path ("/usr/include/c++/" GCC_VERSION "/" GCC_MACHINE); - - std::string extra_args; - - while (const char *arg = *++argv) - { - if (arg [0] != '-') - input_file = arg; - - else if (! strcmp (arg, "-help")) - opt_help = true; - - else if (! strcmp (arg, "-dM")) - opt_dump_macros = true; - - else if (! strcmp (arg, "-pch")) - opt_pch = true; - - else if (! strcmp (arg, "-msse")) - { - pp_macro __macro; - __macro.name = pp_symbol::get ("__SSE__", 7); - env.bind (__macro.name, __macro); - - __macro.name = pp_symbol::get ("__MMX__", 7); - env.bind (__macro.name, __macro); - } - - else if (! strcmp (arg, "-include")) - { - if (argv [1]) - include_pch_file = *++argv; - } - - else if (! strncmp (arg, "-o", 2)) - { - arg += 2; - - if (! arg [0] && argv [1]) - arg = *++argv; - - if (arg) - output_file = arg; - } - - else if (! strncmp (arg, "-conf", 8)) - { - if (argv [1]) - preprocess.file (*++argv, null_out); - } - - else if (! strncmp (arg, "-I", 2)) - { - arg += 2; - - if (! arg [0] && argv [1]) - arg = *++argv; - - if (arg) - preprocess.push_include_path (arg); - } - - else if (! strncmp (arg, "-U", 2)) - { - arg += 2; - - if (! arg [0] && argv [1]) - arg = *++argv; - - if (arg) - { - env.unbind (arg, strlen (arg)); - } - } - - else if (! strncmp (arg, "-D", 2)) - { - arg += 2; - - if (! arg [0] && argv [1]) - arg = *++argv; - - if (arg) - { - pp_macro __macro; - - char const *end = arg; - char const *eq = 0; - - for (; *end; ++end) - { - if (*end == '=') - eq = end; - } - - if (eq != 0) - { - __macro.name = pp_symbol::get (arg, eq - arg); - __macro.definition = pp_symbol::get (eq + 1, end - (eq + 1)); - } - - else - { - __macro.name = pp_symbol::get (arg, end - arg); - __macro.definition = 0; - } - - env.bind (__macro.name, __macro); - } - } - else - { - extra_args += " "; - extra_args += arg; - } - } - - if (! input_file || opt_help) - { - usage (); - return EXIT_FAILURE; - } - - std::string __ifile (input_file); - bool is_c_file = false; - if (__ifile.size () > 2 && __ifile [__ifile.size () - 1] == 'c' && __ifile [__ifile.size () - 2] == '.') - { - is_c_file = true; - env.unbind ("__cplusplus", 11); - - pp_macro __macro; - __macro.name = pp_symbol::get ("__null"); - __macro.definition = pp_symbol::get ("((void*) 0)"); - env.bind (__macro.name, __macro); - - // turn off the pch - include_pch_file = 0; - } - else if (include_pch_file) - { - std::string __pch (include_pch_file); - __pch += ".gch/c++.conf"; - - //std::cerr << "*** pch file " << __pch << std::endl; - preprocess.file (__pch, null_out); - } - - if (opt_dump_macros) - { - preprocess.file (input_file, null_out); - dump_macros (env, preprocess, std::cout); - return EXIT_SUCCESS; - } - - preprocess.file (input_file, out); - - if (opt_pch) - { - if (! output_file) - { - std::cerr << "*** WARNING expected a file name" << std::endl; - return EXIT_FAILURE; - } - - std::string __conf_file (output_file); - __conf_file += ".conf"; - - std::ofstream __out; - __out.open (__conf_file.c_str ()); - dump_macros (env, preprocess, __out); - __out.close (); - - std::string __pp_file (output_file); - __pp_file += ".i"; - - __out.open (__pp_file.c_str ()); - __out.write (result.c_str (), result.size ()); - __out.close (); - return EXIT_SUCCESS; - } - - std::ostream *__out = &std::cout; - std::ofstream __ofile; - - if (output_file) - { - std::string __output_file_name (output_file); - __ofile.open (output_file); - __out = &__ofile; - } - - if (include_pch_file) - { - std::string __pch (include_pch_file); - __pch += ".gch/c++.i"; - - std::ifstream __in (__pch.c_str ()); - - char buffer [1024]; - while (__in.read (buffer, 1024)) - __out->write (buffer, 1024); - - __in.close (); - } - - __out->write (result.c_str (), result.size ()); - - if (output_file) - __ofile.close (); - - return EXIT_SUCCESS; -} - -// kate: space-indent on; indent-width 2; replace-tabs on; - diff --git a/generator/parser/rpp/pp-qt-configuration b/generator/parser/rpp/pp-qt-configuration deleted file mode 100644 index fd79c8506..000000000 --- a/generator/parser/rpp/pp-qt-configuration +++ /dev/null @@ -1,36 +0,0 @@ -#define __cplusplus 1 - -#define __STDC__ - -// Qt -#define QOBJECTDEFS_H -#define QTMETAMACROS_H - -// not yet supported -#define Q_SLOTS slots -#define Q_SIGNALS signals -#define Q_FLAGS(a) -#define Q_FLAG(a) -#define Q_PRIVATE_SLOT(a, b) -#define Q_DECLARE_INTERFACE(a,b) -#define Q_INTERFACES(a) -#define Q_GADGET -#define Q_OVERRIDE(a) -#define Q_OS_OS2 -#define Q_NO_USING_KEYWORD -#define Q_DECL_OVERRIDE override - -// There are symbols in Qt that exist in Debug but -// not in release -#define QT_NO_DEBUG - -#define QT_JAMBI_RUN - -// Qt6 -#define Q_NAMESPACE_EXPORT(...) -#define Q_ENUM_NS(x) -#define Q_FLAG_NS(x) -#define Q_MOC_INCLUDE(...) - -// ignore static_assert -#define static_assert(...) \ No newline at end of file diff --git a/generator/parser/rpp/pp-scanner.h b/generator/parser/rpp/pp-scanner.h deleted file mode 100644 index 862590beb..000000000 --- a/generator/parser/rpp/pp-scanner.h +++ /dev/null @@ -1,373 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt Script Generator project on Qt Labs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PP_SCANNER_H -#define PP_SCANNER_H - -namespace rpp { - -struct pp_skip_blanks -{ - int lines; - - template - _InputIterator operator () (_InputIterator __first, _InputIterator __last) - { - lines = 0; - - for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) - { - if (*__first == '\\') - { - _InputIterator __begin = __first; - ++__begin; - - if (__begin != __last && *__begin == '\n') - ++__first; - else - break; - } - else if (*__first == '\n' || !pp_isspace (*__first)) - break; - } - - return __first; - } -}; - -struct pp_skip_whitespaces -{ - int lines; - - template - _InputIterator operator () (_InputIterator __first, _InputIterator __last) - { - lines = 0; - - for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) - { - if (! pp_isspace (*__first)) - break; - } - - return __first; - } -}; - -struct pp_skip_comment_or_divop -{ - pp_skip_comment_or_divop(bool skipDiv = true) { _skipDiv = skipDiv; } - - int lines; - - template - _InputIterator operator () (_InputIterator __first, _InputIterator __last) - { - enum { - MAYBE_BEGIN, - BEGIN, - MAYBE_END, - END, - IN_COMMENT, - IN_CXX_COMMENT - } state (MAYBE_BEGIN); - - lines = 0; - - for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) - { - switch (state) - { - default: - assert (0); - break; - - case MAYBE_BEGIN: - if (*__first != '/') - return __first; - - state = BEGIN; - break; - - case BEGIN: - if (*__first == '*') - state = IN_COMMENT; - else if (*__first == '/') - state = IN_CXX_COMMENT; - else if (_skipDiv) - return __first; - else - return __first-1; - break; - - case IN_COMMENT: - if (*__first == '*') - state = MAYBE_END; - break; - - case IN_CXX_COMMENT: - if (*__first == '\n') - return __first; - break; - - case MAYBE_END: - if (*__first == '/') - state = END; - else if (*__first != '*') - state = IN_COMMENT; - break; - - case END: - return __first; - } - } - - return __first; - } -private: - bool _skipDiv; -}; - -struct pp_skip_identifier -{ - int lines; - - template - _InputIterator operator () (_InputIterator __first, _InputIterator __last) - { - lines = 0; - - for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) - { - if (! pp_isalnum (*__first) && *__first != '_') - break; - } - - return __first; - } -}; - -struct pp_skip_number -{ - int lines; - - template - _InputIterator operator () (_InputIterator __first, _InputIterator __last) - { - lines = 0; - - for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) - { - if (! pp_isalnum (*__first) && *__first != '.') - break; - } - - return __first; - } -}; - -struct pp_skip_string_literal -{ - int lines; - - template - _InputIterator operator () (_InputIterator __first, _InputIterator __last) - { - enum { - BEGIN, - IN_STRING, - QUOTE, - END - } state (BEGIN); - - lines = 0; - - for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) - { - switch (state) - { - default: - assert (0); - break; - - case BEGIN: - if (*__first != '\"') - return __first; - state = IN_STRING; - break; - - case IN_STRING: - assert (*__first != '\n'); - - if (*__first == '\"') - state = END; - else if (*__first == '\\') - state = QUOTE; - break; - - case QUOTE: - state = IN_STRING; - break; - - case END: - return __first; - } - } - - return __first; - } -}; - -struct pp_skip_char_literal -{ - int lines; - - template - _InputIterator operator () (_InputIterator __first, _InputIterator __last) - { - enum { - BEGIN, - IN_STRING, - QUOTE, - END - } state (BEGIN); - - lines = 0; - - for (; state != END && __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) - { - switch (state) - { - default: - assert (0); - break; - - case BEGIN: - if (*__first != '\'') - return __first; - state = IN_STRING; - break; - - case IN_STRING: - assert (*__first != '\n'); - - if (*__first == '\'') - state = END; - else if (*__first == '\\') - state = QUOTE; - break; - - case QUOTE: - state = IN_STRING; - break; - } - } - - return __first; - } -}; - -struct pp_skip_argument -{ - pp_skip_identifier skip_number; - pp_skip_identifier skip_identifier; - pp_skip_string_literal skip_string_literal; - pp_skip_char_literal skip_char_literal; - pp_skip_comment_or_divop skip_comment_or_divop; - int lines; - - template - _InputIterator operator () (_InputIterator __first, _InputIterator __last) - { - int depth = 0; - lines = 0; - - while (__first != __last) - { - if (!depth && (*__first == ')' || *__first == ',')) - break; - else if (*__first == '(') - ++depth, ++__first; - else if (*__first == ')') - --depth, ++__first; - else if (*__first == '\"') - { - __first = skip_string_literal (__first, __last); - lines += skip_string_literal.lines; - } - else if (*__first == '\'') - { - __first = skip_char_literal (__first, __last); - lines += skip_char_literal.lines; - } - else if (*__first == '/') - { - __first = skip_comment_or_divop (__first, __last); - lines += skip_comment_or_divop.lines; - } - else if (pp_isalpha (*__first) || *__first == '_') - { - __first = skip_identifier (__first, __last); - lines += skip_identifier.lines; - } - else if (pp_isdigit (*__first)) - { - __first = skip_number (__first, __last); - lines += skip_number.lines; - } - else if (*__first == '\n') - { - ++__first; - ++lines; - } - else - ++__first; - } - - return __first; - } -}; - -} // namespace rpp - -#endif // PP_SCANNER_H - -// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/generator/parser/rpp/pp-string.h b/generator/parser/rpp/pp-string.h deleted file mode 100644 index f0c3d4c4d..000000000 --- a/generator/parser/rpp/pp-string.h +++ /dev/null @@ -1,113 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt Script Generator project on Qt Labs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PP_STRING_H -#define PP_STRING_H - -namespace rpp { - -template -class pp_string -{ - typedef std::char_traits<_CharT> traits_type; - typedef std::size_t size_type; - - _CharT const *_M_begin; - std::size_t _M_size; - -public: - inline pp_string (): - _M_begin (0), _M_size(0) {} - - explicit pp_string (std::string const &__s): - _M_begin (__s.c_str ()), _M_size (__s.size ()) {} - - inline pp_string (_CharT const *__begin, std::size_t __size): - _M_begin (__begin), _M_size (__size) {} - - inline _CharT const *begin () const { return _M_begin; } - inline _CharT const *end () const { return _M_begin + _M_size; } - - inline _CharT at (std::size_t index) const { return _M_begin [index]; } - - inline std::size_t size () const { return _M_size; } - - inline int compare (pp_string const &__other) const - { - size_type const __size = this->size(); - size_type const __osize = __other.size(); - size_type const __len = std::min (__size, __osize); - - int __r = traits_type::compare (_M_begin, __other._M_begin, __len); - if (!__r) - __r = (int) (__size - __osize); - - return __r; - } - - inline bool operator == (pp_string const &__other) const - { return compare (__other) == 0; } - - inline bool operator != (pp_string const &__other) const - { return compare (__other) != 0; } - - inline bool operator < (pp_string const &__other) const - { return compare (__other) < 0; } - - inline bool operator == (char const *s) const - { - std::size_t n = strlen (s); - - if (n != _M_size) - return false; - - return ! strncmp (_M_begin, s, n); - } - - inline bool operator != (char const *s) const - { return ! operator == (s); } -}; - -} // namespace rpp - -#endif // PP_STRING_H - -// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/generator/parser/rpp/pp-symbol.h b/generator/parser/rpp/pp-symbol.h deleted file mode 100644 index b078b6651..000000000 --- a/generator/parser/rpp/pp-symbol.h +++ /dev/null @@ -1,101 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt Script Generator project on Qt Labs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PP_SYMBOL_H -#define PP_SYMBOL_H - -namespace rpp { - -class pp_symbol -{ - static rxx_allocator &allocator_instance () - { - static rxx_allocator__allocator; - return __allocator; - } - -public: - static int &N() - { - static int __N; - return __N; - } - - static pp_fast_string const *get (char const *__data, std::size_t __size) - { - ++N(); - char *data = allocator_instance ().allocate (__size + 1); - memcpy(data, __data, __size); - data[__size] = '\0'; - - char *where = allocator_instance ().allocate (sizeof (pp_fast_string)); - return new (where) pp_fast_string (data, __size); - } - - template - static pp_fast_string const *get (_InputIterator __first, _InputIterator __last) - { - ++N(); - std::ptrdiff_t __size; -#if defined(__SUNPRO_CC) - std::distance (__first, __last, __size); -#else - __size = std::distance (__first, __last); -#endif - assert (__size >= 0 && __size < 512); - - char *data = allocator_instance ().allocate (__size + 1); - std::copy (__first, __last, data); - data[__size] = '\0'; - - char *where = allocator_instance ().allocate (sizeof (pp_fast_string)); - return new (where) pp_fast_string (data, __size); - } - - static pp_fast_string const *get(std::string const &__s) - { return get (__s.c_str (), __s.size ()); } -}; - -} // namespace rpp - -#endif // PP_SYMBOL_H - -// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/generator/parser/rpp/pp.h b/generator/parser/rpp/pp.h deleted file mode 100644 index b7d042101..000000000 --- a/generator/parser/rpp/pp.h +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt Script Generator project on Qt Labs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PP_H -#define PP_H - -#if defined(_WIN64) || defined(WIN64) || defined(__WIN64__) \ - || defined(_WIN32) || defined(WIN32) || defined(__WIN32__) -# define PP_OS_WIN -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifdef HAVE_MMAP -# include -#endif - -#include -#include - -#if (_MSC_VER >= 1400) -# define FILENO _fileno -#else -# define FILENO fileno -#endif - -#if defined (PP_OS_WIN) -# define PATH_SEPARATOR '\\' -#else -# define PATH_SEPARATOR '/' -#endif - -#if defined (RPP_JAMBI) -# include "rxx_allocator.h" -#else -# include "rpp-allocator.h" -#endif - -#if defined (_MSC_VER) -# define pp_snprintf _snprintf -#else -# define pp_snprintf snprintf -#endif - -#include "pp-fwd.h" -#include "pp-cctype.h" -#include "pp-string.h" -#include "pp-symbol.h" -#include "pp-internal.h" -#include "pp-iterator.h" -#include "pp-macro.h" -#include "pp-environment.h" -#include "pp-scanner.h" -#include "pp-macro-expander.h" -#include "pp-engine.h" -#include "pp-engine-bits.h" - -#endif // PP_H - -// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/generator/parser/rpp/preprocessor.cpp b/generator/parser/rpp/preprocessor.cpp deleted file mode 100644 index 55c804809..000000000 --- a/generator/parser/rpp/preprocessor.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt Script Generator project on Qt Labs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "preprocessor.h" - -#include - -// register callback for include hooks -static void includeFileHook(const std::string &, const std::string &, FILE *); - -#define PP_HOOK_ON_FILE_INCLUDED(A, B, C) includeFileHook(A, B, C) -#include "pp.h" - -using namespace rpp; - -#include - -class PreprocessorPrivate -{ -public: - QByteArray result; - pp_environment env; - QStringList includePaths; - - void initPP(pp &proc) - { - for (QString path : includePaths) - proc.push_include_path(path.toStdString()); - } -}; - -QHash includedFiles; - -void includeFileHook(const std::string &fileName, const std::string &filePath, FILE *) -{ - includedFiles[QString::fromStdString(fileName)].append(QString::fromStdString(filePath)); -} - -Preprocessor::Preprocessor() -{ - d = new PreprocessorPrivate; - includedFiles.clear(); -} - -Preprocessor::~Preprocessor() -{ - delete d; -} - -void Preprocessor::processFile(const QString &fileName) -{ - pp proc(d->env); - d->initPP(proc); - - d->result.reserve(d->result.size() + 20 * 1024); - - d->result += "# 1 \"" + fileName.toLatin1() + "\"\n"; // ### REMOVE ME - proc.file(fileName.toLocal8Bit().constData(), std::back_inserter(d->result)); -} - -void Preprocessor::processString(const QByteArray &str) -{ - pp proc(d->env); - d->initPP(proc); - - proc(str.begin(), str.end(), std::back_inserter(d->result)); -} - -QByteArray Preprocessor::result() const -{ - return d->result; -} - -void Preprocessor::addIncludePaths(const QStringList &includePaths) -{ - d->includePaths += includePaths; -} - -QStringList Preprocessor::macroNames() const -{ - QStringList macros; - - pp_environment::const_iterator it = d->env.first_macro(); - while (it != d->env.last_macro()) { - const pp_macro *m = *it; - macros += QString::fromLatin1(m->name->begin(), m->name->size()); - ++it; - } - - return macros; -} - -QList Preprocessor::macros() const -{ - QList items; - - pp_environment::const_iterator it = d->env.first_macro(); - while (it != d->env.last_macro()) { - const pp_macro *m = *it; - MacroItem item; - item.name = QString::fromLatin1(m->name->begin(), m->name->size()); - item.definition = QString::fromLatin1(m->definition->begin(), - m->definition->size()); - for (size_t i = 0; i < m->formals.size(); ++i) { - item.parameters += QString::fromLatin1(m->formals[i]->begin(), - m->formals[i]->size()); - } - item.isFunctionLike = m->is.function_like; - -#ifdef PP_WITH_MACRO_POSITION - item.fileName = QString::fromLatin1(m->file->begin(), m->file->size()); -#endif - items += item; - - ++it; - } - - return items; -} - -/* -int main() -{ - Preprocessor pp; - - QStringList paths; - paths << "/usr/include"; - pp.addIncludePaths(paths); - - pp.processFile("pp-configuration"); - pp.processFile("/usr/include/stdio.h"); - - qDebug() << pp.result(); - - return 0; -} -*/ - diff --git a/generator/parser/rpp/preprocessor.h b/generator/parser/rpp/preprocessor.h deleted file mode 100644 index 93231aeb9..000000000 --- a/generator/parser/rpp/preprocessor.h +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt Script Generator project on Qt Labs. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef PREPROCESSOR_H -#define PREPROCESSOR_H - -#include -#include -#include - -class QByteArray; -class PreprocessorPrivate; - -class Preprocessor -{ -public: - Preprocessor(); - ~Preprocessor(); - - void processFile(const QString &fileName); - void processString(const QByteArray &str); - - void addIncludePaths(const QStringList &includePaths); - - QByteArray result() const; - - QStringList macroNames() const; - - struct MacroItem - { - QString name; - QStringList parameters; - QString definition; - bool isFunctionLike; -#ifdef PP_WITH_MACRO_POSITION - QString fileName; -#endif - }; - QList macros() const; - -private: - Q_DISABLE_COPY(Preprocessor) - PreprocessorPrivate *d; -}; - -#endif diff --git a/generator/parser/rpp/rpp.pri b/generator/parser/rpp/rpp.pri deleted file mode 100644 index 46f6a98af..000000000 --- a/generator/parser/rpp/rpp.pri +++ /dev/null @@ -1,20 +0,0 @@ -SOURCES += \ - $$RXXPATH/rpp/preprocessor.cpp - -HEADERS += \ - $$RXXPATH/rpp/pp-cctype.h \ - $$RXXPATH/rpp/pp-engine-bits.h \ - $$RXXPATH/rpp/pp-engine.h \ - $$RXXPATH/rpp/pp-environment.h \ - $$RXXPATH/rpp/pp-fwd.h \ - $$RXXPATH/rpp/pp-internal.h \ - $$RXXPATH/rpp/pp-iterator.h \ - $$RXXPATH/rpp/pp-macro-expander.h \ - $$RXXPATH/rpp/pp-macro.h \ - $$RXXPATH/rpp/pp-scanner.h \ - $$RXXPATH/rpp/pp-string.h \ - $$RXXPATH/rpp/pp-symbol.h \ - $$RXXPATH/rpp/pp.h \ - $$RXXPATH/rpp/preprocessor.h - -INCLUDEPATH += $$PWD $$RXXPATH/rpp diff --git a/generator/parser/rxx.pri b/generator/parser/rxx.pri index 867deb7ce..0f7f0382a 100644 --- a/generator/parser/rxx.pri +++ b/generator/parser/rxx.pri @@ -20,7 +20,6 @@ HEADERS += $$RXXPATH/ast.h \ $$RXXPATH/dumptree.h \ $$RXXPATH/binder.h \ $$RXXPATH/codemodel.h \ - $$RXXPATH/codemodel_pointer.h \ $$RXXPATH/codemodel_fwd.h \ $$RXXPATH/type_compiler.h \ $$RXXPATH/name_compiler.h \ diff --git a/generator/qtscript_masterinclude.h b/generator/qtscript_masterinclude.h index 221f558aa..0d4c47597 100644 --- a/generator/qtscript_masterinclude.h +++ b/generator/qtscript_masterinclude.h @@ -38,6 +38,44 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ + + +// Qt +#define QOBJECTDEFS_H +#define QTMETAMACROS_H + +// not yet supported +#define Q_SLOTS slots +#define Q_SIGNALS signals +#define Q_FLAGS(a) +#define Q_FLAG(a) +#define Q_PRIVATE_SLOT(a, b) +#define Q_DECLARE_INTERFACE(a,b) +#define Q_INTERFACES(a) +#define Q_GADGET +#define Q_OVERRIDE(a) +#define Q_OS_OS2 +#define Q_NO_USING_KEYWORD +#define Q_DECL_OVERRIDE override + +// There are symbols in Qt that exist in Debug but +// not in release +#define QT_NO_DEBUG + +#define QT_JAMBI_RUN + +// Qt6 +#define Q_NAMESPACE_EXPORT(...) +#define Q_ENUM_NS(x) +#define Q_FLAG_NS(x) +#define Q_MOC_INCLUDE(...) + +// ignore static_assert +#define static_assert(...) + + + + // We need to force the endianess in Qt5 #define Q_BYTE_ORDER Q_LITTLE_ENDIAN @@ -75,33 +113,16 @@ #if QT_VERSION >= 0x060700 -// override Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE et al to return something the parser understands: -#include -#define Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(TYPE) \ - friend bool operator==(const TYPE& lhs, const TYPE& rhs); \ - friend bool operator!=(const TYPE& lhs, const TYPE& rhs); \ - friend bool operator< (const TYPE& lhs, const TYPE& rhs); \ - friend bool operator<=(const TYPE& lhs, const TYPE& rhs); \ - friend bool operator> (const TYPE& lhs, const TYPE& rhs); \ - friend bool operator>=(const TYPE& lhs, const TYPE& rhs); - -#define Q_DECLARE_WEAKLY_ORDERED_LITERAL_TYPE(TYPE) Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(TYPE) -#define Q_DECLARE_PARTIALLY_ORDERED_LITERAL_TYPE(TYPE) Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(TYPE) - -#define Q_DECLARE_STRONGLY_ORDERED(TYPE) Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(TYPE) -#define Q_DECLARE_WEAKLY_ORDERED(TYPE) Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(TYPE) -#define Q_DECLARE_PARTIALLY_ORDERED Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(TYPE) - -#define Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(TYPE) \ - public: \ - bool operator==(const TYPE& rhs) const; \ - bool operator!=(const TYPE& rhs) const; \ - private: - -#define Q_DECLARE_EQUALITY_COMPARABLE(TYPE) Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(TYPE) + +// adjust macro to work with simplecpp +#define QT_OVERLOADED_MACRO(MACRO, ...) QT_OVERLOADED_MACRO_IMP(MACRO, QT_VA_ARGS_COUNT(__VA_ARGS__))(__VA_ARGS__) #endif +// Workaround: Modify definition of QT_DEFINE_TAG to not include type definition in variable definition, +// which is currently not handled by the generator parser - and also isn't needed for the wrappers, as far as I can see +// Note: This fixes some missing enums from qnamespace.h in Qt 6.8 +#define QT_DEFINE_TAG(x) constexpr struct x##_t x; // it seems this can be safely ignored (otherwise generator currently stumbles over use of noexcept): #define Q_DECLARE_SHARED(TYPE) @@ -122,9 +143,11 @@ #define QDOC_PROPERTY(text) Q_PROPERTY(text) // don't need this: -#define Q_REVISION(v) +#define Q_REVISION(...) #define Q_DECLARE_OPERATORS_FOR_FLAGS(x) #define Q_GADGET_EXPORT(x) +#define Q_DECLARE_TYPEINFO(...) +#define Q_DECLARE_MIXED_ENUM_OPERATORS_SYMMETRIC(...) #include diff --git a/generator/shellgenerator.cpp b/generator/shellgenerator.cpp index 9886fe089..2bfb79330 100644 --- a/generator/shellgenerator.cpp +++ b/generator/shellgenerator.cpp @@ -135,6 +135,20 @@ namespace { } } +namespace { + + QString trimSpaces(const QString& expr) + { + QString result = expr; + if (!result.contains("\"") && !result.contains("'")) { + // assume all spaces are expendable (some new spaces were introduced by the new simplecpp preprocessor) + result.replace(" ", ""); + } + return result; + } + +}; + void ShellGenerator::writeFunctionArguments(QTextStream &s, const AbstractMetaFunction *meta_function, @@ -167,9 +181,9 @@ void ShellGenerator::writeFunctionArguments(QTextStream &s, } } if ((option & IncludeDefaultExpression) && !arg->defaultValueExpression().isEmpty()) { - s << " = "; + s << " = "; - QString expr = arg->defaultValueExpression(); + QString expr = trimSpaces(arg->defaultValueExpression()); if (expr == "NULL") { expr = "nullptr"; diff --git a/generator/simplecpp/LICENSE b/generator/simplecpp/LICENSE new file mode 100644 index 000000000..b1f013e92 --- /dev/null +++ b/generator/simplecpp/LICENSE @@ -0,0 +1,14 @@ +BSD Zero Clause License + +Copyright (c) 2023 simplecpp team + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/generator/simplecpp/README.txt b/generator/simplecpp/README.txt new file mode 100644 index 000000000..48fc87ca3 --- /dev/null +++ b/generator/simplecpp/README.txt @@ -0,0 +1,5 @@ +This code is taken from https://github.com/danmar/simplecpp, version 1.2.0 + +The code was released under the 0BSD license (see LICENSE file). + +The code is lightly patched (see do_not_stop_on_error.patch). diff --git a/generator/simplecpp/do_not_stop_on_error.patch b/generator/simplecpp/do_not_stop_on_error.patch new file mode 100644 index 000000000..ba0eaa2db --- /dev/null +++ b/generator/simplecpp/do_not_stop_on_error.patch @@ -0,0 +1,17 @@ +diff --git generator/simplecpp/simplecpp.cpp generator/simplecpp/simplecpp.cpp +index 3e9dda6c..ee8f7f7c 100644 +--- generator/simplecpp/simplecpp.cpp ++++ generator/simplecpp/simplecpp.cpp +@@ -3455,10 +3455,12 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL + err.msg = '#' + rawtok->str() + ' ' + err.msg; + outputList->push_back(err); + } ++/* Patched for PythonQt generator: Do not stop on #error directive: + if (rawtok->str() == ERROR) { + output.clear(); + return; + } ++*/ + } + + if (rawtok->str() == DEFINE) { diff --git a/generator/simplecpp/simplecpp.cpp b/generator/simplecpp/simplecpp.cpp new file mode 100644 index 000000000..ee8f7f7cd --- /dev/null +++ b/generator/simplecpp/simplecpp.cpp @@ -0,0 +1,3902 @@ +/* + * simplecpp - A simple and high-fidelity C/C++ preprocessor library + * Copyright (C) 2016-2023 simplecpp team + */ + +#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) +#define SIMPLECPP_WINDOWS +#define NOMINMAX +#endif + +#include "simplecpp.h" + +#include +#include +#include +#include +#include // IWYU pragma: keep +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if __cplusplus >= 201103L +#ifdef SIMPLECPP_WINDOWS +#include +#endif +#include +#endif +#include +#include + +#ifdef SIMPLECPP_WINDOWS +#include +#undef ERROR +#endif + +#if __cplusplus >= 201103L +#define OVERRIDE override +#define EXPLICIT explicit +#else +#define OVERRIDE +#define EXPLICIT +#endif + +#if (__cplusplus < 201103L) && !defined(__APPLE__) +#define nullptr NULL +#endif + +static bool isHex(const std::string &s) +{ + return s.size()>2 && (s.compare(0,2,"0x")==0 || s.compare(0,2,"0X")==0); +} + +static bool isOct(const std::string &s) +{ + return s.size()>1 && (s[0]=='0') && (s[1] >= '0') && (s[1] < '8'); +} + +// TODO: added an undercore since this conflicts with a function of the same name in utils.h from Cppcheck source when building Cppcheck with MSBuild +static bool isStringLiteral_(const std::string &s) +{ + return s.size() > 1 && (s[0]=='\"') && (*s.rbegin()=='\"'); +} + +// TODO: added an undercore since this conflicts with a function of the same name in utils.h from Cppcheck source when building Cppcheck with MSBuild +static bool isCharLiteral_(const std::string &s) +{ + // char literal patterns can include 'a', '\t', '\000', '\xff', 'abcd', and maybe '' + // This only checks for the surrounding '' but doesn't parse the content. + return s.size() > 1 && (s[0]=='\'') && (*s.rbegin()=='\''); +} + +static const simplecpp::TokenString DEFINE("define"); +static const simplecpp::TokenString UNDEF("undef"); + +static const simplecpp::TokenString INCLUDE("include"); + +static const simplecpp::TokenString ERROR("error"); +static const simplecpp::TokenString WARNING("warning"); + +static const simplecpp::TokenString IF("if"); +static const simplecpp::TokenString IFDEF("ifdef"); +static const simplecpp::TokenString IFNDEF("ifndef"); +static const simplecpp::TokenString DEFINED("defined"); +static const simplecpp::TokenString ELSE("else"); +static const simplecpp::TokenString ELIF("elif"); +static const simplecpp::TokenString ENDIF("endif"); + +static const simplecpp::TokenString PRAGMA("pragma"); +static const simplecpp::TokenString ONCE("once"); + +static const simplecpp::TokenString HAS_INCLUDE("__has_include"); + +template static std::string toString(T t) +{ + // NOLINTNEXTLINE(misc-const-correctness) - false positive + std::ostringstream ostr; + ostr << t; + return ostr.str(); +} + +#ifdef SIMPLECPP_DEBUG_MACRO_EXPANSION +static std::string locstring(const simplecpp::Location &loc) +{ + std::ostringstream ostr; + ostr << '[' << loc.file() << ':' << loc.line << ':' << loc.col << ']'; + return ostr.str(); +} +#endif + +static long long stringToLL(const std::string &s) +{ + long long ret; + const bool hex = isHex(s); + const bool oct = isOct(s); + std::istringstream istr(hex ? s.substr(2) : oct ? s.substr(1) : s); + if (hex) + istr >> std::hex; + else if (oct) + istr >> std::oct; + istr >> ret; + return ret; +} + +static unsigned long long stringToULL(const std::string &s) +{ + unsigned long long ret; + const bool hex = isHex(s); + const bool oct = isOct(s); + std::istringstream istr(hex ? s.substr(2) : oct ? s.substr(1) : s); + if (hex) + istr >> std::hex; + else if (oct) + istr >> std::oct; + istr >> ret; + return ret; +} + +static bool endsWith(const std::string &s, const std::string &e) +{ + return (s.size() >= e.size()) && std::equal(e.rbegin(), e.rend(), s.rbegin()); +} + +static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2) +{ + return tok1 && tok2 && tok1->location.sameline(tok2->location); +} + +static bool isAlternativeBinaryOp(const simplecpp::Token *tok, const std::string &alt) +{ + return (tok->name && + tok->str() == alt && + tok->previous && + tok->next && + (tok->previous->number || tok->previous->name || tok->previous->op == ')') && + (tok->next->number || tok->next->name || tok->next->op == '(')); +} + +static bool isAlternativeUnaryOp(const simplecpp::Token *tok, const std::string &alt) +{ + return ((tok->name && tok->str() == alt) && + (!tok->previous || tok->previous->op == '(') && + (tok->next && (tok->next->name || tok->next->number))); +} + +static std::string replaceAll(std::string s, const std::string& from, const std::string& to) +{ + for (size_t pos = s.find(from); pos != std::string::npos; pos = s.find(from, pos + to.size())) + s.replace(pos, from.size(), to); + return s; +} + +const std::string simplecpp::Location::emptyFileName; + +void simplecpp::Location::adjust(const std::string &str) +{ + if (strpbrk(str.c_str(), "\r\n") == nullptr) { + col += str.size(); + return; + } + + for (std::size_t i = 0U; i < str.size(); ++i) { + col++; + if (str[i] == '\n' || str[i] == '\r') { + col = 1; + line++; + if (str[i] == '\r' && (i+1)previous) + tok = tok->previous; + for (; tok; tok = tok->next) { + if (tok->previous) { + std::cout << (sameline(tok, tok->previous) ? ' ' : '\n'); + } + std::cout << tok->str(); + } + std::cout << std::endl; +} + +void simplecpp::Token::printOut() const +{ + for (const Token *tok = this; tok; tok = tok->next) { + if (tok != this) { + std::cout << (sameline(tok, tok->previous) ? ' ' : '\n'); + } + std::cout << tok->str(); + } + std::cout << std::endl; +} + +// cppcheck-suppress noConstructor - we call init() in the inherited to initialize the private members +class simplecpp::TokenList::Stream { +public: + virtual ~Stream() {} + + virtual int get() = 0; + virtual int peek() = 0; + virtual void unget() = 0; + virtual bool good() = 0; + + unsigned char readChar() { + unsigned char ch = static_cast(get()); + + // For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the + // character is non-ASCII character then replace it with 0xff + if (isUtf16) { + const unsigned char ch2 = static_cast(get()); + const int ch16 = makeUtf16Char(ch, ch2); + ch = static_cast(((ch16 >= 0x80) ? 0xff : ch16)); + } + + // Handling of newlines.. + if (ch == '\r') { + ch = '\n'; + + int ch2 = get(); + if (isUtf16) { + const int c2 = get(); + ch2 = makeUtf16Char(ch2, c2); + } + + if (ch2 != '\n') + ungetChar(); + } + + return ch; + } + + unsigned char peekChar() { + unsigned char ch = static_cast(peek()); + + // For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the + // character is non-ASCII character then replace it with 0xff + if (isUtf16) { + (void)get(); + const unsigned char ch2 = static_cast(peek()); + unget(); + const int ch16 = makeUtf16Char(ch, ch2); + ch = static_cast(((ch16 >= 0x80) ? 0xff : ch16)); + } + + // Handling of newlines.. + if (ch == '\r') + ch = '\n'; + + return ch; + } + + void ungetChar() { + unget(); + if (isUtf16) + unget(); + } + +protected: + void init() { + // initialize since we use peek() in getAndSkipBOM() + isUtf16 = false; + bom = getAndSkipBOM(); + isUtf16 = (bom == 0xfeff || bom == 0xfffe); + } + +private: + inline int makeUtf16Char(const unsigned char ch, const unsigned char ch2) const { + return (bom == 0xfeff) ? (ch<<8 | ch2) : (ch2<<8 | ch); + } + + unsigned short getAndSkipBOM() { + const int ch1 = peek(); + + // The UTF-16 BOM is 0xfffe or 0xfeff. + if (ch1 >= 0xfe) { + (void)get(); + const unsigned short byte = (static_cast(ch1) << 8); + if (peek() >= 0xfe) + return byte | static_cast(get()); + unget(); + return 0; + } + + // Skip UTF-8 BOM 0xefbbbf + if (ch1 == 0xef) { + (void)get(); + if (peek() == 0xbb) { + (void)get(); + if (peek() == 0xbf) { + (void)get(); + return 0; + } + unget(); + } + unget(); + } + + return 0; + } + + unsigned short bom; +protected: + bool isUtf16; +}; + +class StdIStream : public simplecpp::TokenList::Stream { +public: + // cppcheck-suppress uninitDerivedMemberVar - we call Stream::init() to initialize the private members + EXPLICIT StdIStream(std::istream &istr) + : istr(istr) { + assert(istr.good()); + init(); + } + + virtual int get() OVERRIDE { + return istr.get(); + } + virtual int peek() OVERRIDE { + return istr.peek(); + } + virtual void unget() OVERRIDE { + istr.unget(); + } + virtual bool good() OVERRIDE { + return istr.good(); + } + +private: + std::istream &istr; +}; + +class StdCharBufStream : public simplecpp::TokenList::Stream { +public: + // cppcheck-suppress uninitDerivedMemberVar - we call Stream::init() to initialize the private members + StdCharBufStream(const unsigned char* str, std::size_t size) + : str(str) + , size(size) + , pos(0) + , lastStatus(0) { + init(); + } + + virtual int get() OVERRIDE { + if (pos >= size) + return lastStatus = EOF; + return str[pos++]; + } + virtual int peek() OVERRIDE { + if (pos >= size) + return lastStatus = EOF; + return str[pos]; + } + virtual void unget() OVERRIDE { + --pos; + } + virtual bool good() OVERRIDE { + return lastStatus != EOF; + } + +private: + const unsigned char *str; + const std::size_t size; + std::size_t pos; + int lastStatus; +}; + +class FileStream : public simplecpp::TokenList::Stream { +public: + // cppcheck-suppress uninitDerivedMemberVar - we call Stream::init() to initialize the private members + EXPLICIT FileStream(const std::string &filename, std::vector &files) + : file(fopen(filename.c_str(), "rb")) + , lastCh(0) + , lastStatus(0) { + if (!file) { + files.push_back(filename); + throw simplecpp::Output(files, simplecpp::Output::FILE_NOT_FOUND, "File is missing: " + filename); + } + init(); + } + + ~FileStream() OVERRIDE { + fclose(file); + file = nullptr; + } + + virtual int get() OVERRIDE { + lastStatus = lastCh = fgetc(file); + return lastCh; + } + virtual int peek() OVERRIDE{ + // keep lastCh intact + const int ch = fgetc(file); + unget_internal(ch); + return ch; + } + virtual void unget() OVERRIDE { + unget_internal(lastCh); + } + virtual bool good() OVERRIDE { + return lastStatus != EOF; + } + +private: + void unget_internal(int ch) { + if (isUtf16) { + // TODO: use ungetc() as well + // UTF-16 has subsequent unget() calls + fseek(file, -1, SEEK_CUR); + } else + ungetc(ch, file); + } + + FileStream(const FileStream&); + FileStream &operator=(const FileStream&); + + FILE *file; + int lastCh; + int lastStatus; +}; + +simplecpp::TokenList::TokenList(std::vector &filenames) : frontToken(nullptr), backToken(nullptr), files(filenames) {} + +simplecpp::TokenList::TokenList(std::istream &istr, std::vector &filenames, const std::string &filename, OutputList *outputList) + : frontToken(nullptr), backToken(nullptr), files(filenames) +{ + StdIStream stream(istr); + readfile(stream,filename,outputList); +} + +simplecpp::TokenList::TokenList(const unsigned char* data, std::size_t size, std::vector &filenames, const std::string &filename, OutputList *outputList) + : frontToken(nullptr), backToken(nullptr), files(filenames) +{ + StdCharBufStream stream(data, size); + readfile(stream,filename,outputList); +} + +simplecpp::TokenList::TokenList(const char* data, std::size_t size, std::vector &filenames, const std::string &filename, OutputList *outputList) + : frontToken(nullptr), backToken(nullptr), files(filenames) +{ + StdCharBufStream stream(reinterpret_cast(data), size); + readfile(stream,filename,outputList); +} + +simplecpp::TokenList::TokenList(const std::string &filename, std::vector &filenames, OutputList *outputList) + : frontToken(nullptr), backToken(nullptr), files(filenames) +{ + try { + FileStream stream(filename, filenames); + readfile(stream,filename,outputList); + } catch (const simplecpp::Output & e) { // TODO handle extra type of errors + outputList->push_back(e); + } +} + +simplecpp::TokenList::TokenList(const TokenList &other) : frontToken(nullptr), backToken(nullptr), files(other.files) +{ + *this = other; +} + +#if __cplusplus >= 201103L +simplecpp::TokenList::TokenList(TokenList &&other) : frontToken(nullptr), backToken(nullptr), files(other.files) +{ + *this = std::move(other); +} +#endif + +simplecpp::TokenList::~TokenList() +{ + clear(); +} + +simplecpp::TokenList &simplecpp::TokenList::operator=(const TokenList &other) +{ + if (this != &other) { + clear(); + files = other.files; + for (const Token *tok = other.cfront(); tok; tok = tok->next) + push_back(new Token(*tok)); + sizeOfType = other.sizeOfType; + } + return *this; +} + +#if __cplusplus >= 201103L +simplecpp::TokenList &simplecpp::TokenList::operator=(TokenList &&other) +{ + if (this != &other) { + clear(); + frontToken = other.frontToken; + other.frontToken = nullptr; + backToken = other.backToken; + other.backToken = nullptr; + files = other.files; + sizeOfType = std::move(other.sizeOfType); + } + return *this; +} +#endif + +void simplecpp::TokenList::clear() +{ + backToken = nullptr; + while (frontToken) { + Token * const next = frontToken->next; + delete frontToken; + frontToken = next; + } + sizeOfType.clear(); +} + +void simplecpp::TokenList::push_back(Token *tok) +{ + if (!frontToken) + frontToken = tok; + else + backToken->next = tok; + tok->previous = backToken; + backToken = tok; +} + +void simplecpp::TokenList::dump() const +{ + std::cout << stringify() << std::endl; +} + +std::string simplecpp::TokenList::stringify() const +{ + std::ostringstream ret; + Location loc(files); + for (const Token *tok = cfront(); tok; tok = tok->next) { + if (tok->location.line < loc.line || tok->location.fileIndex != loc.fileIndex) { + ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n"; + loc = tok->location; + } + + while (tok->location.line > loc.line) { + ret << '\n'; + loc.line++; + } + + if (sameline(tok->previous, tok)) + ret << ' '; + + ret << tok->str(); + + loc.adjust(tok->str()); + } + + return ret.str(); +} + +static bool isNameChar(unsigned char ch) +{ + return std::isalnum(ch) || ch == '_' || ch == '$'; +} + +static std::string escapeString(const std::string &str) +{ + std::ostringstream ostr; + ostr << '\"'; + for (std::size_t i = 1U; i < str.size() - 1; ++i) { + const char c = str[i]; + if (c == '\\' || c == '\"' || c == '\'') + ostr << '\\'; + ostr << c; + } + ostr << '\"'; + return ostr.str(); +} + +static void portabilityBackslash(simplecpp::OutputList *outputList, const std::vector &files, const simplecpp::Location &location) +{ + if (!outputList) + return; + simplecpp::Output err(files); + err.type = simplecpp::Output::PORTABILITY_BACKSLASH; + err.location = location; + err.msg = "Combination 'backslash space newline' is not portable."; + outputList->push_back(err); +} + +static bool isStringLiteralPrefix(const std::string &str) +{ + return str == "u" || str == "U" || str == "L" || str == "u8" || + str == "R" || str == "uR" || str == "UR" || str == "LR" || str == "u8R"; +} + +void simplecpp::TokenList::lineDirective(unsigned int fileIndex, unsigned int line, Location *location) +{ + if (fileIndex != location->fileIndex || line >= location->line) { + location->fileIndex = fileIndex; + location->line = line; + return; + } + + if (line + 2 >= location->line) { + location->line = line; + while (cback()->op != '#') + deleteToken(back()); + deleteToken(back()); + return; + } +} + +static const std::string COMMENT_END("*/"); + +void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename, OutputList *outputList) +{ + std::stack loc; + + unsigned int multiline = 0U; + + const Token *oldLastToken = nullptr; + + Location location(files); + location.fileIndex = fileIndex(filename); + location.line = 1U; + location.col = 1U; + while (stream.good()) { + unsigned char ch = stream.readChar(); + if (!stream.good()) + break; + + if (ch >= 0x80) { + if (outputList) { + simplecpp::Output err(files); + err.type = simplecpp::Output::UNHANDLED_CHAR_ERROR; + err.location = location; + std::ostringstream s; + s << static_cast(ch); + err.msg = "The code contains unhandled character(s) (character code=" + s.str() + "). Neither unicode nor extended ascii is supported."; + outputList->push_back(err); + } + clear(); + return; + } + + if (ch == '\n') { + if (cback() && cback()->op == '\\') { + if (location.col > cback()->location.col + 1U) + portabilityBackslash(outputList, files, cback()->location); + ++multiline; + deleteToken(back()); + } else { + location.line += multiline + 1; + multiline = 0U; + } + if (!multiline) + location.col = 1; + + if (oldLastToken != cback()) { + oldLastToken = cback(); + if (!isLastLinePreprocessor()) + continue; + const std::string lastline(lastLine()); + if (lastline == "# file %str%") { + const Token *strtok = cback(); + while (strtok->comment) + strtok = strtok->previous; + loc.push(location); + location.fileIndex = fileIndex(strtok->str().substr(1U, strtok->str().size() - 2U)); + location.line = 1U; + } else if (lastline == "# line %num%") { + const Token *numtok = cback(); + while (numtok->comment) + numtok = numtok->previous; + lineDirective(location.fileIndex, std::atol(numtok->str().c_str()), &location); + } else if (lastline == "# %num% %str%" || lastline == "# line %num% %str%") { + const Token *strtok = cback(); + while (strtok->comment) + strtok = strtok->previous; + const Token *numtok = strtok->previous; + while (numtok->comment) + numtok = numtok->previous; + lineDirective(fileIndex(replaceAll(strtok->str().substr(1U, strtok->str().size() - 2U),"\\\\","\\")), + std::atol(numtok->str().c_str()), &location); + } + // #endfile + else if (lastline == "# endfile" && !loc.empty()) { + location = loc.top(); + loc.pop(); + } + } + + continue; + } + + if (ch <= ' ') { + location.col++; + continue; + } + + TokenString currentToken; + + if (cback() && cback()->location.line == location.line && cback()->previous && cback()->previous->op == '#') { + const Token* const llTok = lastLineTok(); + if (llTok && llTok->op == '#' && llTok->next && (llTok->next->str() == "error" || llTok->next->str() == "warning")) { + char prev = ' '; + while (stream.good() && (prev == '\\' || (ch != '\r' && ch != '\n'))) { + currentToken += ch; + prev = ch; + ch = stream.readChar(); + } + stream.ungetChar(); + push_back(new Token(currentToken, location)); + location.adjust(currentToken); + continue; + } + } + + // number or name + if (isNameChar(ch)) { + const bool num = std::isdigit(ch); + while (stream.good() && isNameChar(ch)) { + currentToken += ch; + ch = stream.readChar(); + if (num && ch=='\'' && isNameChar(stream.peekChar())) + ch = stream.readChar(); + } + + stream.ungetChar(); + } + + // comment + else if (ch == '/' && stream.peekChar() == '/') { + while (stream.good() && ch != '\r' && ch != '\n') { + currentToken += ch; + ch = stream.readChar(); + } + const std::string::size_type pos = currentToken.find_last_not_of(" \t"); + if (pos < currentToken.size() - 1U && currentToken[pos] == '\\') + portabilityBackslash(outputList, files, location); + if (currentToken[currentToken.size() - 1U] == '\\') { + ++multiline; + currentToken.erase(currentToken.size() - 1U); + } else { + stream.ungetChar(); + } + } + + // comment + else if (ch == '/' && stream.peekChar() == '*') { + currentToken = "/*"; + (void)stream.readChar(); + ch = stream.readChar(); + while (stream.good()) { + currentToken += ch; + if (currentToken.size() >= 4U && endsWith(currentToken, COMMENT_END)) + break; + ch = stream.readChar(); + } + // multiline.. + + std::string::size_type pos = 0; + while ((pos = currentToken.find("\\\n",pos)) != std::string::npos) { + currentToken.erase(pos,2); + ++multiline; + } + if (multiline || isLastLinePreprocessor()) { + pos = 0; + while ((pos = currentToken.find('\n',pos)) != std::string::npos) { + currentToken.erase(pos,1); + ++multiline; + } + } + } + + // string / char literal + else if (ch == '\"' || ch == '\'') { + std::string prefix; + if (cback() && cback()->name && isStringLiteralPrefix(cback()->str()) && + ((cback()->location.col + cback()->str().size()) == location.col) && + (cback()->location.line == location.line)) { + prefix = cback()->str(); + } + // C++11 raw string literal + if (ch == '\"' && !prefix.empty() && *cback()->str().rbegin() == 'R') { + std::string delim; + currentToken = ch; + prefix.resize(prefix.size() - 1); + ch = stream.readChar(); + while (stream.good() && ch != '(' && ch != '\n') { + delim += ch; + ch = stream.readChar(); + } + if (!stream.good() || ch == '\n') { + if (outputList) { + Output err(files); + err.type = Output::SYNTAX_ERROR; + err.location = location; + err.msg = "Invalid newline in raw string delimiter."; + outputList->push_back(err); + } + return; + } + const std::string endOfRawString(')' + delim + currentToken); + while (stream.good() && !(endsWith(currentToken, endOfRawString) && currentToken.size() > 1)) + currentToken += stream.readChar(); + if (!endsWith(currentToken, endOfRawString)) { + if (outputList) { + Output err(files); + err.type = Output::SYNTAX_ERROR; + err.location = location; + err.msg = "Raw string missing terminating delimiter."; + outputList->push_back(err); + } + return; + } + currentToken.erase(currentToken.size() - endOfRawString.size(), endOfRawString.size() - 1U); + currentToken = escapeString(currentToken); + currentToken.insert(0, prefix); + back()->setstr(currentToken); + location.adjust(currentToken); + if (currentToken.find_first_of("\r\n") == std::string::npos) + location.col += 2 + 2 * delim.size(); + else + location.col += 1 + delim.size(); + + continue; + } + + currentToken = readUntil(stream,location,ch,ch,outputList); + if (currentToken.size() < 2U) + // Error is reported by readUntil() + return; + + std::string s = currentToken; + std::string::size_type pos; + int newlines = 0; + while ((pos = s.find_first_of("\r\n")) != std::string::npos) { + s.erase(pos,1); + newlines++; + } + + if (prefix.empty()) + push_back(new Token(s, location, std::isspace(stream.peekChar()))); // push string without newlines + else + back()->setstr(prefix + s); + + if (newlines > 0) { + const Token * const llTok = lastLineTok(); + if (llTok && llTok->op == '#' && llTok->next && (llTok->next->str() == "define" || llTok->next->str() == "pragma") && llTok->next->next) { + multiline += newlines; + location.adjust(s); + continue; + } + } + + location.adjust(currentToken); + continue; + } + + else { + currentToken += ch; + } + + if (*currentToken.begin() == '<') { + const Token * const llTok = lastLineTok(); + if (llTok && llTok->op == '#' && llTok->next && llTok->next->str() == "include") { + currentToken = readUntil(stream, location, '<', '>', outputList); + if (currentToken.size() < 2U) + return; + } + } + + push_back(new Token(currentToken, location, std::isspace(stream.peekChar()))); + + if (multiline) + location.col += currentToken.size(); + else + location.adjust(currentToken); + } + + combineOperators(); +} + +void simplecpp::TokenList::constFold() +{ + while (cfront()) { + // goto last '(' + Token *tok = back(); + while (tok && tok->op != '(') + tok = tok->previous; + + // no '(', goto first token + if (!tok) + tok = front(); + + // Constant fold expression + constFoldUnaryNotPosNeg(tok); + constFoldMulDivRem(tok); + constFoldAddSub(tok); + constFoldShift(tok); + constFoldComparison(tok); + constFoldBitwise(tok); + constFoldLogicalOp(tok); + constFoldQuestionOp(&tok); + + // If there is no '(' we are done with the constant folding + if (tok->op != '(') + break; + + if (!tok->next || !tok->next->next || tok->next->next->op != ')') + break; + + tok = tok->next; + deleteToken(tok->previous); + deleteToken(tok->next); + } +} + +static bool isFloatSuffix(const simplecpp::Token *tok) +{ + if (!tok || tok->str().size() != 1U) + return false; + const char c = std::tolower(tok->str()[0]); + return c == 'f' || c == 'l'; +} + +void simplecpp::TokenList::combineOperators() +{ + std::stack executableScope; + executableScope.push(false); + for (Token *tok = front(); tok; tok = tok->next) { + if (tok->op == '{') { + if (executableScope.top()) { + executableScope.push(true); + continue; + } + const Token *prev = tok->previous; + while (prev && prev->isOneOf(";{}()")) + prev = prev->previous; + executableScope.push(prev && prev->op == ')'); + continue; + } + if (tok->op == '}') { + if (executableScope.size() > 1) + executableScope.pop(); + continue; + } + + if (tok->op == '.') { + // ellipsis ... + if (tok->next && tok->next->op == '.' && tok->next->location.col == (tok->location.col + 1) && + tok->next->next && tok->next->next->op == '.' && tok->next->next->location.col == (tok->location.col + 2)) { + tok->setstr("..."); + deleteToken(tok->next); + deleteToken(tok->next); + continue; + } + // float literals.. + if (tok->previous && tok->previous->number && sameline(tok->previous, tok) && tok->previous->str().find_first_of("._") == std::string::npos) { + tok->setstr(tok->previous->str() + '.'); + deleteToken(tok->previous); + if (sameline(tok, tok->next) && (isFloatSuffix(tok->next) || (tok->next && tok->next->startsWithOneOf("AaBbCcDdEeFfPp")))) { + tok->setstr(tok->str() + tok->next->str()); + deleteToken(tok->next); + } + } + if (tok->next && tok->next->number) { + tok->setstr(tok->str() + tok->next->str()); + deleteToken(tok->next); + } + } + // match: [0-9.]+E [+-] [0-9]+ + const char lastChar = tok->str()[tok->str().size() - 1]; + if (tok->number && !isOct(tok->str()) && + ((!isHex(tok->str()) && (lastChar == 'E' || lastChar == 'e')) || + (isHex(tok->str()) && (lastChar == 'P' || lastChar == 'p'))) && + tok->next && tok->next->isOneOf("+-") && tok->next->next && tok->next->next->number) { + tok->setstr(tok->str() + tok->next->op + tok->next->next->str()); + deleteToken(tok->next); + deleteToken(tok->next); + } + + if (tok->op == '\0' || !tok->next || tok->next->op == '\0') + continue; + if (!sameline(tok,tok->next)) + continue; + if (tok->location.col + 1U != tok->next->location.col) + continue; + + if (tok->next->op == '=' && tok->isOneOf("=!<>+-*/%&|^")) { + if (tok->op == '&' && !executableScope.top()) { + // don't combine &= if it is a anonymous reference parameter with default value: + // void f(x&=2) + int indentlevel = 0; + const Token *start = tok; + while (indentlevel >= 0 && start) { + if (start->op == ')') + ++indentlevel; + else if (start->op == '(') + --indentlevel; + else if (start->isOneOf(";{}")) + break; + start = start->previous; + } + if (indentlevel == -1 && start) { + const Token * const ftok = start; + bool isFuncDecl = ftok->name; + while (isFuncDecl) { + if (!start->name && start->str() != "::" && start->op != '*' && start->op != '&') + isFuncDecl = false; + if (!start->previous) + break; + if (start->previous->isOneOf(";{}:")) + break; + start = start->previous; + } + isFuncDecl &= start != ftok && start->name; + if (isFuncDecl) { + // TODO: we could loop through the parameters here and check if they are correct. + continue; + } + } + } + tok->setstr(tok->str() + "="); + deleteToken(tok->next); + } else if ((tok->op == '|' || tok->op == '&') && tok->op == tok->next->op) { + tok->setstr(tok->str() + tok->next->str()); + deleteToken(tok->next); + } else if (tok->op == ':' && tok->next->op == ':') { + tok->setstr(tok->str() + tok->next->str()); + deleteToken(tok->next); + } else if (tok->op == '-' && tok->next->op == '>') { + tok->setstr(tok->str() + tok->next->str()); + deleteToken(tok->next); + } else if ((tok->op == '<' || tok->op == '>') && tok->op == tok->next->op) { + tok->setstr(tok->str() + tok->next->str()); + deleteToken(tok->next); + if (tok->next && tok->next->op == '=' && tok->next->next && tok->next->next->op != '=') { + tok->setstr(tok->str() + tok->next->str()); + deleteToken(tok->next); + } + } else if ((tok->op == '+' || tok->op == '-') && tok->op == tok->next->op) { + if (tok->location.col + 1U != tok->next->location.col) + continue; + if (tok->previous && tok->previous->number) + continue; + if (tok->next->next && tok->next->next->number) + continue; + tok->setstr(tok->str() + tok->next->str()); + deleteToken(tok->next); + } + } +} + +static const std::string COMPL("compl"); +static const std::string NOT("not"); +void simplecpp::TokenList::constFoldUnaryNotPosNeg(simplecpp::Token *tok) +{ + for (; tok && tok->op != ')'; tok = tok->next) { + // "not" might be ! + if (isAlternativeUnaryOp(tok, NOT)) + tok->op = '!'; + // "compl" might be ~ + else if (isAlternativeUnaryOp(tok, COMPL)) + tok->op = '~'; + + if (tok->op == '!' && tok->next && tok->next->number) { + tok->setstr(tok->next->str() == "0" ? "1" : "0"); + deleteToken(tok->next); + } else if (tok->op == '~' && tok->next && tok->next->number) { + tok->setstr(toString(~stringToLL(tok->next->str()))); + deleteToken(tok->next); + } else { + if (tok->previous && (tok->previous->number || tok->previous->name)) + continue; + if (!tok->next || !tok->next->number) + continue; + switch (tok->op) { + case '+': + tok->setstr(tok->next->str()); + deleteToken(tok->next); + break; + case '-': + tok->setstr(tok->op + tok->next->str()); + deleteToken(tok->next); + break; + } + } + } +} + +void simplecpp::TokenList::constFoldMulDivRem(Token *tok) +{ + for (; tok && tok->op != ')'; tok = tok->next) { + if (!tok->previous || !tok->previous->number) + continue; + if (!tok->next || !tok->next->number) + continue; + + long long result; + if (tok->op == '*') + result = (stringToLL(tok->previous->str()) * stringToLL(tok->next->str())); + else if (tok->op == '/' || tok->op == '%') { + const long long rhs = stringToLL(tok->next->str()); + if (rhs == 0) + throw std::overflow_error("division/modulo by zero"); + const long long lhs = stringToLL(tok->previous->str()); + if (rhs == -1 && lhs == std::numeric_limits::min()) + throw std::overflow_error("division overflow"); + if (tok->op == '/') + result = (lhs / rhs); + else + result = (lhs % rhs); + } else + continue; + + tok = tok->previous; + tok->setstr(toString(result)); + deleteToken(tok->next); + deleteToken(tok->next); + } +} + +void simplecpp::TokenList::constFoldAddSub(Token *tok) +{ + for (; tok && tok->op != ')'; tok = tok->next) { + if (!tok->previous || !tok->previous->number) + continue; + if (!tok->next || !tok->next->number) + continue; + + long long result; + if (tok->op == '+') + result = stringToLL(tok->previous->str()) + stringToLL(tok->next->str()); + else if (tok->op == '-') + result = stringToLL(tok->previous->str()) - stringToLL(tok->next->str()); + else + continue; + + tok = tok->previous; + tok->setstr(toString(result)); + deleteToken(tok->next); + deleteToken(tok->next); + } +} + +void simplecpp::TokenList::constFoldShift(Token *tok) +{ + for (; tok && tok->op != ')'; tok = tok->next) { + if (!tok->previous || !tok->previous->number) + continue; + if (!tok->next || !tok->next->number) + continue; + + long long result; + if (tok->str() == "<<") + result = stringToLL(tok->previous->str()) << stringToLL(tok->next->str()); + else if (tok->str() == ">>") + result = stringToLL(tok->previous->str()) >> stringToLL(tok->next->str()); + else + continue; + + tok = tok->previous; + tok->setstr(toString(result)); + deleteToken(tok->next); + deleteToken(tok->next); + } +} + +static const std::string NOTEQ("not_eq"); +void simplecpp::TokenList::constFoldComparison(Token *tok) +{ + for (; tok && tok->op != ')'; tok = tok->next) { + if (isAlternativeBinaryOp(tok,NOTEQ)) + tok->setstr("!="); + + if (!tok->startsWithOneOf("<>=!")) + continue; + if (!tok->previous || !tok->previous->number) + continue; + if (!tok->next || !tok->next->number) + continue; + + int result; + if (tok->str() == "==") + result = (stringToLL(tok->previous->str()) == stringToLL(tok->next->str())); + else if (tok->str() == "!=") + result = (stringToLL(tok->previous->str()) != stringToLL(tok->next->str())); + else if (tok->str() == ">") + result = (stringToLL(tok->previous->str()) > stringToLL(tok->next->str())); + else if (tok->str() == ">=") + result = (stringToLL(tok->previous->str()) >= stringToLL(tok->next->str())); + else if (tok->str() == "<") + result = (stringToLL(tok->previous->str()) < stringToLL(tok->next->str())); + else if (tok->str() == "<=") + result = (stringToLL(tok->previous->str()) <= stringToLL(tok->next->str())); + else + continue; + + tok = tok->previous; + tok->setstr(toString(result)); + deleteToken(tok->next); + deleteToken(tok->next); + } +} + +static const std::string BITAND("bitand"); +static const std::string BITOR("bitor"); +static const std::string XOR("xor"); +void simplecpp::TokenList::constFoldBitwise(Token *tok) +{ + Token * const tok1 = tok; + for (const char *op = "&^|"; *op; op++) { + const std::string* alternativeOp; + if (*op == '&') + alternativeOp = &BITAND; + else if (*op == '|') + alternativeOp = &BITOR; + else + alternativeOp = &XOR; + for (tok = tok1; tok && tok->op != ')'; tok = tok->next) { + if (tok->op != *op && !isAlternativeBinaryOp(tok, *alternativeOp)) + continue; + if (!tok->previous || !tok->previous->number) + continue; + if (!tok->next || !tok->next->number) + continue; + long long result; + if (*op == '&') + result = (stringToLL(tok->previous->str()) & stringToLL(tok->next->str())); + else if (*op == '^') + result = (stringToLL(tok->previous->str()) ^ stringToLL(tok->next->str())); + else /*if (*op == '|')*/ + result = (stringToLL(tok->previous->str()) | stringToLL(tok->next->str())); + tok = tok->previous; + tok->setstr(toString(result)); + deleteToken(tok->next); + deleteToken(tok->next); + } + } +} + +static const std::string AND("and"); +static const std::string OR("or"); +void simplecpp::TokenList::constFoldLogicalOp(Token *tok) +{ + for (; tok && tok->op != ')'; tok = tok->next) { + if (tok->name) { + if (isAlternativeBinaryOp(tok,AND)) + tok->setstr("&&"); + else if (isAlternativeBinaryOp(tok,OR)) + tok->setstr("||"); + } + if (tok->str() != "&&" && tok->str() != "||") + continue; + if (!tok->previous || !tok->previous->number) + continue; + if (!tok->next || !tok->next->number) + continue; + + int result; + if (tok->str() == "||") + result = (stringToLL(tok->previous->str()) || stringToLL(tok->next->str())); + else /*if (tok->str() == "&&")*/ + result = (stringToLL(tok->previous->str()) && stringToLL(tok->next->str())); + + tok = tok->previous; + tok->setstr(toString(result)); + deleteToken(tok->next); + deleteToken(tok->next); + } +} + +void simplecpp::TokenList::constFoldQuestionOp(Token **tok1) +{ + bool gotoTok1 = false; + for (Token *tok = *tok1; tok && tok->op != ')'; tok = gotoTok1 ? *tok1 : tok->next) { + gotoTok1 = false; + if (tok->str() != "?") + continue; + if (!tok->previous || !tok->next || !tok->next->next) + throw std::runtime_error("invalid expression"); + if (!tok->previous->number) + continue; + if (tok->next->next->op != ':') + continue; + Token * const condTok = tok->previous; + Token * const trueTok = tok->next; + Token * const falseTok = trueTok->next->next; + if (!falseTok) + throw std::runtime_error("invalid expression"); + if (condTok == *tok1) + *tok1 = (condTok->str() != "0" ? trueTok : falseTok); + deleteToken(condTok->next); // ? + deleteToken(trueTok->next); // : + deleteToken(condTok->str() == "0" ? trueTok : falseTok); + deleteToken(condTok); + gotoTok1 = true; + } +} + +void simplecpp::TokenList::removeComments() +{ + Token *tok = frontToken; + while (tok) { + Token * const tok1 = tok; + tok = tok->next; + if (tok1->comment) + deleteToken(tok1); + } +} + +std::string simplecpp::TokenList::readUntil(Stream &stream, const Location &location, const char start, const char end, OutputList *outputList) +{ + std::string ret; + ret += start; + + bool backslash = false; + char ch = 0; + while (ch != end && ch != '\r' && ch != '\n' && stream.good()) { + ch = stream.readChar(); + if (backslash && ch == '\n') { + ch = 0; + backslash = false; + continue; + } + backslash = false; + ret += ch; + if (ch == '\\') { + bool update_ch = false; + char next = 0; + do { + next = stream.readChar(); + if (next == '\r' || next == '\n') { + ret.erase(ret.size()-1U); + backslash = (next == '\r'); + update_ch = false; + } else if (next == '\\') + update_ch = !update_ch; + ret += next; + } while (next == '\\'); + if (update_ch) + ch = next; + } + } + + if (!stream.good() || ch != end) { + clear(); + if (outputList) { + Output err(files); + err.type = Output::SYNTAX_ERROR; + err.location = location; + err.msg = std::string("No pair for character (") + start + "). Can't process file. File is either invalid or unicode, which is currently not supported."; + outputList->push_back(err); + } + return ""; + } + + return ret; +} + +std::string simplecpp::TokenList::lastLine(int maxsize) const +{ + std::string ret; + int count = 0; + for (const Token *tok = cback(); ; tok = tok->previous) { + if (!sameline(tok, cback())) { + break; + } + if (tok->comment) + continue; + if (++count > maxsize) + return ""; + if (!ret.empty()) + ret += ' '; + // add tokens in reverse for performance reasons + if (tok->str()[0] == '\"') + ret += "%rts%"; // %str% + else if (tok->number) + ret += "%mun%"; // %num% + else { + ret += tok->str(); + std::reverse(ret.end() - tok->str().length(), ret.end()); + } + } + std::reverse(ret.begin(), ret.end()); + return ret; +} + +const simplecpp::Token* simplecpp::TokenList::lastLineTok(int maxsize) const +{ + const Token* prevTok = nullptr; + int count = 0; + for (const Token *tok = cback(); ; tok = tok->previous) { + if (!sameline(tok, cback())) + break; + if (tok->comment) + continue; + if (++count > maxsize) + return nullptr; + prevTok = tok; + } + return prevTok; +} + +bool simplecpp::TokenList::isLastLinePreprocessor(int maxsize) const +{ + const Token * const prevTok = lastLineTok(maxsize); + return prevTok && prevTok->op == '#'; +} + +unsigned int simplecpp::TokenList::fileIndex(const std::string &filename) +{ + for (unsigned int i = 0; i < files.size(); ++i) { + if (files[i] == filename) + return i; + } + files.push_back(filename); + return files.size() - 1U; +} + + +namespace simplecpp { + class Macro; +#if __cplusplus >= 201103L + using MacroMap = std::unordered_map; +#else + typedef std::map MacroMap; +#endif + + class Macro { + public: + explicit Macro(std::vector &f) : nameTokDef(nullptr), valueToken(nullptr), endToken(nullptr), files(f), tokenListDefine(f), variadic(false), valueDefinedInCode_(false) {} + + Macro(const Token *tok, std::vector &f) : nameTokDef(nullptr), files(f), tokenListDefine(f), valueDefinedInCode_(true) { + if (sameline(tok->previousSkipComments(), tok)) + throw std::runtime_error("bad macro syntax"); + if (tok->op != '#') + throw std::runtime_error("bad macro syntax"); + const Token * const hashtok = tok; + tok = tok->next; + if (!tok || tok->str() != DEFINE) + throw std::runtime_error("bad macro syntax"); + tok = tok->next; + if (!tok || !tok->name || !sameline(hashtok,tok)) + throw std::runtime_error("bad macro syntax"); + if (!parseDefine(tok)) + throw std::runtime_error("bad macro syntax"); + } + + Macro(const std::string &name, const std::string &value, std::vector &f) : nameTokDef(nullptr), files(f), tokenListDefine(f), valueDefinedInCode_(false) { + const std::string def(name + ' ' + value); + StdCharBufStream stream(reinterpret_cast(def.data()), def.size()); + tokenListDefine.readfile(stream); + if (!parseDefine(tokenListDefine.cfront())) + throw std::runtime_error("bad macro syntax. macroname=" + name + " value=" + value); + } + + Macro(const Macro &other) : nameTokDef(nullptr), files(other.files), tokenListDefine(other.files), valueDefinedInCode_(other.valueDefinedInCode_) { + *this = other; + } + + Macro &operator=(const Macro &other) { + if (this != &other) { + files = other.files; + valueDefinedInCode_ = other.valueDefinedInCode_; + if (other.tokenListDefine.empty()) + parseDefine(other.nameTokDef); + else { + tokenListDefine = other.tokenListDefine; + parseDefine(tokenListDefine.cfront()); + } + usageList = other.usageList; + } + return *this; + } + + bool valueDefinedInCode() const { + return valueDefinedInCode_; + } + + /** + * Expand macro. This will recursively expand inner macros. + * @param output destination tokenlist + * @param rawtok macro token + * @param macros list of macros + * @param inputFiles the input files + * @return token after macro + * @throw Can throw wrongNumberOfParameters or invalidHashHash + */ + const Token * expand(TokenList * const output, + const Token * rawtok, + const MacroMap ¯os, + std::vector &inputFiles) const { + std::set expandedmacros; + +#ifdef SIMPLECPP_DEBUG_MACRO_EXPANSION + std::cout << "expand " << name() << " " << locstring(rawtok->location) << std::endl; +#endif + + TokenList output2(inputFiles); + + if (functionLike() && rawtok->next && rawtok->next->op == '(') { + // Copy macro call to a new tokenlist with no linebreaks + const Token * const rawtok1 = rawtok; + TokenList rawtokens2(inputFiles); + rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location, rawtok->whitespaceahead)); + rawtok = rawtok->next; + rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location, rawtok->whitespaceahead)); + rawtok = rawtok->next; + int par = 1; + while (rawtok && par > 0) { + if (rawtok->op == '(') + ++par; + else if (rawtok->op == ')') + --par; + else if (rawtok->op == '#' && !sameline(rawtok->previous, rawtok)) + throw Error(rawtok->location, "it is invalid to use a preprocessor directive as macro parameter"); + rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location, rawtok->whitespaceahead)); + rawtok = rawtok->next; + } + if (expand(&output2, rawtok1->location, rawtokens2.cfront(), macros, expandedmacros)) + rawtok = rawtok1->next; + } else { + rawtok = expand(&output2, rawtok->location, rawtok, macros, expandedmacros); + } + while (output2.cback() && rawtok) { + unsigned int par = 0; + Token* macro2tok = output2.back(); + while (macro2tok) { + if (macro2tok->op == '(') { + if (par==0) + break; + --par; + } else if (macro2tok->op == ')') + ++par; + macro2tok = macro2tok->previous; + } + if (macro2tok) { // macro2tok->op == '(' + macro2tok = macro2tok->previous; + expandedmacros.insert(name()); + } else if (rawtok->op == '(') + macro2tok = output2.back(); + if (!macro2tok || !macro2tok->name) + break; + if (output2.cfront() != output2.cback() && macro2tok->str() == this->name()) + break; + const MacroMap::const_iterator macro = macros.find(macro2tok->str()); + if (macro == macros.end() || !macro->second.functionLike()) + break; + TokenList rawtokens2(inputFiles); + const Location loc(macro2tok->location); + while (macro2tok) { + Token * const next = macro2tok->next; + rawtokens2.push_back(new Token(macro2tok->str(), loc)); + output2.deleteToken(macro2tok); + macro2tok = next; + } + par = (rawtokens2.cfront() != rawtokens2.cback()) ? 1U : 0U; + const Token *rawtok2 = rawtok; + for (; rawtok2; rawtok2 = rawtok2->next) { + rawtokens2.push_back(new Token(rawtok2->str(), loc)); + if (rawtok2->op == '(') + ++par; + else if (rawtok2->op == ')') { + if (par <= 1U) + break; + --par; + } + } + if (!rawtok2 || par != 1U) + break; + if (macro->second.expand(&output2, rawtok->location, rawtokens2.cfront(), macros, expandedmacros) != nullptr) + break; + rawtok = rawtok2->next; + } + output->takeTokens(output2); + return rawtok; + } + + /** macro name */ + const TokenString &name() const { + return nameTokDef->str(); + } + + /** location for macro definition */ + const Location &defineLocation() const { + return nameTokDef->location; + } + + /** how has this macro been used so far */ + const std::list &usage() const { + return usageList; + } + + /** is this a function like macro */ + bool functionLike() const { + return nameTokDef->next && + nameTokDef->next->op == '(' && + sameline(nameTokDef, nameTokDef->next) && + nameTokDef->next->location.col == nameTokDef->location.col + nameTokDef->str().size(); + } + + /** base class for errors */ + struct Error { + Error(const Location &loc, const std::string &s) : location(loc), what(s) {} + const Location location; + const std::string what; + }; + + /** Struct that is thrown when macro is expanded with wrong number of parameters */ + struct wrongNumberOfParameters : public Error { + wrongNumberOfParameters(const Location &loc, const std::string ¯oName) : Error(loc, "Wrong number of parameters for macro \'" + macroName + "\'.") {} + }; + + /** Struct that is thrown when there is invalid ## usage */ + struct invalidHashHash : public Error { + static inline std::string format(const std::string ¯oName, const std::string &message) { + return "Invalid ## usage when expanding \'" + macroName + "\': " + message; + } + + invalidHashHash(const Location &loc, const std::string ¯oName, const std::string &message) + : Error(loc, format(macroName, message)) { } + + static inline invalidHashHash unexpectedToken(const Location &loc, const std::string ¯oName, const Token *tokenA) { + return invalidHashHash(loc, macroName, "Unexpected token '"+ tokenA->str()+"'"); + } + + static inline invalidHashHash cannotCombine(const Location &loc, const std::string ¯oName, const Token *tokenA, const Token *tokenB) { + return invalidHashHash(loc, macroName, "Combining '"+ tokenA->str()+ "' and '"+ tokenB->str() + "' yields an invalid token."); + } + + static inline invalidHashHash unexpectedNewline(const Location &loc, const std::string ¯oName) { + return invalidHashHash(loc, macroName, "Unexpected newline"); + } + + static inline invalidHashHash universalCharacterUB(const Location &loc, const std::string ¯oName, const Token* tokenA, const std::string& strAB) { + return invalidHashHash(loc, macroName, "Combining '\\"+ tokenA->str()+ "' and '"+ strAB.substr(tokenA->str().size()) + "' yields universal character '\\" + strAB + "'. This is undefined behavior according to C standard chapter 5.1.1.2, paragraph 4."); + } + }; + private: + /** Create new token where Token::macro is set for replaced tokens */ + Token *newMacroToken(const TokenString &str, const Location &loc, bool replaced, const Token *expandedFromToken=nullptr) const { + Token *tok = new Token(str,loc); + if (replaced) + tok->macro = nameTokDef->str(); + if (expandedFromToken) + tok->setExpandedFrom(expandedFromToken, this); + return tok; + } + + bool parseDefine(const Token *nametoken) { + nameTokDef = nametoken; + variadic = false; + if (!nameTokDef) { + valueToken = endToken = nullptr; + args.clear(); + return false; + } + + // function like macro.. + if (functionLike()) { + args.clear(); + const Token *argtok = nameTokDef->next->next; + while (sameline(nametoken, argtok) && argtok->op != ')') { + if (argtok->str() == "..." && + argtok->next && argtok->next->op == ')') { + variadic = true; + if (!argtok->previous->name) + args.push_back("__VA_ARGS__"); + argtok = argtok->next; // goto ')' + break; + } + if (argtok->op != ',') + args.push_back(argtok->str()); + argtok = argtok->next; + } + if (!sameline(nametoken, argtok)) { + endToken = argtok ? argtok->previous : argtok; + valueToken = nullptr; + return false; + } + valueToken = argtok ? argtok->next : nullptr; + } else { + args.clear(); + valueToken = nameTokDef->next; + } + + if (!sameline(valueToken, nameTokDef)) + valueToken = nullptr; + endToken = valueToken; + while (sameline(endToken, nameTokDef)) + endToken = endToken->next; + return true; + } + + unsigned int getArgNum(const TokenString &str) const { + unsigned int par = 0; + while (par < args.size()) { + if (str == args[par]) + return par; + par++; + } + return ~0U; + } + + std::vector getMacroParameters(const Token *nameTokInst, bool calledInDefine) const { + if (!nameTokInst->next || nameTokInst->next->op != '(' || !functionLike()) + return std::vector(); + + std::vector parametertokens; + parametertokens.push_back(nameTokInst->next); + unsigned int par = 0U; + for (const Token *tok = nameTokInst->next->next; calledInDefine ? sameline(tok, nameTokInst) : (tok != nullptr); tok = tok->next) { + if (tok->op == '(') + ++par; + else if (tok->op == ')') { + if (par == 0U) { + parametertokens.push_back(tok); + break; + } + --par; + } else if (par == 0U && tok->op == ',' && (!variadic || parametertokens.size() < args.size())) + parametertokens.push_back(tok); + } + return parametertokens; + } + + const Token *appendTokens(TokenList *tokens, + const Location &rawloc, + const Token * const lpar, + const MacroMap ¯os, + const std::set &expandedmacros, + const std::vector ¶metertokens) const { + if (!lpar || lpar->op != '(') + return nullptr; + unsigned int par = 0; + const Token *tok = lpar; + while (sameline(lpar, tok)) { + if (tok->op == '#' && sameline(tok,tok->next) && tok->next->op == '#' && sameline(tok,tok->next->next)) { + // A##B => AB + tok = expandHashHash(tokens, rawloc, tok, macros, expandedmacros, parametertokens, false); + } else if (tok->op == '#' && sameline(tok, tok->next) && tok->next->op != '#') { + tok = expandHash(tokens, rawloc, tok, expandedmacros, parametertokens); + } else { + if (!expandArg(tokens, tok, rawloc, macros, expandedmacros, parametertokens)) { + tokens->push_back(new Token(*tok)); + if (tok->macro.empty() && (par > 0 || tok->str() != "(")) + tokens->back()->macro = name(); + } + + if (tok->op == '(') + ++par; + else if (tok->op == ')') { + --par; + if (par == 0U) + break; + } + tok = tok->next; + } + } + for (Token *tok2 = tokens->front(); tok2; tok2 = tok2->next) + tok2->location = lpar->location; + return sameline(lpar,tok) ? tok : nullptr; + } + + const Token * expand(TokenList * const output, const Location &loc, const Token * const nameTokInst, const MacroMap ¯os, std::set expandedmacros) const { + expandedmacros.insert(nameTokInst->str()); + +#ifdef SIMPLECPP_DEBUG_MACRO_EXPANSION + std::cout << " expand " << name() << " " << locstring(defineLocation()) << std::endl; +#endif + + usageList.push_back(loc); + + if (nameTokInst->str() == "__FILE__") { + output->push_back(new Token('\"'+loc.file()+'\"', loc)); + return nameTokInst->next; + } + if (nameTokInst->str() == "__LINE__") { + output->push_back(new Token(toString(loc.line), loc)); + return nameTokInst->next; + } + if (nameTokInst->str() == "__COUNTER__") { + output->push_back(new Token(toString(usageList.size()-1U), loc)); + return nameTokInst->next; + } + + const bool calledInDefine = (loc.fileIndex != nameTokInst->location.fileIndex || + loc.line < nameTokInst->location.line); + + std::vector parametertokens1(getMacroParameters(nameTokInst, calledInDefine)); + + if (functionLike()) { + // No arguments => not macro expansion + if (nameTokInst->next && nameTokInst->next->op != '(') { + output->push_back(new Token(nameTokInst->str(), loc)); + return nameTokInst->next; + } + + // Parse macro-call + if (variadic) { + if (parametertokens1.size() < args.size()) { + throw wrongNumberOfParameters(nameTokInst->location, name()); + } + } else { + if (parametertokens1.size() != args.size() + (args.empty() ? 2U : 1U)) + throw wrongNumberOfParameters(nameTokInst->location, name()); + } + } + + // If macro call uses __COUNTER__ then expand that first + TokenList tokensparams(files); + std::vector parametertokens2; + if (!parametertokens1.empty()) { + bool counter = false; + for (const Token *tok = parametertokens1[0]; tok != parametertokens1.back(); tok = tok->next) { + if (tok->str() == "__COUNTER__") { + counter = true; + break; + } + } + + const MacroMap::const_iterator m = macros.find("__COUNTER__"); + + if (!counter || m == macros.end()) + parametertokens2.swap(parametertokens1); + else { + const Macro &counterMacro = m->second; + unsigned int par = 0; + for (const Token *tok = parametertokens1[0]; tok && par < parametertokens1.size(); tok = tok->next) { + if (tok->str() == "__COUNTER__") { + tokensparams.push_back(new Token(toString(counterMacro.usageList.size()), tok->location)); + counterMacro.usageList.push_back(tok->location); + } else { + tokensparams.push_back(new Token(*tok)); + if (tok == parametertokens1[par]) { + parametertokens2.push_back(tokensparams.cback()); + par++; + } + } + } + } + } + + Token * const output_end_1 = output->back(); + + // expand + for (const Token *tok = valueToken; tok != endToken;) { + if (tok->op != '#') { + // A##B => AB + if (sameline(tok, tok->next) && tok->next && tok->next->op == '#' && tok->next->next && tok->next->next->op == '#') { + if (!sameline(tok, tok->next->next->next)) + throw invalidHashHash::unexpectedNewline(tok->location, name()); + if (variadic && tok->op == ',' && tok->next->next->next->str() == args.back()) { + Token *const comma = newMacroToken(tok->str(), loc, isReplaced(expandedmacros), tok); + output->push_back(comma); + tok = expandToken(output, loc, tok->next->next->next, macros, expandedmacros, parametertokens2); + if (output->back() == comma) + output->deleteToken(comma); + continue; + } + TokenList new_output(files); + if (!expandArg(&new_output, tok, parametertokens2)) + output->push_back(newMacroToken(tok->str(), loc, isReplaced(expandedmacros), tok)); + else if (new_output.empty()) // placemarker token + output->push_back(newMacroToken("", loc, isReplaced(expandedmacros))); + else + for (const Token *tok2 = new_output.cfront(); tok2; tok2 = tok2->next) + output->push_back(newMacroToken(tok2->str(), loc, isReplaced(expandedmacros), tok2)); + tok = tok->next; + } else { + tok = expandToken(output, loc, tok, macros, expandedmacros, parametertokens2); + } + continue; + } + + int numberOfHash = 1; + const Token *hashToken = tok->next; + while (sameline(tok,hashToken) && hashToken->op == '#') { + hashToken = hashToken->next; + ++numberOfHash; + } + if (numberOfHash == 4 && tok->next->location.col + 1 == tok->next->next->location.col) { + // # ## # => ## + output->push_back(newMacroToken("##", loc, isReplaced(expandedmacros))); + tok = hashToken; + continue; + } + + if (numberOfHash >= 2 && tok->location.col + 1 < tok->next->location.col) { + output->push_back(new Token(*tok)); + tok = tok->next; + continue; + } + + tok = tok->next; + if (tok == endToken) { + output->push_back(new Token(*tok->previous)); + break; + } + if (tok->op == '#') { + // A##B => AB + tok = expandHashHash(output, loc, tok->previous, macros, expandedmacros, parametertokens2); + } else { + // #123 => "123" + tok = expandHash(output, loc, tok->previous, expandedmacros, parametertokens2); + } + } + + if (!functionLike()) { + for (Token *tok = output_end_1 ? output_end_1->next : output->front(); tok; tok = tok->next) { + tok->macro = nameTokInst->str(); + } + } + + if (!parametertokens1.empty()) + parametertokens1.swap(parametertokens2); + + return functionLike() ? parametertokens2.back()->next : nameTokInst->next; + } + + const Token *recursiveExpandToken(TokenList *output, TokenList &temp, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { + if (!(temp.cback() && temp.cback()->name && tok->next && tok->next->op == '(')) { + output->takeTokens(temp); + return tok->next; + } + + if (!sameline(tok, tok->next)) { + output->takeTokens(temp); + return tok->next; + } + + const MacroMap::const_iterator it = macros.find(temp.cback()->str()); + if (it == macros.end() || expandedmacros.find(temp.cback()->str()) != expandedmacros.end()) { + output->takeTokens(temp); + return tok->next; + } + + const Macro &calledMacro = it->second; + if (!calledMacro.functionLike()) { + output->takeTokens(temp); + return tok->next; + } + + TokenList temp2(files); + temp2.push_back(new Token(temp.cback()->str(), tok->location)); + + const Token * const tok2 = appendTokens(&temp2, loc, tok->next, macros, expandedmacros, parametertokens); + if (!tok2) + return tok->next; + output->takeTokens(temp); + output->deleteToken(output->back()); + calledMacro.expand(output, loc, temp2.cfront(), macros, expandedmacros); + return tok2->next; + } + + const Token *expandToken(TokenList *output, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { + // Not name.. + if (!tok->name) { + output->push_back(newMacroToken(tok->str(), loc, true, tok)); + return tok->next; + } + + // Macro parameter.. + { + TokenList temp(files); + if (tok->str() == "__VA_OPT__") { + if (sameline(tok, tok->next) && tok->next->str() == "(") { + tok = tok->next; + int paren = 1; + while (sameline(tok, tok->next)) { + if (tok->next->str() == "(") + ++paren; + else if (tok->next->str() == ")") + --paren; + if (paren == 0) + return tok->next->next; + tok = tok->next; + if (parametertokens.size() > args.size() && parametertokens.front()->next->str() != ")") + tok = expandToken(output, loc, tok, macros, expandedmacros, parametertokens)->previous; + } + } + throw Error(tok->location, "Missing parenthesis for __VA_OPT__(content)"); + } + if (expandArg(&temp, tok, loc, macros, expandedmacros, parametertokens)) { + if (tok->str() == "__VA_ARGS__" && temp.empty() && output->cback() && output->cback()->str() == "," && + tok->nextSkipComments() && tok->nextSkipComments()->str() == ")") + output->deleteToken(output->back()); + return recursiveExpandToken(output, temp, loc, tok, macros, expandedmacros, parametertokens); + } + } + + // Macro.. + const MacroMap::const_iterator it = macros.find(tok->str()); + if (it != macros.end() && expandedmacros.find(tok->str()) == expandedmacros.end()) { + std::set expandedmacros2(expandedmacros); + expandedmacros2.insert(tok->str()); + + const Macro &calledMacro = it->second; + if (!calledMacro.functionLike()) { + TokenList temp(files); + calledMacro.expand(&temp, loc, tok, macros, expandedmacros); + return recursiveExpandToken(output, temp, loc, tok, macros, expandedmacros2, parametertokens); + } + if (!sameline(tok, tok->next)) { + output->push_back(newMacroToken(tok->str(), loc, true, tok)); + return tok->next; + } + TokenList tokens(files); + tokens.push_back(new Token(*tok)); + const Token * tok2 = nullptr; + if (tok->next->op == '(') + tok2 = appendTokens(&tokens, loc, tok->next, macros, expandedmacros, parametertokens); + else if (expandArg(&tokens, tok->next, loc, macros, expandedmacros, parametertokens)) { + tokens.front()->location = loc; + if (tokens.cfront()->next && tokens.cfront()->next->op == '(') + tok2 = tok->next; + } + if (!tok2) { + output->push_back(newMacroToken(tok->str(), loc, true, tok)); + return tok->next; + } + TokenList temp(files); + calledMacro.expand(&temp, loc, tokens.cfront(), macros, expandedmacros); + return recursiveExpandToken(output, temp, loc, tok2, macros, expandedmacros, parametertokens); + } + + if (tok->str() == DEFINED) { + const Token * const tok2 = tok->next; + const Token * const tok3 = tok2 ? tok2->next : nullptr; + const Token * const tok4 = tok3 ? tok3->next : nullptr; + const Token *defToken = nullptr; + const Token *lastToken = nullptr; + if (sameline(tok, tok4) && tok2->op == '(' && tok3->name && tok4->op == ')') { + defToken = tok3; + lastToken = tok4; + } else if (sameline(tok,tok2) && tok2->name) { + defToken = lastToken = tok2; + } + if (defToken) { + std::string macroName = defToken->str(); + if (defToken->next && defToken->next->op == '#' && defToken->next->next && defToken->next->next->op == '#' && defToken->next->next->next && defToken->next->next->next->name && sameline(defToken,defToken->next->next->next)) { + TokenList temp(files); + if (expandArg(&temp, defToken, parametertokens)) + macroName = temp.cback()->str(); + if (expandArg(&temp, defToken->next->next->next, parametertokens)) + macroName += temp.cback()->str(); + else + macroName += defToken->next->next->next->str(); + lastToken = defToken->next->next->next; + } + const bool def = (macros.find(macroName) != macros.end()); + output->push_back(newMacroToken(def ? "1" : "0", loc, true)); + return lastToken->next; + } + } + + output->push_back(newMacroToken(tok->str(), loc, true, tok)); + return tok->next; + } + + bool expandArg(TokenList *output, const Token *tok, const std::vector ¶metertokens) const { + if (!tok->name) + return false; + + const unsigned int argnr = getArgNum(tok->str()); + if (argnr >= args.size()) + return false; + + // empty variadic parameter + if (variadic && argnr + 1U >= parametertokens.size()) + return true; + + for (const Token *partok = parametertokens[argnr]->next; partok != parametertokens[argnr + 1U]; partok = partok->next) + output->push_back(new Token(*partok)); + + return true; + } + + bool expandArg(TokenList *output, const Token *tok, const Location &loc, const MacroMap ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { + if (!tok->name) + return false; + const unsigned int argnr = getArgNum(tok->str()); + if (argnr >= args.size()) + return false; + if (variadic && argnr + 1U >= parametertokens.size()) // empty variadic parameter + return true; + for (const Token *partok = parametertokens[argnr]->next; partok != parametertokens[argnr + 1U];) { + const MacroMap::const_iterator it = macros.find(partok->str()); + if (it != macros.end() && !partok->isExpandedFrom(&it->second) && (partok->str() == name() || expandedmacros.find(partok->str()) == expandedmacros.end())) { + const std::set expandedmacros2; // temporary amnesia to allow reexpansion of currently expanding macros during argument evaluation + partok = it->second.expand(output, loc, partok, macros, expandedmacros2); + } else { + output->push_back(newMacroToken(partok->str(), loc, isReplaced(expandedmacros), partok)); + output->back()->macro = partok->macro; + partok = partok->next; + } + } + if (tok->whitespaceahead && output->back()) + output->back()->whitespaceahead = true; + return true; + } + + /** + * Expand #X => "X" + * @param output destination tokenlist + * @param loc location for expanded token + * @param tok The # token + * @param expandedmacros set with expanded macros, with this macro + * @param parametertokens parameters given when expanding this macro + * @return token after the X + */ + const Token *expandHash(TokenList *output, const Location &loc, const Token *tok, const std::set &expandedmacros, const std::vector ¶metertokens) const { + TokenList tokenListHash(files); + const MacroMap macros2; // temporarily bypass macro expansion + tok = expandToken(&tokenListHash, loc, tok->next, macros2, expandedmacros, parametertokens); + std::ostringstream ostr; + ostr << '\"'; + for (const Token *hashtok = tokenListHash.cfront(), *next; hashtok; hashtok = next) { + next = hashtok->next; + ostr << hashtok->str(); + if (next && hashtok->whitespaceahead) + ostr << ' '; + } + ostr << '\"'; + output->push_back(newMacroToken(escapeString(ostr.str()), loc, isReplaced(expandedmacros))); + return tok; + } + + /** + * Expand A##B => AB + * The A should already be expanded. Call this when you reach the first # token + * @param output destination tokenlist + * @param loc location for expanded token + * @param tok first # token + * @param macros all macros + * @param expandedmacros set with expanded macros, with this macro + * @param parametertokens parameters given when expanding this macro + * @param expandResult expand ## result i.e. "AB"? + * @return token after B + */ + const Token *expandHashHash(TokenList *output, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set &expandedmacros, const std::vector ¶metertokens, bool expandResult=true) const { + Token *A = output->back(); + if (!A) + throw invalidHashHash(tok->location, name(), "Missing first argument"); + if (!sameline(tok, tok->next) || !sameline(tok, tok->next->next)) + throw invalidHashHash::unexpectedNewline(tok->location, name()); + + const bool canBeConcatenatedWithEqual = A->isOneOf("+-*/%&|^") || A->str() == "<<" || A->str() == ">>"; + const bool canBeConcatenatedStringOrChar = isStringLiteral_(A->str()) || isCharLiteral_(A->str()); + const bool unexpectedA = (!A->name && !A->number && !A->str().empty() && !canBeConcatenatedWithEqual && !canBeConcatenatedStringOrChar); + + Token * const B = tok->next->next; + if (!B->name && !B->number && B->op && !B->isOneOf("#=")) + throw invalidHashHash::unexpectedToken(tok->location, name(), B); + + if ((canBeConcatenatedWithEqual && B->op != '=') || + (!canBeConcatenatedWithEqual && B->op == '=')) + throw invalidHashHash::cannotCombine(tok->location, name(), A, B); + + // Superficial check; more in-depth would in theory be possible _after_ expandArg + if (canBeConcatenatedStringOrChar && (B->number || !B->name)) + throw invalidHashHash::cannotCombine(tok->location, name(), A, B); + + TokenList tokensB(files); + const Token *nextTok = B->next; + + if (canBeConcatenatedStringOrChar) { + if (unexpectedA) + throw invalidHashHash::unexpectedToken(tok->location, name(), A); + + // It seems clearer to handle this case separately even though the code is similar-ish, but we don't want to merge here. + // TODO The question is whether the ## or varargs may still apply, and how to provoke? + if (expandArg(&tokensB, B, parametertokens)) { + for (Token *b = tokensB.front(); b; b = b->next) + b->location = loc; + } else { + tokensB.push_back(new Token(*B)); + tokensB.back()->location = loc; + } + output->takeTokens(tokensB); + } else { + std::string strAB; + + const bool varargs = variadic && !args.empty() && B->str() == args[args.size()-1U]; + + if (expandArg(&tokensB, B, parametertokens)) { + if (tokensB.empty()) + strAB = A->str(); + else if (varargs && A->op == ',') + strAB = ","; + else if (varargs && unexpectedA) + throw invalidHashHash::unexpectedToken(tok->location, name(), A); + else { + strAB = A->str() + tokensB.cfront()->str(); + tokensB.deleteToken(tokensB.front()); + } + } else { + if (unexpectedA) + throw invalidHashHash::unexpectedToken(tok->location, name(), A); + strAB = A->str() + B->str(); + } + + // producing universal character is undefined behavior + if (A->previous && A->previous->str() == "\\") { + if (strAB[0] == 'u' && strAB.size() == 5) + throw invalidHashHash::universalCharacterUB(tok->location, name(), A, strAB); + if (strAB[0] == 'U' && strAB.size() == 9) + throw invalidHashHash::universalCharacterUB(tok->location, name(), A, strAB); + } + + if (varargs && tokensB.empty() && tok->previous->str() == ",") + output->deleteToken(A); + else if (strAB != "," && macros.find(strAB) == macros.end()) { + A->setstr(strAB); + for (Token *b = tokensB.front(); b; b = b->next) + b->location = loc; + output->takeTokens(tokensB); + } else if (sameline(B, nextTok) && sameline(B, nextTok->next) && nextTok->op == '#' && nextTok->next->op == '#') { + TokenList output2(files); + output2.push_back(new Token(strAB, tok->location)); + nextTok = expandHashHash(&output2, loc, nextTok, macros, expandedmacros, parametertokens); + output->deleteToken(A); + output->takeTokens(output2); + } else { + output->deleteToken(A); + TokenList tokens(files); + tokens.push_back(new Token(strAB, tok->location)); + // for function like macros, push the (...) + if (tokensB.empty() && sameline(B,B->next) && B->next->op=='(') { + const MacroMap::const_iterator it = macros.find(strAB); + if (it != macros.end() && expandedmacros.find(strAB) == expandedmacros.end() && it->second.functionLike()) { + const Token * const tok2 = appendTokens(&tokens, loc, B->next, macros, expandedmacros, parametertokens); + if (tok2) + nextTok = tok2->next; + } + } + if (expandResult) + expandToken(output, loc, tokens.cfront(), macros, expandedmacros, parametertokens); + else + output->takeTokens(tokens); + for (Token *b = tokensB.front(); b; b = b->next) + b->location = loc; + output->takeTokens(tokensB); + } + } + + return nextTok; + } + + static bool isReplaced(const std::set &expandedmacros) { + // return true if size > 1 + std::set::const_iterator it = expandedmacros.begin(); + if (it == expandedmacros.end()) + return false; + ++it; + return (it != expandedmacros.end()); + } + + /** name token in definition */ + const Token *nameTokDef; + + /** arguments for macro */ + std::vector args; + + /** first token in replacement string */ + const Token *valueToken; + + /** token after replacement string */ + const Token *endToken; + + /** files */ + std::vector &files; + + /** this is used for -D where the definition is not seen anywhere in code */ + TokenList tokenListDefine; + + /** usage of this macro */ + mutable std::list usageList; + + /** is macro variadic? */ + bool variadic; + + /** was the value of this macro actually defined in the code? */ + bool valueDefinedInCode_; + }; +} + +namespace simplecpp { + +#ifdef __CYGWIN__ + bool startsWith(const std::string &str, const std::string &s) + { + return (str.size() >= s.size() && str.compare(0, s.size(), s) == 0); + } + + std::string convertCygwinToWindowsPath(const std::string &cygwinPath) + { + std::string windowsPath; + + std::string::size_type pos = 0; + if (cygwinPath.size() >= 11 && startsWith(cygwinPath, "/cygdrive/")) { + const unsigned char driveLetter = cygwinPath[10]; + if (std::isalpha(driveLetter)) { + if (cygwinPath.size() == 11) { + windowsPath = toupper(driveLetter); + windowsPath += ":\\"; // volume root directory + pos = 11; + } else if (cygwinPath[11] == '/') { + windowsPath = toupper(driveLetter); + windowsPath += ":"; + pos = 11; + } + } + } + + for (; pos < cygwinPath.size(); ++pos) { + unsigned char c = cygwinPath[pos]; + if (c == '/') + c = '\\'; + windowsPath += c; + } + + return windowsPath; + } +#endif +} + +#ifdef SIMPLECPP_WINDOWS + +#if __cplusplus >= 201103L +using MyMutex = std::mutex; +template +using MyLock = std::lock_guard; +#else +class MyMutex { +public: + MyMutex() { + InitializeCriticalSection(&m_criticalSection); + } + + ~MyMutex() { + DeleteCriticalSection(&m_criticalSection); + } + + CRITICAL_SECTION* lock() { + return &m_criticalSection; + } +private: + CRITICAL_SECTION m_criticalSection; +}; + +template +class MyLock { +public: + explicit MyLock(T& m) + : m_mutex(m) { + EnterCriticalSection(m_mutex.lock()); + } + + ~MyLock() { + LeaveCriticalSection(m_mutex.lock()); + } + +private: + MyLock& operator=(const MyLock&); + MyLock(const MyLock&); + + T& m_mutex; +}; +#endif + +class RealFileNameMap { +public: + RealFileNameMap() {} + + bool getCacheEntry(const std::string& path, std::string& returnPath) { + MyLock lock(m_mutex); + + const std::map::iterator it = m_fileMap.find(path); + if (it != m_fileMap.end()) { + returnPath = it->second; + return true; + } + return false; + } + + void addToCache(const std::string& path, const std::string& actualPath) { + MyLock lock(m_mutex); + m_fileMap[path] = actualPath; + } + +private: + std::map m_fileMap; + MyMutex m_mutex; +}; + +static RealFileNameMap realFileNameMap; + +static bool realFileName(const std::string &f, std::string &result) +{ + // are there alpha characters in last subpath? + bool alpha = false; + for (std::string::size_type pos = 1; pos <= f.size(); ++pos) { + const unsigned char c = f[f.size() - pos]; + if (c == '/' || c == '\\') + break; + if (std::isalpha(c)) { + alpha = true; + break; + } + } + + // do not convert this path if there are no alpha characters (either pointless or cause wrong results for . and ..) + if (!alpha) + return false; + + // Lookup filename or foldername on file system + if (!realFileNameMap.getCacheEntry(f, result)) { + + WIN32_FIND_DATAA FindFileData; + +#ifdef __CYGWIN__ + const std::string fConverted = simplecpp::convertCygwinToWindowsPath(f); + const HANDLE hFind = FindFirstFileExA(fConverted.c_str(), FindExInfoBasic, &FindFileData, FindExSearchNameMatch, NULL, 0); +#else + HANDLE hFind = FindFirstFileExA(f.c_str(), FindExInfoBasic, &FindFileData, FindExSearchNameMatch, NULL, 0); +#endif + + if (INVALID_HANDLE_VALUE == hFind) + return false; + result = FindFileData.cFileName; + realFileNameMap.addToCache(f, result); + FindClose(hFind); + } + return true; +} + +static RealFileNameMap realFilePathMap; + +/** Change case in given path to match filesystem */ +static std::string realFilename(const std::string &f) +{ + std::string ret; + ret.reserve(f.size()); // this will be the final size + if (realFilePathMap.getCacheEntry(f, ret)) + return ret; + + // Current subpath + std::string subpath; + + for (std::string::size_type pos = 0; pos < f.size(); ++pos) { + const unsigned char c = f[pos]; + + // Separator.. add subpath and separator + if (c == '/' || c == '\\') { + // if subpath is empty just add separator + if (subpath.empty()) { + ret += c; + continue; + } + + const bool isDriveSpecification = + (pos == 2 && subpath.size() == 2 && std::isalpha(subpath[0]) && subpath[1] == ':'); + + // Append real filename (proper case) + std::string f2; + if (!isDriveSpecification && realFileName(f.substr(0, pos), f2)) + ret += f2; + else + ret += subpath; + + subpath.clear(); + + // Append separator + ret += c; + } else { + subpath += c; + } + } + + if (!subpath.empty()) { + std::string f2; + if (realFileName(f,f2)) + ret += f2; + else + ret += subpath; + } + + realFilePathMap.addToCache(f, ret); + return ret; +} + +static bool isAbsolutePath(const std::string &path) +{ + if (path.length() >= 3 && path[0] > 0 && std::isalpha(path[0]) && path[1] == ':' && (path[2] == '\\' || path[2] == '/')) + return true; + return path.length() > 1U && (path[0] == '/' || path[0] == '\\'); +} +#else +#define realFilename(f) f + +static bool isAbsolutePath(const std::string &path) +{ + return path.length() > 1U && path[0] == '/'; +} +#endif + +namespace simplecpp { + /** + * perform path simplifications for . and .. + */ + std::string simplifyPath(std::string path) + { + if (path.empty()) + return path; + + std::string::size_type pos; + + // replace backslash separators + std::replace(path.begin(), path.end(), '\\', '/'); + + const bool unc(path.compare(0,2,"//") == 0); + + // replace "//" with "/" + pos = 0; + while ((pos = path.find("//",pos)) != std::string::npos) { + path.erase(pos,1); + } + + // remove "./" + pos = 0; + while ((pos = path.find("./",pos)) != std::string::npos) { + if (pos == 0 || path[pos - 1U] == '/') + path.erase(pos,2); + else + pos += 2; + } + + // remove trailing dot if path ends with "/." + if (endsWith(path,"/.")) + path.erase(path.size()-1); + + // simplify ".." + pos = 1; // don't simplify ".." if path starts with that + while ((pos = path.find("/..", pos)) != std::string::npos) { + // not end of path, then string must be "/../" + if (pos + 3 < path.size() && path[pos + 3] != '/') { + ++pos; + continue; + } + // get previous subpath + std::string::size_type pos1 = path.rfind('/', pos - 1U); + if (pos1 == std::string::npos) { + pos1 = 0; + } else { + pos1 += 1U; + } + const std::string previousSubPath = path.substr(pos1, pos - pos1); + if (previousSubPath == "..") { + // don't simplify + ++pos; + } else { + // remove previous subpath and ".." + path.erase(pos1, pos - pos1 + 4); + if (path.empty()) + path = "."; + // update pos + pos = (pos1 == 0) ? 1 : (pos1 - 1); + } + } + + // Remove trailing '/'? + //if (path.size() > 1 && endsWith(path, "/")) + // path.erase(path.size()-1); + + if (unc) + path = '/' + path; + + // cppcheck-suppress duplicateExpressionTernary - platform-dependent implementation + return strpbrk(path.c_str(), "*?") == nullptr ? realFilename(path) : path; + } +} + +/** Evaluate sizeof(type) */ +static void simplifySizeof(simplecpp::TokenList &expr, const std::map &sizeOfType) +{ + for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { + if (tok->str() != "sizeof") + continue; + simplecpp::Token *tok1 = tok->next; + if (!tok1) { + throw std::runtime_error("missing sizeof argument"); + } + simplecpp::Token *tok2 = tok1->next; + if (!tok2) { + throw std::runtime_error("missing sizeof argument"); + } + if (tok1->op == '(') { + tok1 = tok1->next; + while (tok2->op != ')') { + tok2 = tok2->next; + if (!tok2) { + throw std::runtime_error("invalid sizeof expression"); + } + } + } + + std::string type; + for (simplecpp::Token *typeToken = tok1; typeToken != tok2; typeToken = typeToken->next) { + if ((typeToken->str() == "unsigned" || typeToken->str() == "signed") && typeToken->next->name) + continue; + if (typeToken->str() == "*" && type.find('*') != std::string::npos) + continue; + if (!type.empty()) + type += ' '; + type += typeToken->str(); + } + + const std::map::const_iterator it = sizeOfType.find(type); + if (it != sizeOfType.end()) + tok->setstr(toString(it->second)); + else + continue; + + tok2 = tok2->next; + while (tok->next != tok2) + expr.deleteToken(tok->next); + } +} + +/** Evaluate __has_include(file) */ +static bool isCpp17OrLater(const simplecpp::DUI &dui) +{ + const std::string std_ver = simplecpp::getCppStdString(dui.std); + return !std_ver.empty() && (std_ver >= "201703L"); +} + +static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const std::string &sourcefile, const std::string &header, bool systemheader); +static void simplifyHasInclude(simplecpp::TokenList &expr, const simplecpp::DUI &dui) +{ + if (!isCpp17OrLater(dui)) + return; + + for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { + if (tok->str() != HAS_INCLUDE) + continue; + simplecpp::Token *tok1 = tok->next; + if (!tok1) { + throw std::runtime_error("missing __has_include argument"); + } + simplecpp::Token *tok2 = tok1->next; + if (!tok2) { + throw std::runtime_error("missing __has_include argument"); + } + if (tok1->op == '(') { + tok1 = tok1->next; + while (tok2->op != ')') { + tok2 = tok2->next; + if (!tok2) { + throw std::runtime_error("invalid __has_include expression"); + } + } + } + + const std::string &sourcefile = tok->location.file(); + const bool systemheader = (tok1 && tok1->op == '<'); + std::string header; + if (systemheader) { + simplecpp::Token *tok3 = tok1->next; + if (!tok3) { + throw std::runtime_error("missing __has_include closing angular bracket"); + } + while (tok3->op != '>') { + tok3 = tok3->next; + if (!tok3) { + throw std::runtime_error("invalid __has_include expression"); + } + } + + for (simplecpp::Token *headerToken = tok1->next; headerToken != tok3; headerToken = headerToken->next) + header += headerToken->str(); + // cppcheck-suppress selfAssignment - platform-dependent implementation + header = realFilename(header); + } else { + header = realFilename(tok1->str().substr(1U, tok1->str().size() - 2U)); + } + std::ifstream f; + const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader); + tok->setstr(header2.empty() ? "0" : "1"); + + tok2 = tok2->next; + while (tok->next != tok2) + expr.deleteToken(tok->next); + } +} + +static const char * const altopData[] = {"and","or","bitand","bitor","compl","not","not_eq","xor"}; +static const std::set altop(&altopData[0], &altopData[8]); +static void simplifyName(simplecpp::TokenList &expr) +{ + for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { + if (tok->name) { + if (altop.find(tok->str()) != altop.end()) { + bool alt; + if (tok->str() == "not" || tok->str() == "compl") { + alt = isAlternativeUnaryOp(tok,tok->str()); + } else { + alt = isAlternativeBinaryOp(tok,tok->str()); + } + if (alt) + continue; + } + tok->setstr("0"); + } + } +} + +/* + * Reads at least minlen and at most maxlen digits (inc. prefix) in base base + * from s starting at position pos and converts them to a + * unsigned long long value, updating pos to point to the first + * unused element of s. + * Returns ULLONG_MAX if the result is not representable and + * throws if the above requirements were not possible to satisfy. + */ +static unsigned long long stringToULLbounded( + const std::string& s, + std::size_t& pos, + int base = 0, + std::ptrdiff_t minlen = 1, + std::size_t maxlen = std::string::npos +) +{ + const std::string sub = s.substr(pos, maxlen); + const char * const start = sub.c_str(); + char* end; + const unsigned long long value = std::strtoull(start, &end, base); + pos += end - start; + if (end - start < minlen) + throw std::runtime_error("expected digit"); + return value; +} + +/* Converts character literal (including prefix, but not ud-suffix) + * to long long value. + * + * Assumes ASCII-compatible single-byte encoded str for narrow literals + * and UTF-8 otherwise. + * + * For target assumes + * - execution character set encoding matching str + * - UTF-32 execution wide-character set encoding + * - requirements for __STDC_UTF_16__, __STDC_UTF_32__ and __STDC_ISO_10646__ satisfied + * - char16_t is 16bit wide + * - char32_t is 32bit wide + * - wchar_t is 32bit wide and unsigned + * - matching char signedness to host + * - matching sizeof(int) to host + * + * For host assumes + * - ASCII-compatible execution character set + * + * For host and target assumes + * - CHAR_BIT == 8 + * - two's complement + * + * Implements multi-character narrow literals according to GCC's behavior, + * except multi code unit universal character names are not supported. + * Multi-character wide literals are not supported. + * Limited support of universal character names for non-UTF-8 execution character set encodings. + */ +long long simplecpp::characterLiteralToLL(const std::string& str) +{ + // default is wide/utf32 + bool narrow = false; + bool utf8 = false; + bool utf16 = false; + + std::size_t pos; + + if (!str.empty() && str[0] == '\'') { + narrow = true; + pos = 1; + } else if (str.size() >= 2 && str[0] == 'u' && str[1] == '\'') { + utf16 = true; + pos = 2; + } else if (str.size() >= 3 && str[0] == 'u' && str[1] == '8' && str[2] == '\'') { + utf8 = true; + pos = 3; + } else if (str.size() >= 2 && (str[0] == 'L' || str[0] == 'U') && str[1] == '\'') { + pos = 2; + } else + throw std::runtime_error("expected a character literal"); + + unsigned long long multivalue = 0; + + std::size_t nbytes = 0; + + while (pos + 1 < str.size()) { + if (str[pos] == '\'' || str[pos] == '\n') + throw std::runtime_error("raw single quotes and newlines not allowed in character literals"); + + if (nbytes >= 1 && !narrow) + throw std::runtime_error("multiple characters only supported in narrow character literals"); + + unsigned long long value; + + if (str[pos] == '\\') { + pos++; + const char escape = str[pos++]; + + if (pos >= str.size()) + throw std::runtime_error("unexpected end of character literal"); + + switch (escape) { + // obscure GCC extensions + case '%': + case '(': + case '[': + case '{': + // standard escape sequences + case '\'': + case '"': + case '?': + case '\\': + value = static_cast(escape); + break; + + case 'a': + value = static_cast('\a'); + break; + case 'b': + value = static_cast('\b'); + break; + case 'f': + value = static_cast('\f'); + break; + case 'n': + value = static_cast('\n'); + break; + case 'r': + value = static_cast('\r'); + break; + case 't': + value = static_cast('\t'); + break; + case 'v': + value = static_cast('\v'); + break; + + // GCC extension for ESC character + case 'e': + case 'E': + value = static_cast('\x1b'); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + // octal escape sequences consist of 1 to 3 digits + value = stringToULLbounded(str, --pos, 8, 1, 3); + break; + + case 'x': + // hexadecimal escape sequences consist of at least 1 digit + value = stringToULLbounded(str, pos, 16); + break; + + case 'u': + case 'U': { + // universal character names have exactly 4 or 8 digits + const std::size_t ndigits = (escape == 'u' ? 4 : 8); + value = stringToULLbounded(str, pos, 16, ndigits, ndigits); + + // UTF-8 encodes code points above 0x7f in multiple code units + // code points above 0x10ffff are not allowed + if (((narrow || utf8) && value > 0x7f) || (utf16 && value > 0xffff) || value > 0x10ffff) + throw std::runtime_error("code point too large"); + + if (value >= 0xd800 && value <= 0xdfff) + throw std::runtime_error("surrogate code points not allowed in universal character names"); + + break; + } + + default: + throw std::runtime_error("invalid escape sequence"); + } + } else { + value = static_cast(str[pos++]); + + if (!narrow && value >= 0x80) { + // Assuming this is a UTF-8 encoded code point. + // This decoder may not completely validate the input. + // Noncharacters are neither rejected nor replaced. + + int additional_bytes; + if (value >= 0xf5) // higher values would result in code points above 0x10ffff + throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid"); + if (value >= 0xf0) + additional_bytes = 3; + else if (value >= 0xe0) + additional_bytes = 2; + else if (value >= 0xc2) // 0xc0 and 0xc1 are always overlong 2-bytes encodings + additional_bytes = 1; + else + throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid"); + + value &= (1 << (6 - additional_bytes)) - 1; + + while (additional_bytes--) { + if (pos + 1 >= str.size()) + throw std::runtime_error("assumed UTF-8 encoded source, but character literal ends unexpectedly"); + + const unsigned char c = str[pos++]; + + if (((c >> 6) != 2) // ensure c has form 0xb10xxxxxx + || (!value && additional_bytes == 1 && c < 0xa0) // overlong 3-bytes encoding + || (!value && additional_bytes == 2 && c < 0x90)) // overlong 4-bytes encoding + throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid"); + + value = (value << 6) | (c & ((1 << 7) - 1)); + } + + if (value >= 0xd800 && value <= 0xdfff) + throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid"); + + if ((utf8 && value > 0x7f) || (utf16 && value > 0xffff) || value > 0x10ffff) + throw std::runtime_error("code point too large"); + } + } + + if (((narrow || utf8) && value > std::numeric_limits::max()) || (utf16 && value >> 16) || value >> 32) + throw std::runtime_error("numeric escape sequence too large"); + + multivalue <<= CHAR_BIT; + multivalue |= value; + nbytes++; + } + + if (pos + 1 != str.size() || str[pos] != '\'') + throw std::runtime_error("missing closing quote in character literal"); + + if (!nbytes) + throw std::runtime_error("empty character literal"); + + // ordinary narrow character literal's value is determined by (possibly signed) char + if (narrow && nbytes == 1) + return static_cast(multivalue); + + // while multi-character literal's value is determined by (signed) int + if (narrow) + return static_cast(multivalue); + + // All other cases are unsigned. Since long long is at least 64bit wide, + // while the literals at most 32bit wide, the conversion preserves all values. + return multivalue; +} + +static void simplifyNumbers(simplecpp::TokenList &expr) +{ + for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { + if (tok->str().size() == 1U) + continue; + if (tok->str().compare(0,2,"0x") == 0) + tok->setstr(toString(stringToULL(tok->str()))); + else if (!tok->number && tok->str().find('\'') != std::string::npos) + tok->setstr(toString(simplecpp::characterLiteralToLL(tok->str()))); + } +} + +static void simplifyComments(simplecpp::TokenList &expr) +{ + for (simplecpp::Token *tok = expr.front(); tok;) { + simplecpp::Token * const d = tok; + tok = tok->next; + if (d->comment) + expr.deleteToken(d); + } +} + +static long long evaluate(simplecpp::TokenList &expr, const simplecpp::DUI &dui, const std::map &sizeOfType) +{ + simplifyComments(expr); + simplifySizeof(expr, sizeOfType); + simplifyHasInclude(expr, dui); + simplifyName(expr); + simplifyNumbers(expr); + expr.constFold(); + // TODO: handle invalid expressions + return expr.cfront() && expr.cfront() == expr.cback() && expr.cfront()->number ? stringToLL(expr.cfront()->str()) : 0LL; +} + +static const simplecpp::Token *gotoNextLine(const simplecpp::Token *tok) +{ + const unsigned int line = tok->location.line; + const unsigned int file = tok->location.fileIndex; + while (tok && tok->location.line == line && tok->location.fileIndex == file) + tok = tok->next; + return tok; +} + +#ifdef SIMPLECPP_WINDOWS + +class NonExistingFilesCache { +public: + NonExistingFilesCache() {} + + bool contains(const std::string& path) { + MyLock lock(m_mutex); + return (m_pathSet.find(path) != m_pathSet.end()); + } + + void add(const std::string& path) { + MyLock lock(m_mutex); + m_pathSet.insert(path); + } + + void clear() { + MyLock lock(m_mutex); + m_pathSet.clear(); + } + +private: + std::set m_pathSet; + MyMutex m_mutex; +}; + +static NonExistingFilesCache nonExistingFilesCache; + +#endif + +static std::string openHeader(std::ifstream &f, const std::string &path) +{ + std::string simplePath = simplecpp::simplifyPath(path); +#ifdef SIMPLECPP_WINDOWS + if (nonExistingFilesCache.contains(simplePath)) + return ""; // file is known not to exist, skip expensive file open call +#endif + f.open(simplePath.c_str()); + if (f.is_open()) + return simplePath; +#ifdef SIMPLECPP_WINDOWS + nonExistingFilesCache.add(simplePath); +#endif + return ""; +} + +static std::string getRelativeFileName(const std::string &sourcefile, const std::string &header) +{ + if (sourcefile.find_first_of("\\/") != std::string::npos) + return simplecpp::simplifyPath(sourcefile.substr(0, sourcefile.find_last_of("\\/") + 1U) + header); + return simplecpp::simplifyPath(header); +} + +static std::string openHeaderRelative(std::ifstream &f, const std::string &sourcefile, const std::string &header) +{ + return openHeader(f, getRelativeFileName(sourcefile, header)); +} + +static std::string getIncludePathFileName(const std::string &includePath, const std::string &header) +{ + std::string path = includePath; + if (!path.empty() && path[path.size()-1U]!='/' && path[path.size()-1U]!='\\') + path += '/'; + return path + header; +} + +static std::string openHeaderIncludePath(std::ifstream &f, const simplecpp::DUI &dui, const std::string &header) +{ + for (std::list::const_iterator it = dui.includePaths.begin(); it != dui.includePaths.end(); ++it) { + std::string simplePath = openHeader(f, getIncludePathFileName(*it, header)); + if (!simplePath.empty()) + return simplePath; + } + return ""; +} + +static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const std::string &sourcefile, const std::string &header, bool systemheader) +{ + if (isAbsolutePath(header)) + return openHeader(f, header); + + std::string ret; + + if (systemheader) { + ret = openHeaderIncludePath(f, dui, header); + return ret; + } + + ret = openHeaderRelative(f, sourcefile, header); + if (ret.empty()) + return openHeaderIncludePath(f, dui, header); + return ret; +} + +static std::string getFileName(const std::map &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader) +{ + if (filedata.empty()) { + return ""; + } + if (isAbsolutePath(header)) { + return (filedata.find(header) != filedata.end()) ? simplecpp::simplifyPath(header) : ""; + } + + if (!systemheader) { + const std::string relativeFilename = getRelativeFileName(sourcefile, header); + if (filedata.find(relativeFilename) != filedata.end()) + return relativeFilename; + } + + for (std::list::const_iterator it = dui.includePaths.begin(); it != dui.includePaths.end(); ++it) { + std::string s = simplecpp::simplifyPath(getIncludePathFileName(*it, header)); + if (filedata.find(s) != filedata.end()) + return s; + } + + if (systemheader && filedata.find(header) != filedata.end()) + return header; + + return ""; +} + +static bool hasFile(const std::map &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader) +{ + return !getFileName(filedata, sourcefile, header, dui, systemheader).empty(); +} + +std::map simplecpp::load(const simplecpp::TokenList &rawtokens, std::vector &filenames, const simplecpp::DUI &dui, simplecpp::OutputList *outputList) +{ +#ifdef SIMPLECPP_WINDOWS + if (dui.clearIncludeCache) + nonExistingFilesCache.clear(); +#endif + + std::map ret; + + std::list filelist; + + // -include files + for (std::list::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) { + const std::string &filename = realFilename(*it); + + if (ret.find(filename) != ret.end()) + continue; + + std::ifstream fin(filename.c_str()); + if (!fin.is_open()) { + if (outputList) { + simplecpp::Output err(filenames); + err.type = simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND; + err.location = Location(filenames); + err.msg = "Can not open include file '" + filename + "' that is explicitly included."; + outputList->push_back(err); + } + continue; + } + fin.close(); + + TokenList *tokenlist = new TokenList(filename, filenames, outputList); + if (!tokenlist->front()) { + delete tokenlist; + continue; + } + + if (dui.removeComments) + tokenlist->removeComments(); + ret[filename] = tokenlist; + filelist.push_back(tokenlist->front()); + } + + for (const Token *rawtok = rawtokens.cfront(); rawtok || !filelist.empty(); rawtok = rawtok ? rawtok->next : nullptr) { + if (rawtok == nullptr) { + rawtok = filelist.back(); + filelist.pop_back(); + } + + if (rawtok->op != '#' || sameline(rawtok->previousSkipComments(), rawtok)) + continue; + + rawtok = rawtok->nextSkipComments(); + if (!rawtok || rawtok->str() != INCLUDE) + continue; + + const std::string &sourcefile = rawtok->location.file(); + + const Token * const htok = rawtok->nextSkipComments(); + if (!sameline(rawtok, htok)) + continue; + + const bool systemheader = (htok->str()[0] == '<'); + const std::string header(realFilename(htok->str().substr(1U, htok->str().size() - 2U))); + if (hasFile(ret, sourcefile, header, dui, systemheader)) + continue; + + std::ifstream f; + const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader); + if (!f.is_open()) + continue; + f.close(); + + TokenList *tokens = new TokenList(header2, filenames, outputList); + if (dui.removeComments) + tokens->removeComments(); + ret[header2] = tokens; + if (tokens->front()) + filelist.push_back(tokens->front()); + } + + return ret; +} + +static bool preprocessToken(simplecpp::TokenList &output, const simplecpp::Token **tok1, simplecpp::MacroMap ¯os, std::vector &files, simplecpp::OutputList *outputList) +{ + const simplecpp::Token * const tok = *tok1; + const simplecpp::MacroMap::const_iterator it = macros.find(tok->str()); + if (it != macros.end()) { + simplecpp::TokenList value(files); + try { + *tok1 = it->second.expand(&value, tok, macros, files); + } catch (simplecpp::Macro::Error &err) { + if (outputList) { + simplecpp::Output out(files); + out.type = simplecpp::Output::SYNTAX_ERROR; + out.location = err.location; + out.msg = "failed to expand \'" + tok->str() + "\', " + err.what; + outputList->push_back(out); + } + return false; + } + output.takeTokens(value); + } else { + if (!tok->comment) + output.push_back(new simplecpp::Token(*tok)); + *tok1 = tok->next; + } + return true; +} + +static void getLocaltime(struct tm <ime) +{ + time_t t; + time(&t); +#ifndef _WIN32 + // NOLINTNEXTLINE(misc-include-cleaner) - false positive + localtime_r(&t, <ime); +#else + localtime_s(<ime, &t); +#endif +} + +static std::string getDateDefine(const struct tm *timep) +{ + char buf[] = "??? ?? ????"; + strftime(buf, sizeof(buf), "%b %d %Y", timep); + return std::string("\"").append(buf).append("\""); +} + +static std::string getTimeDefine(const struct tm *timep) +{ + char buf[] = "??:??:??"; + strftime(buf, sizeof(buf), "%T", timep); + return std::string("\"").append(buf).append("\""); +} + +void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenList &rawtokens, std::vector &files, std::map &filedata, const simplecpp::DUI &dui, simplecpp::OutputList *outputList, std::list *macroUsage, std::list *ifCond) +{ +#ifdef SIMPLECPP_WINDOWS + if (dui.clearIncludeCache) + nonExistingFilesCache.clear(); +#endif + + std::map sizeOfType(rawtokens.sizeOfType); + sizeOfType.insert(std::make_pair("char", sizeof(char))); + sizeOfType.insert(std::make_pair("short", sizeof(short))); + sizeOfType.insert(std::make_pair("short int", sizeOfType["short"])); + sizeOfType.insert(std::make_pair("int", sizeof(int))); + sizeOfType.insert(std::make_pair("long", sizeof(long))); + sizeOfType.insert(std::make_pair("long int", sizeOfType["long"])); + sizeOfType.insert(std::make_pair("long long", sizeof(long long))); + sizeOfType.insert(std::make_pair("float", sizeof(float))); + sizeOfType.insert(std::make_pair("double", sizeof(double))); + sizeOfType.insert(std::make_pair("long double", sizeof(long double))); + sizeOfType.insert(std::make_pair("char *", sizeof(char *))); + sizeOfType.insert(std::make_pair("short *", sizeof(short *))); + sizeOfType.insert(std::make_pair("short int *", sizeOfType["short *"])); + sizeOfType.insert(std::make_pair("int *", sizeof(int *))); + sizeOfType.insert(std::make_pair("long *", sizeof(long *))); + sizeOfType.insert(std::make_pair("long int *", sizeOfType["long *"])); + sizeOfType.insert(std::make_pair("long long *", sizeof(long long *))); + sizeOfType.insert(std::make_pair("float *", sizeof(float *))); + sizeOfType.insert(std::make_pair("double *", sizeof(double *))); + sizeOfType.insert(std::make_pair("long double *", sizeof(long double *))); + + // use a dummy vector for the macros because as this is not part of the file and would add an empty entry - e.g. /usr/include/poll.h + std::vector dummy; + + const bool hasInclude = isCpp17OrLater(dui); + MacroMap macros; + for (std::list::const_iterator it = dui.defines.begin(); it != dui.defines.end(); ++it) { + const std::string ¯ostr = *it; + const std::string::size_type eq = macrostr.find('='); + const std::string::size_type par = macrostr.find('('); + const std::string macroname = macrostr.substr(0, std::min(eq,par)); + if (dui.undefined.find(macroname) != dui.undefined.end()) + continue; + const std::string lhs(macrostr.substr(0,eq)); + const std::string rhs(eq==std::string::npos ? std::string("1") : macrostr.substr(eq+1)); + const Macro macro(lhs, rhs, dummy); + macros.insert(std::pair(macro.name(), macro)); + } + + macros.insert(std::make_pair("__FILE__", Macro("__FILE__", "__FILE__", dummy))); + macros.insert(std::make_pair("__LINE__", Macro("__LINE__", "__LINE__", dummy))); + macros.insert(std::make_pair("__COUNTER__", Macro("__COUNTER__", "__COUNTER__", dummy))); + struct tm ltime = {}; + getLocaltime(ltime); + macros.insert(std::make_pair("__DATE__", Macro("__DATE__", getDateDefine(<ime), dummy))); + macros.insert(std::make_pair("__TIME__", Macro("__TIME__", getTimeDefine(<ime), dummy))); + + if (!dui.std.empty()) { + const cstd_t c_std = simplecpp::getCStd(dui.std); + if (c_std != CUnknown) { + const std::string std_def = simplecpp::getCStdString(c_std); + if (!std_def.empty()) + macros.insert(std::make_pair("__STDC_VERSION__", Macro("__STDC_VERSION__", std_def, dummy))); + } else { + const cppstd_t cpp_std = simplecpp::getCppStd(dui.std); + if (cpp_std == CPPUnknown) { + if (outputList) { + simplecpp::Output err(files); + err.type = Output::DUI_ERROR; + err.msg = "unknown standard specified: '" + dui.std + "'"; + outputList->push_back(err); + } + output.clear(); + return; + } + const std::string std_def = simplecpp::getCppStdString(cpp_std); + if (!std_def.empty()) + macros.insert(std::make_pair("__cplusplus", Macro("__cplusplus", std_def, dummy))); + } + } + + // True => code in current #if block should be kept + // ElseIsTrue => code in current #if block should be dropped. the code in the #else should be kept. + // AlwaysFalse => drop all code in #if and #else + enum IfState { True, ElseIsTrue, AlwaysFalse }; + std::stack ifstates; + ifstates.push(True); + + std::stack includetokenstack; + + std::set pragmaOnce; + + includetokenstack.push(rawtokens.cfront()); + for (std::list::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) { + const std::map::const_iterator f = filedata.find(*it); + if (f != filedata.end()) + includetokenstack.push(f->second->cfront()); + } + + std::map > maybeUsedMacros; + + for (const Token *rawtok = nullptr; rawtok || !includetokenstack.empty();) { + if (rawtok == nullptr) { + rawtok = includetokenstack.top(); + includetokenstack.pop(); + continue; + } + + if (rawtok->op == '#' && !sameline(rawtok->previousSkipComments(), rawtok)) { + if (!sameline(rawtok, rawtok->next)) { + rawtok = rawtok->next; + continue; + } + rawtok = rawtok->next; + if (!rawtok->name) { + rawtok = gotoNextLine(rawtok); + continue; + } + + if (ifstates.size() <= 1U && (rawtok->str() == ELIF || rawtok->str() == ELSE || rawtok->str() == ENDIF)) { + if (outputList) { + simplecpp::Output err(files); + err.type = Output::SYNTAX_ERROR; + err.location = rawtok->location; + err.msg = "#" + rawtok->str() + " without #if"; + outputList->push_back(err); + } + output.clear(); + return; + } + + if (ifstates.top() == True && (rawtok->str() == ERROR || rawtok->str() == WARNING)) { + if (outputList) { + simplecpp::Output err(rawtok->location.files); + err.type = rawtok->str() == ERROR ? Output::ERROR : Output::WARNING; + err.location = rawtok->location; + for (const Token *tok = rawtok->next; tok && sameline(rawtok,tok); tok = tok->next) { + if (!err.msg.empty() && isNameChar(tok->str()[0])) + err.msg += ' '; + err.msg += tok->str(); + } + err.msg = '#' + rawtok->str() + ' ' + err.msg; + outputList->push_back(err); + } +/* Patched for PythonQt generator: Do not stop on #error directive: + if (rawtok->str() == ERROR) { + output.clear(); + return; + } +*/ + } + + if (rawtok->str() == DEFINE) { + if (ifstates.top() != True) + continue; + try { + const Macro ¯o = Macro(rawtok->previous, files); + if (dui.undefined.find(macro.name()) == dui.undefined.end()) { + const MacroMap::iterator it = macros.find(macro.name()); + if (it == macros.end()) + macros.insert(std::pair(macro.name(), macro)); + else + it->second = macro; + } + } catch (const std::runtime_error &) { + if (outputList) { + simplecpp::Output err(files); + err.type = Output::SYNTAX_ERROR; + err.location = rawtok->location; + err.msg = "Failed to parse #define"; + outputList->push_back(err); + } + output.clear(); + return; + } + } else if (ifstates.top() == True && rawtok->str() == INCLUDE) { + TokenList inc1(files); + for (const Token *inctok = rawtok->next; sameline(rawtok,inctok); inctok = inctok->next) { + if (!inctok->comment) + inc1.push_back(new Token(*inctok)); + } + TokenList inc2(files); + if (!inc1.empty() && inc1.cfront()->name) { + const Token *inctok = inc1.cfront(); + if (!preprocessToken(inc2, &inctok, macros, files, outputList)) { + output.clear(); + return; + } + } else { + inc2.takeTokens(inc1); + } + + if (!inc1.empty() && !inc2.empty() && inc2.cfront()->op == '<' && inc2.cback()->op == '>') { + TokenString hdr; + // TODO: Sometimes spaces must be added in the string + // Somehow preprocessToken etc must be told that the location should be source location not destination location + for (const Token *tok = inc2.cfront(); tok; tok = tok->next) { + hdr += tok->str(); + } + inc2.clear(); + inc2.push_back(new Token(hdr, inc1.cfront()->location)); + inc2.front()->op = '<'; + } + + if (inc2.empty() || inc2.cfront()->str().size() <= 2U) { + if (outputList) { + simplecpp::Output err(files); + err.type = Output::SYNTAX_ERROR; + err.location = rawtok->location; + err.msg = "No header in #include"; + outputList->push_back(err); + } + output.clear(); + return; + } + + const Token * const inctok = inc2.cfront(); + + const bool systemheader = (inctok->str()[0] == '<'); + const std::string header(realFilename(inctok->str().substr(1U, inctok->str().size() - 2U))); + std::string header2 = getFileName(filedata, rawtok->location.file(), header, dui, systemheader); + if (header2.empty()) { + // try to load file.. + std::ifstream f; + header2 = openHeader(f, dui, rawtok->location.file(), header, systemheader); + if (f.is_open()) { + f.close(); + TokenList * const tokens = new TokenList(header2, files, outputList); + if (dui.removeComments) + tokens->removeComments(); + filedata[header2] = tokens; + } + } + if (header2.empty()) { + if (outputList) { + simplecpp::Output out(files); + out.type = Output::MISSING_HEADER; + out.location = rawtok->location; + out.msg = "Header not found: " + inctok->str(); + outputList->push_back(out); + } + } else if (includetokenstack.size() >= 400) { + if (outputList) { + simplecpp::Output out(files); + out.type = Output::INCLUDE_NESTED_TOO_DEEPLY; + out.location = rawtok->location; + out.msg = "#include nested too deeply"; + outputList->push_back(out); + } + } else if (pragmaOnce.find(header2) == pragmaOnce.end()) { + includetokenstack.push(gotoNextLine(rawtok)); + const TokenList * const includetokens = filedata.find(header2)->second; + rawtok = includetokens ? includetokens->cfront() : nullptr; + continue; + } + } else if (rawtok->str() == IF || rawtok->str() == IFDEF || rawtok->str() == IFNDEF || rawtok->str() == ELIF) { + if (!sameline(rawtok,rawtok->next)) { + if (outputList) { + simplecpp::Output out(files); + out.type = Output::SYNTAX_ERROR; + out.location = rawtok->location; + out.msg = "Syntax error in #" + rawtok->str(); + outputList->push_back(out); + } + output.clear(); + return; + } + + bool conditionIsTrue; + if (ifstates.top() == AlwaysFalse || (ifstates.top() == ElseIsTrue && rawtok->str() != ELIF)) + conditionIsTrue = false; + else if (rawtok->str() == IFDEF) { + conditionIsTrue = (macros.find(rawtok->next->str()) != macros.end() || (hasInclude && rawtok->next->str() == HAS_INCLUDE)); + maybeUsedMacros[rawtok->next->str()].push_back(rawtok->next->location); + } else if (rawtok->str() == IFNDEF) { + conditionIsTrue = (macros.find(rawtok->next->str()) == macros.end() && !(hasInclude && rawtok->next->str() == HAS_INCLUDE)); + maybeUsedMacros[rawtok->next->str()].push_back(rawtok->next->location); + } else { /*if (rawtok->str() == IF || rawtok->str() == ELIF)*/ + TokenList expr(files); + for (const Token *tok = rawtok->next; tok && tok->location.sameline(rawtok->location); tok = tok->next) { + if (!tok->name) { + expr.push_back(new Token(*tok)); + continue; + } + + if (tok->str() == DEFINED) { + tok = tok->next; + const bool par = (tok && tok->op == '('); + if (par) + tok = tok->next; + maybeUsedMacros[rawtok->next->str()].push_back(rawtok->next->location); + if (tok) { + if (macros.find(tok->str()) != macros.end()) + expr.push_back(new Token("1", tok->location)); + else if (hasInclude && tok->str() == HAS_INCLUDE) + expr.push_back(new Token("1", tok->location)); + else + expr.push_back(new Token("0", tok->location)); + } + if (par) + tok = tok ? tok->next : nullptr; + if (!tok || !sameline(rawtok,tok) || (par && tok->op != ')')) { + if (outputList) { + Output out(rawtok->location.files); + out.type = Output::SYNTAX_ERROR; + out.location = rawtok->location; + out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; + outputList->push_back(out); + } + output.clear(); + return; + } + continue; + } + + if (hasInclude && tok->str() == HAS_INCLUDE) { + tok = tok->next; + const bool par = (tok && tok->op == '('); + if (par) + tok = tok->next; + bool closingAngularBracket = false; + if (tok) { + const std::string &sourcefile = rawtok->location.file(); + const bool systemheader = (tok && tok->op == '<'); + std::string header; + + if (systemheader) { + while ((tok = tok->next) && tok->op != '>') + header += tok->str(); + // cppcheck-suppress selfAssignment - platform-dependent implementation + header = realFilename(header); + if (tok && tok->op == '>') + closingAngularBracket = true; + } else { + header = realFilename(tok->str().substr(1U, tok->str().size() - 2U)); + closingAngularBracket = true; + } + std::ifstream f; + const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader); + expr.push_back(new Token(header2.empty() ? "0" : "1", tok->location)); + } + if (par) + tok = tok ? tok->next : nullptr; + if (!tok || !sameline(rawtok,tok) || (par && tok->op != ')') || (!closingAngularBracket)) { + if (outputList) { + Output out(rawtok->location.files); + out.type = Output::SYNTAX_ERROR; + out.location = rawtok->location; + out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; + outputList->push_back(out); + } + output.clear(); + return; + } + continue; + } + + maybeUsedMacros[rawtok->next->str()].push_back(rawtok->next->location); + + const Token *tmp = tok; + if (!preprocessToken(expr, &tmp, macros, files, outputList)) { + output.clear(); + return; + } + if (!tmp) + break; + tok = tmp->previous; + } + try { + if (ifCond) { + std::string E; + for (const simplecpp::Token *tok = expr.cfront(); tok; tok = tok->next) + E += (E.empty() ? "" : " ") + tok->str(); + const long long result = evaluate(expr, dui, sizeOfType); + conditionIsTrue = (result != 0); + ifCond->push_back(IfCond(rawtok->location, E, result)); + } else { + const long long result = evaluate(expr, dui, sizeOfType); + conditionIsTrue = (result != 0); + } + } catch (const std::exception &e) { + if (outputList) { + Output out(rawtok->location.files); + out.type = Output::SYNTAX_ERROR; + out.location = rawtok->location; + out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; + if (e.what() && *e.what()) + out.msg += std::string(", ") + e.what(); + outputList->push_back(out); + } + output.clear(); + return; + } + } + + if (rawtok->str() != ELIF) { + // push a new ifstate.. + if (ifstates.top() != True) + ifstates.push(AlwaysFalse); + else + ifstates.push(conditionIsTrue ? True : ElseIsTrue); + } else if (ifstates.top() == True) { + ifstates.top() = AlwaysFalse; + } else if (ifstates.top() == ElseIsTrue && conditionIsTrue) { + ifstates.top() = True; + } + } else if (rawtok->str() == ELSE) { + ifstates.top() = (ifstates.top() == ElseIsTrue) ? True : AlwaysFalse; + } else if (rawtok->str() == ENDIF) { + ifstates.pop(); + } else if (rawtok->str() == UNDEF) { + if (ifstates.top() == True) { + const Token *tok = rawtok->next; + while (sameline(rawtok,tok) && tok->comment) + tok = tok->next; + if (sameline(rawtok, tok)) + macros.erase(tok->str()); + } + } else if (ifstates.top() == True && rawtok->str() == PRAGMA && rawtok->next && rawtok->next->str() == ONCE && sameline(rawtok,rawtok->next)) { + pragmaOnce.insert(rawtok->location.file()); + } + rawtok = gotoNextLine(rawtok); + continue; + } + + if (ifstates.top() != True) { + // drop code + rawtok = gotoNextLine(rawtok); + continue; + } + + bool hash=false, hashhash=false; + if (rawtok->op == '#' && sameline(rawtok,rawtok->next)) { + if (rawtok->next->op != '#') { + hash = true; + rawtok = rawtok->next; // skip '#' + } else if (sameline(rawtok,rawtok->next->next)) { + hashhash = true; + rawtok = rawtok->next->next; // skip '#' '#' + } + } + + const Location loc(rawtok->location); + TokenList tokens(files); + + if (!preprocessToken(tokens, &rawtok, macros, files, outputList)) { + output.clear(); + return; + } + + if (hash || hashhash) { + std::string s; + for (const Token *hashtok = tokens.cfront(); hashtok; hashtok = hashtok->next) + s += hashtok->str(); + if (hash) + output.push_back(new Token('\"' + s + '\"', loc)); + else if (output.back()) + output.back()->setstr(output.cback()->str() + s); + else + output.push_back(new Token(s, loc)); + } else { + output.takeTokens(tokens); + } + } + + if (macroUsage) { + for (simplecpp::MacroMap::const_iterator macroIt = macros.begin(); macroIt != macros.end(); ++macroIt) { + const Macro ¯o = macroIt->second; + std::list usage = macro.usage(); + const std::list& temp = maybeUsedMacros[macro.name()]; + usage.insert(usage.end(), temp.begin(), temp.end()); + for (std::list::const_iterator usageIt = usage.begin(); usageIt != usage.end(); ++usageIt) { + MacroUsage mu(usageIt->files, macro.valueDefinedInCode()); + mu.macroName = macro.name(); + mu.macroLocation = macro.defineLocation(); + mu.useLocation = *usageIt; + macroUsage->push_back(mu); + } + } + } +} + +void simplecpp::cleanup(std::map &filedata) +{ + for (std::map::iterator it = filedata.begin(); it != filedata.end(); ++it) + delete it->second; + filedata.clear(); +} + +simplecpp::cstd_t simplecpp::getCStd(const std::string &std) +{ + if (std == "c90" || std == "c89" || std == "iso9899:1990" || std == "iso9899:199409" || std == "gnu90" || std == "gnu89") + return C89; + if (std == "c99" || std == "c9x" || std == "iso9899:1999" || std == "iso9899:199x" || std == "gnu99"|| std == "gnu9x") + return C99; + if (std == "c11" || std == "c1x" || std == "iso9899:2011" || std == "gnu11" || std == "gnu1x") + return C11; + if (std == "c17" || std == "c18" || std == "iso9899:2017" || std == "iso9899:2018" || std == "gnu17"|| std == "gnu18") + return C17; + if (std == "c23" || std == "gnu23" || std == "c2x" || std == "gnu2x") + return C23; + return CUnknown; +} + +std::string simplecpp::getCStdString(cstd_t std) +{ + switch (std) { + case C89: + // __STDC_VERSION__ is not set for C90 although the macro was added in the 1994 amendments + return ""; + case C99: + return "199901L"; + case C11: + return "201112L"; + case C17: + return "201710L"; + case C23: + // supported by GCC 9+ and Clang 9+ + // Clang 9, 10, 11, 12, 13 return "201710L" + // Clang 14, 15, 16, 17 return "202000L" + // Clang 9, 10, 11, 12, 13, 14, 15, 16, 17 do not support "c23" and "gnu23" + return "202311L"; + case CUnknown: + return ""; + } + return ""; +} + +std::string simplecpp::getCStdString(const std::string &std) +{ + return getCStdString(getCStd(std)); +} + +simplecpp::cppstd_t simplecpp::getCppStd(const std::string &std) +{ + if (std == "c++98" || std == "c++03" || std == "gnu++98" || std == "gnu++03") + return CPP03; + if (std == "c++11" || std == "gnu++11" || std == "c++0x" || std == "gnu++0x") + return CPP11; + if (std == "c++14" || std == "c++1y" || std == "gnu++14" || std == "gnu++1y") + return CPP14; + if (std == "c++17" || std == "c++1z" || std == "gnu++17" || std == "gnu++1z") + return CPP17; + if (std == "c++20" || std == "c++2a" || std == "gnu++20" || std == "gnu++2a") + return CPP20; + if (std == "c++23" || std == "c++2b" || std == "gnu++23" || std == "gnu++2b") + return CPP23; + if (std == "c++26" || std == "c++2c" || std == "gnu++26" || std == "gnu++2c") + return CPP26; + return CPPUnknown; +} + +std::string simplecpp::getCppStdString(cppstd_t std) +{ + switch (std) { + case CPP03: + return "199711L"; + case CPP11: + return "201103L"; + case CPP14: + return "201402L"; + case CPP17: + return "201703L"; + case CPP20: + // GCC 10 returns "201703L" - correct in 11+ + return "202002L"; + case CPP23: + // supported by GCC 11+ and Clang 12+ + // GCC 11, 12, 13 return "202100L" + // Clang 12, 13, 14, 15, 16 do not support "c++23" and "gnu++23" and return "202101L" + // Clang 17, 18 return "202302L" + return "202302L"; + case CPP26: + // supported by Clang 17+ + return "202400L"; + case CPPUnknown: + return ""; + } + return ""; +} + +std::string simplecpp::getCppStdString(const std::string &std) +{ + return getCppStdString(getCppStd(std)); +} + +#if (__cplusplus < 201103L) && !defined(__APPLE__) +#undef nullptr +#endif diff --git a/generator/simplecpp/simplecpp.h b/generator/simplecpp/simplecpp.h new file mode 100644 index 000000000..f5c69593c --- /dev/null +++ b/generator/simplecpp/simplecpp.h @@ -0,0 +1,402 @@ +/* -*- C++ -*- + * simplecpp - A simple and high-fidelity C/C++ preprocessor library + * Copyright (C) 2016-2023 simplecpp team + */ + +#ifndef simplecppH +#define simplecppH + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# ifdef SIMPLECPP_EXPORT +# define SIMPLECPP_LIB __declspec(dllexport) +# elif defined(SIMPLECPP_IMPORT) +# define SIMPLECPP_LIB __declspec(dllimport) +# else +# define SIMPLECPP_LIB +# endif +#else +# define SIMPLECPP_LIB +#endif + +#if (__cplusplus < 201103L) && !defined(__APPLE__) +#define nullptr NULL +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +// suppress warnings about "conversion from 'type1' to 'type2', possible loss of data" +# pragma warning(disable : 4267) +# pragma warning(disable : 4244) +#endif + +namespace simplecpp { + /** C code standard */ + enum cstd_t { CUnknown=-1, C89, C99, C11, C17, C23 }; + + /** C++ code standard */ + enum cppstd_t { CPPUnknown=-1, CPP03, CPP11, CPP14, CPP17, CPP20, CPP23, CPP26 }; + + typedef std::string TokenString; + class Macro; + + /** + * Location in source code + */ + class SIMPLECPP_LIB Location { + public: + explicit Location(const std::vector &f) : files(f), fileIndex(0), line(1U), col(0U) {} + + Location(const Location &loc) : files(loc.files), fileIndex(loc.fileIndex), line(loc.line), col(loc.col) {} + + Location &operator=(const Location &other) { + if (this != &other) { + fileIndex = other.fileIndex; + line = other.line; + col = other.col; + } + return *this; + } + + /** increment this location by string */ + void adjust(const std::string &str); + + bool operator<(const Location &rhs) const { + if (fileIndex != rhs.fileIndex) + return fileIndex < rhs.fileIndex; + if (line != rhs.line) + return line < rhs.line; + return col < rhs.col; + } + + bool sameline(const Location &other) const { + return fileIndex == other.fileIndex && line == other.line; + } + + const std::string& file() const { + return fileIndex < files.size() ? files[fileIndex] : emptyFileName; + } + + const std::vector &files; + unsigned int fileIndex; + unsigned int line; + unsigned int col; + private: + static const std::string emptyFileName; + }; + + /** + * token class. + * @todo don't use std::string representation - for both memory and performance reasons + */ + class SIMPLECPP_LIB Token { + public: + Token(const TokenString &s, const Location &loc, bool wsahead = false) : + whitespaceahead(wsahead), location(loc), previous(nullptr), next(nullptr), string(s) { + flags(); + } + + Token(const Token &tok) : + macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name), number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location), previous(nullptr), next(nullptr), string(tok.string), mExpandedFrom(tok.mExpandedFrom) { + } + + void flags() { + name = (std::isalpha(static_cast(string[0])) || string[0] == '_' || string[0] == '$') + && (std::memchr(string.c_str(), '\'', string.size()) == nullptr); + comment = string.size() > 1U && string[0] == '/' && (string[1] == '/' || string[1] == '*'); + number = isNumberLike(string); + op = (string.size() == 1U && !name && !comment && !number) ? string[0] : '\0'; + } + + const TokenString& str() const { + return string; + } + void setstr(const std::string &s) { + string = s; + flags(); + } + + bool isOneOf(const char ops[]) const; + bool startsWithOneOf(const char c[]) const; + bool endsWithOneOf(const char c[]) const; + static bool isNumberLike(const std::string& str) { + return std::isdigit(static_cast(str[0])) || + (str.size() > 1U && (str[0] == '-' || str[0] == '+') && std::isdigit(static_cast(str[1]))); + } + + TokenString macro; + char op; + bool comment; + bool name; + bool number; + bool whitespaceahead; + Location location; + Token *previous; + Token *next; + + const Token *previousSkipComments() const { + const Token *tok = this->previous; + while (tok && tok->comment) + tok = tok->previous; + return tok; + } + + const Token *nextSkipComments() const { + const Token *tok = this->next; + while (tok && tok->comment) + tok = tok->next; + return tok; + } + + void setExpandedFrom(const Token *tok, const Macro* m) { + mExpandedFrom = tok->mExpandedFrom; + mExpandedFrom.insert(m); + if (tok->whitespaceahead) + whitespaceahead = true; + } + bool isExpandedFrom(const Macro* m) const { + return mExpandedFrom.find(m) != mExpandedFrom.end(); + } + + void printAll() const; + void printOut() const; + private: + TokenString string; + + std::set mExpandedFrom; + + // Not implemented - prevent assignment + Token &operator=(const Token &tok); + }; + + /** Output from preprocessor */ + struct SIMPLECPP_LIB Output { + explicit Output(const std::vector &files) : type(ERROR), location(files) {} + enum Type { + ERROR, /* #error */ + WARNING, /* #warning */ + MISSING_HEADER, + INCLUDE_NESTED_TOO_DEEPLY, + SYNTAX_ERROR, + PORTABILITY_BACKSLASH, + UNHANDLED_CHAR_ERROR, + EXPLICIT_INCLUDE_NOT_FOUND, + FILE_NOT_FOUND, + DUI_ERROR + } type; + explicit Output(const std::vector& files, Type type, const std::string& msg) : type(type), location(files), msg(msg) {} + Location location; + std::string msg; + }; + + typedef std::list OutputList; + + /** List of tokens. */ + class SIMPLECPP_LIB TokenList { + public: + class Stream; + + explicit TokenList(std::vector &filenames); + /** generates a token list from the given std::istream parameter */ + TokenList(std::istream &istr, std::vector &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr); + /** generates a token list from the given buffer */ + TokenList(const unsigned char* data, std::size_t size, std::vector &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr); + /** generates a token list from the given buffer */ + TokenList(const char* data, std::size_t size, std::vector &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr); + /** generates a token list from the given filename parameter */ + TokenList(const std::string &filename, std::vector &filenames, OutputList *outputList = nullptr); + TokenList(const TokenList &other); +#if __cplusplus >= 201103L + TokenList(TokenList &&other); +#endif + ~TokenList(); + TokenList &operator=(const TokenList &other); +#if __cplusplus >= 201103L + TokenList &operator=(TokenList &&other); +#endif + + void clear(); + bool empty() const { + return !frontToken; + } + void push_back(Token *tok); + + void dump() const; + std::string stringify() const; + + void readfile(Stream &stream, const std::string &filename=std::string(), OutputList *outputList = nullptr); + void constFold(); + + void removeComments(); + + Token *front() { + return frontToken; + } + + const Token *cfront() const { + return frontToken; + } + + Token *back() { + return backToken; + } + + const Token *cback() const { + return backToken; + } + + void deleteToken(Token *tok) { + if (!tok) + return; + Token * const prev = tok->previous; + Token * const next = tok->next; + if (prev) + prev->next = next; + if (next) + next->previous = prev; + if (frontToken == tok) + frontToken = next; + if (backToken == tok) + backToken = prev; + delete tok; + } + + void takeTokens(TokenList &other) { + if (!other.frontToken) + return; + if (!frontToken) { + frontToken = other.frontToken; + } else { + backToken->next = other.frontToken; + other.frontToken->previous = backToken; + } + backToken = other.backToken; + other.frontToken = other.backToken = nullptr; + } + + /** sizeof(T) */ + std::map sizeOfType; + + const std::vector& getFiles() const { + return files; + } + + private: + void combineOperators(); + + void constFoldUnaryNotPosNeg(Token *tok); + void constFoldMulDivRem(Token *tok); + void constFoldAddSub(Token *tok); + void constFoldShift(Token *tok); + void constFoldComparison(Token *tok); + void constFoldBitwise(Token *tok); + void constFoldLogicalOp(Token *tok); + void constFoldQuestionOp(Token **tok1); + + std::string readUntil(Stream &stream, const Location &location, char start, char end, OutputList *outputList); + void lineDirective(unsigned int fileIndex, unsigned int line, Location *location); + + std::string lastLine(int maxsize=1000) const; + const Token* lastLineTok(int maxsize=1000) const; + bool isLastLinePreprocessor(int maxsize=1000) const; + + unsigned int fileIndex(const std::string &filename); + + Token *frontToken; + Token *backToken; + std::vector &files; + }; + + /** Tracking how macros are used */ + struct SIMPLECPP_LIB MacroUsage { + explicit MacroUsage(const std::vector &f, bool macroValueKnown_) : macroLocation(f), useLocation(f), macroValueKnown(macroValueKnown_) {} + std::string macroName; + Location macroLocation; + Location useLocation; + bool macroValueKnown; + }; + + /** Tracking #if/#elif expressions */ + struct SIMPLECPP_LIB IfCond { + explicit IfCond(const Location& location, const std::string &E, long long result) : location(location), E(E), result(result) {} + Location location; // location of #if/#elif + std::string E; // preprocessed condition + long long result; // condition result + }; + + /** + * Command line preprocessor settings. + * On the command line these are configured by -D, -U, -I, --include, -std + */ + struct SIMPLECPP_LIB DUI { + DUI() : clearIncludeCache(false), removeComments(false) {} + std::list defines; + std::set undefined; + std::list includePaths; + std::list includes; + std::string std; + bool clearIncludeCache; + bool removeComments; /** remove comment tokens from included files */ + }; + + SIMPLECPP_LIB long long characterLiteralToLL(const std::string& str); + + SIMPLECPP_LIB std::map load(const TokenList &rawtokens, std::vector &filenames, const DUI &dui, OutputList *outputList = nullptr); + + /** + * Preprocess + * @todo simplify interface + * @param output TokenList that receives the preprocessing output + * @param rawtokens Raw tokenlist for top sourcefile + * @param files internal data of simplecpp + * @param filedata output from simplecpp::load() + * @param dui defines, undefs, and include paths + * @param outputList output: list that will receive output messages + * @param macroUsage output: macro usage + * @param ifCond output: #if/#elif expressions + */ + SIMPLECPP_LIB void preprocess(TokenList &output, const TokenList &rawtokens, std::vector &files, std::map &filedata, const DUI &dui, OutputList *outputList = nullptr, std::list *macroUsage = nullptr, std::list *ifCond = nullptr); + + /** + * Deallocate data + */ + SIMPLECPP_LIB void cleanup(std::map &filedata); + + /** Simplify path */ + SIMPLECPP_LIB std::string simplifyPath(std::string path); + + /** Convert Cygwin path to Windows path */ + SIMPLECPP_LIB std::string convertCygwinToWindowsPath(const std::string &cygwinPath); + + /** Returns the C version a given standard */ + SIMPLECPP_LIB cstd_t getCStd(const std::string &std); + + /** Returns the C++ version a given standard */ + SIMPLECPP_LIB cppstd_t getCppStd(const std::string &std); + + /** Returns the __STDC_VERSION__ value for a given standard */ + SIMPLECPP_LIB std::string getCStdString(const std::string &std); + SIMPLECPP_LIB std::string getCStdString(cstd_t std); + + /** Returns the __cplusplus value for a given standard */ + SIMPLECPP_LIB std::string getCppStdString(const std::string &std); + SIMPLECPP_LIB std::string getCppStdString(cppstd_t std); +} + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + +#if (__cplusplus < 201103L) && !defined(__APPLE__) +#undef nullptr +#endif + +#endif diff --git a/generator/simplecpp/simplecpp.pri b/generator/simplecpp/simplecpp.pri new file mode 100644 index 000000000..9c9689b93 --- /dev/null +++ b/generator/simplecpp/simplecpp.pri @@ -0,0 +1,9 @@ +isEmpty(SIMPLECPPPATH):SIMPLECPPPATH = $$PWD + +SOURCES += \ + $$SIMPLECPPPATH/simplecpp.cpp + +HEADERS += \ + $$SIMPLECPPPATH/simplecpp.h + +INCLUDEPATH += $$PWD $$SIMPLECPPPATH diff --git a/generator/typesystem_core.xml b/generator/typesystem_core.xml index d94d7819d..4a5c2de04 100644 --- a/generator/typesystem_core.xml +++ b/generator/typesystem_core.xml @@ -164,6 +164,22 @@ + + + + + + + + + + + + + + + + @@ -272,6 +288,7 @@ + @@ -497,6 +514,7 @@ + @@ -667,9 +685,14 @@ + + + + + @@ -2259,7 +2282,6 @@ public: - @@ -2306,4 +2328,6 @@ public: + + diff --git a/generator/typesystem_gui.xml b/generator/typesystem_gui.xml index e77d69816..fcd29cf14 100644 --- a/generator/typesystem_gui.xml +++ b/generator/typesystem_gui.xml @@ -21,6 +21,8 @@ + + @@ -3100,6 +3102,7 @@ PyObject* constScanLine(QImage* image, int line) { + @@ -3178,6 +3181,7 @@ PyObject* constScanLine(QImage* image, int line) { + diff --git a/generator/typesystem_multimedia.xml b/generator/typesystem_multimedia.xml index 161754f46..b2c326978 100644 --- a/generator/typesystem_multimedia.xml +++ b/generator/typesystem_multimedia.xml @@ -90,7 +90,9 @@ - + + + @@ -113,6 +115,7 @@ +